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 <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
Krzysztof Chruscinski 2022-04-05 09:36:10 +02:00 committed by Carles Cufí
commit 3ee5c57934
3 changed files with 178 additions and 34 deletions

View file

@ -26,6 +26,20 @@
__shell_root_cmds_end = .; __shell_root_cmds_end = .;
} GROUP_ROM_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) } 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,,) SECTION_DATA_PROLOGUE(font_entry_sections,,)
{ {
__font_entry_start = .; __font_entry_start = .;

View file

@ -91,15 +91,12 @@ typedef void (*shell_dynamic_get)(size_t idx,
/** /**
* @brief Shell command descriptor. * @brief Shell command descriptor.
*/ */
struct shell_cmd_entry { union shell_cmd_entry {
bool is_dynamic; /*!< Pointer to function returning dynamic commands.*/
union union_cmd_entry { shell_dynamic_get dynamic_get;
/*!< Pointer to function returning dynamic commands.*/
shell_dynamic_get dynamic_get;
/*!< Pointer to array of static commands. */ /*!< Pointer to array of static commands. */
const struct shell_static_entry *entry; const struct shell_static_entry *entry;
} u;
}; };
struct shell; 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, typedef int (*shell_dict_cmd_handler)(const struct shell *shell, size_t argc,
char **argv, void *data); 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. * @brief Shell static command descriptor.
*/ */
struct shell_static_entry { struct shell_static_entry {
const char *syntax; /*!< Command syntax strings. */ const char *syntax; /*!< Command syntax strings. */
const char *help; /*!< Command help string. */ 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. */ shell_cmd_handler handler; /*!< Command handler. */
struct shell_static_args args; /*!< Command arguments. */ 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) \ mandatory, optional) \
static const struct shell_static_entry UTIL_CAT(_shell_, syntax) = \ static const struct shell_static_entry UTIL_CAT(_shell_, syntax) = \
SHELL_CMD_ARG(syntax, subcmd, help, handler, mandatory, optional); \ 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("." \ __attribute__ ((section("." \
STRINGIFY(UTIL_CAT(shell_root_cmd_, syntax))))) \ STRINGIFY(UTIL_CAT(shell_root_cmd_, syntax))))) \
__attribute__((used)) = { \ __attribute__((used)) = { \
.is_dynamic = false, \ .entry = &UTIL_CAT(_shell_, syntax) \
.u = {.entry = &UTIL_CAT(_shell_, syntax)} \
} }
/** /**
@ -227,9 +234,9 @@ struct shell_static_entry {
(\ (\
static shell_cmd_handler dummy_##syntax##_handler __unused = \ static shell_cmd_handler dummy_##syntax##_handler __unused = \
handler;\ handler;\
static const struct shell_cmd_entry *dummy_subcmd_##syntax \ static const union shell_cmd_entry *dummy_subcmd_##syntax \
__unused = subcmd\ __unused = subcmd\
)\ ) \
) )
/** /**
* @brief Macro for defining and adding a root command (level 0) with * @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[] = { \ static const struct shell_static_entry shell_##name[] = { \
__VA_ARGS__ \ __VA_ARGS__ \
}; \ }; \
static const struct shell_cmd_entry name = { \ static const union shell_cmd_entry name = { \
.is_dynamic = false, \ .entry = shell_##name \
.u = { .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. * @brief Define ending subcommands set.
* *
@ -298,10 +379,12 @@ struct shell_static_entry {
* @param[in] name Name of the dynamic entry. * @param[in] name Name of the dynamic entry.
* @param[in] get Pointer to the function returning dynamic commands array * @param[in] get Pointer to the function returning dynamic commands array
*/ */
#define SHELL_DYNAMIC_CMD_CREATE(name, get) \ #define SHELL_DYNAMIC_CMD_CREATE(name, get) \
static const struct shell_cmd_entry name = { \ static const union shell_cmd_entry name \
.is_dynamic = true, \ __attribute__ ((section("." \
.u = { .dynamic_get = get } \ 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) : "", \ .syntax = (_expr) ? (const char *)STRINGIFY(_syntax) : "", \
.help = (_expr) ? (const char *)_help : NULL, \ .help = (_expr) ? (const char *)_help : NULL, \
.subcmd = (const struct shell_cmd_entry *)((_expr) ? \ .subcmd = (const union shell_cmd_entry *)((_expr) ? \
_subcmd : NULL), \ _subcmd : NULL), \
.handler = (shell_cmd_handler)((_expr) ? _handler : NULL), \ .handler = (shell_cmd_handler)((_expr) ? _handler : NULL), \
.args = { .mandatory = _mand, .optional = _opt} \ .args = { .mandatory = _mand, .optional = _opt} \

View file

@ -9,14 +9,49 @@
#include "shell_utils.h" #include "shell_utils.h"
#include "shell_wildcard.h" #include "shell_wildcard.h"
extern const struct shell_cmd_entry __shell_root_cmds_start[]; extern const union 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_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]; 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 */ /* Calculates relative line number of given position in buffer */
static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons, static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
uint16_t buffer_pos) uint16_t buffer_pos)
@ -226,19 +261,19 @@ static inline uint32_t shell_root_cmd_count(void)
{ {
return ((uint8_t *)__shell_root_cmds_end - return ((uint8_t *)__shell_root_cmds_end -
(uint8_t *)__shell_root_cmds_start)/ (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. */ /* Function returning pointer to parent command matching requested syntax. */
const struct shell_static_entry *root_cmd_find(const char *syntax) const struct shell_static_entry *root_cmd_find(const char *syntax)
{ {
const size_t cmd_count = shell_root_cmd_count(); 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) { for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) {
cmd = shell_root_cmd_get(cmd_idx); cmd = shell_root_cmd_get(cmd_idx);
if (strcmp(syntax, cmd->u.entry->syntax) == 0) { if (strcmp(syntax, cmd->entry->syntax) == 0) {
return cmd->u.entry; return cmd->entry;
} }
} }
@ -254,20 +289,32 @@ const struct shell_static_entry *z_shell_cmd_get(
if (parent == NULL) { if (parent == NULL) {
return (idx < shell_root_cmd_count()) ? 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); __ASSERT_NO_MSG(dloc != NULL);
if (parent->subcmd) { if (parent->subcmd) {
if (parent->subcmd->is_dynamic) { if (is_dynamic_cmd(parent->subcmd)) {
parent->subcmd->u.dynamic_get(idx, dloc); parent->subcmd->dynamic_get(idx, dloc);
if (dloc->syntax != NULL) { if (dloc->syntax != NULL) {
res = dloc; res = dloc;
} }
} else { } else {
if (parent->subcmd->u.entry[idx].syntax != NULL) { const struct shell_static_entry *entry_list;
res = &parent->subcmd->u.entry[idx];
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];
} }
} }
} }