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