1 /*
2 * Copyright (c) 2022,2024 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/usb/usbd.h>
13 #include <zephyr/drivers/usb/udc.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/sys/iterable_sections.h>
16
17 /* Default configurations used in the shell context. */
18 USBD_CONFIGURATION_DEFINE(config_1_fs, USB_SCD_REMOTE_WAKEUP, 200, NULL);
19 USBD_CONFIGURATION_DEFINE(config_1_hs, USB_SCD_REMOTE_WAKEUP, 200, NULL);
20 USBD_CONFIGURATION_DEFINE(config_2_fs, USB_SCD_SELF_POWERED, 200, NULL);
21 USBD_CONFIGURATION_DEFINE(config_2_hs, USB_SCD_SELF_POWERED, 200, NULL);
22
23 static struct usbd_shell_config {
24 struct usbd_config_node *cfg_nd;
25 enum usbd_speed speed;
26 const char *name;
27 } sh_configs[] = {
28 {.cfg_nd = &config_1_fs, .speed = USBD_SPEED_FS, .name = "FS1",},
29 {.cfg_nd = &config_1_hs, .speed = USBD_SPEED_HS, .name = "HS1",},
30 {.cfg_nd = &config_2_fs, .speed = USBD_SPEED_FS, .name = "FS2",},
31 {.cfg_nd = &config_2_hs, .speed = USBD_SPEED_HS, .name = "HS2",},
32 };
33
34 static struct usbd_shell_speed {
35 enum usbd_speed speed;
36 const char *name;
37 } sh_speed[] = {
38 {.speed = USBD_SPEED_FS, .name = "fs",},
39 {.speed = USBD_SPEED_HS, .name = "hs",},
40 };
41
42 /* Default string descriptors used in the shell context. */
43 USBD_DESC_LANG_DEFINE(lang);
44 USBD_DESC_MANUFACTURER_DEFINE(mfr, "ZEPHYR");
45 USBD_DESC_PRODUCT_DEFINE(product, "Zephyr USBD foobaz");
46 IF_ENABLED(CONFIG_HWINFO, (USBD_DESC_SERIAL_NUMBER_DEFINE(sn)));
47
48 /* Default device descriptors and context used in the shell. */
49 USBD_DEVICE_DEFINE(sh_uds_ctx, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
50 0x2fe3, 0xffff);
51
52 static struct usbd_context *my_uds_ctx = &sh_uds_ctx;
53 static enum usbd_speed current_cmd_speed = USBD_SPEED_FS;
54
cmd_wakeup_request(const struct shell * sh,size_t argc,char ** argv)55 static int cmd_wakeup_request(const struct shell *sh,
56 size_t argc, char **argv)
57 {
58 int err;
59
60 err = usbd_wakeup_request(my_uds_ctx);
61 if (err) {
62 shell_error(sh, "dev: Failed to wakeup remote %d", err);
63 } else {
64 shell_print(sh, "dev: Requested remote wakeup");
65 }
66
67 return err;
68 }
69
cmd_register(const struct shell * sh,size_t argc,char ** argv)70 static int cmd_register(const struct shell *sh,
71 size_t argc, char **argv)
72 {
73 uint8_t cfg;
74 int ret;
75
76 cfg = strtol(argv[3], NULL, 10);
77 ret = usbd_register_class(my_uds_ctx, argv[1], current_cmd_speed, cfg);
78 if (ret) {
79 shell_error(sh,
80 "dev: failed to register USB class %s to configuration %s %u",
81 argv[1], argv[2], cfg);
82 } else {
83 shell_print(sh,
84 "dev: register USB class %s to configuration %s %u",
85 argv[1], argv[2], cfg);
86 }
87
88 return ret;
89 }
90
cmd_unregister(const struct shell * sh,size_t argc,char ** argv)91 static int cmd_unregister(const struct shell *sh,
92 size_t argc, char **argv)
93 {
94 uint8_t cfg;
95 int ret;
96
97 cfg = strtol(argv[3], NULL, 10);
98 ret = usbd_unregister_class(my_uds_ctx, argv[1], current_cmd_speed, cfg);
99 if (ret) {
100 shell_error(sh,
101 "dev: failed to remove USB class %s from configuration %s %u",
102 argv[1], argv[2], cfg);
103 } else {
104 shell_print(sh,
105 "dev: removed USB class %s from configuration %s %u",
106 argv[1], argv[2], cfg);
107 }
108
109 return ret;
110 }
111
cmd_usbd_default_strings(const struct shell * sh,size_t argc,char ** argv)112 static int cmd_usbd_default_strings(const struct shell *sh,
113 size_t argc, char **argv)
114 {
115 int err;
116
117 err = usbd_add_descriptor(my_uds_ctx, &lang);
118 err |= usbd_add_descriptor(my_uds_ctx, &mfr);
119 err |= usbd_add_descriptor(my_uds_ctx, &product);
120 IF_ENABLED(CONFIG_HWINFO, (
121 err |= usbd_add_descriptor(my_uds_ctx, &sn);
122 ))
123
124 if (err) {
125 shell_error(sh, "dev: Failed to add default string descriptors, %d", err);
126 } else {
127 shell_print(sh, "dev: added default string descriptors");
128 }
129
130 return err;
131 }
132
register_classes(const struct shell * sh)133 static int register_classes(const struct shell *sh)
134 {
135 int err;
136
137 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
138 err = usbd_register_class(my_uds_ctx, c_nd->c_data->name,
139 USBD_SPEED_FS, 1);
140 if (err) {
141 shell_error(sh,
142 "dev: failed to register FS %s (%d)",
143 c_nd->c_data->name, err);
144 return err;
145 }
146
147 shell_print(sh, "dev: register FS %s", c_nd->c_data->name);
148 }
149
150 if (!USBD_SUPPORTS_HIGH_SPEED ||
151 usbd_caps_speed(my_uds_ctx) != USBD_SPEED_HS) {
152 return 0;
153 }
154
155 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
156 err = usbd_register_class(my_uds_ctx, c_nd->c_data->name,
157 USBD_SPEED_HS, 1);
158 if (err) {
159 shell_error(sh,
160 "dev: failed to register HS %s (%d)",
161 c_nd->c_data->name, err);
162 return err;
163 }
164
165 shell_print(sh, "dev: register HS %s", c_nd->c_data->name);
166 }
167
168 return 0;
169 }
170
cmd_usbd_init(const struct shell * sh,size_t argc,char ** argv)171 static int cmd_usbd_init(const struct shell *sh,
172 size_t argc, char **argv)
173 {
174 int err;
175
176 err = usbd_init(my_uds_ctx);
177
178 if (err == -EALREADY) {
179 shell_error(sh, "dev: USB already initialized");
180 } else if (err) {
181 shell_error(sh, "dev: Failed to initialize device support (%d)", err);
182 } else {
183 shell_print(sh, "dev: USB initialized");
184 }
185
186 return err;
187 }
188
cmd_usbd_default_config(const struct shell * sh,size_t argc,char ** argv)189 static int cmd_usbd_default_config(const struct shell *sh,
190 size_t argc, char **argv)
191 {
192 int err;
193
194 err = cmd_usbd_default_strings(sh, 0, NULL);
195 if (err) {
196 return err;
197 }
198
199 if (usbd_caps_speed(my_uds_ctx) == USBD_SPEED_HS) {
200 err = usbd_add_configuration(my_uds_ctx, USBD_SPEED_HS, &config_1_hs);
201 if (err) {
202 shell_error(sh, "dev: Failed to add HS configuration");
203 return err;
204 }
205 }
206
207 err = usbd_add_configuration(my_uds_ctx, USBD_SPEED_FS, &config_1_fs);
208 if (err) {
209 shell_error(sh, "dev: Failed to add FS configuration");
210 return err;
211 }
212
213 err = register_classes(sh);
214 if (err) {
215 return err;
216 }
217
218 return cmd_usbd_init(sh, 0, NULL);
219 }
220
cmd_usbd_enable(const struct shell * sh,size_t argc,char ** argv)221 static int cmd_usbd_enable(const struct shell *sh,
222 size_t argc, char **argv)
223 {
224 int err;
225
226 err = usbd_enable(my_uds_ctx);
227
228 if (err == -EALREADY) {
229 shell_error(sh, "dev: USB already enabled");
230 } else if (err) {
231 shell_error(sh, "dev: Failed to enable USB, error %d", err);
232 } else {
233 shell_print(sh, "dev: USB enabled");
234 }
235
236 return err;
237 }
238
cmd_usbd_disable(const struct shell * sh,size_t argc,char ** argv)239 static int cmd_usbd_disable(const struct shell *sh,
240 size_t argc, char **argv)
241 {
242 int err;
243
244 err = usbd_disable(my_uds_ctx);
245
246 if (err) {
247 shell_error(sh, "dev: Failed to disable USB");
248 return err;
249 }
250
251 shell_print(sh, "dev: USB disabled");
252
253 return 0;
254 }
255
cmd_usbd_shutdown(const struct shell * sh,size_t argc,char ** argv)256 static int cmd_usbd_shutdown(const struct shell *sh,
257 size_t argc, char **argv)
258 {
259 int err;
260
261 err = usbd_shutdown(my_uds_ctx);
262
263 if (err) {
264 shell_error(sh, "dev: Failed to shutdown USB");
265 return err;
266 }
267
268 shell_print(sh, "dev: USB completely disabled");
269
270 return 0;
271 }
272
cmd_select(const struct shell * sh,size_t argc,char ** argv)273 static int cmd_select(const struct shell *sh, size_t argc, char **argv)
274 {
275 STRUCT_SECTION_FOREACH(usbd_context, ctx) {
276 if (strcmp(argv[1], ctx->name) == 0) {
277 my_uds_ctx = ctx;
278 shell_print(sh,
279 "dev: select %s as my USB device context",
280 argv[1]);
281
282 return 0;
283 }
284 }
285
286 shell_error(sh, "dev: failed to select %s", argv[1]);
287
288 return -ENODEV;
289 }
290
cmd_device_bcd_usb(const struct shell * sh,size_t argc,char * argv[])291 static int cmd_device_bcd_usb(const struct shell *sh, size_t argc,
292 char *argv[])
293 {
294 uint16_t bcd;
295 int ret;
296
297 bcd = strtol(argv[2], NULL, 16);
298 ret = usbd_device_set_bcd_usb(my_uds_ctx, current_cmd_speed, bcd);
299 if (ret) {
300 shell_error(sh, "dev: failed to set device bcdUSB to %x", bcd);
301 } else {
302 shell_error(sh, "dev: set device bcdUSB to %x", bcd);
303 }
304
305 return ret;
306 }
307
cmd_device_pid(const struct shell * sh,size_t argc,char * argv[])308 static int cmd_device_pid(const struct shell *sh, size_t argc,
309 char *argv[])
310 {
311 uint16_t pid;
312 int ret;
313
314 pid = strtol(argv[1], NULL, 16);
315 ret = usbd_device_set_pid(my_uds_ctx, pid);
316 if (ret) {
317 shell_error(sh, "dev: failed to set device idProduct to %x", pid);
318 }
319
320 return ret;
321 }
322
cmd_device_vid(const struct shell * sh,size_t argc,char * argv[])323 static int cmd_device_vid(const struct shell *sh, size_t argc,
324 char *argv[])
325 {
326 uint16_t vid;
327 int ret;
328
329 vid = strtol(argv[1], NULL, 16);
330 ret = usbd_device_set_vid(my_uds_ctx, vid);
331 if (ret) {
332 shell_error(sh, "dev: failed to set device idVendor to %x", vid);
333 }
334
335 return ret;
336 }
337
cmd_device_code_triple(const struct shell * sh,size_t argc,char * argv[])338 static int cmd_device_code_triple(const struct shell *sh, size_t argc,
339 char *argv[])
340 {
341 uint8_t class, subclass, protocol;
342 int ret;
343
344 class = strtol(argv[2], NULL, 16);
345 subclass = strtol(argv[3], NULL, 16);
346 protocol = strtol(argv[4], NULL, 16);
347 ret = usbd_device_set_code_triple(my_uds_ctx, current_cmd_speed,
348 class, subclass, protocol);
349 if (ret) {
350 shell_error(sh, "dev: failed to set device code triple to %x %x %x",
351 class, subclass, protocol);
352 } else {
353 shell_error(sh, "dev: set device code triple to %x %x %x",
354 class, subclass, protocol);
355 }
356
357 return ret;
358 }
359
cmd_config_add(const struct shell * sh,size_t argc,char * argv[])360 static int cmd_config_add(const struct shell *sh, size_t argc,
361 char *argv[])
362 {
363 int ret = -EINVAL;
364
365 for (unsigned int i = 0; i < ARRAY_SIZE(sh_configs); i++) {
366 if (!strcmp(argv[1], sh_configs[i].name)) {
367 ret = usbd_add_configuration(my_uds_ctx,
368 sh_configs[i].speed,
369 sh_configs[i].cfg_nd);
370 break;
371 }
372 }
373
374 if (ret) {
375 shell_error(sh, "dev: failed to add configuration %s", argv[1]);
376 }
377
378 return ret;
379 }
380
cmd_config_set_selfpowered(const struct shell * sh,const bool self,size_t argc,char * argv[])381 static int cmd_config_set_selfpowered(const struct shell *sh, const bool self,
382 size_t argc, char *argv[])
383 {
384 uint8_t cfg;
385 int ret;
386
387 cfg = strtol(argv[2], NULL, 10);
388
389 ret = usbd_config_attrib_self(my_uds_ctx, current_cmd_speed, cfg, self);
390 if (ret) {
391 shell_error(sh,
392 "dev: failed to set attribute Self-powered to %u",
393 cfg);
394 } else {
395 shell_print(sh,
396 "dev: set configuration %u attribute Self-powered to %u",
397 cfg, self);
398 }
399
400 return ret;
401 }
402
cmd_config_selfpowered(const struct shell * sh,size_t argc,char * argv[])403 static int cmd_config_selfpowered(const struct shell *sh,
404 size_t argc, char *argv[])
405 {
406 return cmd_config_set_selfpowered(sh, true, argc, argv);
407 }
408
cmd_config_buspowered(const struct shell * sh,size_t argc,char * argv[])409 static int cmd_config_buspowered(const struct shell *sh,
410 size_t argc, char *argv[])
411 {
412 return cmd_config_set_selfpowered(sh, false, argc, argv);
413 }
414
cmd_config_rwup(const struct shell * sh,const bool rwup,size_t argc,char * argv[])415 static int cmd_config_rwup(const struct shell *sh, const bool rwup,
416 size_t argc, char *argv[])
417 {
418 uint8_t cfg;
419 int ret;
420
421 cfg = strtol(argv[2], NULL, 10);
422
423 ret = usbd_config_attrib_rwup(my_uds_ctx, current_cmd_speed, cfg, rwup);
424 if (ret) {
425 shell_error(sh,
426 "dev: failed set configuration %u Remote Wakeup to %u",
427 cfg, rwup);
428 } else {
429 shell_print(sh,
430 "dev: set configuration %u Remote Wakeup to %u",
431 cfg, rwup);
432 }
433
434 return ret;
435 }
436
cmd_config_set_rwup(const struct shell * sh,size_t argc,char * argv[])437 static int cmd_config_set_rwup(const struct shell *sh,
438 size_t argc, char *argv[])
439 {
440 return cmd_config_rwup(sh, true, argc, argv);
441 }
442
cmd_config_clear_rwup(const struct shell * sh,size_t argc,char * argv[])443 static int cmd_config_clear_rwup(const struct shell *sh,
444 size_t argc, char *argv[])
445 {
446 return cmd_config_rwup(sh, false, argc, argv);
447 }
448
cmd_config_power(const struct shell * sh,size_t argc,char * argv[])449 static int cmd_config_power(const struct shell *sh, size_t argc,
450 char *argv[])
451 {
452 uint16_t power;
453 uint8_t cfg;
454 int ret;
455
456 cfg = strtol(argv[2], NULL, 10);
457 power = strtol(argv[3], NULL, 10);
458
459 if (power > UINT8_MAX) {
460 power = UINT8_MAX;
461 shell_print(sh, "dev: limit bMaxPower value to %u", power);
462 }
463
464 ret = usbd_config_maxpower(my_uds_ctx, current_cmd_speed, cfg, power);
465 if (ret) {
466 shell_error(sh,
467 "dev: failed to set configuration %u bMaxPower value to %u",
468 cfg, power);
469 } else {
470 shell_print(sh,
471 "dev: set configuration %u bMaxPower value to %u",
472 cfg, power);
473 }
474
475 return ret;
476 }
477
configuration_speed(size_t idx,struct shell_static_entry * entry)478 static void configuration_speed(size_t idx, struct shell_static_entry *entry)
479 {
480 size_t match_idx = 0;
481
482 entry->syntax = NULL;
483 entry->handler = NULL;
484 entry->help = NULL;
485 entry->subcmd = NULL;
486
487 for (unsigned int i = 0; i < ARRAY_SIZE(sh_speed); i++) {
488 if (match_idx == idx) {
489 entry->syntax = sh_speed[i].name;
490 current_cmd_speed = sh_speed[i].speed;
491 break;
492 }
493
494 ++match_idx;
495 }
496 }
497
498 SHELL_DYNAMIC_CMD_CREATE(dsub_config_speed, configuration_speed);
499
configuration_lookup(size_t idx,struct shell_static_entry * entry)500 static void configuration_lookup(size_t idx, struct shell_static_entry *entry)
501 {
502 size_t match_idx = 0;
503
504 entry->syntax = NULL;
505 entry->handler = NULL;
506 entry->help = NULL;
507 entry->subcmd = NULL;
508
509 for (unsigned int i = 0; i < ARRAY_SIZE(sh_configs); i++) {
510 if (match_idx == idx) {
511 entry->syntax = sh_configs[i].name;
512 break;
513 }
514
515 ++match_idx;
516 }
517 }
518
519 SHELL_DYNAMIC_CMD_CREATE(dsub_config_name, configuration_lookup);
520
class_node_name_lookup(size_t idx,struct shell_static_entry * entry)521 static void class_node_name_lookup(size_t idx, struct shell_static_entry *entry)
522 {
523 size_t match_idx = 0;
524
525 entry->syntax = NULL;
526 entry->handler = NULL;
527 entry->help = NULL;
528 entry->subcmd = &dsub_config_speed;
529
530 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
531 if ((c_nd->c_data->name != NULL) &&
532 (strlen(c_nd->c_data->name) != 0)) {
533 if (match_idx == idx) {
534 entry->syntax = c_nd->c_data->name;
535 break;
536 }
537
538 ++match_idx;
539 }
540 }
541 }
542
device_context_lookup(size_t idx,struct shell_static_entry * entry)543 static void device_context_lookup(size_t idx, struct shell_static_entry *entry)
544 {
545 size_t match_idx = 0;
546
547 entry->syntax = NULL;
548 entry->handler = NULL;
549 entry->help = NULL;
550 entry->subcmd = NULL;
551
552 STRUCT_SECTION_FOREACH(usbd_context, ctx) {
553 if ((ctx->name != NULL) && (strlen(ctx->name) != 0)) {
554 if (match_idx == idx) {
555 entry->syntax = ctx->name;
556 break;
557 }
558
559 ++match_idx;
560 }
561 }
562 }
563
564 SHELL_DYNAMIC_CMD_CREATE(dsub_node_name, class_node_name_lookup);
565 SHELL_DYNAMIC_CMD_CREATE(dsub_context_name, device_context_lookup);
566
567 SHELL_STATIC_SUBCMD_SET_CREATE(device_cmds,
568 SHELL_CMD_ARG(pid, NULL,
569 "<idProduct> sets device Product ID",
570 cmd_device_pid, 2, 0),
571 SHELL_CMD_ARG(vid, NULL,
572 "<idVendor> sets device Vendor ID",
573 cmd_device_vid, 2, 0),
574 SHELL_CMD_ARG(bcd_usb, &dsub_config_speed,
575 "<speed> <bcdUSB> sets device USB specification version",
576 cmd_device_bcd_usb, 3, 0),
577 SHELL_CMD_ARG(triple, &dsub_config_speed,
578 "<speed> <Base Class> <SubClass> <Protocol> sets device code triple",
579 cmd_device_code_triple, 5, 0),
580 SHELL_SUBCMD_SET_END
581 );
582
583 SHELL_STATIC_SUBCMD_SET_CREATE(config_cmds,
584 SHELL_CMD_ARG(add, &dsub_config_name,
585 "<configuration name> adds one of the pre-defined configurations",
586 cmd_config_add, 2, 0),
587 SHELL_CMD_ARG(power, &dsub_config_speed,
588 "<speed> <configuration value> <bMaxPower> sets the bMaxPower",
589 cmd_config_power, 4, 0),
590 SHELL_CMD_ARG(set-rwup, &dsub_config_speed,
591 "<speed> <configuration value> sets Remote Wakeup bit",
592 cmd_config_set_rwup, 3, 0),
593 SHELL_CMD_ARG(clear-rwup, &dsub_config_speed,
594 "<speed> <configuration value> clears Remote Wakeup bit",
595 cmd_config_clear_rwup, 3, 0),
596 SHELL_CMD_ARG(selfpowered, &dsub_config_speed,
597 "<speed> <configuration value> sets Self-power bit",
598 cmd_config_selfpowered, 3, 0),
599 SHELL_CMD_ARG(buspowered, &dsub_config_speed,
600 "<speed> <configuration value> clears Self-power bit",
601 cmd_config_buspowered, 3, 0),
602 SHELL_SUBCMD_SET_END
603 );
604
605 SHELL_STATIC_SUBCMD_SET_CREATE(class_cmds,
606 SHELL_CMD_ARG(register, &dsub_node_name,
607 "<name> <speed> <configuration value> registers class instance",
608 cmd_register, 4, 0),
609 SHELL_CMD_ARG(unregister, &dsub_node_name,
610 "<name> <speed> <configuration value> unregisters class instance",
611 cmd_unregister, 4, 0),
612 SHELL_SUBCMD_SET_END
613 );
614
615 SHELL_STATIC_SUBCMD_SET_CREATE(sub_usbd_cmds,
616 SHELL_CMD_ARG(defstr, NULL,
617 "[none] adds default string descriptors",
618 cmd_usbd_default_strings, 1, 0),
619 SHELL_CMD_ARG(defcfg, NULL,
620 "[none] initializes default configuration with all available classes",
621 cmd_usbd_default_config, 1, 0),
622 SHELL_CMD_ARG(init, NULL,
623 "[none] initializes USB device support",
624 cmd_usbd_init, 1, 0),
625 SHELL_CMD_ARG(enable, NULL,
626 "[none] enables USB device support]",
627 cmd_usbd_enable, 1, 0),
628 SHELL_CMD_ARG(disable, NULL,
629 "[none] disables USB device support",
630 cmd_usbd_disable, 1, 0),
631 SHELL_CMD_ARG(shutdown, NULL,
632 "[none] shutdown USB device support",
633 cmd_usbd_shutdown, 1, 0),
634 SHELL_CMD_ARG(select, &dsub_context_name,
635 "<USB device context name> selects context used by the shell",
636 cmd_select, 2, 0),
637 SHELL_CMD_ARG(device, &device_cmds,
638 "device commands",
639 NULL, 1, 0),
640 SHELL_CMD_ARG(config, &config_cmds,
641 "configuration commands",
642 NULL, 1, 0),
643 SHELL_CMD_ARG(class, &class_cmds,
644 "class commands",
645 NULL, 1, 0),
646 SHELL_CMD_ARG(wakeup, NULL,
647 "[none] signals remote wakeup",
648 cmd_wakeup_request, 1, 0),
649 SHELL_SUBCMD_SET_END
650 );
651
652 SHELL_CMD_REGISTER(usbd, &sub_usbd_cmds, "USB device support commands", NULL);
653