Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[linux-2.6] / drivers / media / video / pvrusb2 / pvrusb2-debugifc.c
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 = NULL;
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 static 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   */