1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/shell/shell.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/usb/usbh.h>
14 
15 #include "usbh_device.h"
16 #include "usbh_ch9.h"
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(usbh_shell, CONFIG_USBH_LOG_LEVEL);
20 
21 #define FOOBAZ_VREQ_OUT		0x5b
22 #define FOOBAZ_VREQ_IN		0x5c
23 
24 USBH_CONTROLLER_DEFINE(uhs_ctx, DEVICE_DT_GET(DT_NODELABEL(zephyr_uhc0)));
25 static struct usb_device *udev;
26 static uint8_t vreq_test_buf[1024];
27 
print_dev_desc(const struct shell * sh,const struct usb_device_descriptor * const desc)28 static void print_dev_desc(const struct shell *sh,
29 			   const struct usb_device_descriptor *const desc)
30 {
31 	shell_print(sh, "bLength\t\t\t%u", desc->bLength);
32 	shell_print(sh, "bDescriptorType\t\t%u", desc->bDescriptorType);
33 	shell_print(sh, "bcdUSB\t\t\t%x", desc->bcdUSB);
34 	shell_print(sh, "bDeviceClass\t\t%u", desc->bDeviceClass);
35 	shell_print(sh, "bDeviceSubClass\t\t%u", desc->bDeviceSubClass);
36 	shell_print(sh, "bDeviceProtocol\t\t%u", desc->bDeviceProtocol);
37 	shell_print(sh, "bMaxPacketSize0\t\t%u", desc->bMaxPacketSize0);
38 	shell_print(sh, "idVendor\t\t%x", desc->idVendor);
39 	shell_print(sh, "idProduct\t\t%x", desc->idProduct);
40 	shell_print(sh, "bcdDevice\t\t%x", desc->bcdDevice);
41 	shell_print(sh, "iManufacturer\t\t%u", desc->iManufacturer);
42 	shell_print(sh, "iProduct\t\t%u", desc->iProduct);
43 	shell_print(sh, "iSerial\t\t\t%u", desc->iSerialNumber);
44 	shell_print(sh, "bNumConfigurations\t%u", desc->bNumConfigurations);
45 }
46 
print_cfg_desc(const struct shell * sh,const struct usb_cfg_descriptor * const desc)47 static void print_cfg_desc(const struct shell *sh,
48 			   const struct usb_cfg_descriptor *const desc)
49 {
50 	shell_print(sh, "bLength\t\t\t%u", desc->bLength);
51 	shell_print(sh, "bDescriptorType\t\t%u", desc->bDescriptorType);
52 	shell_print(sh, "wTotalLength\t\t%x", desc->wTotalLength);
53 	shell_print(sh, "bNumInterfaces\t\t%u", desc->bNumInterfaces);
54 	shell_print(sh, "bConfigurationValue\t%u", desc->bConfigurationValue);
55 	shell_print(sh, "iConfiguration\t\t%u", desc->iConfiguration);
56 	shell_print(sh, "bmAttributes\t\t%02x", desc->bmAttributes);
57 	shell_print(sh, "bMaxPower\t\t%u mA", desc->bMaxPower * 2);
58 }
59 
cmd_bulk(const struct shell * sh,size_t argc,char ** argv)60 static int cmd_bulk(const struct shell *sh, size_t argc, char **argv)
61 {
62 	struct uhc_transfer *xfer;
63 	struct net_buf *buf;
64 	uint8_t ep;
65 	size_t len;
66 
67 	ep = strtol(argv[1], NULL, 16);
68 	len = MIN(sizeof(vreq_test_buf), strtol(argv[2], NULL, 10));
69 
70 	xfer = usbh_xfer_alloc(udev, ep, 0, 512, 10, NULL);
71 	if (!xfer) {
72 		return -ENOMEM;
73 	}
74 
75 	buf = usbh_xfer_buf_alloc(udev, len);
76 	if (!buf) {
77 		return -ENOMEM;
78 	}
79 
80 	xfer->buf = buf;
81 	if (USB_EP_DIR_IS_OUT(ep)) {
82 		net_buf_add_mem(buf, vreq_test_buf, len);
83 	}
84 
85 	return uhc_ep_enqueue(uhs_ctx.dev, xfer);
86 }
87 
cmd_vendor_in(const struct shell * sh,size_t argc,char ** argv)88 static int cmd_vendor_in(const struct shell *sh,
89 			 size_t argc, char **argv)
90 {
91 	const uint8_t bmRequestType = (USB_REQTYPE_DIR_TO_HOST << 7) |
92 				      (USB_REQTYPE_TYPE_VENDOR << 5);
93 	const uint8_t bRequest = FOOBAZ_VREQ_IN;
94 	const uint16_t wValue = 0x0000;
95 	struct net_buf *buf;
96 	uint16_t wLength;
97 	int ret;
98 
99 	wLength = MIN(sizeof(vreq_test_buf), strtol(argv[1], NULL, 10));
100 	buf = usbh_xfer_buf_alloc(udev, wLength);
101 	if (!buf) {
102 		shell_print(sh, "host: Failed to allocate buffer");
103 		return -ENOMEM;
104 	}
105 
106 	ret = usbh_req_setup(udev, bmRequestType, bRequest, wValue, 0, wLength, buf);
107 	if (ret == 0) {
108 		memcpy(vreq_test_buf, buf->data, MIN(buf->len, wLength));
109 	}
110 
111 	usbh_xfer_buf_free(udev, buf);
112 
113 	return ret;
114 }
115 
cmd_vendor_out(const struct shell * sh,size_t argc,char ** argv)116 static int cmd_vendor_out(const struct shell *sh,
117 			  size_t argc, char **argv)
118 {
119 	const uint8_t bmRequestType = (USB_REQTYPE_DIR_TO_DEVICE << 7) |
120 				      (USB_REQTYPE_TYPE_VENDOR << 5);
121 	const uint8_t bRequest = FOOBAZ_VREQ_OUT;
122 	const uint16_t wValue = 0x0000;
123 	struct net_buf *buf;
124 	uint16_t wLength;
125 	int ret;
126 
127 	wLength = MIN(sizeof(vreq_test_buf), strtol(argv[1], NULL, 10));
128 	buf = usbh_xfer_buf_alloc(udev, wLength);
129 	if (!buf) {
130 		shell_print(sh, "host: Failed to allocate buffer");
131 		return -ENOMEM;
132 	}
133 
134 	net_buf_add_mem(buf, &vreq_test_buf, wLength);
135 	ret = usbh_req_setup(udev, bmRequestType, bRequest, wValue, 0, wLength, buf);
136 	usbh_xfer_buf_free(udev, buf);
137 
138 	return ret;
139 }
140 
cmd_desc_device(const struct shell * sh,size_t argc,char ** argv)141 static int cmd_desc_device(const struct shell *sh,
142 			   size_t argc, char **argv)
143 {
144 	struct usb_device_descriptor desc;
145 	int err;
146 
147 	err = usbh_req_desc_dev(udev, &desc);
148 	if (err) {
149 		shell_print(sh, "host: Failed to request device descriptor");
150 	} else {
151 		print_dev_desc(sh, &desc);
152 	}
153 
154 	return err;
155 }
156 
cmd_desc_config(const struct shell * sh,size_t argc,char ** argv)157 static int cmd_desc_config(const struct shell *sh,
158 			   size_t argc, char **argv)
159 {
160 	struct usb_cfg_descriptor desc;
161 	uint8_t cfg;
162 	int err;
163 
164 	cfg = strtol(argv[1], NULL, 10);
165 
166 	err = usbh_req_desc_cfg(udev, cfg, sizeof(desc), &desc);
167 	if (err) {
168 		shell_print(sh, "host: Failed to request configuration descriptor");
169 	} else {
170 		print_cfg_desc(sh, &desc);
171 	}
172 
173 	return err;
174 }
175 
cmd_desc_string(const struct shell * sh,size_t argc,char ** argv)176 static int cmd_desc_string(const struct shell *sh,
177 			   size_t argc, char **argv)
178 {
179 	const uint8_t type = USB_DESC_STRING;
180 	struct net_buf *buf;
181 	uint8_t id;
182 	uint8_t idx;
183 	int err;
184 
185 	id = strtol(argv[1], NULL, 10);
186 	idx = strtol(argv[2], NULL, 10);
187 
188 	buf = usbh_xfer_buf_alloc(udev, 128);
189 	if (!buf) {
190 		return -ENOMEM;
191 	}
192 
193 	err = usbh_req_desc(udev, type, idx, id, 128, buf);
194 	if (err) {
195 		shell_print(sh, "host: Failed to request configuration descriptor");
196 	} else {
197 		shell_hexdump(sh, buf->data, buf->len);
198 	}
199 
200 	usbh_xfer_buf_free(udev, buf);
201 
202 	return err;
203 }
204 
cmd_feature_set_halt(const struct shell * sh,size_t argc,char ** argv)205 static int cmd_feature_set_halt(const struct shell *sh,
206 				size_t argc, char **argv)
207 {
208 	uint8_t ep;
209 	int err;
210 
211 	ep = strtol(argv[1], NULL, 16);
212 
213 	/* TODO: add usbh_req_set_sfs_halt(&uhs_ctx, NULL, 0); */
214 	err = usbh_req_set_sfs_rwup(udev);
215 	if (err) {
216 		shell_error(sh, "host: Failed to set halt feature");
217 	} else {
218 		shell_print(sh, "host: Device 0x%02x, ep 0x%02x halt feature set",
219 			    udev->addr, ep);
220 	}
221 
222 	return err;
223 }
224 
cmd_feature_clear_rwup(const struct shell * sh,size_t argc,char ** argv)225 static int cmd_feature_clear_rwup(const struct shell *sh,
226 				  size_t argc, char **argv)
227 {
228 	int err;
229 
230 	err = usbh_req_clear_sfs_rwup(udev);
231 	if (err) {
232 		shell_error(sh, "host: Failed to clear rwup feature");
233 	} else {
234 		shell_print(sh, "host: Device 0x%02x, rwup feature cleared", udev->addr);
235 	}
236 
237 	return err;
238 }
239 
cmd_feature_set_rwup(const struct shell * sh,size_t argc,char ** argv)240 static int cmd_feature_set_rwup(const struct shell *sh,
241 				size_t argc, char **argv)
242 {
243 	int err;
244 
245 	err = usbh_req_set_sfs_rwup(udev);
246 	if (err) {
247 		shell_error(sh, "host: Failed to set rwup feature");
248 	} else {
249 		shell_print(sh, "host: Device 0x%02x, rwup feature set", udev->addr);
250 	}
251 
252 	return err;
253 }
254 
cmd_feature_set_ppwr(const struct shell * sh,size_t argc,char ** argv)255 static int cmd_feature_set_ppwr(const struct shell *sh,
256 				size_t argc, char **argv)
257 {
258 	uint8_t port;
259 	int err;
260 
261 	port = strtol(argv[1], NULL, 10);
262 
263 	err = usbh_req_set_hcfs_ppwr(udev, port);
264 	if (err) {
265 		shell_error(sh, "host: Failed to set ppwr feature");
266 	} else {
267 		shell_print(sh, "host: Device 0x%02x, port %d, ppwr feature set",
268 			    udev->addr, port);
269 	}
270 
271 	return err;
272 }
273 
cmd_feature_set_prst(const struct shell * sh,size_t argc,char ** argv)274 static int cmd_feature_set_prst(const struct shell *sh,
275 				size_t argc, char **argv)
276 {
277 	uint8_t port;
278 	int err;
279 
280 	port = strtol(argv[1], NULL, 10);
281 
282 	err = usbh_req_set_hcfs_prst(udev, port);
283 	if (err) {
284 		shell_error(sh, "host: Failed to set prst feature");
285 	} else {
286 		shell_print(sh, "host: Device 0x%02x, port %d, prst feature set",
287 			    udev->addr, port);
288 	}
289 
290 	return err;
291 }
292 
cmd_config_set(const struct shell * sh,size_t argc,char ** argv)293 static int cmd_config_set(const struct shell *sh,
294 			  size_t argc, char **argv)
295 {
296 	uint8_t cfg;
297 	int err;
298 
299 	cfg = strtol(argv[1], NULL, 10);
300 
301 	err = usbh_req_set_cfg(udev, cfg);
302 	if (err) {
303 		shell_error(sh, "host: Failed to set configuration");
304 	} else {
305 		shell_print(sh, "host: Device 0x%02x, new configuration %u",
306 			    udev->addr, cfg);
307 	}
308 
309 	return err;
310 }
311 
cmd_config_get(const struct shell * sh,size_t argc,char ** argv)312 static int cmd_config_get(const struct shell *sh,
313 			  size_t argc, char **argv)
314 {
315 	uint8_t cfg;
316 	int err;
317 
318 	err = usbh_req_get_cfg(udev, &cfg);
319 	if (err) {
320 		shell_error(sh, "host: Failed to set configuration");
321 	} else {
322 		shell_print(sh, "host: Device 0x%02x, current configuration %u",
323 			    udev->addr, cfg);
324 	}
325 
326 	return err;
327 }
328 
cmd_device_interface(const struct shell * sh,size_t argc,char ** argv)329 static int cmd_device_interface(const struct shell *sh,
330 				size_t argc, char **argv)
331 {
332 	uint8_t iface;
333 	uint8_t alt;
334 	int err;
335 
336 	iface = strtol(argv[1], NULL, 10);
337 	alt = strtol(argv[2], NULL, 10);
338 
339 	err = usbh_req_set_alt(udev, iface, alt);
340 	if (err) {
341 		shell_error(sh, "host: Failed to set interface alternate");
342 	} else {
343 		shell_print(sh, "host: Device 0x%02x, new %u alternate %u",
344 			    udev->addr, iface, alt);
345 	}
346 
347 	return err;
348 }
349 
cmd_device_address(const struct shell * sh,size_t argc,char ** argv)350 static int cmd_device_address(const struct shell *sh,
351 			      size_t argc, char **argv)
352 {
353 	uint8_t addr;
354 	int err;
355 
356 	addr = strtol(argv[1], NULL, 10);
357 
358 	err = usbh_req_set_address(udev, addr);
359 	if (err) {
360 		shell_error(sh, "host: Failed to set address");
361 	} else {
362 		shell_print(sh, "host: New device address is 0x%02x", addr);
363 	}
364 
365 	return err;
366 }
367 
cmd_bus_suspend(const struct shell * sh,size_t argc,char ** argv)368 static int cmd_bus_suspend(const struct shell *sh,
369 			   size_t argc, char **argv)
370 {
371 	int err;
372 
373 	err = uhc_bus_suspend(uhs_ctx.dev);
374 	if (err) {
375 		shell_error(sh, "host: Failed to perform bus suspend %d", err);
376 	} else {
377 		shell_print(sh, "host: USB bus suspended");
378 	}
379 
380 	return err;
381 }
382 
cmd_bus_resume(const struct shell * sh,size_t argc,char ** argv)383 static int cmd_bus_resume(const struct shell *sh,
384 			  size_t argc, char **argv)
385 {
386 	int err;
387 
388 	err = uhc_bus_resume(uhs_ctx.dev);
389 	if (err) {
390 		shell_error(sh, "host: Failed to perform bus resume %d", err);
391 	} else {
392 		shell_print(sh, "host: USB bus resumed");
393 	}
394 
395 	err = uhc_sof_enable(uhs_ctx.dev);
396 	if (err) {
397 		shell_error(sh, "host: Failed to start SoF generator %d", err);
398 	}
399 
400 	return err;
401 }
402 
cmd_bus_reset(const struct shell * sh,size_t argc,char ** argv)403 static int cmd_bus_reset(const struct shell *sh,
404 			 size_t argc, char **argv)
405 {
406 	int err;
407 
408 	err = uhc_bus_reset(uhs_ctx.dev);
409 	if (err) {
410 		shell_error(sh, "host: Failed to perform bus reset %d", err);
411 	} else {
412 		shell_print(sh, "host: USB bus reset");
413 	}
414 
415 	err = uhc_sof_enable(uhs_ctx.dev);
416 	if (err) {
417 		shell_error(sh, "host: Failed to start SoF generator %d", err);
418 	}
419 
420 	return err;
421 }
422 
cmd_usbh_init(const struct shell * sh,size_t argc,char ** argv)423 static int cmd_usbh_init(const struct shell *sh,
424 			 size_t argc, char **argv)
425 {
426 	int err;
427 
428 	udev = usbh_device_get_any(&uhs_ctx);
429 
430 	err = usbh_init(&uhs_ctx);
431 	if (err == -EALREADY) {
432 		shell_error(sh, "host: USB host already initialized");
433 	} else if (err) {
434 		shell_error(sh, "host: Failed to initialize %d", err);
435 	} else {
436 		shell_print(sh, "host: USB host initialized");
437 	}
438 
439 	return err;
440 }
441 
cmd_usbh_enable(const struct shell * sh,size_t argc,char ** argv)442 static int cmd_usbh_enable(const struct shell *sh,
443 			   size_t argc, char **argv)
444 {
445 	int err;
446 
447 	err = usbh_enable(&uhs_ctx);
448 	if (err) {
449 		shell_error(sh, "host: Failed to enable USB host support");
450 	} else {
451 		shell_print(sh, "host: USB host enabled");
452 	}
453 
454 	return err;
455 }
456 
cmd_usbh_disable(const struct shell * sh,size_t argc,char ** argv)457 static int cmd_usbh_disable(const struct shell *sh,
458 			    size_t argc, char **argv)
459 {
460 	int err;
461 
462 	err = usbh_disable(&uhs_ctx);
463 	if (err) {
464 		shell_error(sh, "host: Failed to disable USB host support");
465 	} else {
466 		shell_print(sh, "host: USB host disabled");
467 	}
468 
469 	return err;
470 }
471 
472 SHELL_STATIC_SUBCMD_SET_CREATE(desc_cmds,
473 	SHELL_CMD_ARG(device, NULL, NULL,
474 		      cmd_desc_device, 1, 0),
475 	SHELL_CMD_ARG(configuration, NULL, "<index>",
476 		      cmd_desc_config, 2, 0),
477 	SHELL_CMD_ARG(string, NULL, "<id> <index>",
478 		      cmd_desc_string, 3, 0),
479 	SHELL_SUBCMD_SET_END
480 );
481 
482 SHELL_STATIC_SUBCMD_SET_CREATE(feature_set_cmds,
483 	SHELL_CMD_ARG(rwup, NULL, NULL,
484 		      cmd_feature_set_rwup, 1, 0),
485 	SHELL_CMD_ARG(ppwr, NULL, "<port>",
486 		      cmd_feature_set_ppwr, 2, 0),
487 	SHELL_CMD_ARG(prst, NULL, "<port>",
488 		      cmd_feature_set_prst, 2, 0),
489 	SHELL_CMD_ARG(halt, NULL, "<endpoint>",
490 		      cmd_feature_set_halt, 2, 0),
491 	SHELL_SUBCMD_SET_END
492 );
493 
494 SHELL_STATIC_SUBCMD_SET_CREATE(feature_clear_cmds,
495 	SHELL_CMD_ARG(rwup, NULL, NULL,
496 		      cmd_feature_clear_rwup, 1, 0),
497 	SHELL_CMD_ARG(halt, NULL, "<endpoint>",
498 		      cmd_feature_set_halt, 2, 0),
499 	SHELL_SUBCMD_SET_END
500 );
501 
502 SHELL_STATIC_SUBCMD_SET_CREATE(config_cmds,
503 	SHELL_CMD_ARG(get, NULL, NULL,
504 		      cmd_config_get, 1, 0),
505 	SHELL_CMD_ARG(set, NULL, "<configuration>",
506 		      cmd_config_set, 2, 0),
507 	SHELL_SUBCMD_SET_END
508 );
509 
510 SHELL_STATIC_SUBCMD_SET_CREATE(device_cmds,
511 	SHELL_CMD_ARG(address, NULL, "<address>",
512 		      cmd_device_address, 2, 0),
513 	SHELL_CMD_ARG(config, &config_cmds, "get|set configuration",
514 		      NULL, 1, 0),
515 	SHELL_CMD_ARG(interface, NULL, "<interface> <alternate>",
516 		      cmd_device_interface, 3, 0),
517 	SHELL_CMD_ARG(descriptor, &desc_cmds, "descriptor request",
518 		      NULL, 1, 0),
519 	SHELL_CMD_ARG(feature-set, &feature_set_cmds, "feature selector",
520 		      NULL, 1, 0),
521 	SHELL_CMD_ARG(feature-clear, &feature_clear_cmds, "feature selector",
522 		      NULL, 1, 0),
523 	SHELL_CMD_ARG(vendor_in, NULL, "<length>",
524 		      cmd_vendor_in, 2, 0),
525 	SHELL_CMD_ARG(vendor_out, NULL, "<length>",
526 		      cmd_vendor_out, 2, 0),
527 	SHELL_CMD_ARG(bulk, NULL, "<endpoint> <length>",
528 		      cmd_bulk, 3, 0),
529 	SHELL_SUBCMD_SET_END
530 );
531 
532 SHELL_STATIC_SUBCMD_SET_CREATE(bus_cmds,
533 	SHELL_CMD_ARG(suspend, NULL, "[nono]",
534 		      cmd_bus_suspend, 1, 0),
535 	SHELL_CMD_ARG(resume, NULL, "[nono]",
536 		      cmd_bus_resume, 1, 0),
537 	SHELL_CMD_ARG(reset, NULL, "[nono]",
538 		      cmd_bus_reset, 1, 0),
539 	SHELL_SUBCMD_SET_END
540 );
541 
542 SHELL_STATIC_SUBCMD_SET_CREATE(sub_usbh_cmds,
543 	SHELL_CMD_ARG(init, NULL, "[none]",
544 		      cmd_usbh_init, 1, 0),
545 	SHELL_CMD_ARG(enable, NULL, "[none]",
546 		      cmd_usbh_enable, 1, 0),
547 	SHELL_CMD_ARG(disable, NULL, "[none]",
548 		      cmd_usbh_disable, 1, 0),
549 	SHELL_CMD_ARG(bus, &bus_cmds, "bus commands",
550 		      NULL, 1, 0),
551 	SHELL_CMD_ARG(device, &device_cmds, "device commands",
552 		      NULL, 1, 0),
553 	SHELL_SUBCMD_SET_END
554 );
555 
556 SHELL_CMD_REGISTER(usbh, &sub_usbh_cmds, "USBH commands", NULL);
557