Each struct cmd_tbl entry carries a ->cmd_rep function pointer used to
report whether a command may be repeated by pressing Enter at an empty
prompt. For all but one command it points at cmd_always_repeatable() or
cmd_never_repeatable(), trampolines that just set the repeatable flag
and call cmd_invoke() - so a whole pointer per command encodes a single
bit, on every board.

Replace it with a CMDF_REPEATABLE flag in the existing cmd_flags byte.
The _rep argument of the U_BOOT_CMD*() macros now sets this bit, and
cmd_call() reads it directly before invoking ->cmd, dropping the
trampolines entirely.

Sub-command dispatchers (U_BOOT_CMD_WITH_SUBCMDS, and the x86 zboot
command) still need to report the repeatability of the sub-command they
pick, which they do through the extended (..., int *repeatable)
signature. Keep that ability with a CMDF_SUBCMD_REP flag: such a command
stores its dispatcher in ->cmd and is tagged CMDF_SUBCMD_REP, and
cmd_call() calls it with the repeatable pointer. The dispatcher refines
*repeatable from the chosen sub-command's CMDF_REPEATABLE bit.

This removes a pointer from every struct cmd_tbl entry: the struct
shrinks from 56 to 48 bytes on 64-bit and by 4 bytes per entry on
32-bit, with no change in behaviour and no new config option. On
turris_1x_nor (ppc32, ~100 commands) this recovers about 670 bytes,
enough to keep the board within its NOR section after the getopt
conversion.

Signed-off-by: Simon Glass <[email protected]>
---

Changes in v4:
- Add new patch to replace the ->cmd_rep pointer with a
  CMDF_REPEATABLE flag, dropping a pointer from every command entry

 cmd/help.c        |   2 +-
 cmd/test.c        |   2 +-
 cmd/x86/zboot.c   |   2 +-
 common/command.c  |  44 +++++++-------------
 include/command.h | 104 ++++++++++++++++++++++------------------------
 5 files changed, 67 insertions(+), 87 deletions(-)

