Commit 4ea54e3f2394cfca9ffaa14c181d2ae8a11677a8

Authored by Codrin Ciubotariu
Committed by York Sun
1 parent 9de059871f

common/cmd_ethsw: Add generic commands for Ethernet Switches

This patch creates a flexible parser for Ethernet Switch
configurations that should support complex commands.
The parser searches for predefined keywords in the command
and calls the proper function when a match is found.
Also, the parser allows for optional keywords, such as
"port", to apply the command on a port
or on all ports. For now, the defined commands are:
ethsw [port <port_no>] { enable | disable | show }

Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@freescale.com>
Reviewed-by: York Sun <yorksun@freescale.com>

Showing 3 changed files with 396 additions and 0 deletions Side-by-side Diff

... ... @@ -214,6 +214,7 @@
214 214 obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
215 215 obj-$(CONFIG_CMD_DFU) += cmd_dfu.o
216 216 obj-$(CONFIG_CMD_GPT) += cmd_gpt.o
  217 +obj-$(CONFIG_CMD_ETHSW) += cmd_ethsw.o
217 218  
218 219 # Power
219 220 obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o
  1 +/*
  2 + * Copyright 2015 Freescale Semiconductor, Inc.
  3 + *
  4 + * SPDX-License-Identifier: GPL-2.0+
  5 + *
  6 + * Ethernet Switch commands
  7 + */
  8 +
  9 +#include <common.h>
  10 +#include <command.h>
  11 +#include <errno.h>
  12 +#include <ethsw.h>
  13 +
  14 +static const char *ethsw_name;
  15 +
  16 +static struct keywords_to_function {
  17 + enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS];
  18 + int cmd_func_offset;
  19 + int (*keyword_function)(struct ethsw_command_def *parsed_cmd);
  20 +} ethsw_cmd_def[] = {
  21 + {
  22 + .cmd_keyword = {
  23 + ethsw_id_enable,
  24 + ethsw_id_key_end,
  25 + },
  26 + .cmd_func_offset = offsetof(struct ethsw_command_func,
  27 + port_enable),
  28 + .keyword_function = NULL,
  29 + }, {
  30 + .cmd_keyword = {
  31 + ethsw_id_disable,
  32 + ethsw_id_key_end,
  33 + },
  34 + .cmd_func_offset = offsetof(struct ethsw_command_func,
  35 + port_disable),
  36 + .keyword_function = NULL,
  37 + }, {
  38 + .cmd_keyword = {
  39 + ethsw_id_show,
  40 + ethsw_id_key_end,
  41 + },
  42 + .cmd_func_offset = offsetof(struct ethsw_command_func,
  43 + port_show),
  44 + .keyword_function = NULL,
  45 + },
  46 +};
  47 +
  48 +struct keywords_optional {
  49 + int cmd_keyword[ETHSW_MAX_CMD_PARAMS];
  50 +} cmd_opt_def[] = {
  51 + {
  52 + .cmd_keyword = {
  53 + ethsw_id_port,
  54 + ethsw_id_port_no,
  55 + ethsw_id_key_end,
  56 + },
  57 + },
  58 +};
  59 +
  60 +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char
  61 + *const argv[], int *argc_nr,
  62 + struct ethsw_command_def *parsed_cmd);
  63 +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
  64 + char *const argv[], int *argc_nr,
  65 + struct ethsw_command_def *parsed_cmd);
  66 +
  67 +/*
  68 + * Define properties for each keyword;
  69 + * keep the order synced with enum ethsw_keyword_id
  70 + */
  71 +struct keyword_def {
  72 + const char *keyword_name;
  73 + int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[],
  74 + int *argc_nr, struct ethsw_command_def *parsed_cmd);
  75 +} keyword[] = {
  76 + {
  77 + .keyword_name = "help",
  78 + .match = &keyword_match_gen,
  79 + }, {
  80 + .keyword_name = "show",
  81 + .match = &keyword_match_gen,
  82 + }, {
  83 + .keyword_name = "port",
  84 + .match = &keyword_match_port
  85 + }, {
  86 + .keyword_name = "enable",
  87 + .match = &keyword_match_gen,
  88 + }, {
  89 + .keyword_name = "disable",
  90 + .match = &keyword_match_gen,
  91 + },
  92 +};
  93 +
  94 +/*
  95 + * Function used by an Ethernet Switch driver to set the functions
  96 + * that must be called by the parser when an ethsw command is given
  97 + */
  98 +int ethsw_define_functions(const struct ethsw_command_func *cmd_func)
  99 +{
  100 + int i;
  101 + void **aux_p;
  102 + int (*cmd_func_aux)(struct ethsw_command_def *);
  103 +
  104 + if (!cmd_func->ethsw_name)
  105 + return -EINVAL;
  106 +
  107 + ethsw_name = cmd_func->ethsw_name;
  108 +
  109 + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
  110 + /*
  111 + * get the pointer to the function send by the Ethernet Switch
  112 + * driver that corresponds to the proper ethsw command
  113 + */
  114 + if (ethsw_cmd_def[i].keyword_function)
  115 + continue;
  116 +
  117 + aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset;
  118 +
  119 + cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p;
  120 + ethsw_cmd_def[i].keyword_function = cmd_func_aux;
  121 + }
  122 +
  123 + return 0;
  124 +}
  125 +
  126 +/* Generic function used to match a keyword only by a string */
  127 +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc,
  128 + char *const argv[], int *argc_nr,
  129 + struct ethsw_command_def *parsed_cmd)
  130 +{
  131 + if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) {
  132 + parsed_cmd->cmd_to_keywords[*argc_nr] = key_id;
  133 +
  134 + return 1;
  135 + }
  136 + return 0;
  137 +}
  138 +
  139 +/* Function used to match the command's port */
  140 +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
  141 + char *const argv[], int *argc_nr,
  142 + struct ethsw_command_def *parsed_cmd)
  143 +{
  144 + unsigned long val;
  145 +
  146 + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
  147 + return 0;
  148 +
  149 + if (*argc_nr + 1 >= argc)
  150 + return 0;
  151 +
  152 + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
  153 + parsed_cmd->port = val;
  154 + (*argc_nr)++;
  155 + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no;
  156 + return 1;
  157 + }
  158 +
  159 + return 0;
  160 +}
  161 +
  162 +/* Finds optional keywords and modifies *argc_va to skip them */
  163 +static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd,
  164 + int *argc_val)
  165 +{
  166 + int i;
  167 + int keyw_opt_matched;
  168 + int argc_val_max;
  169 + int const *cmd_keyw_p;
  170 + int const *cmd_keyw_opt_p;
  171 +
  172 + /* remember the best match */
  173 + argc_val_max = *argc_val;
  174 +
  175 + /*
  176 + * check if our command's optional keywords match the optional
  177 + * keywords of an available command
  178 + */
  179 + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
  180 + keyw_opt_matched = 0;
  181 + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched];
  182 + cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched];
  183 +
  184 + /*
  185 + * increase the number of keywords that
  186 + * matched with a command
  187 + */
  188 + while (keyw_opt_matched + *argc_val <
  189 + parsed_cmd->cmd_keywords_nr &&
  190 + *cmd_keyw_opt_p != ethsw_id_key_end &&
  191 + *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) {
  192 + keyw_opt_matched++;
  193 + cmd_keyw_p++;
  194 + cmd_keyw_opt_p++;
  195 + }
  196 +
  197 + /*
  198 + * if all our optional command's keywords perfectly match an
  199 + * optional pattern, then we can move to the next defined
  200 + * keywords in our command; remember the one that matched the
  201 + * greatest number of keywords
  202 + */
  203 + if (keyw_opt_matched + *argc_val <=
  204 + parsed_cmd->cmd_keywords_nr &&
  205 + *cmd_keyw_opt_p == ethsw_id_key_end &&
  206 + *argc_val + keyw_opt_matched > argc_val_max)
  207 + argc_val_max = *argc_val + keyw_opt_matched;
  208 + }
  209 +
  210 + *argc_val = argc_val_max;
  211 +}
  212 +
  213 +/*
  214 + * Finds the function to call based on keywords and
  215 + * modifies *argc_va to skip them
  216 + */
  217 +static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd,
  218 + int *argc_val)
  219 +{
  220 + int i;
  221 + int keyw_matched;
  222 + int *cmd_keyw_p;
  223 + int *cmd_keyw_def_p;
  224 +
  225 + /*
  226 + * check if our command's keywords match the
  227 + * keywords of an available command
  228 + */
  229 + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
  230 + keyw_matched = 0;
  231 + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched];
  232 + cmd_keyw_def_p = &ethsw_cmd_def[i].cmd_keyword[keyw_matched];
  233 +
  234 + /*
  235 + * increase the number of keywords that
  236 + * matched with a command
  237 + */
  238 + while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr &&
  239 + *cmd_keyw_def_p != ethsw_id_key_end &&
  240 + *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) {
  241 + keyw_matched++;
  242 + cmd_keyw_p++;
  243 + cmd_keyw_def_p++;
  244 + }
  245 +
  246 + /*
  247 + * if all our command's keywords perfectly match an
  248 + * available command, then we get the function we need to call
  249 + * to configure the Ethernet Switch
  250 + */
  251 + if (keyw_matched && keyw_matched + *argc_val ==
  252 + parsed_cmd->cmd_keywords_nr &&
  253 + *cmd_keyw_def_p == ethsw_id_key_end) {
  254 + *argc_val += keyw_matched;
  255 + parsed_cmd->cmd_function =
  256 + ethsw_cmd_def[i].keyword_function;
  257 + return;
  258 + }
  259 + }
  260 +}
  261 +
  262 +/* find all the keywords in the command */
  263 +static int keywords_find(int argc, char * const argv[],
  264 + struct ethsw_command_def *parsed_cmd)
  265 +{
  266 + int i;
  267 + int j;
  268 + int argc_val;
  269 + int rc = CMD_RET_SUCCESS;
  270 +
  271 + for (i = 1; i < argc; i++) {
  272 + for (j = 0; j < ethsw_id_count; j++) {
  273 + if (keyword[j].match(j, argc, argv, &i, parsed_cmd))
  274 + break;
  275 + }
  276 + }
  277 +
  278 + /* if there is no keyword match for a word, the command is invalid */
  279 + for (i = 1; i < argc; i++)
  280 + if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end)
  281 + rc = CMD_RET_USAGE;
  282 +
  283 + parsed_cmd->cmd_keywords_nr = argc;
  284 + argc_val = 1;
  285 +
  286 + /* get optional parameters first */
  287 + cmd_keywords_opt_check(parsed_cmd, &argc_val);
  288 +
  289 + if (argc_val == parsed_cmd->cmd_keywords_nr)
  290 + return CMD_RET_USAGE;
  291 +
  292 + /*
  293 + * check the keywords and if a match is found,
  294 + * get the function to call
  295 + */
  296 + cmd_keywords_check(parsed_cmd, &argc_val);
  297 +
  298 + /* error if not all commands' parameters were matched */
  299 + if (argc_val == parsed_cmd->cmd_keywords_nr) {
  300 + if (!parsed_cmd->cmd_function) {
  301 + printf("Command not available for: %s\n", ethsw_name);
  302 + rc = CMD_RET_FAILURE;
  303 + }
  304 + } else {
  305 + rc = CMD_RET_USAGE;
  306 + }
  307 +
  308 + return rc;
  309 +}
  310 +
  311 +static void command_def_init(struct ethsw_command_def *parsed_cmd)
  312 +{
  313 + int i;
  314 +
  315 + for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++)
  316 + parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end;
  317 +
  318 + parsed_cmd->port = ETHSW_CMD_PORT_ALL;
  319 + parsed_cmd->cmd_function = NULL;
  320 +}
  321 +
  322 +/* function to interpret commands starting with "ethsw " */
  323 +static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  324 +{
  325 + struct ethsw_command_def parsed_cmd;
  326 + int rc = CMD_RET_SUCCESS;
  327 +
  328 + if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS)
  329 + return CMD_RET_USAGE;
  330 +
  331 + command_def_init(&parsed_cmd);
  332 +
  333 + rc = keywords_find(argc, argv, &parsed_cmd);
  334 +
  335 + if (rc == CMD_RET_SUCCESS)
  336 + rc = parsed_cmd.cmd_function(&parsed_cmd);
  337 +
  338 + return rc;
  339 +}
  340 +
  341 +#define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \
  342 +"- enable/disable a port; show shows a port's configuration"
  343 +
  344 +U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw,
  345 + "Ethernet l2 switch commands",
  346 + ETHSW_PORT_CONF_HELP"\n"
  347 +);
  1 +/*
  2 + * Copyright 2015 Freescale Semiconductor, Inc.
  3 + *
  4 + * SPDX-License-Identifier: GPL-2.0+
  5 + *
  6 + * Ethernet Switch commands
  7 + */
  8 +
  9 +#ifndef _CMD_ETHSW_H_
  10 +#define _CMD_ETHSW_H_
  11 +
  12 +#define ETHSW_MAX_CMD_PARAMS 20
  13 +#define ETHSW_CMD_PORT_ALL -1
  14 +
  15 +/* IDs used to track keywords in a command */
  16 +enum ethsw_keyword_id {
  17 + ethsw_id_key_end = -1,
  18 + ethsw_id_help,
  19 + ethsw_id_show,
  20 + ethsw_id_port,
  21 + ethsw_id_enable,
  22 + ethsw_id_disable,
  23 + ethsw_id_count, /* keep last */
  24 +};
  25 +
  26 +enum ethsw_keyword_opt_id {
  27 + ethsw_id_port_no = ethsw_id_count + 1,
  28 + ethsw_id_count_all, /* keep last */
  29 +};
  30 +
  31 +struct ethsw_command_def {
  32 + int cmd_to_keywords[ETHSW_MAX_CMD_PARAMS];
  33 + int cmd_keywords_nr;
  34 + int port;
  35 + int (*cmd_function)(struct ethsw_command_def *parsed_cmd);
  36 +};
  37 +
  38 +/* Structure to be created and initialized by an Ethernet Switch driver */
  39 +struct ethsw_command_func {
  40 + const char *ethsw_name;
  41 + int (*port_enable)(struct ethsw_command_def *parsed_cmd);
  42 + int (*port_disable)(struct ethsw_command_def *parsed_cmd);
  43 + int (*port_show)(struct ethsw_command_def *parsed_cmd);
  44 +};
  45 +
  46 +int ethsw_define_functions(const struct ethsw_command_func *cmd_func);
  47 +
  48 +#endif /* _CMD_ETHSW_H_ */