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