| /* |
| * |
| * |
| * Copyright (C) 2005 Mike Isely <isely@pobox.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| |
| #include <linux/string.h> |
| #include "pvrusb2-debugifc.h" |
| #include "pvrusb2-hdw.h" |
| #include "pvrusb2-debug.h" |
| |
| struct debugifc_mask_item { |
| const char *name; |
| unsigned long msk; |
| }; |
| |
| |
| static unsigned int debugifc_count_whitespace(const char *buf, |
| unsigned int count) |
| { |
| unsigned int scnt; |
| char ch; |
| |
| for (scnt = 0; scnt < count; scnt++) { |
| ch = buf[scnt]; |
| if (ch == ' ') continue; |
| if (ch == '\t') continue; |
| if (ch == '\n') continue; |
| break; |
| } |
| return scnt; |
| } |
| |
| |
| static unsigned int debugifc_count_nonwhitespace(const char *buf, |
| unsigned int count) |
| { |
| unsigned int scnt; |
| char ch; |
| |
| for (scnt = 0; scnt < count; scnt++) { |
| ch = buf[scnt]; |
| if (ch == ' ') break; |
| if (ch == '\t') break; |
| if (ch == '\n') break; |
| } |
| return scnt; |
| } |
| |
| |
| static unsigned int debugifc_isolate_word(const char *buf,unsigned int count, |
| const char **wstrPtr, |
| unsigned int *wlenPtr) |
| { |
| const char *wptr; |
| unsigned int consume_cnt = 0; |
| unsigned int wlen; |
| unsigned int scnt; |
| |
| wptr = NULL; |
| wlen = 0; |
| scnt = debugifc_count_whitespace(buf,count); |
| consume_cnt += scnt; count -= scnt; buf += scnt; |
| if (!count) goto done; |
| |
| scnt = debugifc_count_nonwhitespace(buf,count); |
| if (!scnt) goto done; |
| wptr = buf; |
| wlen = scnt; |
| consume_cnt += scnt; count -= scnt; buf += scnt; |
| |
| done: |
| *wstrPtr = wptr; |
| *wlenPtr = wlen; |
| return consume_cnt; |
| } |
| |
| |
| static int debugifc_parse_unsigned_number(const char *buf,unsigned int count, |
| u32 *num_ptr) |
| { |
| u32 result = 0; |
| u32 val; |
| int ch; |
| int radix = 10; |
| if ((count >= 2) && (buf[0] == '0') && |
| ((buf[1] == 'x') || (buf[1] == 'X'))) { |
| radix = 16; |
| count -= 2; |
| buf += 2; |
| } else if ((count >= 1) && (buf[0] == '0')) { |
| radix = 8; |
| } |
| |
| while (count--) { |
| ch = *buf++; |
| if ((ch >= '0') && (ch <= '9')) { |
| val = ch - '0'; |
| } else if ((ch >= 'a') && (ch <= 'f')) { |
| val = ch - 'a' + 10; |
| } else if ((ch >= 'A') && (ch <= 'F')) { |
| val = ch - 'A' + 10; |
| } else { |
| return -EINVAL; |
| } |
| if (val >= radix) return -EINVAL; |
| result *= radix; |
| result += val; |
| } |
| *num_ptr = result; |
| return 0; |
| } |
| |
| |
| static int debugifc_match_keyword(const char *buf,unsigned int count, |
| const char *keyword) |
| { |
| unsigned int kl; |
| if (!keyword) return 0; |
| kl = strlen(keyword); |
| if (kl != count) return 0; |
| return !memcmp(buf,keyword,kl); |
| } |
| |
| |
| int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) |
| { |
| int bcnt = 0; |
| int ccnt; |
| ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n", |
| pvr2_hdw_get_desc(hdw)); |
| bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
| ccnt = scnprintf(buf,acnt,"Driver state info:\n"); |
| bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
| ccnt = pvr2_hdw_state_report(hdw,buf,acnt); |
| bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
| |
| return bcnt; |
| } |
| |
| |
| int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, |
| char *buf,unsigned int acnt) |
| { |
| int bcnt = 0; |
| int ccnt; |
| int ret; |
| u32 gpio_dir,gpio_in,gpio_out; |
| struct pvr2_stream_stats stats; |
| struct pvr2_stream *sp; |
| |
| ret = pvr2_hdw_is_hsm(hdw); |
| ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", |
| (ret < 0 ? "FAIL" : (ret ? "high" : "full"))); |
| bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
| |
| gpio_dir = 0; gpio_in = 0; gpio_out = 0; |
| pvr2_hdw_gpio_get_dir(hdw,&gpio_dir); |
| pvr2_hdw_gpio_get_out(hdw,&gpio_out); |
| pvr2_hdw_gpio_get_in(hdw,&gpio_in); |
| ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n", |
| gpio_dir,gpio_in,gpio_out); |
| bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
| |
| ccnt = scnprintf(buf,acnt,"Streaming is %s\n", |
| pvr2_hdw_get_streaming(hdw) ? "on" : "off"); |
| bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
| |
| |
| sp = pvr2_hdw_get_video_stream(hdw); |
| if (sp) { |
| pvr2_stream_get_stats(sp, &stats, 0); |
| ccnt = scnprintf( |
| buf,acnt, |
| "Bytes streamed=%u" |
| " URBs: queued=%u idle=%u ready=%u" |
| " processed=%u failed=%u\n", |
| stats.bytes_processed, |
| stats.buffers_in_queue, |
| stats.buffers_in_idle, |
| stats.buffers_in_ready, |
| stats.buffers_processed, |
| stats.buffers_failed); |
| bcnt += ccnt; acnt -= ccnt; buf += ccnt; |
| } |
| |
| return bcnt; |
| } |
| |
| |
| static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, |
| unsigned int count) |
| { |
| const char *wptr; |
| unsigned int wlen; |
| unsigned int scnt; |
| |
| scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); |
| if (!scnt) return 0; |
| count -= scnt; buf += scnt; |
| if (!wptr) return 0; |
| |
| pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr); |
| if (debugifc_match_keyword(wptr,wlen,"reset")) { |
| scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); |
| if (!scnt) return -EINVAL; |
| count -= scnt; buf += scnt; |
| if (!wptr) return -EINVAL; |
| if (debugifc_match_keyword(wptr,wlen,"cpu")) { |
| pvr2_hdw_cpureset_assert(hdw,!0); |
| pvr2_hdw_cpureset_assert(hdw,0); |
| return 0; |
| } else if (debugifc_match_keyword(wptr,wlen,"bus")) { |
| pvr2_hdw_device_reset(hdw); |
| } else if (debugifc_match_keyword(wptr,wlen,"soft")) { |
| return pvr2_hdw_cmd_powerup(hdw); |
| } else if (debugifc_match_keyword(wptr,wlen,"deep")) { |
| return pvr2_hdw_cmd_deep_reset(hdw); |
| } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { |
| return pvr2_upload_firmware2(hdw); |
| } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { |
| return pvr2_hdw_cmd_decoder_reset(hdw); |
| } else if (debugifc_match_keyword(wptr,wlen,"worker")) { |
| return pvr2_hdw_untrip(hdw); |
| } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) { |
| pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), |
| NULL, !0); |
| return 0; |
| } |
| return -EINVAL; |
| } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { |
| scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); |
| if (!scnt) return -EINVAL; |
| count -= scnt; buf += scnt; |
| if (!wptr) return -EINVAL; |
| if (debugifc_match_keyword(wptr,wlen,"fetch")) { |
| scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); |
| if (scnt && wptr) { |
| count -= scnt; buf += scnt; |
| if (debugifc_match_keyword(wptr, wlen, |
| "prom")) { |
| pvr2_hdw_cpufw_set_enabled(hdw, 2, !0); |
| } else if (debugifc_match_keyword(wptr, wlen, |
| "ram8k")) { |
| pvr2_hdw_cpufw_set_enabled(hdw, 0, !0); |
| } else if (debugifc_match_keyword(wptr, wlen, |
| "ram16k")) { |
| pvr2_hdw_cpufw_set_enabled(hdw, 1, !0); |
| } else { |
| return -EINVAL; |
| } |
| } |
| pvr2_hdw_cpufw_set_enabled(hdw,0,!0); |
| return 0; |
| } else if (debugifc_match_keyword(wptr,wlen,"done")) { |
| pvr2_hdw_cpufw_set_enabled(hdw,0,0); |
| return 0; |
| } else { |
| return -EINVAL; |
| } |
| } else if (debugifc_match_keyword(wptr,wlen,"gpio")) { |
| int dir_fl = 0; |
| int ret; |
| u32 msk,val; |
| scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); |
| if (!scnt) return -EINVAL; |
| count -= scnt; buf += scnt; |
| if (!wptr) return -EINVAL; |
| if (debugifc_match_keyword(wptr,wlen,"dir")) { |
| dir_fl = !0; |
| } else if (!debugifc_match_keyword(wptr,wlen,"out")) { |
| return -EINVAL; |
| } |
| scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); |
| if (!scnt) return -EINVAL; |
| count -= scnt; buf += scnt; |
| if (!wptr) return -EINVAL; |
| ret = debugifc_parse_unsigned_number(wptr,wlen,&msk); |
| if (ret) return ret; |
| scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); |
| if (wptr) { |
| ret = debugifc_parse_unsigned_number(wptr,wlen,&val); |
| if (ret) return ret; |
| } else { |
| val = msk; |
| msk = 0xffffffff; |
| } |
| if (dir_fl) { |
| ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val); |
| } else { |
| ret = pvr2_hdw_gpio_chg_out(hdw,msk,val); |
| } |
| return ret; |
| } |
| pvr2_trace(PVR2_TRACE_DEBUGIFC, |
| "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr); |
| return -EINVAL; |
| } |
| |
| |
| int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, |
| unsigned int count) |
| { |
| unsigned int bcnt = 0; |
| int ret; |
| |
| while (count) { |
| for (bcnt = 0; bcnt < count; bcnt++) { |
| if (buf[bcnt] == '\n') break; |
| } |
| |
| ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt); |
| if (ret < 0) return ret; |
| if (bcnt < count) bcnt++; |
| buf += bcnt; |
| count -= bcnt; |
| } |
| |
| return 0; |
| } |
| |
| |
| /* |
| Stuff for Emacs to see, in order to encourage consistent editing style: |
| *** Local Variables: *** |
| *** mode: c *** |
| *** fill-column: 75 *** |
| *** tab-width: 8 *** |
| *** c-basic-offset: 8 *** |
| *** End: *** |
| */ |