diff --git a/cmd/help.c b/cmd/help.c
index b87c19003d4..ab6f9c95739 100644
--- a/cmd/help.c
+++ b/cmd/help.c
@@ -28,7 +28,7 @@ U_BOOT_CMD(
  * nor can we rely on the CONFIG_SYS_LONGHELP helper macro
  */
 ll_entry_declare(struct cmd_tbl, question_mark, cmd) = {
-       "?",    CONFIG_SYS_MAXARGS, 0, cmd_always_repeatable,   do_help,
+       "?",    CONFIG_SYS_MAXARGS, CMDF_REPEATABLE,    do_help,
        "alias for 'help'",
 #ifdef  CONFIG_SYS_LONGHELP
        ""
diff --git a/cmd/test.c b/cmd/test.c
index f0b89aa0af0..32c5238972d 100644
--- a/cmd/test.c
+++ b/cmd/test.c
@@ -244,7 +244,7 @@ U_BOOT_CMD(
  * This does not use the U_BOOT_CMD macro as [ can't be used in symbol names
  */
 ll_entry_declare(struct cmd_tbl, lbracket, cmd) = {
-       "[",    CONFIG_SYS_MAXARGS, 0, cmd_always_repeatable, do_test,
+       "[",    CONFIG_SYS_MAXARGS, CMDF_REPEATABLE, do_test,
        "alias for 'test'",
 #ifdef CONFIG_SYS_LONGHELP
        " <test expression> ]"
diff --git a/cmd/x86/zboot.c b/cmd/x86/zboot.c
index 3876d163236..ef3e27e622d 100644
--- a/cmd/x86/zboot.c
+++ b/cmd/x86/zboot.c
@@ -165,7 +165,7 @@ int do_zboot_parent(struct cmd_tbl *cmdtp, int flag, int 
argc,
        return CMD_RET_FAILURE;
 }
 
-U_BOOT_CMDREP_COMPLETE(
+U_BOOT_SUBCMD_DECL(
        zboot, 8, do_zboot_parent, "Boot bzImage",
        "[addr] [size] [initrd addr] [initrd size] [setup] [cmdline]\n"
        "      addr -        The optional starting address of the bzimage.\n"
diff --git a/common/command.c b/common/command.c
index c747bef7883..4204c6e2df2 100644
--- a/common/command.c
+++ b/common/command.c
@@ -508,11 +508,6 @@ void fixup_cmdtable(struct cmd_tbl *cmdtp, int size)
        for (i = 0; i < size; i++) {
                ulong addr;
 
-               addr = (ulong)(cmdtp->cmd_rep) + gd->reloc_off;
-               cmdtp->cmd_rep =
-                       (int (*)(struct cmd_tbl *, int, int,
-                                char * const [], int *))addr;
-
                addr = (ulong)(cmdtp->cmd) + gd->reloc_off;
 #ifdef DEBUG_COMMANDS
                printf("Command \"%s\": 0x%08lx => 0x%08lx\n",
@@ -558,30 +553,6 @@ int cmd_invoke(struct cmd_tbl *cmdtp, int flag, int argc, 
char *const argv[])
        return cmdtp->cmd(cmdtp, flag, argc, argv);
 }
 
-int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-                         char *const argv[], int *repeatable)
-{
-       *repeatable = 1;
-
-       return cmd_invoke(cmdtp, flag, argc, argv);
-}
-
-int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-                        char *const argv[], int *repeatable)
-{
-       *repeatable = 0;
-
-       return cmd_invoke(cmdtp, flag, argc, argv);
-}
-
-int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-                          char *const argv[])
-{
-       int repeatable;
-
-       return cmdtp->cmd_rep(cmdtp, flag, argc, argv, &repeatable);
-}
-
 /**
  * Call a command function. This should be the only route in U-Boot to call
  * a command, so that we can track whether we are waiting for input or
@@ -599,7 +570,20 @@ static int cmd_call(struct cmd_tbl *cmdtp, int flag, int 
argc,
 {
        int result;
 
-       result = cmdtp->cmd_rep(cmdtp, flag, argc, argv, repeatable);
+       *repeatable = !!(cmdtp->cmd_flags & CMDF_REPEATABLE);
+
+       if (cmdtp->cmd_flags & CMDF_SUBCMD_REP) {
+               /*
+                * A sub-command dispatcher refines *repeatable for the
+                * sub-command it picks, so call it with the extended signature.
+                */
+               cmd_rep_func_t func = (cmd_rep_func_t)cmdtp->cmd;
+
+               result = func(cmdtp, flag, argc, argv, repeatable);
+       } else {
+               result = cmd_invoke(cmdtp, flag, argc, argv);
+       }
+
        if (result)
                debug("Command failed, result=%d\n", result);
        return result;
diff --git a/include/command.h b/include/command.h
index 495d6a07d75..b201100870a 100644
--- a/include/command.h
+++ b/include/command.h
@@ -39,16 +39,6 @@ struct cmd_tbl {
        char            *name;          /* Command Name                 */
        short           maxargs;        /* maximum number of arguments  */
        u8              cmd_flags;      /* CMDF_... flags               */
-                                       /*
-                                        * Same as ->cmd() except the command
-                                        * tells us if it can be repeated.
-                                        * Replaces the old ->repeatable field
-                                        * which was not able to make
-                                        * repeatable property different for
-                                        * the main command and sub-commands.
-                                        */
-       int             (*cmd_rep)(struct cmd_tbl *cmd, int flags, int argc,
-                                  char *const argv[], int *repeatable);
                                        /* Implementation function      */
        int             (*cmd)(struct cmd_tbl *cmd, int flags, int argc,
                               char *const argv[]);
@@ -76,8 +66,24 @@ enum {
         * (int (*)(struct getopt_state *)) instead of the classic one
         */
        CMDF_GETOPT     = BIT(0),
+       /* the command may be repeated by pressing Enter at an empty prompt */
+       CMDF_REPEATABLE = BIT(1),
+       /*
+        * ->cmd holds a sub-command dispatcher with the extended signature
+        * (..., int *repeatable), so it can report the repeatable property of
+        * the sub-command it dispatched to. cmd_call() calls it accordingly.
+        */
+       CMDF_SUBCMD_REP = BIT(2),
 };
 
+/*
+ * Type of a sub-command dispatcher (a CMDF_SUBCMD_REP command's ->cmd). It has
+ * the classic command arguments plus a @repeatable output which it sets from
+ * the sub-command it dispatches to.
+ */
+typedef int (*cmd_rep_func_t)(struct cmd_tbl *cmdtp, int flag, int argc,
+                             char *const argv[], int *repeatable);
+
 /**
  * cmd_arg_get() - Get a particular argument
  *
@@ -142,17 +148,9 @@ int cmd_usage(const struct cmd_tbl *cmdtp);
  */
 int cmd_invoke(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 
-/* Dummy ->cmd and ->cmd_rep wrappers. */
-int cmd_always_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-                         char *const argv[], int *repeatable);
-int cmd_never_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-                        char *const argv[], int *repeatable);
-int cmd_discard_repeatable(struct cmd_tbl *cmdtp, int flag, int argc,
-                          char *const argv[]);
-
 static inline bool cmd_is_repeatable(struct cmd_tbl *cmdtp)
 {
-       return cmdtp->cmd_rep == cmd_always_repeatable;
+       return cmdtp->cmd_flags & CMDF_REPEATABLE;
 }
 
 #ifdef CONFIG_AUTO_COMPLETE
@@ -416,8 +414,9 @@ int cmd_source_script(ulong addr, const char *fit_uname, 
const char *confname);
                    !cmd_is_repeatable(subcmd))                         \
                        return CMD_RET_SUCCESS;                         \
                                                                        \
-               return subcmd->cmd_rep(subcmd, flag, argc - 1,          \
-                                      argv + 1, repeatable);           \
+               *repeatable &= cmd_is_repeatable(subcmd);               \
+                                                                       \
+               return cmd_invoke(subcmd, flag, argc - 1, argv + 1);    \
        }
 
 #ifdef CONFIG_AUTO_COMPLETE
@@ -441,21 +440,17 @@ int cmd_source_script(ulong addr, const char *fit_uname, 
const char *confname);
        U_BOOT_SUBCMDS_COMPLETE(_cmdname)
 
 #if CONFIG_IS_ENABLED(CMDLINE)
-#define U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,                
\
-                                    _usage, _help, _comp)              \
-               { #_name, _maxargs, 0, _cmd_rep, cmd_discard_repeatable,        
\
-                 _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
+/* Encode the _rep argument as the CMDF_REPEATABLE flag bit */
+#define _CMD_REP_FLAG(_rep)    ((_rep) ? CMDF_REPEATABLE : 0)
 
 #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,         \
                                _usage, _help, _comp)                   \
-               { #_name, _maxargs, 0,                                  \
-                _rep ? cmd_always_repeatable : cmd_never_repeatable,   \
+               { #_name, _maxargs, _CMD_REP_FLAG(_rep),                \
                 _cmd, _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #define U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,  \
                                         _usage, _help, _comp)          \
-               { #_name, _maxargs, CMDF_GETOPT,                        \
-                _rep ? cmd_always_repeatable : cmd_never_repeatable,   \
+               { #_name, _maxargs, CMDF_GETOPT | _CMD_REP_FLAG(_rep),  \
                 (int (*)(struct cmd_tbl *, int, int,                   \
                          char *const []))(_cmd),                       \
                 _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
@@ -480,11 +475,18 @@ int cmd_source_script(ulong addr, const char *fit_uname, 
const char *confname);
                U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, \
                                                 _cmd, _usage, _help, _comp)
 
-#define U_BOOT_CMDREP_COMPLETE(_name, _maxargs, _cmd_rep, _usage,      \
-                              _help, _comp)                            \
-       ll_entry_declare(struct cmd_tbl, _name, cmd) =                  \
-               U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep, \
-                                            _usage, _help, _comp)
+/*
+ * Declare a top-level command whose ->cmd is a sub-command dispatcher with
+ * the extended (..., int *repeatable) signature (see CMDF_SUBCMD_REP). The
+ * dispatcher is stored in ->cmd and tagged so cmd_call() invokes it with the
+ * repeatable pointer.
+ */
+#define U_BOOT_SUBCMD_DECL(_name, _maxargs, _do_cmd, _usage, _help, _comp) \
+       ll_entry_declare(struct cmd_tbl, _name, cmd) = {                \
+               #_name, _maxargs, CMDF_SUBCMD_REP | CMDF_REPEATABLE,    \
+               (int (*)(struct cmd_tbl *, int, int,                    \
+                        char *const []))(_do_cmd),                     \
+               _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #else
 #define U_BOOT_SUBCMD_START(name)      static struct cmd_tbl name[] = {};
@@ -498,14 +500,6 @@ int cmd_source_script(ulong addr, const char *fit_uname, 
const char *confname);
                return 0;                                               \
        }
 
-#define _CMD_REMOVE_REP(_name, _cmd)                                   \
-       int __remove_ ## _name(void)                                    \
-       {                                                               \
-               if (0)                                                  \
-                       _cmd(NULL, 0, 0, NULL, NULL);                   \
-               return 0;                                               \
-       }
-
 #define _CMD_REMOVE_GETOPT(_name, _cmd)                                        
\
        int __remove_ ## _name(void)                                    \
        {                                                               \
@@ -514,19 +508,14 @@ int cmd_source_script(ulong addr, const char *fit_uname, 
const char *confname);
                return 0;                                               \
        }
 
-#define U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,                
\
-                                    _usage, _help, _comp)              \
-               { #_name, _maxargs, 0, 0 ? _cmd_rep : NULL, NULL, _usage, \
-                       _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
-
 #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, \
                                  _help, _comp)                         \
