From 3ee5c579343efd69f06da5053927ab5ad7b04211 Mon Sep 17 00:00:00 2001 From: Krzysztof Chruscinski Date: Tue, 5 Apr 2022 09:36:10 +0200 Subject: [PATCH] shell: Add new way of adding subcommands Added macro SHELL_SUBCMD_SET_CREATE which creates a set of subcommands. SHELL_SUBCMD_ADD and SHELL_SUBCMD_COND_ADD can be used from any file to add command to the set. This approach allows to have subcommands added from multiple files. Signed-off-by: Krzysztof Chruscinski --- .../linker/common-rom/common-rom-misc.ld | 14 ++ include/zephyr/shell/shell.h | 127 +++++++++++++++--- subsys/shell/shell_utils.c | 71 ++++++++-- 3 files changed, 178 insertions(+), 34 deletions(-) diff --git a/include/zephyr/linker/common-rom/common-rom-misc.ld b/include/zephyr/linker/common-rom/common-rom-misc.ld index 1f74730c830..f590e100138 100644 --- a/include/zephyr/linker/common-rom/common-rom-misc.ld +++ b/include/zephyr/linker/common-rom/common-rom-misc.ld @@ -26,6 +26,20 @@ __shell_root_cmds_end = .; } GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) + SECTION_DATA_PROLOGUE(shell_subcmds_sections,,) + { + __shell_subcmds_start = .; + KEEP(*(SORT(.shell_subcmd_*))); + __shell_subcmds_end = .; + } GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) + + SECTION_DATA_PROLOGUE(shell_dynamic_subcmds_sections,,) + { + __shell_dynamic_subcmds_start = .; + KEEP(*(SORT(.shell_dynamic_subcmd_*))); + __shell_dynamic_subcmds_end = .; + } GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) + SECTION_DATA_PROLOGUE(font_entry_sections,,) { __font_entry_start = .; diff --git a/include/zephyr/shell/shell.h b/include/zephyr/shell/shell.h index 4eadd6b945f..4ce95b58945 100644 --- a/include/zephyr/shell/shell.h +++ b/include/zephyr/shell/shell.h @@ -91,15 +91,12 @@ typedef void (*shell_dynamic_get)(size_t idx, /** * @brief Shell command descriptor. */ -struct shell_cmd_entry { - bool is_dynamic; - union union_cmd_entry { - /*!< Pointer to function returning dynamic commands.*/ - shell_dynamic_get dynamic_get; +union shell_cmd_entry { + /*!< Pointer to function returning dynamic commands.*/ + shell_dynamic_get dynamic_get; - /*!< Pointer to array of static commands. */ - const struct shell_static_entry *entry; - } u; + /*!< Pointer to array of static commands. */ + const struct shell_static_entry *entry; }; struct shell; @@ -158,15 +155,26 @@ typedef int (*shell_cmd_handler)(const struct shell *shell, typedef int (*shell_dict_cmd_handler)(const struct shell *shell, size_t argc, char **argv, void *data); +/* When entries are added to the memory section a padding is applied for + * native_posix_64 and x86_64 targets. Adding padding to allow handle data + * in the memory section as array. + */ +#if (defined(CONFIG_ARCH_POSIX) && defined(CONFIG_64BIT)) || defined(CONFIG_X86_64) +#define Z_SHELL_STATIC_ENTRY_PADDING 24 +#else +#define Z_SHELL_STATIC_ENTRY_PADDING 0 +#endif + /* * @brief Shell static command descriptor. */ struct shell_static_entry { const char *syntax; /*!< Command syntax strings. */ const char *help; /*!< Command help string. */ - const struct shell_cmd_entry *subcmd; /*!< Pointer to subcommand. */ + const union shell_cmd_entry *subcmd; /*!< Pointer to subcommand. */ shell_cmd_handler handler; /*!< Command handler. */ struct shell_static_args args; /*!< Command arguments. */ + uint8_t padding[Z_SHELL_STATIC_ENTRY_PADDING]; }; /** @@ -188,12 +196,11 @@ struct shell_static_entry { mandatory, optional) \ static const struct shell_static_entry UTIL_CAT(_shell_, syntax) = \ SHELL_CMD_ARG(syntax, subcmd, help, handler, mandatory, optional); \ - static const struct shell_cmd_entry UTIL_CAT(shell_cmd_, syntax) \ + static const union shell_cmd_entry UTIL_CAT(shell_cmd_, syntax) \ __attribute__ ((section("." \ STRINGIFY(UTIL_CAT(shell_root_cmd_, syntax))))) \ __attribute__((used)) = { \ - .is_dynamic = false, \ - .u = {.entry = &UTIL_CAT(_shell_, syntax)} \ + .entry = &UTIL_CAT(_shell_, syntax) \ } /** @@ -227,9 +234,9 @@ struct shell_static_entry { (\ static shell_cmd_handler dummy_##syntax##_handler __unused = \ handler;\ - static const struct shell_cmd_entry *dummy_subcmd_##syntax \ + static const union shell_cmd_entry *dummy_subcmd_##syntax \ __unused = subcmd\ - )\ + ) \ ) /** * @brief Macro for defining and adding a root command (level 0) with @@ -281,11 +288,85 @@ struct shell_static_entry { static const struct shell_static_entry shell_##name[] = { \ __VA_ARGS__ \ }; \ - static const struct shell_cmd_entry name = { \ - .is_dynamic = false, \ - .u = { .entry = shell_##name } \ + static const union shell_cmd_entry name = { \ + .entry = shell_##name \ } +#define Z_SHELL_UNDERSCORE(x) _##x +#define Z_SHELL_SUBCMD_NAME(...) \ + UTIL_CAT(shell_subcmd, MACRO_MAP_CAT(Z_SHELL_UNDERSCORE, __VA_ARGS__)) + +/** @brief Create set of subcommands. + * + * Commands to this set are added using @ref SHELL_SUBCMD_ADD and @ref SHELL_SUBCMD_COND_ADD. + * Commands can be added from multiple files. + * + * @param[in] _name Name of the set. @p _name is used to refer the set in the parent + * command. + * + * @param[in] _parent Set of comma separated parent commands in parenthesis, e.g. + * (foo_cmd) if subcommands are for the root command "foo_cmd". + */ +#define SHELL_SUBCMD_SET_CREATE(_name, _parent) \ + static const struct shell_static_entry _name \ + __attribute__ ((section("." \ + STRINGIFY(Z_SHELL_SUBCMD_NAME(NUM_VA_ARGS_LESS_1 _parent, \ + __DEBRACKET _parent))))) \ + __attribute__((used)) + +/** @brief Conditionally add command to the set of subcommands. + * + * Add command to the set created with @ref SHELL_SUBCMD_SET_CREATE. + * + * @note The name of the section is formed as concatenation of number of parent + * commands, names of all parent commands and own syntax. Number of parent commands + * is added to ensure that section prefix is unique. Without it subcommands of + * (foo) and (foo, cmd1) would mix. + * + * @param[in] _flag Compile time flag. Command is present only if flag + * exists and equals 1. + * @param[in] _parent Parent command sequence. Comma separated in parenthesis. + * @param[in] _syntax Command syntax (for example: history). + * @param[in] _subcmd Pointer to a subcommands array. + * @param[in] _help Pointer to a command help string. + * @param[in] _handler Pointer to a function handler. + * @param[in] _mand Number of mandatory arguments including command name. + * @param[in] _opt Number of optional arguments. + */ +#define SHELL_SUBCMD_COND_ADD(_flag, _parent, _syntax, _subcmd, _help, _handler, \ + _mand, _opt) \ + COND_CODE_1(_flag, \ + (static const struct shell_static_entry \ + Z_SHELL_SUBCMD_NAME(__DEBRACKET _parent, _syntax)\ + __attribute__ ((section("." \ + STRINGIFY(Z_SHELL_SUBCMD_NAME(NUM_VA_ARGS_LESS_1 _parent, \ + __DEBRACKET _parent, _syntax))))) \ + __attribute__((used)) = \ + SHELL_EXPR_CMD_ARG(1, _syntax, _subcmd, _help, \ + _handler, _mand, _opt)\ + ), \ + (static shell_cmd_handler dummy_##syntax##_handler __unused = _handler;\ + static const union shell_cmd_entry dummy_subcmd_##syntax __unused = { \ + .entry = (const struct shell_static_entry *)_subcmd\ + } \ + ) \ + ) + +/** @brief Add command to the set of subcommands. + * + * Add command to the set created with @ref SHELL_SUBCMD_SET_CREATE. + * + * @param[in] _parent Parent command sequence. Comma separated in parenthesis. + * @param[in] _syntax Command syntax (for example: history). + * @param[in] _subcmd Pointer to a subcommands array. + * @param[in] _help Pointer to a command help string. + * @param[in] _handler Pointer to a function handler. + * @param[in] _mand Number of mandatory arguments including command name. + * @param[in] _opt Number of optional arguments. + */ +#define SHELL_SUBCMD_ADD(_parent, _syntax, _subcmd, _help, _handler, _mand, _opt) \ + SHELL_SUBCMD_COND_ADD(1, _parent, _syntax, _subcmd, _help, _handler, _mand, _opt) + /** * @brief Define ending subcommands set. * @@ -298,10 +379,12 @@ struct shell_static_entry { * @param[in] name Name of the dynamic entry. * @param[in] get Pointer to the function returning dynamic commands array */ -#define SHELL_DYNAMIC_CMD_CREATE(name, get) \ - static const struct shell_cmd_entry name = { \ - .is_dynamic = true, \ - .u = { .dynamic_get = get } \ +#define SHELL_DYNAMIC_CMD_CREATE(name, get) \ + static const union shell_cmd_entry name \ + __attribute__ ((section("." \ + STRINGIFY(UTIL_CAT(shell_dynamic_subcmd_, syntax))))) \ + __attribute__((used)) = { \ + .dynamic_get = get \ } /** @@ -367,7 +450,7 @@ struct shell_static_entry { { \ .syntax = (_expr) ? (const char *)STRINGIFY(_syntax) : "", \ .help = (_expr) ? (const char *)_help : NULL, \ - .subcmd = (const struct shell_cmd_entry *)((_expr) ? \ + .subcmd = (const union shell_cmd_entry *)((_expr) ? \ _subcmd : NULL), \ .handler = (shell_cmd_handler)((_expr) ? _handler : NULL), \ .args = { .mandatory = _mand, .optional = _opt} \ diff --git a/subsys/shell/shell_utils.c b/subsys/shell/shell_utils.c index 1edd69c26ca..fd0b6b8b7dd 100644 --- a/subsys/shell/shell_utils.c +++ b/subsys/shell/shell_utils.c @@ -9,14 +9,49 @@ #include "shell_utils.h" #include "shell_wildcard.h" -extern const struct shell_cmd_entry __shell_root_cmds_start[]; -extern const struct shell_cmd_entry __shell_root_cmds_end[]; +extern const union shell_cmd_entry __shell_root_cmds_start[]; +extern const union shell_cmd_entry __shell_root_cmds_end[]; -static inline const struct shell_cmd_entry *shell_root_cmd_get(uint32_t id) +extern const union shell_cmd_entry __shell_dynamic_subcmds_start[]; +extern const union shell_cmd_entry __shell_dynamic_subcmds_end[]; + +extern const union shell_cmd_entry __shell_subcmds_start[]; +extern const union shell_cmd_entry __shell_subcmds_end[]; + +/* Macro creates empty entry at the bottom of the memory section with subcommands + * it is used to detect end of subcommand set that is located before this marker. + */ +#define Z_SHELL_SUBCMD_END_MARKER_CREATE() \ + static const struct shell_static_entry z_shell_subcmd_end_marker\ + __attribute__ ((section("." \ + STRINGIFY(Z_SHELL_SUBCMD_NAME(999))))) \ + __attribute__((used)) + +Z_SHELL_SUBCMD_END_MARKER_CREATE(); + +static inline const union shell_cmd_entry *shell_root_cmd_get(uint32_t id) { return &__shell_root_cmds_start[id]; } +/* Determine if entry is a dynamic command by checking if address is within + * dynamic commands memory section. + */ +static inline bool is_dynamic_cmd(const union shell_cmd_entry *entry) +{ + return (entry >= __shell_dynamic_subcmds_start) && + (entry < __shell_dynamic_subcmds_end); +} + +/* Determine if entry is a section command by checking if address is within + * subcommands memory section. + */ +static inline bool is_section_cmd(const union shell_cmd_entry *entry) +{ + return (entry >= __shell_subcmds_start) && + (entry < __shell_subcmds_end); +} + /* Calculates relative line number of given position in buffer */ static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons, uint16_t buffer_pos) @@ -226,19 +261,19 @@ static inline uint32_t shell_root_cmd_count(void) { return ((uint8_t *)__shell_root_cmds_end - (uint8_t *)__shell_root_cmds_start)/ - sizeof(struct shell_cmd_entry); + sizeof(union shell_cmd_entry); } /* Function returning pointer to parent command matching requested syntax. */ const struct shell_static_entry *root_cmd_find(const char *syntax) { const size_t cmd_count = shell_root_cmd_count(); - const struct shell_cmd_entry *cmd; + const union shell_cmd_entry *cmd; for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) { cmd = shell_root_cmd_get(cmd_idx); - if (strcmp(syntax, cmd->u.entry->syntax) == 0) { - return cmd->u.entry; + if (strcmp(syntax, cmd->entry->syntax) == 0) { + return cmd->entry; } } @@ -254,20 +289,32 @@ const struct shell_static_entry *z_shell_cmd_get( if (parent == NULL) { return (idx < shell_root_cmd_count()) ? - shell_root_cmd_get(idx)->u.entry : NULL; + shell_root_cmd_get(idx)->entry : NULL; } __ASSERT_NO_MSG(dloc != NULL); if (parent->subcmd) { - if (parent->subcmd->is_dynamic) { - parent->subcmd->u.dynamic_get(idx, dloc); + if (is_dynamic_cmd(parent->subcmd)) { + parent->subcmd->dynamic_get(idx, dloc); if (dloc->syntax != NULL) { res = dloc; } } else { - if (parent->subcmd->u.entry[idx].syntax != NULL) { - res = &parent->subcmd->u.entry[idx]; + const struct shell_static_entry *entry_list; + + if (is_section_cmd(parent->subcmd)) { + /* First element is null */ + entry_list = + (const struct shell_static_entry *)parent->subcmd; + idx++; + } else { + entry_list = parent->subcmd->entry; + } + + + if (entry_list[idx].syntax != NULL) { + res = &entry_list[idx]; } } }