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
12struct debugifc_mask_item {
13	const char *name;
14	unsigned long msk;
15};
16
17
18static 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
35static 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
51static 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
79static 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
105static 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
116int 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
132int 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
179static 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
289int 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