Message ID | 1437746136-14379-4-git-send-email-codrin.ciubotariu@freescale.com |
---|---|
State | Accepted |
Delegated to: | York Sun |
Headers | show |
Hi Codrin, On Fri, Jul 24, 2015 at 8:55 AM, Codrin Ciubotariu <codrin.ciubotariu@freescale.com> wrote: > 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> > --- > > Changes for v3: > - parser removed from previous patch: > "drivers/net/vsc9953: Refractor the parser for VSC9953 commands"; > - removed member "err" from struct command_def; > - using CMD_RET_* macros instead of magic numbers; > - moved each variable declaration on a separate line, with a single space; > - the code from functions cmd_keywords*_check() should be more clear now; > - removed unused function command_def_cleanup(); > > common/Makefile | 1 + > common/cmd_ethsw.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++++++ > include/ethsw.h | 48 ++++++++ > 3 files changed, 395 insertions(+) > create mode 100644 common/cmd_ethsw.c > create mode 100644 include/ethsw.h > > diff --git a/common/Makefile b/common/Makefile > index d6c1d48..f0b4eec 100644 > --- a/common/Makefile > +++ b/common/Makefile > @@ -211,6 +211,7 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o > obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o > obj-$(CONFIG_CMD_DFU) += cmd_dfu.o > obj-$(CONFIG_CMD_GPT) += cmd_gpt.o > +obj-$(CONFIG_CMD_ETHSW) += cmd_ethsw.o > > # Power > obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o > diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c > new file mode 100644 > index 0000000..ebeaae0 > --- /dev/null > +++ b/common/cmd_ethsw.c > @@ -0,0 +1,346 @@ > +/* > + * Copyright 2015 Freescale Semiconductor, Inc. > + * > + * SPDX-License-Identifier: GPL-2.0+ > + * > + * Ethernet Switch commands > + */ > + > +#include <common.h> > +#include <command.h> > +#include <errno.h> > +#include <ethsw.h> > + > +static const char *ethsw_name; > + > +static struct keywords_to_function { > + enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS]; > + int cmd_func_offset; > + int (*keyword_function)(struct ethsw_command_def *parsed_cmd); > +} ethsw_cmd_def[] = { > + { > + .cmd_keyword = { > + ethsw_id_enable, > + ethsw_id_key_end, > + }, > + .cmd_func_offset = offsetof(struct ethsw_command_func, > + port_enable), > + .keyword_function = NULL, > + }, { > + .cmd_keyword = { > + ethsw_id_disable, > + ethsw_id_key_end, > + }, > + .cmd_func_offset = offsetof(struct ethsw_command_func, > + port_disable), > + .keyword_function = NULL, > + }, { > + .cmd_keyword = { > + ethsw_id_show, > + ethsw_id_key_end, > + }, > + .cmd_func_offset = offsetof(struct ethsw_command_func, > + port_show), > + .keyword_function = NULL, > + }, > +}; > + > +struct keywords_optional { > + int cmd_keyword[ETHSW_MAX_CMD_PARAMS]; > +} cmd_opt_def[] = { > + { > + .cmd_keyword = { > + ethsw_id_port, > + ethsw_id_port_no, > + ethsw_id_key_end, > + }, > + }, > +}; > + > +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char > + *const argv[], int *argc_nr, > + struct ethsw_command_def *parsed_cmd); > +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, > + char *const argv[], int *argc_nr, > + struct ethsw_command_def *parsed_cmd); > + > +/* > + * Define properties for each keyword; > + * keep the order synced with enum ethsw_keyword_id > + */ > +struct keyword_def { > + const char *keyword_name; > + int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[], > + int *argc_nr, struct ethsw_command_def *parsed_cmd); > +} keyword[] = { > + { > + .keyword_name = "help", > + .match = &keyword_match_gen, > + }, { > + .keyword_name = "show", > + .match = &keyword_match_gen, > + }, { > + .keyword_name = "port", > + .match = &keyword_match_port > + }, { > + .keyword_name = "enable", > + .match = &keyword_match_gen, > + }, { > + .keyword_name = "disable", > + .match = &keyword_match_gen, > + }, > +}; > + > +/* > + * Function used by an Ethernet Switch driver to set the functions > + * that must be called by the parser when an ethsw command is given > + */ > +int ethsw_define_functions(const struct ethsw_command_func *cmd_func) > +{ > + int i; > + void **aux_p; > + int (*cmd_func_aux)(struct ethsw_command_def *); > + > + if (!cmd_func->ethsw_name) > + return -EINVAL; > + > + ethsw_name = cmd_func->ethsw_name; > + > + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { > + /* > + * get the pointer to the function send by the Ethernet Switch > + * driver that corresponds to the proper ethsw command > + */ > + if (ethsw_cmd_def[i].keyword_function) > + continue; > + > + aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset; > + > + cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p; > + ethsw_cmd_def[i].keyword_function = cmd_func_aux; > + } > + > + return 0; > +} > + > +/* Generic function used to match a keyword only by a string */ > +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, > + char *const argv[], int *argc_nr, > + struct ethsw_command_def *parsed_cmd) > +{ > + if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) { > + parsed_cmd->cmd_to_keywords[*argc_nr] = key_id; > + > + return 1; > + } > + return 0; > +} > + > +/* Function used to match the command's port */ > +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, > + char *const argv[], int *argc_nr, > + struct ethsw_command_def *parsed_cmd) > +{ > + unsigned long val; > + > + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) > + return 0; > + > + if (*argc_nr + 1 >= argc) > + return 0; > + > + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { > + parsed_cmd->port = val; > + (*argc_nr)++; > + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no; > + return 1; > + } > + > + return 0; > +} > + > +/* Finds optional keywords and modifies *argc_va to skip them */ > +static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd, > + int *argc_val) > +{ > + int i; > + int keyw_opt_matched; > + int argc_val_max; > + int const *cmd_keyw_p; > + int const *cmd_keyw_opt_p; > + > + /* remember the best match */ > + argc_val_max = *argc_val; > + > + /* > + * check if our command's optional keywords match the optional > + * keywords of an available command > + */ > + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { > + keyw_opt_matched = 0; > + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched]; > + cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched]; > + > + /* > + * increase the number of keywords that > + * matched with a command > + */ > + while (keyw_opt_matched + *argc_val < > + parsed_cmd->cmd_keywords_nr && > + *cmd_keyw_opt_p != ethsw_id_key_end && > + *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) { > + keyw_opt_matched++; > + cmd_keyw_p++; > + cmd_keyw_opt_p++; > + } > + > + /* if all our optional command's keywords perfectly match an > + * optional pattern, then we can move to the next defined > + * keywords in our command; remember the one that matched the > + * greatest number of keywords > + */ Improper comment format. Please make sure you always run your patches through checkpatch.pl. I recommend using patman! > + if (keyw_opt_matched + *argc_val <= > + parsed_cmd->cmd_keywords_nr && > + *cmd_keyw_opt_p == ethsw_id_key_end && > + *argc_val + keyw_opt_matched > argc_val_max) > + argc_val_max = *argc_val + keyw_opt_matched; > + } > + > + *argc_val = argc_val_max; > +} > + > +/* > + * Finds the function to call based on keywords and > + * modifies *argc_va to skip them > + */ > +static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd, > + int *argc_val) > +{ > + int i; > + int keyw_matched; > + int *cmd_keyw_p; > + int *cmd_keyw_def_p; > + > + /* > + * check if our command's keywords match the > + * keywords of an available command > + */ > + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { > + keyw_matched = 0; > + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched]; > + cmd_keyw_def_p = ðsw_cmd_def[i].cmd_keyword[keyw_matched]; > + > + /* > + * increase the number of keywords that > + * matched with a command > + */ > + while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr && > + *cmd_keyw_def_p != ethsw_id_key_end && > + *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) { > + keyw_matched++; > + cmd_keyw_p++; > + cmd_keyw_def_p++; > + } > + > + /* > + * if all our command's keywords perfectly match an > + * available command, then we get the function we need to call > + * to configure the Ethernet Switch > + */ > + if (keyw_matched && keyw_matched + *argc_val == > + parsed_cmd->cmd_keywords_nr && > + *cmd_keyw_def_p == ethsw_id_key_end) { > + *argc_val += keyw_matched; > + parsed_cmd->cmd_function = > + ethsw_cmd_def[i].keyword_function; > + return; > + } > + } > +} > + > +/* find all the keywords in the command */ > +static int keywords_find(int argc, char * const argv[], > + struct ethsw_command_def *parsed_cmd) > +{ > + int i; > + int j; > + int argc_val; > + int rc = CMD_RET_SUCCESS; > + > + for (i = 1; i < argc; i++) { > + for (j = 0; j < ethsw_id_count; j++) { > + if (keyword[j].match(j, argc, argv, &i, parsed_cmd)) > + break; > + } > + } > + > + /* if there is no keyword match for a word, the command is invalid */ > + for (i = 1; i < argc; i++) > + if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end) > + rc = CMD_RET_USAGE; > + > + parsed_cmd->cmd_keywords_nr = argc; > + argc_val = 1; > + > + /* get optional parameters first */ > + cmd_keywords_opt_check(parsed_cmd, &argc_val); > + > + if (argc_val == parsed_cmd->cmd_keywords_nr) > + return CMD_RET_USAGE; > + > + /* > + * check the keywords and if a match is found, > + * get the function to call > + */ > + cmd_keywords_check(parsed_cmd, &argc_val); > + > + /* error if not all commands' parameters were matched */ > + if (argc_val == parsed_cmd->cmd_keywords_nr) { > + if (!parsed_cmd->cmd_function) { > + printf("Command not available for: %s\n", ethsw_name); > + rc = CMD_RET_FAILURE; > + } > + } else { > + rc = CMD_RET_USAGE; > + } > + > + return rc; > +} > + > +static void command_def_init(struct ethsw_command_def *parsed_cmd) > +{ > + int i; > + > + for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++) > + parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end; > + > + parsed_cmd->port = ETHSW_CMD_PORT_ALL; > + parsed_cmd->cmd_function = NULL; > +} > + > +/* function to interpret commands starting with "ethsw " */ > +static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) > +{ > + struct ethsw_command_def parsed_cmd; > + int rc = CMD_RET_SUCCESS; > + > + if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS) > + return CMD_RET_USAGE; > + > + command_def_init(&parsed_cmd); > + > + rc = keywords_find(argc, argv, &parsed_cmd); > + > + if (rc == CMD_RET_SUCCESS) > + rc = parsed_cmd.cmd_function(&parsed_cmd); > + > + return rc; > +} > + > +#define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \ > +"- enable/disable a port; show shows a port's configuration" > + > +U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, > + "Ethernet l2 switch commands", > + ETHSW_PORT_CONF_HELP"\n" > +); > diff --git a/include/ethsw.h b/include/ethsw.h > new file mode 100644 > index 0000000..9e80095 > --- /dev/null > +++ b/include/ethsw.h > @@ -0,0 +1,48 @@ > +/* > + * Copyright 2015 Freescale Semiconductor, Inc. > + * > + * SPDX-License-Identifier: GPL-2.0+ > + * > + * Ethernet Switch commands > + */ > + > +#ifndef _CMD_ETHSW_H_ > +#define _CMD_ETHSW_H_ > + > +#define ETHSW_MAX_CMD_PARAMS 20 > +#define ETHSW_CMD_PORT_ALL -1 > + > +/* IDs used to track keywords in a command */ > +enum ethsw_keyword_id { > + ethsw_id_key_end = -1, > + ethsw_id_help, > + ethsw_id_show, > + ethsw_id_port, > + ethsw_id_enable, > + ethsw_id_disable, > + ethsw_id_count, /* keep last */ > +}; > + > +enum ethsw_keyword_opt_id { > + ethsw_id_port_no = ethsw_id_count + 1, > + ethsw_id_count_all, /* keep last */ > +}; > + > +struct ethsw_command_def { > + int cmd_to_keywords[ETHSW_MAX_CMD_PARAMS]; > + int cmd_keywords_nr; > + int port; > + int (*cmd_function)(struct ethsw_command_def *parsed_cmd); > +}; > + > +/* Structure to be created and initialized by an Ethernet Switch driver */ > +struct ethsw_command_func { > + const char *ethsw_name; > + int (*port_enable)(struct ethsw_command_def *parsed_cmd); > + int (*port_disable)(struct ethsw_command_def *parsed_cmd); > + int (*port_show)(struct ethsw_command_def *parsed_cmd); > +}; > + > +int ethsw_define_functions(const struct ethsw_command_func *cmd_func); > + > +#endif /* _CMD_ETHSW_H_ */ > -- > 1.9.3 > > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > http://lists.denx.de/mailman/listinfo/u-boot
On 08/07/2015 01:18 PM, Joe Hershberger wrote: <snip> >> + } >> + >> + /* if all our optional command's keywords perfectly match an >> + * optional pattern, then we can move to the next defined >> + * keywords in our command; remember the one that matched the >> + * greatest number of keywords >> + */ > > Improper comment format. Please make sure you always run your patches > through checkpatch.pl. I recommend using patman! > Joe, Do you mean the multiple-line comment should start from the second line? I can change it when I merge the patch. Checkpatch/patman doesn't complain about this format. York
Hi York, On Fri, Aug 7, 2015 at 5:58 PM, York Sun <yorksun@freescale.com> wrote: > On 08/07/2015 01:18 PM, Joe Hershberger wrote: > <snip> > >>> + } >>> + >>> + /* if all our optional command's keywords perfectly match an >>> + * optional pattern, then we can move to the next defined >>> + * keywords in our command; remember the one that matched the >>> + * greatest number of keywords >>> + */ >> >> Improper comment format. Please make sure you always run your patches >> through checkpatch.pl. I recommend using patman! >> > > Joe, > > Do you mean the multiple-line comment should start from the second line? I can > change it when I merge the patch. > > Checkpatch/patman doesn't complain about this format. That is what I mean. I'm fairly certain that the format used here is incorrect. Perhaps there is an exception set in checkpatch.pl for this path or something. U-Boot appears to defer to Linux Kernel style for this. It appears to be an exception for "net" and "drivers/net" based on this: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/CodingStyle I'm not sure that u-boot is planning on following the same thing. I honestly haven't gone looking for how much of this is in the U-Boot source for net/. As the network stack maintainer, I would prefer to avoid a network stack difference from the rest of the codebase unless someone can present a good reason to do this. Thanks, -Joe
diff --git a/common/Makefile b/common/Makefile index d6c1d48..f0b4eec 100644 --- a/common/Makefile +++ b/common/Makefile @@ -211,6 +211,7 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMD_DFU) += cmd_dfu.o obj-$(CONFIG_CMD_GPT) += cmd_gpt.o +obj-$(CONFIG_CMD_ETHSW) += cmd_ethsw.o # Power obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c new file mode 100644 index 0000000..ebeaae0 --- /dev/null +++ b/common/cmd_ethsw.c @@ -0,0 +1,346 @@ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Ethernet Switch commands + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <ethsw.h> + +static const char *ethsw_name; + +static struct keywords_to_function { + enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS]; + int cmd_func_offset; + int (*keyword_function)(struct ethsw_command_def *parsed_cmd); +} ethsw_cmd_def[] = { + { + .cmd_keyword = { + ethsw_id_enable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_enable), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_disable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_disable), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_show), + .keyword_function = NULL, + }, +}; + +struct keywords_optional { + int cmd_keyword[ETHSW_MAX_CMD_PARAMS]; +} cmd_opt_def[] = { + { + .cmd_keyword = { + ethsw_id_port, + ethsw_id_port_no, + ethsw_id_key_end, + }, + }, +}; + +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char + *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); + +/* + * Define properties for each keyword; + * keep the order synced with enum ethsw_keyword_id + */ +struct keyword_def { + const char *keyword_name; + int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[], + int *argc_nr, struct ethsw_command_def *parsed_cmd); +} keyword[] = { + { + .keyword_name = "help", + .match = &keyword_match_gen, + }, { + .keyword_name = "show", + .match = &keyword_match_gen, + }, { + .keyword_name = "port", + .match = &keyword_match_port + }, { + .keyword_name = "enable", + .match = &keyword_match_gen, + }, { + .keyword_name = "disable", + .match = &keyword_match_gen, + }, +}; + +/* + * Function used by an Ethernet Switch driver to set the functions + * that must be called by the parser when an ethsw command is given + */ +int ethsw_define_functions(const struct ethsw_command_func *cmd_func) +{ + int i; + void **aux_p; + int (*cmd_func_aux)(struct ethsw_command_def *); + + if (!cmd_func->ethsw_name) + return -EINVAL; + + ethsw_name = cmd_func->ethsw_name; + + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + /* + * get the pointer to the function send by the Ethernet Switch + * driver that corresponds to the proper ethsw command + */ + if (ethsw_cmd_def[i].keyword_function) + continue; + + aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset; + + cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p; + ethsw_cmd_def[i].keyword_function = cmd_func_aux; + } + + return 0; +} + +/* Generic function used to match a keyword only by a string */ +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) { + parsed_cmd->cmd_to_keywords[*argc_nr] = key_id; + + return 1; + } + return 0; +} + +/* Function used to match the command's port */ +static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + unsigned long val; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 0; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->port = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no; + return 1; + } + + return 0; +} + +/* Finds optional keywords and modifies *argc_va to skip them */ +static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd, + int *argc_val) +{ + int i; + int keyw_opt_matched; + int argc_val_max; + int const *cmd_keyw_p; + int const *cmd_keyw_opt_p; + + /* remember the best match */ + argc_val_max = *argc_val; + + /* + * check if our command's optional keywords match the optional + * keywords of an available command + */ + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + keyw_opt_matched = 0; + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched]; + cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched]; + + /* + * increase the number of keywords that + * matched with a command + */ + while (keyw_opt_matched + *argc_val < + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_opt_p != ethsw_id_key_end && + *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) { + keyw_opt_matched++; + cmd_keyw_p++; + cmd_keyw_opt_p++; + } + + /* if all our optional command's keywords perfectly match an + * optional pattern, then we can move to the next defined + * keywords in our command; remember the one that matched the + * greatest number of keywords + */ + if (keyw_opt_matched + *argc_val <= + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_opt_p == ethsw_id_key_end && + *argc_val + keyw_opt_matched > argc_val_max) + argc_val_max = *argc_val + keyw_opt_matched; + } + + *argc_val = argc_val_max; +} + +/* + * Finds the function to call based on keywords and + * modifies *argc_va to skip them + */ +static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd, + int *argc_val) +{ + int i; + int keyw_matched; + int *cmd_keyw_p; + int *cmd_keyw_def_p; + + /* + * check if our command's keywords match the + * keywords of an available command + */ + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + keyw_matched = 0; + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched]; + cmd_keyw_def_p = ðsw_cmd_def[i].cmd_keyword[keyw_matched]; + + /* + * increase the number of keywords that + * matched with a command + */ + while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr && + *cmd_keyw_def_p != ethsw_id_key_end && + *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) { + keyw_matched++; + cmd_keyw_p++; + cmd_keyw_def_p++; + } + + /* + * if all our command's keywords perfectly match an + * available command, then we get the function we need to call + * to configure the Ethernet Switch + */ + if (keyw_matched && keyw_matched + *argc_val == + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_def_p == ethsw_id_key_end) { + *argc_val += keyw_matched; + parsed_cmd->cmd_function = + ethsw_cmd_def[i].keyword_function; + return; + } + } +} + +/* find all the keywords in the command */ +static int keywords_find(int argc, char * const argv[], + struct ethsw_command_def *parsed_cmd) +{ + int i; + int j; + int argc_val; + int rc = CMD_RET_SUCCESS; + + for (i = 1; i < argc; i++) { + for (j = 0; j < ethsw_id_count; j++) { + if (keyword[j].match(j, argc, argv, &i, parsed_cmd)) + break; + } + } + + /* if there is no keyword match for a word, the command is invalid */ + for (i = 1; i < argc; i++) + if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end) + rc = CMD_RET_USAGE; + + parsed_cmd->cmd_keywords_nr = argc; + argc_val = 1; + + /* get optional parameters first */ + cmd_keywords_opt_check(parsed_cmd, &argc_val); + + if (argc_val == parsed_cmd->cmd_keywords_nr) + return CMD_RET_USAGE; + + /* + * check the keywords and if a match is found, + * get the function to call + */ + cmd_keywords_check(parsed_cmd, &argc_val); + + /* error if not all commands' parameters were matched */ + if (argc_val == parsed_cmd->cmd_keywords_nr) { + if (!parsed_cmd->cmd_function) { + printf("Command not available for: %s\n", ethsw_name); + rc = CMD_RET_FAILURE; + } + } else { + rc = CMD_RET_USAGE; + } + + return rc; +} + +static void command_def_init(struct ethsw_command_def *parsed_cmd) +{ + int i; + + for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++) + parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end; + + parsed_cmd->port = ETHSW_CMD_PORT_ALL; + parsed_cmd->cmd_function = NULL; +} + +/* function to interpret commands starting with "ethsw " */ +static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct ethsw_command_def parsed_cmd; + int rc = CMD_RET_SUCCESS; + + if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS) + return CMD_RET_USAGE; + + command_def_init(&parsed_cmd); + + rc = keywords_find(argc, argv, &parsed_cmd); + + if (rc == CMD_RET_SUCCESS) + rc = parsed_cmd.cmd_function(&parsed_cmd); + + return rc; +} + +#define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \ +"- enable/disable a port; show shows a port's configuration" + +U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, + "Ethernet l2 switch commands", + ETHSW_PORT_CONF_HELP"\n" +); diff --git a/include/ethsw.h b/include/ethsw.h new file mode 100644 index 0000000..9e80095 --- /dev/null +++ b/include/ethsw.h @@ -0,0 +1,48 @@ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Ethernet Switch commands + */ + +#ifndef _CMD_ETHSW_H_ +#define _CMD_ETHSW_H_ + +#define ETHSW_MAX_CMD_PARAMS 20 +#define ETHSW_CMD_PORT_ALL -1 + +/* IDs used to track keywords in a command */ +enum ethsw_keyword_id { + ethsw_id_key_end = -1, + ethsw_id_help, + ethsw_id_show, + ethsw_id_port, + ethsw_id_enable, + ethsw_id_disable, + ethsw_id_count, /* keep last */ +}; + +enum ethsw_keyword_opt_id { + ethsw_id_port_no = ethsw_id_count + 1, + ethsw_id_count_all, /* keep last */ +}; + +struct ethsw_command_def { + int cmd_to_keywords[ETHSW_MAX_CMD_PARAMS]; + int cmd_keywords_nr; + int port; + int (*cmd_function)(struct ethsw_command_def *parsed_cmd); +}; + +/* Structure to be created and initialized by an Ethernet Switch driver */ +struct ethsw_command_func { + const char *ethsw_name; + int (*port_enable)(struct ethsw_command_def *parsed_cmd); + int (*port_disable)(struct ethsw_command_def *parsed_cmd); + int (*port_show)(struct ethsw_command_def *parsed_cmd); +}; + +int ethsw_define_functions(const struct ethsw_command_func *cmd_func); + +#endif /* _CMD_ETHSW_H_ */
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> --- Changes for v3: - parser removed from previous patch: "drivers/net/vsc9953: Refractor the parser for VSC9953 commands"; - removed member "err" from struct command_def; - using CMD_RET_* macros instead of magic numbers; - moved each variable declaration on a separate line, with a single space; - the code from functions cmd_keywords*_check() should be more clear now; - removed unused function command_def_cleanup(); common/Makefile | 1 + common/cmd_ethsw.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/ethsw.h | 48 ++++++++ 3 files changed, 395 insertions(+) create mode 100644 common/cmd_ethsw.c create mode 100644 include/ethsw.h