common/cmd_ethsw: Add generic commands for Ethernet Switches
[platform/kernel/u-boot.git] / common / cmd_ethsw.c
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 );