diff options
Diffstat (limited to 'drivers/media/video/pvrusb2/pvrusb2-debugifc.c')
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-debugifc.c | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c new file mode 100644 index 000000000000..586900e365ff --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c | |||
@@ -0,0 +1,478 @@ | |||
1 | /* | ||
2 | * | ||
3 | * $Id$ | ||
4 | * | ||
5 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/string.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include "pvrusb2-debugifc.h" | ||
25 | #include "pvrusb2-hdw.h" | ||
26 | #include "pvrusb2-debug.h" | ||
27 | #include "pvrusb2-i2c-core.h" | ||
28 | |||
29 | struct debugifc_mask_item { | ||
30 | const char *name; | ||
31 | unsigned long msk; | ||
32 | }; | ||
33 | |||
34 | static struct debugifc_mask_item mask_items[] = { | ||
35 | {"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)}, | ||
36 | {"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)}, | ||
37 | {"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)}, | ||
38 | {"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)}, | ||
39 | {"ENC_RUN",(1<<PVR2_SUBSYS_B_ENC_RUN)}, | ||
40 | }; | ||
41 | |||
42 | |||
43 | static unsigned int debugifc_count_whitespace(const char *buf, | ||
44 | unsigned int count) | ||
45 | { | ||
46 | unsigned int scnt; | ||
47 | char ch; | ||
48 | |||
49 | for (scnt = 0; scnt < count; scnt++) { | ||
50 | ch = buf[scnt]; | ||
51 | if (ch == ' ') continue; | ||
52 | if (ch == '\t') continue; | ||
53 | if (ch == '\n') continue; | ||
54 | break; | ||
55 | } | ||
56 | return scnt; | ||
57 | } | ||
58 | |||
59 | |||
60 | static unsigned int debugifc_count_nonwhitespace(const char *buf, | ||
61 | unsigned int count) | ||
62 | { | ||
63 | unsigned int scnt; | ||
64 | char ch; | ||
65 | |||
66 | for (scnt = 0; scnt < count; scnt++) { | ||
67 | ch = buf[scnt]; | ||
68 | if (ch == ' ') break; | ||
69 | if (ch == '\t') break; | ||
70 | if (ch == '\n') break; | ||
71 | } | ||
72 | return scnt; | ||
73 | } | ||
74 | |||
75 | |||
76 | static unsigned int debugifc_isolate_word(const char *buf,unsigned int count, | ||
77 | const char **wstrPtr, | ||
78 | unsigned int *wlenPtr) | ||
79 | { | ||
80 | const char *wptr; | ||
81 | unsigned int consume_cnt = 0; | ||
82 | unsigned int wlen; | ||
83 | unsigned int scnt; | ||
84 | |||
85 | wptr = 0; | ||
86 | wlen = 0; | ||
87 | scnt = debugifc_count_whitespace(buf,count); | ||
88 | consume_cnt += scnt; count -= scnt; buf += scnt; | ||
89 | if (!count) goto done; | ||
90 | |||
91 | scnt = debugifc_count_nonwhitespace(buf,count); | ||
92 | if (!scnt) goto done; | ||
93 | wptr = buf; | ||
94 | wlen = scnt; | ||
95 | consume_cnt += scnt; count -= scnt; buf += scnt; | ||
96 | |||
97 | done: | ||
98 | *wstrPtr = wptr; | ||
99 | *wlenPtr = wlen; | ||
100 | return consume_cnt; | ||
101 | } | ||
102 | |||
103 | |||
104 | static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, | ||
105 | u32 *num_ptr) | ||
106 | { | ||
107 | u32 result = 0; | ||
108 | u32 val; | ||
109 | int ch; | ||
110 | int radix = 10; | ||
111 | if ((count >= 2) && (buf[0] == '0') && | ||
112 | ((buf[1] == 'x') || (buf[1] == 'X'))) { | ||
113 | radix = 16; | ||
114 | count -= 2; | ||
115 | buf += 2; | ||
116 | } else if ((count >= 1) && (buf[0] == '0')) { | ||
117 | radix = 8; | ||
118 | } | ||
119 | |||
120 | while (count--) { | ||
121 | ch = *buf++; | ||
122 | if ((ch >= '0') && (ch <= '9')) { | ||
123 | val = ch - '0'; | ||
124 | } else if ((ch >= 'a') && (ch <= 'f')) { | ||
125 | val = ch - 'a' + 10; | ||
126 | } else if ((ch >= 'A') && (ch <= 'F')) { | ||
127 | val = ch - 'A' + 10; | ||
128 | } else { | ||
129 | return -EINVAL; | ||
130 | } | ||
131 | if (val >= radix) return -EINVAL; | ||
132 | result *= radix; | ||
133 | result += val; | ||
134 | } | ||
135 | *num_ptr = result; | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | |||
140 | static int debugifc_match_keyword(const char *buf,unsigned int count, | ||
141 | const char *keyword) | ||
142 | { | ||
143 | unsigned int kl; | ||
144 | if (!keyword) return 0; | ||
145 | kl = strlen(keyword); | ||
146 | if (kl != count) return 0; | ||
147 | return !memcmp(buf,keyword,kl); | ||
148 | } | ||
149 | |||
150 | |||
151 | static unsigned long debugifc_find_mask(const char *buf,unsigned int count) | ||
152 | { | ||
153 | struct debugifc_mask_item *mip; | ||
154 | unsigned int idx; | ||
155 | for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { | ||
156 | mip = mask_items + idx; | ||
157 | if (debugifc_match_keyword(buf,count,mip->name)) { | ||
158 | return mip->msk; | ||
159 | } | ||
160 | } | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | |||
165 | static int debugifc_print_mask(char *buf,unsigned int sz, | ||
166 | unsigned long msk,unsigned long val) | ||
167 | { | ||
168 | struct debugifc_mask_item *mip; | ||
169 | unsigned int idx; | ||
170 | int bcnt = 0; | ||
171 | int ccnt; | ||
172 | for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { | ||
173 | mip = mask_items + idx; | ||
174 | if (!(mip->msk & msk)) continue; | ||
175 | ccnt = scnprintf(buf,sz,"%s%c%s", | ||
176 | (bcnt ? " " : ""), | ||
177 | ((mip->msk & val) ? '+' : '-'), | ||
178 | mip->name); | ||
179 | sz -= ccnt; | ||
180 | buf += ccnt; | ||
181 | bcnt += ccnt; | ||
182 | } | ||
183 | return bcnt; | ||
184 | } | ||
185 | |||
186 | static unsigned int debugifc_parse_subsys_mask(const char *buf, | ||
187 | unsigned int count, | ||
188 | unsigned long *mskPtr, | ||
189 | unsigned long *valPtr) | ||
190 | { | ||
191 | const char *wptr; | ||
192 | unsigned int consume_cnt = 0; | ||
193 | unsigned int scnt; | ||
194 | unsigned int wlen; | ||
195 | int mode; | ||
196 | unsigned long m1,msk,val; | ||
197 | |||
198 | msk = 0; | ||
199 | val = 0; | ||
200 | |||
201 | while (count) { | ||
202 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
203 | if (!scnt) break; | ||
204 | consume_cnt += scnt; count -= scnt; buf += scnt; | ||
205 | if (!wptr) break; | ||
206 | |||
207 | mode = 0; | ||
208 | if (wlen) switch (wptr[0]) { | ||
209 | case '+': | ||
210 | wptr++; | ||
211 | wlen--; | ||
212 | break; | ||
213 | case '-': | ||
214 | mode = 1; | ||
215 | wptr++; | ||
216 | wlen--; | ||
217 | break; | ||
218 | } | ||
219 | if (!wlen) continue; | ||
220 | m1 = debugifc_find_mask(wptr,wlen); | ||
221 | if (!m1) break; | ||
222 | msk |= m1; | ||
223 | if (!mode) val |= m1; | ||
224 | } | ||
225 | *mskPtr = msk; | ||
226 | *valPtr = val; | ||
227 | return consume_cnt; | ||
228 | } | ||
229 | |||
230 | |||
231 | int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) | ||
232 | { | ||
233 | int bcnt = 0; | ||
234 | int ccnt; | ||
235 | struct pvr2_hdw_debug_info dbg; | ||
236 | |||
237 | pvr2_hdw_get_debug_info(hdw,&dbg); | ||
238 | |||
239 | ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s", | ||
240 | (dbg.big_lock_held ? "held" : "free"), | ||
241 | (dbg.ctl_lock_held ? "held" : "free")); | ||
242 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
243 | if (dbg.ctl_lock_held) { | ||
244 | ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d" | ||
245 | " cmd_wlen=%d cmd_rlen=%d" | ||
246 | " wpend=%d rpend=%d tmout=%d rstatus=%d" | ||
247 | " wstatus=%d", | ||
248 | dbg.cmd_debug_state,dbg.cmd_code, | ||
249 | dbg.cmd_debug_write_len, | ||
250 | dbg.cmd_debug_read_len, | ||
251 | dbg.cmd_debug_write_pend, | ||
252 | dbg.cmd_debug_read_pend, | ||
253 | dbg.cmd_debug_timeout, | ||
254 | dbg.cmd_debug_rstatus, | ||
255 | dbg.cmd_debug_wstatus); | ||
256 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
257 | } | ||
258 | ccnt = scnprintf(buf,acnt,"\n"); | ||
259 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
260 | ccnt = scnprintf( | ||
261 | buf,acnt,"driver flags: %s %s %s\n", | ||
262 | (dbg.flag_init_ok ? "initialized" : "uninitialized"), | ||
263 | (dbg.flag_ok ? "ok" : "fail"), | ||
264 | (dbg.flag_disconnected ? "disconnected" : "connected")); | ||
265 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
266 | ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); | ||
267 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
268 | ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags); | ||
269 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
270 | ccnt = scnprintf(buf,acnt,"\n"); | ||
271 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
272 | ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); | ||
273 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
274 | ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags); | ||
275 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
276 | ccnt = scnprintf(buf,acnt,"\n"); | ||
277 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
278 | |||
279 | ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n"); | ||
280 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
281 | ccnt = pvr2_i2c_report(hdw,buf,acnt); | ||
282 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
283 | |||
284 | return bcnt; | ||
285 | } | ||
286 | |||
287 | |||
288 | int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, | ||
289 | char *buf,unsigned int acnt) | ||
290 | { | ||
291 | int bcnt = 0; | ||
292 | int ccnt; | ||
293 | unsigned long msk; | ||
294 | int ret; | ||
295 | u32 gpio_dir,gpio_in,gpio_out; | ||
296 | |||
297 | ret = pvr2_hdw_is_hsm(hdw); | ||
298 | ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", | ||
299 | (ret < 0 ? "FAIL" : (ret ? "high" : "full"))); | ||
300 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
301 | |||
302 | gpio_dir = 0; gpio_in = 0; gpio_out = 0; | ||
303 | pvr2_hdw_gpio_get_dir(hdw,&gpio_dir); | ||
304 | pvr2_hdw_gpio_get_out(hdw,&gpio_out); | ||
305 | pvr2_hdw_gpio_get_in(hdw,&gpio_in); | ||
306 | ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n", | ||
307 | gpio_dir,gpio_in,gpio_out); | ||
308 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
309 | |||
310 | ccnt = scnprintf(buf,acnt,"Streaming is %s\n", | ||
311 | pvr2_hdw_get_streaming(hdw) ? "on" : "off"); | ||
312 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
313 | |||
314 | msk = pvr2_hdw_subsys_get(hdw); | ||
315 | ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); | ||
316 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
317 | ccnt = debugifc_print_mask(buf,acnt,msk,msk); | ||
318 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
319 | ccnt = scnprintf(buf,acnt,"\n"); | ||
320 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
321 | ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); | ||
322 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
323 | ccnt = debugifc_print_mask(buf,acnt,~msk,msk); | ||
324 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
325 | ccnt = scnprintf(buf,acnt,"\n"); | ||
326 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
327 | |||
328 | msk = pvr2_hdw_subsys_stream_get(hdw); | ||
329 | ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: "); | ||
330 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
331 | ccnt = debugifc_print_mask(buf,acnt,msk,msk); | ||
332 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
333 | ccnt = scnprintf(buf,acnt,"\n"); | ||
334 | bcnt += ccnt; acnt -= ccnt; buf += ccnt; | ||
335 | |||
336 | return bcnt; | ||
337 | } | ||
338 | |||
339 | |||
340 | int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, | ||
341 | unsigned int count) | ||
342 | { | ||
343 | const char *wptr; | ||
344 | unsigned int wlen; | ||
345 | unsigned int scnt; | ||
346 | |||
347 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
348 | if (!scnt) return 0; | ||
349 | count -= scnt; buf += scnt; | ||
350 | if (!wptr) return 0; | ||
351 | |||
352 | pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr); | ||
353 | if (debugifc_match_keyword(wptr,wlen,"reset")) { | ||
354 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
355 | if (!scnt) return -EINVAL; | ||
356 | count -= scnt; buf += scnt; | ||
357 | if (!wptr) return -EINVAL; | ||
358 | if (debugifc_match_keyword(wptr,wlen,"cpu")) { | ||
359 | pvr2_hdw_cpureset_assert(hdw,!0); | ||
360 | pvr2_hdw_cpureset_assert(hdw,0); | ||
361 | return 0; | ||
362 | } else if (debugifc_match_keyword(wptr,wlen,"bus")) { | ||
363 | pvr2_hdw_device_reset(hdw); | ||
364 | } else if (debugifc_match_keyword(wptr,wlen,"soft")) { | ||
365 | return pvr2_hdw_cmd_powerup(hdw); | ||
366 | } else if (debugifc_match_keyword(wptr,wlen,"deep")) { | ||
367 | return pvr2_hdw_cmd_deep_reset(hdw); | ||
368 | } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { | ||
369 | return pvr2_upload_firmware2(hdw); | ||
370 | } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { | ||
371 | return pvr2_hdw_cmd_decoder_reset(hdw); | ||
372 | } | ||
373 | return -EINVAL; | ||
374 | } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) { | ||
375 | unsigned long msk = 0; | ||
376 | unsigned long val = 0; | ||
377 | if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { | ||
378 | pvr2_trace(PVR2_TRACE_DEBUGIFC, | ||
379 | "debugifc parse error on subsys mask"); | ||
380 | return -EINVAL; | ||
381 | } | ||
382 | pvr2_hdw_subsys_bit_chg(hdw,msk,val); | ||
383 | return 0; | ||
384 | } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) { | ||
385 | unsigned long msk = 0; | ||
386 | unsigned long val = 0; | ||
387 | if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { | ||
388 | pvr2_trace(PVR2_TRACE_DEBUGIFC, | ||
389 | "debugifc parse error on stream mask"); | ||
390 | return -EINVAL; | ||
391 | } | ||
392 | pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val); | ||
393 | return 0; | ||
394 | } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { | ||
395 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
396 | if (!scnt) return -EINVAL; | ||
397 | count -= scnt; buf += scnt; | ||
398 | if (!wptr) return -EINVAL; | ||
399 | if (debugifc_match_keyword(wptr,wlen,"fetch")) { | ||
400 | pvr2_hdw_cpufw_set_enabled(hdw,!0); | ||
401 | return 0; | ||
402 | } else if (debugifc_match_keyword(wptr,wlen,"done")) { | ||
403 | pvr2_hdw_cpufw_set_enabled(hdw,0); | ||
404 | return 0; | ||
405 | } else { | ||
406 | return -EINVAL; | ||
407 | } | ||
408 | } else if (debugifc_match_keyword(wptr,wlen,"gpio")) { | ||
409 | int dir_fl = 0; | ||
410 | int ret; | ||
411 | u32 msk,val; | ||
412 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
413 | if (!scnt) return -EINVAL; | ||
414 | count -= scnt; buf += scnt; | ||
415 | if (!wptr) return -EINVAL; | ||
416 | if (debugifc_match_keyword(wptr,wlen,"dir")) { | ||
417 | dir_fl = !0; | ||
418 | } else if (!debugifc_match_keyword(wptr,wlen,"out")) { | ||
419 | return -EINVAL; | ||
420 | } | ||
421 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
422 | if (!scnt) return -EINVAL; | ||
423 | count -= scnt; buf += scnt; | ||
424 | if (!wptr) return -EINVAL; | ||
425 | ret = debugifc_parse_unsigned_number(wptr,wlen,&msk); | ||
426 | if (ret) return ret; | ||
427 | scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); | ||
428 | if (wptr) { | ||
429 | ret = debugifc_parse_unsigned_number(wptr,wlen,&val); | ||
430 | if (ret) return ret; | ||
431 | } else { | ||
432 | val = msk; | ||
433 | msk = 0xffffffff; | ||
434 | } | ||
435 | if (dir_fl) { | ||
436 | ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val); | ||
437 | } else { | ||
438 | ret = pvr2_hdw_gpio_chg_out(hdw,msk,val); | ||
439 | } | ||
440 | return ret; | ||
441 | } | ||
442 | pvr2_trace(PVR2_TRACE_DEBUGIFC, | ||
443 | "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr); | ||
444 | return -EINVAL; | ||
445 | } | ||
446 | |||
447 | |||
448 | int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, | ||
449 | unsigned int count) | ||
450 | { | ||
451 | unsigned int bcnt = 0; | ||
452 | int ret; | ||
453 | |||
454 | while (count) { | ||
455 | for (bcnt = 0; bcnt < count; bcnt++) { | ||
456 | if (buf[bcnt] == '\n') break; | ||
457 | } | ||
458 | |||
459 | ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt); | ||
460 | if (ret < 0) return ret; | ||
461 | if (bcnt < count) bcnt++; | ||
462 | buf += bcnt; | ||
463 | count -= bcnt; | ||
464 | } | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | |||
470 | /* | ||
471 | Stuff for Emacs to see, in order to encourage consistent editing style: | ||
472 | *** Local Variables: *** | ||
473 | *** mode: c *** | ||
474 | *** fill-column: 75 *** | ||
475 | *** tab-width: 8 *** | ||
476 | *** c-basic-offset: 8 *** | ||
477 | *** End: *** | ||
478 | */ | ||