1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> |
5 | */ |
6 | |
7 | #include <linux/string.h> |
8 | #include "pvrusb2-debugifc.h" |
9 | #include "pvrusb2-hdw.h" |
10 | #include "pvrusb2-debug.h" |
11 | |
12 | struct debugifc_mask_item { |
13 | const char *name; |
14 | unsigned long msk; |
15 | }; |
16 | |
17 | |
18 | static unsigned int debugifc_count_whitespace(const char *buf, |
19 | unsigned int count) |
20 | { |
21 | unsigned int scnt; |
22 | char ch; |
23 | |
24 | for (scnt = 0; scnt < count; scnt++) { |
25 | ch = buf[scnt]; |
26 | if (ch == ' ') continue; |
27 | if (ch == '\t') continue; |
28 | if (ch == '\n') continue; |
29 | break; |
30 | } |
31 | return scnt; |
32 | } |
33 | |
34 | |
35 | static unsigned int debugifc_count_nonwhitespace(const char *buf, |
36 | unsigned int count) |
37 | { |
38 | unsigned int scnt; |
39 | char ch; |
40 | |
41 | for (scnt = 0; scnt < count; scnt++) { |
42 | ch = buf[scnt]; |
43 | if (ch == ' ') break; |
44 | if (ch == '\t') break; |
45 | if (ch == '\n') break; |
46 | } |
47 | return scnt; |
48 | } |
49 | |
50 | |
51 | static unsigned int debugifc_isolate_word(const char *buf,unsigned int count, |
52 | const char **wstrPtr, |
53 | unsigned int *wlenPtr) |
54 | { |
55 | const char *wptr; |
56 | unsigned int consume_cnt = 0; |
57 | unsigned int wlen; |
58 | unsigned int scnt; |
59 | |
60 | wptr = NULL; |
61 | wlen = 0; |
62 | scnt = debugifc_count_whitespace(buf,count); |
63 | consume_cnt += scnt; count -= scnt; buf += scnt; |
64 | if (!count) goto done; |
65 | |
66 | scnt = debugifc_count_nonwhitespace(buf,count); |
67 | if (!scnt) goto done; |
68 | wptr = buf; |
69 | wlen = scnt; |
70 | consume_cnt += scnt; count -= scnt; buf += scnt; |
71 | |
72 | done: |
73 | *wstrPtr = wptr; |
74 | *wlenPtr = wlen; |
75 | return consume_cnt; |
76 | } |
77 | |
78 | |
79 | static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, |
80 | u32 *num_ptr) |
81 | { |
82 | u32 result = 0; |
83 | int radix = 10; |
84 | if ((count >= 2) && (buf[0] == '0') && |
85 | ((buf[1] == 'x') || (buf[1] == 'X'))) { |
86 | radix = 16; |
87 | count -= 2; |
88 | buf += 2; |
89 | } else if ((count >= 1) && (buf[0] == '0')) { |
90 | radix = 8; |
91 | } |
92 | |
93 | while (count--) { |
94 | int val = hex_to_bin(ch: *buf++); |
95 | if (val < 0 || val >= radix) |
96 | return -EINVAL; |
97 | result *= radix; |
98 | result += val; |
99 | } |
100 | *num_ptr = result; |
101 | return 0; |
102 | } |
103 | |
104 | |
105 | static int debugifc_match_keyword(const char *buf,unsigned int count, |
106 | const char *keyword) |
107 | { |
108 | unsigned int kl; |
109 | if (!keyword) return 0; |
110 | kl = strlen(keyword); |
111 | if (kl != count) return 0; |
112 | return !memcmp(p: buf,q: keyword,size: kl); |
113 | } |
114 | |
115 | |
116 | int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) |
117 | { |
118 | int bcnt = 0; |
119 | int ccnt; |
120 | ccnt = scnprintf(buf, size: acnt, fmt: "Driver hardware description: %s\n" , |
121 | pvr2_hdw_get_desc(hdw)); |
122 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
123 | ccnt = scnprintf(buf,size: acnt,fmt: "Driver state info:\n" ); |
124 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
125 | ccnt = pvr2_hdw_state_report(hdw,buf_ptr: buf,buf_size: acnt); |
126 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
127 | |
128 | return bcnt; |
129 | } |
130 | |
131 | |
132 | int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, |
133 | char *buf,unsigned int acnt) |
134 | { |
135 | int bcnt = 0; |
136 | int ccnt; |
137 | int ret; |
138 | u32 gpio_dir,gpio_in,gpio_out; |
139 | struct pvr2_stream_stats stats; |
140 | struct pvr2_stream *sp; |
141 | |
142 | ret = pvr2_hdw_is_hsm(hdw); |
143 | ccnt = scnprintf(buf,size: acnt,fmt: "USB link speed: %s\n" , |
144 | (ret < 0 ? "FAIL" : (ret ? "high" : "full" ))); |
145 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
146 | |
147 | gpio_dir = 0; gpio_in = 0; gpio_out = 0; |
148 | pvr2_hdw_gpio_get_dir(hdw,&gpio_dir); |
149 | pvr2_hdw_gpio_get_out(hdw,&gpio_out); |
150 | pvr2_hdw_gpio_get_in(hdw,&gpio_in); |
151 | ccnt = scnprintf(buf,size: acnt,fmt: "GPIO state: dir=0x%x in=0x%x out=0x%x\n" , |
152 | gpio_dir,gpio_in,gpio_out); |
153 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
154 | |
155 | ccnt = scnprintf(buf,size: acnt,fmt: "Streaming is %s\n" , |
156 | pvr2_hdw_get_streaming(hdw) ? "on" : "off" ); |
157 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
158 | |
159 | |
160 | sp = pvr2_hdw_get_video_stream(hdw); |
161 | if (sp) { |
162 | pvr2_stream_get_stats(sp, &stats, zero_counts: 0); |
163 | ccnt = scnprintf( |
164 | buf,size: acnt, |
165 | fmt: "Bytes streamed=%u URBs: queued=%u idle=%u ready=%u processed=%u failed=%u\n" , |
166 | stats.bytes_processed, |
167 | stats.buffers_in_queue, |
168 | stats.buffers_in_idle, |
169 | stats.buffers_in_ready, |
170 | stats.buffers_processed, |
171 | stats.buffers_failed); |
172 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
173 | } |
174 | |
175 | return bcnt; |
176 | } |
177 | |
178 | |
179 | static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, |
180 | unsigned int count) |
181 | { |
182 | const char *wptr; |
183 | unsigned int wlen; |
184 | unsigned int scnt; |
185 | |
186 | scnt = debugifc_isolate_word(buf,count,wstrPtr: &wptr,wlenPtr: &wlen); |
187 | if (!scnt) return 0; |
188 | count -= scnt; buf += scnt; |
189 | if (!wptr) return 0; |
190 | |
191 | pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"" ,wlen,wptr); |
192 | if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "reset" )) { |
193 | scnt = debugifc_isolate_word(buf,count,wstrPtr: &wptr,wlenPtr: &wlen); |
194 | if (!scnt) return -EINVAL; |
195 | count -= scnt; buf += scnt; |
196 | if (!wptr) return -EINVAL; |
197 | if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "cpu" )) { |
198 | pvr2_hdw_cpureset_assert(hdw,!0); |
199 | pvr2_hdw_cpureset_assert(hdw,0); |
200 | return 0; |
201 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "bus" )) { |
202 | pvr2_hdw_device_reset(hdw); |
203 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "soft" )) { |
204 | return pvr2_hdw_cmd_powerup(hdw); |
205 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "deep" )) { |
206 | return pvr2_hdw_cmd_deep_reset(hdw); |
207 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "firmware" )) { |
208 | return pvr2_upload_firmware2(hdw); |
209 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "decoder" )) { |
210 | return pvr2_hdw_cmd_decoder_reset(hdw); |
211 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "worker" )) { |
212 | return pvr2_hdw_untrip(hdw); |
213 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "usbstats" )) { |
214 | pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), |
215 | NULL, zero_counts: !0); |
216 | return 0; |
217 | } |
218 | return -EINVAL; |
219 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "cpufw" )) { |
220 | scnt = debugifc_isolate_word(buf,count,wstrPtr: &wptr,wlenPtr: &wlen); |
221 | if (!scnt) return -EINVAL; |
222 | count -= scnt; buf += scnt; |
223 | if (!wptr) return -EINVAL; |
224 | if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "fetch" )) { |
225 | scnt = debugifc_isolate_word(buf,count,wstrPtr: &wptr,wlenPtr: &wlen); |
226 | if (scnt && wptr) { |
227 | count -= scnt; buf += scnt; |
228 | if (debugifc_match_keyword(buf: wptr, count: wlen, |
229 | keyword: "prom" )) { |
230 | pvr2_hdw_cpufw_set_enabled(hdw, mode: 2, enable_flag: !0); |
231 | } else if (debugifc_match_keyword(buf: wptr, count: wlen, |
232 | keyword: "ram8k" )) { |
233 | pvr2_hdw_cpufw_set_enabled(hdw, mode: 0, enable_flag: !0); |
234 | } else if (debugifc_match_keyword(buf: wptr, count: wlen, |
235 | keyword: "ram16k" )) { |
236 | pvr2_hdw_cpufw_set_enabled(hdw, mode: 1, enable_flag: !0); |
237 | } else { |
238 | return -EINVAL; |
239 | } |
240 | } |
241 | pvr2_hdw_cpufw_set_enabled(hdw,mode: 0,enable_flag: !0); |
242 | return 0; |
243 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "done" )) { |
244 | pvr2_hdw_cpufw_set_enabled(hdw,mode: 0,enable_flag: 0); |
245 | return 0; |
246 | } else { |
247 | return -EINVAL; |
248 | } |
249 | } else if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "gpio" )) { |
250 | int dir_fl = 0; |
251 | int ret; |
252 | u32 msk,val; |
253 | scnt = debugifc_isolate_word(buf,count,wstrPtr: &wptr,wlenPtr: &wlen); |
254 | if (!scnt) return -EINVAL; |
255 | count -= scnt; buf += scnt; |
256 | if (!wptr) return -EINVAL; |
257 | if (debugifc_match_keyword(buf: wptr,count: wlen,keyword: "dir" )) { |
258 | dir_fl = !0; |
259 | } else if (!debugifc_match_keyword(buf: wptr,count: wlen,keyword: "out" )) { |
260 | return -EINVAL; |
261 | } |
262 | scnt = debugifc_isolate_word(buf,count,wstrPtr: &wptr,wlenPtr: &wlen); |
263 | if (!scnt) return -EINVAL; |
264 | count -= scnt; buf += scnt; |
265 | if (!wptr) return -EINVAL; |
266 | ret = debugifc_parse_unsigned_number(buf: wptr,count: wlen,num_ptr: &msk); |
267 | if (ret) return ret; |
268 | scnt = debugifc_isolate_word(buf,count,wstrPtr: &wptr,wlenPtr: &wlen); |
269 | if (wptr) { |
270 | ret = debugifc_parse_unsigned_number(buf: wptr,count: wlen,num_ptr: &val); |
271 | if (ret) return ret; |
272 | } else { |
273 | val = msk; |
274 | msk = 0xffffffff; |
275 | } |
276 | if (dir_fl) { |
277 | ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val); |
278 | } else { |
279 | ret = pvr2_hdw_gpio_chg_out(hdw,msk,val); |
280 | } |
281 | return ret; |
282 | } |
283 | pvr2_trace(PVR2_TRACE_DEBUGIFC, |
284 | "debugifc failed to recognize cmd: \"%.*s\"" ,wlen,wptr); |
285 | return -EINVAL; |
286 | } |
287 | |
288 | |
289 | int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, |
290 | unsigned int count) |
291 | { |
292 | unsigned int bcnt = 0; |
293 | int ret; |
294 | |
295 | while (count) { |
296 | for (bcnt = 0; bcnt < count; bcnt++) { |
297 | if (buf[bcnt] == '\n') break; |
298 | } |
299 | |
300 | ret = pvr2_debugifc_do1cmd(hdw,buf,count: bcnt); |
301 | if (ret < 0) return ret; |
302 | if (bcnt < count) bcnt++; |
303 | buf += bcnt; |
304 | count -= bcnt; |
305 | } |
306 | |
307 | return 0; |
308 | } |
309 | |