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 
debugifc_count_whitespace(const char * buf,unsigned int count)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 
debugifc_count_nonwhitespace(const char * buf,unsigned int count)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 
debugifc_isolate_word(const char * buf,unsigned int count,const char ** wstrPtr,unsigned int * wlenPtr)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 
debugifc_parse_unsigned_number(const char * buf,unsigned int count,u32 * num_ptr)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(*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 
debugifc_match_keyword(const char * buf,unsigned int count,const char * keyword)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(buf,keyword,kl);
113 }
114 
115 
pvr2_debugifc_print_info(struct pvr2_hdw * hdw,char * buf,unsigned int acnt)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, acnt, "Driver hardware description: %s\n",
121 			 pvr2_hdw_get_desc(hdw));
122 	bcnt += ccnt; acnt -= ccnt; buf += ccnt;
123 	ccnt = scnprintf(buf,acnt,"Driver state info:\n");
124 	bcnt += ccnt; acnt -= ccnt; buf += ccnt;
125 	ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
126 	bcnt += ccnt; acnt -= ccnt; buf += ccnt;
127 
128 	return bcnt;
129 }
130 
131 
pvr2_debugifc_print_status(struct pvr2_hdw * hdw,char * buf,unsigned int acnt)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,acnt,"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,acnt,"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,acnt,"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, 0);
163 		ccnt = scnprintf(
164 			buf,acnt,
165 			"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 
pvr2_debugifc_do1cmd(struct pvr2_hdw * hdw,const char * buf,unsigned int count)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,&wptr,&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(wptr,wlen,"reset")) {
193 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
194 		if (!scnt) return -EINVAL;
195 		count -= scnt; buf += scnt;
196 		if (!wptr) return -EINVAL;
197 		if (debugifc_match_keyword(wptr,wlen,"cpu")) {
198 			pvr2_hdw_cpureset_assert(hdw,!0);
199 			pvr2_hdw_cpureset_assert(hdw,0);
200 			return 0;
201 		} else if (debugifc_match_keyword(wptr,wlen,"bus")) {
202 			pvr2_hdw_device_reset(hdw);
203 		} else if (debugifc_match_keyword(wptr,wlen,"soft")) {
204 			return pvr2_hdw_cmd_powerup(hdw);
205 		} else if (debugifc_match_keyword(wptr,wlen,"deep")) {
206 			return pvr2_hdw_cmd_deep_reset(hdw);
207 		} else if (debugifc_match_keyword(wptr,wlen,"firmware")) {
208 			return pvr2_upload_firmware2(hdw);
209 		} else if (debugifc_match_keyword(wptr,wlen,"decoder")) {
210 			return pvr2_hdw_cmd_decoder_reset(hdw);
211 		} else if (debugifc_match_keyword(wptr,wlen,"worker")) {
212 			return pvr2_hdw_untrip(hdw);
213 		} else if (debugifc_match_keyword(wptr,wlen,"usbstats")) {
214 			pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw),
215 					      NULL, !0);
216 			return 0;
217 		}
218 		return -EINVAL;
219 	} else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
220 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
221 		if (!scnt) return -EINVAL;
222 		count -= scnt; buf += scnt;
223 		if (!wptr) return -EINVAL;
224 		if (debugifc_match_keyword(wptr,wlen,"fetch")) {
225 			scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
226 			if (scnt && wptr) {
227 				count -= scnt; buf += scnt;
228 				if (debugifc_match_keyword(wptr, wlen,
229 							   "prom")) {
230 					pvr2_hdw_cpufw_set_enabled(hdw, 2, !0);
231 				} else if (debugifc_match_keyword(wptr, wlen,
232 								  "ram8k")) {
233 					pvr2_hdw_cpufw_set_enabled(hdw, 0, !0);
234 				} else if (debugifc_match_keyword(wptr, wlen,
235 								  "ram16k")) {
236 					pvr2_hdw_cpufw_set_enabled(hdw, 1, !0);
237 				} else {
238 					return -EINVAL;
239 				}
240 			}
241 			pvr2_hdw_cpufw_set_enabled(hdw,0,!0);
242 			return 0;
243 		} else if (debugifc_match_keyword(wptr,wlen,"done")) {
244 			pvr2_hdw_cpufw_set_enabled(hdw,0,0);
245 			return 0;
246 		} else {
247 			return -EINVAL;
248 		}
249 	} else if (debugifc_match_keyword(wptr,wlen,"gpio")) {
250 		int dir_fl = 0;
251 		int ret;
252 		u32 msk,val;
253 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
254 		if (!scnt) return -EINVAL;
255 		count -= scnt; buf += scnt;
256 		if (!wptr) return -EINVAL;
257 		if (debugifc_match_keyword(wptr,wlen,"dir")) {
258 			dir_fl = !0;
259 		} else if (!debugifc_match_keyword(wptr,wlen,"out")) {
260 			return -EINVAL;
261 		}
262 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
263 		if (!scnt) return -EINVAL;
264 		count -= scnt; buf += scnt;
265 		if (!wptr) return -EINVAL;
266 		ret = debugifc_parse_unsigned_number(wptr,wlen,&msk);
267 		if (ret) return ret;
268 		scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
269 		if (wptr) {
270 			ret = debugifc_parse_unsigned_number(wptr,wlen,&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 
pvr2_debugifc_docmd(struct pvr2_hdw * hdw,const char * buf,unsigned int count)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,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