diff --git a/subsys/usb/device_next/usbd_shell.c b/subsys/usb/device_next/usbd_shell.c index a7868b27fb..9f0a3e6721 100644 --- a/subsys/usb/device_next/usbd_shell.c +++ b/subsys/usb/device_next/usbd_shell.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Nordic Semiconductor ASA + * Copyright (c) 2022,2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,25 +14,46 @@ #include #include -const struct shell *ctx_shell; +/* Default configurations used in the shell context. */ +USBD_CONFIGURATION_DEFINE(config_1_fs, USB_SCD_REMOTE_WAKEUP, 200); +USBD_CONFIGURATION_DEFINE(config_1_hs, USB_SCD_REMOTE_WAKEUP, 200); +USBD_CONFIGURATION_DEFINE(config_2_fs, USB_SCD_SELF_POWERED, 200); +USBD_CONFIGURATION_DEFINE(config_2_hs, USB_SCD_SELF_POWERED, 200); -USBD_CONFIGURATION_DEFINE(config_baz, USB_SCD_REMOTE_WAKEUP, 200); -USBD_CONFIGURATION_DEFINE(config_foo, USB_SCD_SELF_POWERED, 200); +static struct usbd_shell_config { + struct usbd_config_node *cfg_nd; + enum usbd_speed speed; + const char *name; +} sh_configs[] = { + {.cfg_nd = &config_1_fs, .speed = USBD_SPEED_FS, .name = "FS1",}, + {.cfg_nd = &config_1_hs, .speed = USBD_SPEED_HS, .name = "HS1",}, + {.cfg_nd = &config_2_fs, .speed = USBD_SPEED_FS, .name = "FS2",}, + {.cfg_nd = &config_2_hs, .speed = USBD_SPEED_HS, .name = "HS2",}, +}; +static struct usbd_shell_speed { + enum usbd_speed speed; + const char *name; +} sh_speed[] = { + {.speed = USBD_SPEED_FS, .name = "fs",}, + {.speed = USBD_SPEED_HS, .name = "hs",}, +}; + +/* Default string descriptors used in the shell context. */ USBD_DESC_LANG_DEFINE(lang); USBD_DESC_MANUFACTURER_DEFINE(mfr, "ZEPHYR"); USBD_DESC_PRODUCT_DEFINE(product, "Zephyr USBD foobaz"); USBD_DESC_SERIAL_NUMBER_DEFINE(sn, "0123456789ABCDEF"); +/* Default device descriptors and context used in the shell. */ USBD_DEVICE_DEFINE(sh_uds_ctx, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), 0x2fe3, 0xffff); static struct usbd_contex *my_uds_ctx = &sh_uds_ctx; -/* TODO: Rework commands to allow specifying speed */ -static enum usbd_speed speed = USBD_SPEED_FS; +static enum usbd_speed current_cmd_speed = USBD_SPEED_FS; -int cmd_wakeup_request(const struct shell *sh, - size_t argc, char **argv) +static int cmd_wakeup_request(const struct shell *sh, + size_t argc, char **argv) { int err; @@ -52,17 +73,16 @@ static int cmd_register(const struct shell *sh, uint8_t cfg; int ret; - cfg = strtol(argv[2], NULL, 10); - ret = usbd_register_class(my_uds_ctx, argv[1], speed, cfg); - + cfg = strtol(argv[3], NULL, 10); + ret = usbd_register_class(my_uds_ctx, argv[1], current_cmd_speed, cfg); if (ret) { shell_error(sh, - "dev: failed to add USB class %s to configuration %u", - argv[1], cfg); + "dev: failed to register USB class %s to configuration %s %u", + argv[1], argv[2], cfg); } else { shell_print(sh, - "dev: added USB class %s to configuration %u", - argv[1], cfg); + "dev: register USB class %s to configuration %s %u", + argv[1], argv[2], cfg); } return ret; @@ -74,23 +94,23 @@ static int cmd_unregister(const struct shell *sh, uint8_t cfg; int ret; - cfg = strtol(argv[2], NULL, 10); - ret = usbd_unregister_class(my_uds_ctx, argv[1], speed, cfg); + cfg = strtol(argv[3], NULL, 10); + ret = usbd_unregister_class(my_uds_ctx, argv[1], current_cmd_speed, cfg); if (ret) { shell_error(sh, - "dev: failed to remove USB class %s from configuration %u", - argv[1], cfg); + "dev: failed to remove USB class %s from configuration %s %u", + argv[1], argv[2], cfg); } else { shell_print(sh, - "dev: removed USB class %s from configuration %u", - argv[1], cfg); + "dev: removed USB class %s from configuration %s %u", + argv[1], argv[2], cfg); } return ret; } -static int cmd_usbd_magic(const struct shell *sh, - size_t argc, char **argv) +static int cmd_usbd_default_strings(const struct shell *sh, + size_t argc, char **argv) { int err; @@ -100,52 +120,49 @@ static int cmd_usbd_magic(const struct shell *sh, err |= usbd_add_descriptor(my_uds_ctx, &sn); if (err) { - shell_error(sh, "dev: Failed to initialize descriptors, %d", err); - } - - err = usbd_add_configuration(my_uds_ctx, speed, &config_foo); - if (err) { - shell_error(sh, "dev: Failed to add configuration"); - } - - if (IS_ENABLED(CONFIG_USBD_LOOPBACK_CLASS)) { - err = usbd_register_class(my_uds_ctx, "loopback_0", speed, 1); - if (err) { - shell_error(sh, "dev: Failed to add loopback_0 class"); - } - } - - ctx_shell = sh; - err = usbd_init(my_uds_ctx); - if (err) { - shell_error(sh, "dev: Failed to initialize device support"); - } - - err = usbd_enable(my_uds_ctx); - if (err) { - shell_error(sh, "dev: Failed to enable device support"); + shell_error(sh, "dev: Failed to add default string descriptors, %d", err); + } else { + shell_print(sh, "dev: added default string descriptors"); } return err; } -static int cmd_usbd_defaults(const struct shell *sh, - size_t argc, char **argv) +static int register_classes(const struct shell *sh) { int err; - err = usbd_add_descriptor(my_uds_ctx, &lang); - err |= usbd_add_descriptor(my_uds_ctx, &mfr); - err |= usbd_add_descriptor(my_uds_ctx, &product); - err |= usbd_add_descriptor(my_uds_ctx, &sn); + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { + err = usbd_register_class(my_uds_ctx, c_nd->c_data->name, + USBD_SPEED_FS, 1); + if (err) { + shell_error(sh, + "dev: failed to register FS %s (%d)", + c_nd->c_data->name, err); + return err; + } - if (err) { - shell_error(sh, "dev: Failed to initialize descriptors, %d", err); - } else { - shell_print(sh, "dev: USB descriptors initialized"); + shell_print(sh, "dev: register FS %s", c_nd->c_data->name); } - return err; + if (usbd_caps_speed(my_uds_ctx) != USBD_SPEED_HS) { + return 0; + } + + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) { + err = usbd_register_class(my_uds_ctx, c_nd->c_data->name, + USBD_SPEED_HS, 1); + if (err) { + shell_error(sh, + "dev: failed to register HS %s (%d)", + c_nd->c_data->name, err); + return err; + } + + shell_print(sh, "dev: register HS %s", c_nd->c_data->name); + } + + return 0; } static int cmd_usbd_init(const struct shell *sh, @@ -153,13 +170,12 @@ static int cmd_usbd_init(const struct shell *sh, { int err; - ctx_shell = sh; err = usbd_init(my_uds_ctx); if (err == -EALREADY) { shell_error(sh, "dev: USB already initialized"); } else if (err) { - shell_error(sh, "dev: Failed to initialize %d", err); + shell_error(sh, "dev: Failed to initialize device support (%d)", err); } else { shell_print(sh, "dev: USB initialized"); } @@ -167,6 +183,38 @@ static int cmd_usbd_init(const struct shell *sh, return err; } +static int cmd_usbd_default_config(const struct shell *sh, + size_t argc, char **argv) +{ + int err; + + err = cmd_usbd_default_strings(sh, 0, NULL); + if (err) { + return err; + } + + if (usbd_caps_speed(my_uds_ctx) == USBD_SPEED_HS) { + err = usbd_add_configuration(my_uds_ctx, USBD_SPEED_HS, &config_1_hs); + if (err) { + shell_error(sh, "dev: Failed to add HS configuration"); + return err; + } + } + + err = usbd_add_configuration(my_uds_ctx, USBD_SPEED_FS, &config_1_fs); + if (err) { + shell_error(sh, "dev: Failed to add FS configuration"); + return err; + } + + err = register_classes(sh); + if (err) { + return err; + } + + return cmd_usbd_init(sh, 0, NULL); +} + static int cmd_usbd_enable(const struct shell *sh, size_t argc, char **argv) { @@ -243,10 +291,12 @@ static int cmd_device_bcd(const struct shell *sh, size_t argc, uint16_t bcd; int ret; - bcd = strtol(argv[1], NULL, 16); - ret = usbd_device_set_bcd(my_uds_ctx, speed, bcd); + bcd = strtol(argv[2], NULL, 16); + ret = usbd_device_set_bcd(my_uds_ctx, current_cmd_speed, bcd); if (ret) { shell_error(sh, "dev: failed to set device bcdUSB to %x", bcd); + } else { + shell_error(sh, "dev: set device bcdUSB to %x", bcd); } return ret; @@ -288,14 +338,17 @@ static int cmd_device_code_triple(const struct shell *sh, size_t argc, uint8_t class, subclass, protocol; int ret; - class = strtol(argv[1], NULL, 16); - subclass = strtol(argv[2], NULL, 16); - protocol = strtol(argv[3], NULL, 16); - ret = usbd_device_set_code_triple(my_uds_ctx, speed, + class = strtol(argv[2], NULL, 16); + subclass = strtol(argv[3], NULL, 16); + protocol = strtol(argv[4], NULL, 16); + ret = usbd_device_set_code_triple(my_uds_ctx, current_cmd_speed, class, subclass, protocol); if (ret) { shell_error(sh, "dev: failed to set device code triple to %x %x %x", class, subclass, protocol); + } else { + shell_error(sh, "dev: set device code triple to %x %x %x", + class, subclass, protocol); } return ret; @@ -304,93 +357,164 @@ static int cmd_device_code_triple(const struct shell *sh, size_t argc, static int cmd_config_add(const struct shell *sh, size_t argc, char *argv[]) { - uint8_t cfg; - int ret; + int ret = -EINVAL; - cfg = strtol(argv[1], NULL, 10); - - if (cfg == 1) { - ret = usbd_add_configuration(my_uds_ctx, speed, &config_foo); - } else if (cfg == 2) { - ret = usbd_add_configuration(my_uds_ctx, speed, &config_baz); - } else { - shell_error(sh, "dev: Configuration %u not available", cfg); - return -EINVAL; + for (unsigned int i = 0; i < ARRAY_SIZE(sh_configs); i++) { + if (!strcmp(argv[1], sh_configs[i].name)) { + ret = usbd_add_configuration(my_uds_ctx, + sh_configs[i].speed, + sh_configs[i].cfg_nd); + break; + } } if (ret) { - shell_error(sh, "dev: failed to add configuration %u", cfg); + shell_error(sh, "dev: failed to add configuration %s", argv[1]); } return ret; } -static int cmd_config_self(const struct shell *sh, size_t argc, - char *argv[]) +static int cmd_config_set_selfpowered(const struct shell *sh, const bool self, + size_t argc, char *argv[]) { - bool self; uint8_t cfg; int ret; - cfg = strtol(argv[1], NULL, 10); - if (!strcmp(argv[2], "yes")) { - self = true; - } else { - self = false; - } + cfg = strtol(argv[2], NULL, 10); - ret = usbd_config_attrib_self(my_uds_ctx, speed, cfg, self); + ret = usbd_config_attrib_self(my_uds_ctx, current_cmd_speed, cfg, self); if (ret) { shell_error(sh, - "dev: failed to set attribute self powered to %u", + "dev: failed to set attribute Self-powered to %u", cfg); + } else { + shell_print(sh, + "dev: set configuration %u attribute Self-powered to %u", + cfg, self); } return ret; } -static int cmd_config_rwup(const struct shell *sh, size_t argc, - char *argv[]) +static int cmd_config_selfpowered(const struct shell *sh, + size_t argc, char *argv[]) +{ + return cmd_config_set_selfpowered(sh, true, argc, argv); +} + +static int cmd_config_buspowered(const struct shell *sh, + size_t argc, char *argv[]) +{ + return cmd_config_set_selfpowered(sh, false, argc, argv); +} + +static int cmd_config_rwup(const struct shell *sh, const bool rwup, + size_t argc, char *argv[]) { - bool rwup; uint8_t cfg; int ret; - cfg = strtol(argv[1], NULL, 10); - if (!strcmp(argv[2], "yes")) { - rwup = true; - } else { - rwup = false; - } + cfg = strtol(argv[2], NULL, 10); - ret = usbd_config_attrib_rwup(my_uds_ctx, speed, cfg, rwup); + ret = usbd_config_attrib_rwup(my_uds_ctx, current_cmd_speed, cfg, rwup); if (ret) { shell_error(sh, - "dev: failed to set attribute remote wakeup to %x", - cfg); + "dev: failed set configuration %u Remote Wakeup to %u", + cfg, rwup); + } else { + shell_print(sh, + "dev: set configuration %u Remote Wakeup to %u", + cfg, rwup); } return ret; } +static int cmd_config_set_rwup(const struct shell *sh, + size_t argc, char *argv[]) +{ + return cmd_config_rwup(sh, true, argc, argv); +} + +static int cmd_config_clear_rwup(const struct shell *sh, + size_t argc, char *argv[]) +{ + return cmd_config_rwup(sh, false, argc, argv); +} + static int cmd_config_power(const struct shell *sh, size_t argc, char *argv[]) { + uint16_t power; uint8_t cfg; - uint8_t power; int ret; - cfg = strtol(argv[1], NULL, 10); - power = strtol(argv[1], NULL, 10); + cfg = strtol(argv[2], NULL, 10); + power = strtol(argv[3], NULL, 10); - ret = usbd_config_maxpower(my_uds_ctx, speed, cfg, power); + if (power > UINT8_MAX) { + power = UINT8_MAX; + shell_print(sh, "dev: limit bMaxPower value to %u", power); + } + + ret = usbd_config_maxpower(my_uds_ctx, current_cmd_speed, cfg, power); if (ret) { - shell_error(sh, "dev: failed to set bMaxPower value to %u", cfg); + shell_error(sh, + "dev: failed to set configuration %u bMaxPower value to %u", + cfg, power); + } else { + shell_print(sh, + "dev: set configuration %u bMaxPower value to %u", + cfg, power); } return ret; } +static void configuration_speed(size_t idx, struct shell_static_entry *entry) +{ + size_t match_idx = 0; + + entry->syntax = NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; + + for (unsigned int i = 0; i < ARRAY_SIZE(sh_speed); i++) { + if (match_idx == idx) { + entry->syntax = sh_speed[i].name; + current_cmd_speed = sh_speed[i].speed; + break; + } + + ++match_idx; + } +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_config_speed, configuration_speed); + +static void configuration_lookup(size_t idx, struct shell_static_entry *entry) +{ + size_t match_idx = 0; + + entry->syntax = NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; + + for (unsigned int i = 0; i < ARRAY_SIZE(sh_configs); i++) { + if (match_idx == idx) { + entry->syntax = sh_configs[i].name; + break; + } + + ++match_idx; + } +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_config_name, configuration_lookup); + static void class_node_name_lookup(size_t idx, struct shell_static_entry *entry) { size_t match_idx = 0; @@ -398,7 +522,7 @@ static void class_node_name_lookup(size_t idx, struct shell_static_entry *entry) entry->syntax = NULL; entry->handler = NULL; entry->help = NULL; - entry->subcmd = NULL; + entry->subcmd = &dsub_config_speed; STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { if ((c_nd->c_data->name != NULL) && @@ -438,60 +562,87 @@ SHELL_DYNAMIC_CMD_CREATE(dsub_node_name, class_node_name_lookup); SHELL_DYNAMIC_CMD_CREATE(dsub_context_name, device_context_lookup); SHELL_STATIC_SUBCMD_SET_CREATE(device_cmds, - SHELL_CMD_ARG(bcd, NULL, "", - cmd_device_bcd, 2, 0), - SHELL_CMD_ARG(pid, NULL, "", + SHELL_CMD_ARG(pid, NULL, + " sets device Product ID", cmd_device_pid, 2, 0), - SHELL_CMD_ARG(vid, NULL, "", + SHELL_CMD_ARG(vid, NULL, + " sets device Vendor ID", cmd_device_vid, 2, 0), - SHELL_CMD_ARG(triple, NULL, " ", - cmd_device_code_triple, 4, 0), + SHELL_CMD_ARG(bcd, &dsub_config_speed, + " sets device release number", + cmd_device_bcd, 3, 0), + SHELL_CMD_ARG(triple, &dsub_config_speed, + " sets device code triple", + cmd_device_code_triple, 5, 0), SHELL_SUBCMD_SET_END ); SHELL_STATIC_SUBCMD_SET_CREATE(config_cmds, - SHELL_CMD_ARG(add, NULL, "", + SHELL_CMD_ARG(add, &dsub_config_name, + " adds one of the pre-defined configurations", cmd_config_add, 2, 0), - SHELL_CMD_ARG(power, NULL, " ", - cmd_config_power, 3, 0), - SHELL_CMD_ARG(rwup, NULL, " ", - cmd_config_rwup, 3, 0), - SHELL_CMD_ARG(self, NULL, " ", - cmd_config_self, 3, 0), + SHELL_CMD_ARG(power, &dsub_config_speed, + " sets the bMaxPower", + cmd_config_power, 4, 0), + SHELL_CMD_ARG(set-rwup, &dsub_config_speed, + " sets Remote Wakeup bit", + cmd_config_set_rwup, 3, 0), + SHELL_CMD_ARG(clear-rwup, &dsub_config_speed, + " clears Remote Wakeup bit", + cmd_config_clear_rwup, 3, 0), + SHELL_CMD_ARG(selfpowered, &dsub_config_speed, + " sets Self-power bit", + cmd_config_selfpowered, 3, 0), + SHELL_CMD_ARG(buspowered, &dsub_config_speed, + " clears Self-power bit", + cmd_config_buspowered, 3, 0), SHELL_SUBCMD_SET_END ); SHELL_STATIC_SUBCMD_SET_CREATE(class_cmds, - SHELL_CMD_ARG(add, &dsub_node_name, " ", - cmd_register, 3, 0), - SHELL_CMD_ARG(remove, &dsub_node_name, " ", - cmd_unregister, 3, 0), + SHELL_CMD_ARG(register, &dsub_node_name, + " registers class instance", + cmd_register, 4, 0), + SHELL_CMD_ARG(unregister, &dsub_node_name, + " unregisters class instance", + cmd_unregister, 4, 0), SHELL_SUBCMD_SET_END ); SHELL_STATIC_SUBCMD_SET_CREATE(sub_usbd_cmds, - SHELL_CMD_ARG(wakeup, NULL, "[none]", - cmd_wakeup_request, 1, 0), - SHELL_CMD_ARG(magic, NULL, "[none]", - cmd_usbd_magic, 1, 0), - SHELL_CMD_ARG(defaults, NULL, "[none]", - cmd_usbd_defaults, 1, 0), - SHELL_CMD_ARG(init, NULL, "[none]", + SHELL_CMD_ARG(defstr, NULL, + "[none] adds default string descriptors", + cmd_usbd_default_strings, 1, 0), + SHELL_CMD_ARG(defcfg, NULL, + "[none] initializes default configuration with all available classes", + cmd_usbd_default_config, 1, 0), + SHELL_CMD_ARG(init, NULL, + "[none] initializes USB device support", cmd_usbd_init, 1, 0), - SHELL_CMD_ARG(enable, NULL, "[none]", + SHELL_CMD_ARG(enable, NULL, + "[none] enables USB device support]", cmd_usbd_enable, 1, 0), - SHELL_CMD_ARG(disable, NULL, "[none]", + SHELL_CMD_ARG(disable, NULL, + "[none] disables USB device support", cmd_usbd_disable, 1, 0), - SHELL_CMD_ARG(shutdown, NULL, "[none]", + SHELL_CMD_ARG(shutdown, NULL, + "[none] shutdown USB device support", cmd_usbd_shutdown, 1, 0), - SHELL_CMD_ARG(select, &dsub_context_name, "", + SHELL_CMD_ARG(select, &dsub_context_name, + " selects context used by the shell", cmd_select, 2, 0), - SHELL_CMD_ARG(device, &device_cmds, "device commands", + SHELL_CMD_ARG(device, &device_cmds, + "device commands", NULL, 1, 0), - SHELL_CMD_ARG(config, &config_cmds, "configuration commands", + SHELL_CMD_ARG(config, &config_cmds, + "configuration commands", NULL, 1, 0), - SHELL_CMD_ARG(class, &class_cmds, "class commands", + SHELL_CMD_ARG(class, &class_cmds, + "class commands", NULL, 1, 0), + SHELL_CMD_ARG(wakeup, NULL, + "[none] signals remote wakeup", + cmd_wakeup_request, 1, 0), SHELL_SUBCMD_SET_END );