-               { #_name, _maxargs, 0, NULL, 0 ? _cmd : NULL, _usage,   \
+               { #_name, _maxargs, 0, 0 ? _cmd : NULL, _usage,         \
                        _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
 
 #define U_BOOT_CMD_MKENT_GETOPT_COMPLETE(_name, _maxargs, _rep, _cmd,  \
                                         _usage, _help, _comp)          \
-               { #_name, _maxargs, CMDF_GETOPT, NULL,                  \
+               { #_name, _maxargs, CMDF_GETOPT,                        \
                 0 ? (int (*)(struct cmd_tbl *, int, int,               \
                              char *const []))(_cmd) : NULL, _usage,    \
                        _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
@@ -547,9 +536,16 @@ int cmd_source_script(ulong addr, const char *fit_uname, 
const char *confname);
                                   _help, _comp)                        \
        _CMD_REMOVE_GETOPT(sub_ ## _name, _cmd)
 
-#define U_BOOT_CMDREP_COMPLETE(_name, _maxargs, _cmd_rep, _usage,      \
-                              _help, _comp)                            \
-       _CMD_REMOVE_REP(sub_ ## _name, _cmd_rep)
+#define _CMD_REMOVE_REP(_name, _cmd)                                   \
+       int __remove_ ## _name(void)                                    \
+       {                                                               \
+               if (0)                                                  \
+                       _cmd(NULL, 0, 0, NULL, NULL);                   \
+               return 0;                                               \
+       }
+
+#define U_BOOT_SUBCMD_DECL(_name, _maxargs, _do_cmd, _usage, _help, _comp) \
+       _CMD_REMOVE_REP(sub_ ## _name, _do_cmd)
 
 #endif /* CONFIG_CMDLINE */
 
@@ -571,7 +567,7 @@ int cmd_source_script(ulong addr, const char *fit_uname, 
const char *confname);
 
 #define U_BOOT_CMD_WITH_SUBCMDS(_name, _usage, _help, ...)             \
        U_BOOT_SUBCMDS(_name, __VA_ARGS__)                              \
-       U_BOOT_CMDREP_COMPLETE(_name, CONFIG_SYS_MAXARGS, do_##_name,   \
-                              _usage, _help, complete_##_name)
+       U_BOOT_SUBCMD_DECL(_name, CONFIG_SYS_MAXARGS, do_##_name,       \
+                          _usage, _help, complete_##_name)
 
 #endif /* __COMMAND_H */
-- 
2.43.0

Reply via email to