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 
60 K_SEM_DEFINE(bulk_req_sync, 0, 1);
61 
bulk_req_cb(struct usb_device * const dev,struct uhc_transfer * const xfer)62 static int bulk_req_cb(struct usb_device *const dev, struct uhc_transfer *const xfer)
63 {
64 	if (xfer->err == -ECONNRESET) {
65 		LOG_INF("Bulk transfer canceled");
66 	} else if (xfer->err) {
67 		LOG_WRN("Bulk request failed, err %d", xfer->err);
68 	} else {
69 		LOG_INF("Bulk request finished");
70 	}
71 
72 	usbh_xfer_buf_free(dev, xfer->buf);
73 	usbh_xfer_free(dev, xfer);
74 	k_sem_give(&bulk_req_sync);
75 
76 	return 0;
77 }
78 
cmd_bulk(const struct shell * sh,size_t argc,char ** argv)79 static int cmd_bulk(const struct shell *sh, size_t argc, char **argv)
80 {
81 	struct uhc_transfer *xfer;
82 	struct net_buf *buf;
83 	uint8_t ep;
84 	size_t len;
85 	int ret;
86 
87 	ep = strtol(argv[1], NULL, 16);
88 	len = MIN(sizeof(vreq_test_buf), strtol(argv[2], NULL, 10));
89 
90 	xfer = usbh_xfer_alloc(udev, ep, bulk_req_cb, NULL);
91 	if (!xfer) {
92 		shell_error(sh, "host: Failed to allocate transfer");
93 		return -ENOMEM;
94 	}
95 
96 	buf = usbh_xfer_buf_alloc(udev, len);
97 	if (!buf) {
98 		shell_error(sh, "host: Failed to allocate buffer");
99 		usbh_xfer_free(udev, xfer);
100 		return -ENOMEM;
101 	}
102 
103 	xfer->buf = buf;
104 	if (USB_EP_DIR_IS_OUT(ep)) {
105 		net_buf_add_mem(buf, vreq_test_buf, len);
106 	}
107 
108 	k_sem_reset(&bulk_req_sync);
109 	ret = usbh_xfer_enqueue(udev, xfer);
110 	if (ret) {
111 		usbh_xfer_buf_free(udev, xfer->buf);
112 		usbh_xfer_free(udev, xfer);
113 		return ret;
114 	}
115 
116 	if (k_sem_take(&bulk_req_sync, K_MSEC(1000)) != 0) {
117 		shell_print(sh, "host: Bulk transfer timeout");
118 		ret = usbh_xfer_dequeue(udev, xfer);
119 		if (ret != 0) {
120 			shell_error(sh, "host: Failed to cancel transfer");
121 			return ret;
122 		}
123 
124 		return -ETIMEDOUT;
125 	}
126 
127 	shell_print(sh, "host: Bulk transfer finished");
128 
129 	return 0;
130 }
131 
cmd_vendor_in(const struct shell * sh,size_t argc,char ** argv)132 static int cmd_vendor_in(const struct shell *sh,
133 			 size_t argc, char **argv)
134 {
135 	const uint8_t bmRequestType = (USB_REQTYPE_DIR_TO_HOST << 7) |
136 				      (USB_REQTYPE_TYPE_VENDOR << 5);
137 	const uint8_t bRequest = FOOBAZ_VREQ_IN;
138 	const uint16_t wValue = 0x0000;
139 	struct net_buf *buf;
140 	uint16_t wLength;
141 	int ret;
142 
143 	wLength = MIN(sizeof(vreq_test_buf), strtol(argv[1], NULL, 10));
144 	buf = usbh_xfer_buf_alloc(udev, wLength);
145 	if (!buf) {
146 		shell_print(sh, "host: Failed to allocate buffer");
147 		return -ENOMEM;
148 	}
149 
150 	ret = usbh_req_setup(udev, bmRequestType, bRequest, wValue, 0, wLength, buf);
151 	if (ret == 0) {
152 		memcpy(vreq_test_buf, buf->data, MIN(buf->len, wLength));
153 	}
154 
155 	usbh_xfer_buf_free(udev, buf);
156 
157 	return ret;
158 }
159 
cmd_vendor_out(const struct shell * sh,size_t argc,char ** argv)160 static int cmd_vendor_out(const struct shell *sh,
161 			  size_t argc, char **argv)
162 {
163 	const uint8_t bmRequestType = (USB_REQTYPE_DIR_TO_DEVICE << 7) |
164 				      (USB_REQTYPE_TYPE_VENDOR << 5);
165 	const uint8_t bRequest = FOOBAZ_VREQ_OUT;
166 	const uint16_t wValue = 0x0000;
167 	struct net_buf *buf;
168 	uint16_t wLength;
169 	int ret;
170 
171 	wLength = MIN(sizeof(vreq_test_buf), strtol(argv[1], NULL, 10));
172 	buf = usbh_xfer_buf_alloc(udev, wLength);
173 	if (!buf) {
174 		shell_print(sh, "host: Failed to allocate buffer");
175 		return -ENOMEM;
176 	}
177 
178 	net_buf_add_mem(buf, &vreq_test_buf, wLength);
179 	ret = usbh_req_setup(udev, bmRequestType, bRequest, wValue, 0, wLength, buf);
180 	usbh_xfer_buf_free(udev, buf);
181 
182 	return ret;
183 }
184 
cmd_desc_device(const struct shell * sh,size_t argc,char ** argv)185 static int cmd_desc_device(const struct shell *sh,
186 			   size_t argc, char **argv)
187 {
188 	struct usb_device_descriptor desc;
189 	int err;
190 
191 	err = usbh_req_desc_dev(udev, sizeof(desc), &desc);
192 	if (err) {
193 		shell_print(sh, "host: Failed to request device descriptor");
194 	} else {
195 		print_dev_desc(sh, &desc);
196 	}
197 
198 	return err;
199 }
200 
cmd_desc_config(const struct shell * sh,size_t argc,char ** argv)201 static int cmd_desc_config(const struct shell *sh,
202 			   size_t argc, char **argv)
203 {
204 	struct usb_cfg_descriptor desc;
205 	uint8_t cfg;
206 	int err;
207 
208 	cfg = strtol(argv[1], NULL, 10);
209 
210 	err = usbh_req_desc_cfg(udev, cfg, sizeof(desc), &desc);
211 	if (err) {
212 		shell_print(sh, "host: Failed to request configuration descriptor");
213 	} else {
214 		print_cfg_desc(sh, &desc);
215 	}
216 
217 	return err;
218 }
219 
cmd_desc_string(const struct shell * sh,size_t argc,char ** argv)220 static int cmd_desc_string(const struct shell *sh,
221 			   size_t argc, char **argv)
222 {
223 	const uint8_t type = USB_DESC_STRING;
224 	struct net_buf *buf;
225 	uint8_t id;
226 	uint8_t idx;
227 	int err;
228 
229 	id = strtol(argv[1], NULL, 10);
230 	idx = strtol(argv[2], NULL, 10);
231 
232 	buf = usbh_xfer_buf_alloc(udev, 128);
233 	if (!buf) {
234 		return -ENOMEM;
235 	}
236 
237 	err = usbh_req_desc(udev, type, idx, id, 128, buf);
238 	if (err) {
239 		shell_print(sh, "host: Failed to request configuration descriptor");
240 	} else {
241 		shell_hexdump(sh, buf->data, buf->len);
242 	}
243 
244 	usbh_xfer_buf_free(udev, buf);
245 
246 	return err;
247 }
248 
cmd_feature_set_halt(const struct shell * sh,size_t argc,char ** argv)249 static int cmd_feature_set_halt(const struct shell *sh,
250 				size_t argc, char **argv)
251 {
252 	uint8_t ep;
253 	int err;
254 
255 	ep = strtol(argv[1], NULL, 16);
256 
257 	/* TODO: add usbh_req_set_sfs_halt(&uhs_ctx, NULL, 0); */
258 	err = usbh_req_set_sfs_rwup(udev);
259 	if (err) {
260 		shell_error(sh, "host: Failed to set halt feature");
261 	} else {
262 		shell_print(sh, "host: Device 0x%02x, ep 0x%02x halt feature set",
263 			    udev->addr, ep);
264 	}
265 
266 	return err;
267 }
268 
cmd_feature_clear_rwup(const struct shell * sh,size_t argc,char ** argv)269 static int cmd_feature_clear_rwup(const struct shell *sh,
270 				  size_t argc, char **argv)
271 {
272 	int err;
273 
274 	err = usbh_req_clear_sfs_rwup(udev);
275 	if (err) {
276 		shell_error(sh, "host: Failed to clear rwup feature");
277 	} else {
278 		shell_print(sh, "host: Device 0x%02x, rwup feature cleared", udev->addr);
279 	}
280 
281 	return err;
282 }
283 
cmd_feature_set_rwup(const struct shell * sh,size_t argc,char ** argv)284 static int cmd_feature_set_rwup(const struct shell *sh,
285 				size_t argc, char **argv)
286 {
287 	int err;
288 
289 	err = usbh_req_set_sfs_rwup(udev);
290 	if (err) {
291 		shell_error(sh, "host: Failed to set rwup feature");
292 	} else {
293 		shell_print(sh, "host: Device 0x%02x, rwup feature set", udev->addr);
294 	}
295 
296 	return err;
297 }
298 
cmd_feature_set_ppwr(const struct shell * sh,size_t argc,char ** argv)299 static int cmd_feature_set_ppwr(const struct shell *sh,
300 				size_t argc, char **argv)
301 {
302 	uint8_t port;
303 	int err;
304 
305 	port = strtol(argv[1], NULL, 10);
306 
307 	err = usbh_req_set_hcfs_ppwr(udev, port);
308 	if (err) {
309 		shell_error(sh, "host: Failed to set ppwr feature");
310 	} else {
311 		shell_print(sh, "host: Device 0x%02x, port %d, ppwr feature set",
312 			    udev->addr, port);
313 	}
314 
315 	return err;
316 }
317 
cmd_feature_set_prst(const struct shell * sh,size_t argc,char ** argv)318 static int cmd_feature_set_prst(const struct shell *sh,
319 				size_t argc, char **argv)
320 {
321 	uint8_t port;
322 	int err;
323 
324 	port = strtol(argv[1], NULL, 10);
325 
326 	err = usbh_req_set_hcfs_prst(udev, port);
327 	if (err) {
328 		shell_error(sh, "host: Failed to set prst feature");
329 	} else {
330 		shell_print(sh, "host: Device 0x%02x, port %d, prst feature set",
331 			    udev->addr, port);
332 	}
333 
334 	return err;
335 }
336 
cmd_config_set(const struct shell * sh,size_t argc,char ** argv)337 static int cmd_config_set(const struct shell *sh,
338 			  size_t argc, char **argv)
339 {
340 	uint8_t cfg;
341 	int err;
342 
343 	cfg = strtol(argv[1], NULL, 10);
344 
345 	err = usbh_req_set_cfg(udev, cfg);
346 	if (err) {
347 		shell_error(sh, "host: Failed to set configuration");
348 	} else {
349 		shell_print(sh, "host: Device 0x%02x, new configuration %u",
350 			    udev->addr, cfg);
351 	}
352 
353 	return err;
354 }
355 
cmd_config_get(const struct shell * sh,size_t argc,char ** argv)356 static int cmd_config_get(const struct shell *sh,
357 			  size_t argc, char **argv)
358 {
359 	uint8_t cfg;
360 	int err;
361 
362 	err = usbh_req_get_cfg(udev, &cfg);
363 	if (err) {
364 		shell_error(sh, "host: Failed to set configuration");
365 	} else {
366 		shell_print(sh, "host: Device 0x%02x, current configuration %u",
367 			    udev->addr, cfg);
368 	}
369 
370 	return err;
371 }
372 
cmd_device_interface(const struct shell * sh,size_t argc,char ** argv)373 static int cmd_device_interface(const struct shell *sh,
374 				size_t argc, char **argv)
375 {
376 	uint8_t iface;
377 	uint8_t alt;
378 	int err;
379 
380 	iface = strtol(argv[1], NULL, 10);
381 	alt = strtol(argv[2], NULL, 10);
382 
383 	err = usbh_req_set_alt(udev, iface, alt);
384 	if (err) {
385 		shell_error(sh, "host: Failed to set interface alternate");
386 	} else {
387 		shell_print(sh, "host: Device 0x%02x, new %u alternate %u",
388 			    udev->addr, iface, alt);
389 	}
390 
391 	return err;
392 }
393 
cmd_device_address(const struct shell * sh,size_t argc,char ** argv)394 static int cmd_device_address(const struct shell *sh,
395 			      size_t argc, char **argv)
396 {
397 	uint8_t addr;
398 	int err;
399 
400 	addr = strtol(argv[1], NULL, 10);
401 
402 	err = usbh_req_set_address(udev, addr);
403 	if (err) {
404 		shell_error(sh, "host: Failed to set address");
405 	} else {
406 		shell_print(sh, "host: New device address is 0x%02x", addr);
407 	}
408 
409 	return err;
410 }
411 
cmd_bus_suspend(const struct shell * sh,size_t argc,char ** argv)412 static int cmd_bus_suspend(const struct shell *sh,
413 			   size_t argc, char **argv)
414 {
415 	int err;
416 
417 	err = uhc_bus_suspend(uhs_ctx.dev);
418 	if (err) {
419 		shell_error(sh, "host: Failed to perform bus suspend %d", err);
420 	} else {
421 		shell_print(sh, "host: USB bus suspended");
422 	}
423 
424 	return err;
425 }
426 
cmd_bus_resume(const struct shell * sh,size_t argc,char ** argv)427 static int cmd_bus_resume(const struct shell *sh,
428 			  size_t argc, char **argv)
429 {
430 	int err;
431 
432 	err = uhc_bus_resume(uhs_ctx.dev);
433 	if (err) {
434 		shell_error(sh, "host: Failed to perform bus resume %d", err);
435 	} else {
436 		shell_print(sh, "host: USB bus resumed");
437 	}
438 
439 	err = uhc_sof_enable(uhs_ctx.dev);
440 	if (err) {
441 		shell_error(sh, "host: Failed to start SoF generator %d", err);
442 	}
443 
444 	return err;
445 }
446 
cmd_bus_reset(const struct shell * sh,size_t argc,char ** argv)447 static int cmd_bus_reset(const struct shell *sh,
448 			 size_t argc, char **argv)
449 {
450 	int err;
451 
452 	err = uhc_bus_reset(uhs_ctx.dev);
453 	if (err) {
454 		shell_error(sh, "host: Failed to perform bus reset %d", err);
455 	} else {
456 		shell_print(sh, "host: USB bus reset");
457 	}
458 
459 	err = uhc_sof_enable(uhs_ctx.dev);
460 	if (err) {
461 		shell_error(sh, "host: Failed to start SoF generator %d", err);
462 	}
463 
464 	return err;
465 }
466 
cmd_usbh_init(const struct shell * sh,size_t argc,char ** argv)467 static int cmd_usbh_init(const struct shell *sh,
468 			 size_t argc, char **argv)
469 {
470 	int err;
471 
472 	udev = usbh_device_get_any(&uhs_ctx);
473 
474 	err = usbh_init(&uhs_ctx);
475 	if (err == -EALREADY) {
476 		shell_error(sh, "host: USB host already initialized");
477 	} else if (err) {
478 		shell_error(sh, "host: Failed to initialize %d", err);
479 	} else {
480 		shell_print(sh, "host: USB host initialized");
481 	}
482 
483 	return err;
484 }
485 
cmd_usbh_enable(const struct shell * sh,size_t argc,char ** argv)486 static int cmd_usbh_enable(const struct shell *sh,
487 			   size_t argc, char **argv)
488 {
489 	int err;
490 
491 	err = usbh_enable(&uhs_ctx);
492 	if (err) {
493 		shell_error(sh, "host: Failed to enable USB host support");
494 	} else {
495 		shell_print(sh, "host: USB host enabled");
496 	}
497 
498 	return err;
499 }
500 
cmd_usbh_disable(const struct shell * sh,size_t argc,char ** argv)501 static int cmd_usbh_disable(const struct shell *sh,
502 			    size_t argc, char **argv)
503 {
504 	int err;
505 
506 	err = usbh_disable(&uhs_ctx);
507 	if (err) {
508 		shell_error(sh, "host: Failed to disable USB host support");
509 	} else {
510 		shell_print(sh, "host: USB host disabled");
511 	}
512 
513 	return err;
514 }
515 
516 SHELL_STATIC_SUBCMD_SET_CREATE(desc_cmds,
517 	SHELL_CMD_ARG(device, NULL, NULL,
518 		      cmd_desc_device, 1, 0),
519 	SHELL_CMD_ARG(configuration, NULL, "<index>",
520 		      cmd_desc_config, 2, 0),
521 	SHELL_CMD_ARG(string, NULL, "<id> <index>",
522 		      cmd_desc_string, 3, 0),
523 	SHELL_SUBCMD_SET_END
524 );
525 
526 SHELL_STATIC_SUBCMD_SET_CREATE(feature_set_cmds,
527 	SHELL_CMD_ARG(rwup, NULL, NULL,
528 		      cmd_feature_set_rwup, 1, 0),
529 	SHELL_CMD_ARG(ppwr, NULL, "<port>",
530 		      cmd_feature_set_ppwr, 2, 0),
531 	SHELL_CMD_ARG(prst, NULL, "<port>",
532 		      cmd_feature_set_prst, 2, 0),
533 	SHELL_CMD_ARG(halt, NULL, "<endpoint>",
534 		      cmd_feature_set_halt, 2, 0),
535 	SHELL_SUBCMD_SET_END
536 );
537 
538 SHELL_STATIC_SUBCMD_SET_CREATE(feature_clear_cmds,
539 	SHELL_CMD_ARG(rwup, NULL, NULL,
540 		      cmd_feature_clear_rwup, 1, 0),
541 	SHELL_CMD_ARG(halt, NULL, "<endpoint>",
542 		      cmd_feature_set_halt, 2, 0),
543 	SHELL_SUBCMD_SET_END
544 );
545 
546 SHELL_STATIC_SUBCMD_SET_CREATE(config_cmds,
547 	SHELL_CMD_ARG(get, NULL, NULL,
548 		      cmd_config_get, 1, 0),
549 	SHELL_CMD_ARG(set, NULL, "<configuration>",
550 		      cmd_config_set, 2, 0),
551 	SHELL_SUBCMD_SET_END
552 );
553 
554 SHELL_STATIC_SUBCMD_SET_CREATE(device_cmds,
555 	SHELL_CMD_ARG(address, NULL, "<address>",
556 		      cmd_device_address, 2, 0),
557 	SHELL_CMD_ARG(config, &config_cmds, "get|set configuration",
558 		      NULL, 1, 0),
559 	SHELL_CMD_ARG(interface, NULL, "<interface> <alternate>",
560 		      cmd_device_interface, 3, 0),
561 	SHELL_CMD_ARG(descriptor, &desc_cmds, "descriptor request",
562 		      NULL, 1, 0),
563 	SHELL_CMD_ARG(feature-set, &feature_set_cmds, "feature selector",
564 		      NULL, 1, 0),
565 	SHELL_CMD_ARG(feature-clear, &feature_clear_cmds, "feature selector",
566 		      NULL, 1, 0),
567 	SHELL_CMD_ARG(vendor_in, NULL, "<length>",
568 		      cmd_vendor_in, 2, 0),
569 	SHELL_CMD_ARG(vendor_out, NULL, "<length>",
570 		      cmd_vendor_out, 2, 0),
571 	SHELL_CMD_ARG(bulk, NULL, "<endpoint> <length>",
572 		      cmd_bulk, 3, 0),
573 	SHELL_SUBCMD_SET_END
574 );
575 
576 SHELL_STATIC_SUBCMD_SET_CREATE(bus_cmds,
577 	SHELL_CMD_ARG(suspend, NULL, "[nono]",
578 		      cmd_bus_suspend, 1, 0),
579 	SHELL_CMD_ARG(resume, NULL, "[nono]",
580 		      cmd_bus_resume, 1, 0),
581 	SHELL_CMD_ARG(reset, NULL, "[nono]",
582 		      cmd_bus_reset, 1, 0),
583 	SHELL_SUBCMD_SET_END
584 );
585 
586 SHELL_STATIC_SUBCMD_SET_CREATE(sub_usbh_cmds,
587 	SHELL_CMD_ARG(init, NULL, "[none]",
588 		      cmd_usbh_init, 1, 0),
589 	SHELL_CMD_ARG(enable, NULL, "[none]",
590 		      cmd_usbh_enable, 1, 0),
591 	SHELL_CMD_ARG(disable, NULL, "[none]",
592 		      cmd_usbh_disable, 1, 0),
593 	SHELL_CMD_ARG(bus, &bus_cmds, "bus commands",
594 		      NULL, 1, 0),
595 	SHELL_CMD_ARG(device, &device_cmds, "device commands",
596 		      NULL, 1, 0),
597 	SHELL_SUBCMD_SET_END
598 );
599 
600 SHELL_CMD_REGISTER(usbh, &sub_usbh_cmds, "USBH commands", NULL);
601