drivers/net/vsc9953: Add commands to enable/disable HW learning
[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 #define ETHSW_PORT_STATS_HELP "ethsw [port <port_no>] statistics " \
17 "{ [help] | [clear] } - show an l2 switch port's statistics"
18
19 static int ethsw_port_stats_help_key_func(struct ethsw_command_def *parsed_cmd)
20 {
21         printf(ETHSW_PORT_STATS_HELP"\n");
22
23         return CMD_RET_SUCCESS;
24 }
25
26 #define ETHSW_LEARN_HELP "ethsw [port <port_no>] learning " \
27 "{ [help] | show | auto | disable } " \
28 "- enable/disable/show learning configuration on a port"
29
30 static int ethsw_learn_help_key_func(struct ethsw_command_def *parsed_cmd)
31 {
32         printf(ETHSW_LEARN_HELP"\n");
33
34         return CMD_RET_SUCCESS;
35 }
36
37 static struct keywords_to_function {
38         enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS];
39         int cmd_func_offset;
40         int (*keyword_function)(struct ethsw_command_def *parsed_cmd);
41 } ethsw_cmd_def[] = {
42                 {
43                         .cmd_keyword = {
44                                         ethsw_id_enable,
45                                         ethsw_id_key_end,
46                         },
47                         .cmd_func_offset = offsetof(struct ethsw_command_func,
48                                                     port_enable),
49                         .keyword_function = NULL,
50                 }, {
51                         .cmd_keyword = {
52                                         ethsw_id_disable,
53                                         ethsw_id_key_end,
54                         },
55                         .cmd_func_offset = offsetof(struct ethsw_command_func,
56                                                     port_disable),
57                         .keyword_function = NULL,
58                 }, {
59                         .cmd_keyword = {
60                                         ethsw_id_show,
61                                         ethsw_id_key_end,
62                         },
63                         .cmd_func_offset = offsetof(struct ethsw_command_func,
64                                                     port_show),
65                         .keyword_function = NULL,
66                 }, {
67                         .cmd_keyword = {
68                                         ethsw_id_statistics,
69                                         ethsw_id_help,
70                                         ethsw_id_key_end,
71                         },
72                         .cmd_func_offset = -1,
73                         .keyword_function = &ethsw_port_stats_help_key_func,
74                 }, {
75                         .cmd_keyword = {
76                                         ethsw_id_statistics,
77                                         ethsw_id_key_end,
78                         },
79                         .cmd_func_offset = offsetof(struct ethsw_command_func,
80                                                     port_stats),
81                         .keyword_function = NULL,
82                 }, {
83                         .cmd_keyword = {
84                                         ethsw_id_statistics,
85                                         ethsw_id_clear,
86                                         ethsw_id_key_end,
87                         },
88                         .cmd_func_offset = offsetof(struct ethsw_command_func,
89                                                     port_stats_clear),
90                         .keyword_function = NULL,
91                 }, {
92                         .cmd_keyword = {
93                                         ethsw_id_learning,
94                                         ethsw_id_key_end,
95                         },
96                         .cmd_func_offset = -1,
97                         .keyword_function = &ethsw_learn_help_key_func,
98                 }, {
99                         .cmd_keyword = {
100                                         ethsw_id_learning,
101                                         ethsw_id_help,
102                                         ethsw_id_key_end,
103                         },
104                         .cmd_func_offset = -1,
105                         .keyword_function = &ethsw_learn_help_key_func,
106                 }, {
107                         .cmd_keyword = {
108                                         ethsw_id_learning,
109                                         ethsw_id_show,
110                                         ethsw_id_key_end,
111                         },
112                         .cmd_func_offset = offsetof(struct ethsw_command_func,
113                                                     port_learn_show),
114                         .keyword_function = NULL,
115                 }, {
116                         .cmd_keyword = {
117                                         ethsw_id_learning,
118                                         ethsw_id_auto,
119                                         ethsw_id_key_end,
120                         },
121                         .cmd_func_offset = offsetof(struct ethsw_command_func,
122                                                     port_learn),
123                         .keyword_function = NULL,
124                 }, {
125                         .cmd_keyword = {
126                                         ethsw_id_learning,
127                                         ethsw_id_disable,
128                                         ethsw_id_key_end,
129                         },
130                         .cmd_func_offset = offsetof(struct ethsw_command_func,
131                                                     port_learn),
132                         .keyword_function = NULL,
133                 },
134 };
135
136 struct keywords_optional {
137         int cmd_keyword[ETHSW_MAX_CMD_PARAMS];
138 } cmd_opt_def[] = {
139                 {
140                                 .cmd_keyword = {
141                                                 ethsw_id_port,
142                                                 ethsw_id_port_no,
143                                                 ethsw_id_key_end,
144                                 },
145                 },
146 };
147
148 static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char
149                              *const argv[], int *argc_nr,
150                              struct ethsw_command_def *parsed_cmd);
151 static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
152                               char *const argv[], int *argc_nr,
153                               struct ethsw_command_def *parsed_cmd);
154
155 /*
156  * Define properties for each keyword;
157  * keep the order synced with enum ethsw_keyword_id
158  */
159 struct keyword_def {
160         const char *keyword_name;
161         int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[],
162                      int *argc_nr, struct ethsw_command_def *parsed_cmd);
163 } keyword[] = {
164                 {
165                                 .keyword_name = "help",
166                                 .match = &keyword_match_gen,
167                 }, {
168                                 .keyword_name = "show",
169                                 .match = &keyword_match_gen,
170                 }, {
171                                 .keyword_name = "port",
172                                 .match = &keyword_match_port
173                 },  {
174                                 .keyword_name = "enable",
175                                 .match = &keyword_match_gen,
176                 }, {
177                                 .keyword_name = "disable",
178                                 .match = &keyword_match_gen,
179                 }, {
180                                 .keyword_name = "statistics",
181                                 .match = &keyword_match_gen,
182                 }, {
183                                 .keyword_name = "clear",
184                                 .match = &keyword_match_gen,
185                 }, {
186                                 .keyword_name = "learning",
187                                 .match = &keyword_match_gen,
188                 }, {
189                                 .keyword_name = "auto",
190                                 .match = &keyword_match_gen,
191                 },
192 };
193
194 /*
195  * Function used by an Ethernet Switch driver to set the functions
196  * that must be called by the parser when an ethsw command is given
197  */
198 int ethsw_define_functions(const struct ethsw_command_func *cmd_func)
199 {
200         int i;
201         void **aux_p;
202         int (*cmd_func_aux)(struct ethsw_command_def *);
203
204         if (!cmd_func->ethsw_name)
205                 return -EINVAL;
206
207         ethsw_name = cmd_func->ethsw_name;
208
209         for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
210                 /*
211                  * get the pointer to the function send by the Ethernet Switch
212                  * driver that corresponds to the proper ethsw command
213                  */
214                 if (ethsw_cmd_def[i].keyword_function)
215                         continue;
216
217                 aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset;
218
219                 cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p;
220                 ethsw_cmd_def[i].keyword_function = cmd_func_aux;
221         }
222
223         return 0;
224 }
225
226 /* Generic function used to match a keyword only by a string */
227 static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc,
228                              char *const argv[], int *argc_nr,
229                              struct ethsw_command_def *parsed_cmd)
230 {
231         if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) {
232                 parsed_cmd->cmd_to_keywords[*argc_nr] = key_id;
233
234                 return 1;
235         }
236         return 0;
237 }
238
239 /* Function used to match the command's port */
240 static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
241                               char *const argv[], int *argc_nr,
242                               struct ethsw_command_def *parsed_cmd)
243 {
244         unsigned long val;
245
246         if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
247                 return 0;
248
249         if (*argc_nr + 1 >= argc)
250                 return 0;
251
252         if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
253                 parsed_cmd->port = val;
254                 (*argc_nr)++;
255                 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no;
256                 return 1;
257         }
258
259         return 0;
260 }
261
262 /* Finds optional keywords and modifies *argc_va to skip them */
263 static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd,
264                                    int *argc_val)
265 {
266         int i;
267         int keyw_opt_matched;
268         int argc_val_max;
269         int const *cmd_keyw_p;
270         int const *cmd_keyw_opt_p;
271
272         /* remember the best match */
273         argc_val_max = *argc_val;
274
275         /*
276          * check if our command's optional keywords match the optional
277          * keywords of an available command
278          */
279         for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
280                 keyw_opt_matched = 0;
281                 cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched];
282                 cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched];
283
284                 /*
285                  * increase the number of keywords that
286                  * matched with a command
287                  */
288                 while (keyw_opt_matched + *argc_val <
289                        parsed_cmd->cmd_keywords_nr &&
290                        *cmd_keyw_opt_p != ethsw_id_key_end &&
291                        *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) {
292                         keyw_opt_matched++;
293                         cmd_keyw_p++;
294                         cmd_keyw_opt_p++;
295                 }
296
297                 /*
298                  * if all our optional command's keywords perfectly match an
299                  * optional pattern, then we can move to the next defined
300                  * keywords in our command; remember the one that matched the
301                  * greatest number of keywords
302                  */
303                 if (keyw_opt_matched + *argc_val <=
304                     parsed_cmd->cmd_keywords_nr &&
305                     *cmd_keyw_opt_p == ethsw_id_key_end &&
306                     *argc_val + keyw_opt_matched > argc_val_max)
307                         argc_val_max = *argc_val + keyw_opt_matched;
308         }
309
310         *argc_val = argc_val_max;
311 }
312
313 /*
314  * Finds the function to call based on keywords and
315  * modifies *argc_va to skip them
316  */
317 static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd,
318                                int *argc_val)
319 {
320         int i;
321         int keyw_matched;
322         int *cmd_keyw_p;
323         int *cmd_keyw_def_p;
324
325         /*
326          * check if our command's keywords match the
327          * keywords of an available command
328          */
329         for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
330                 keyw_matched = 0;
331                 cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched];
332                 cmd_keyw_def_p = &ethsw_cmd_def[i].cmd_keyword[keyw_matched];
333
334                 /*
335                  * increase the number of keywords that
336                  * matched with a command
337                  */
338                 while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr &&
339                        *cmd_keyw_def_p != ethsw_id_key_end &&
340                        *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) {
341                         keyw_matched++;
342                         cmd_keyw_p++;
343                         cmd_keyw_def_p++;
344                 }
345
346                 /*
347                  * if all our command's keywords perfectly match an
348                  * available command, then we get the function we need to call
349                  * to configure the Ethernet Switch
350                  */
351                 if (keyw_matched && keyw_matched + *argc_val ==
352                     parsed_cmd->cmd_keywords_nr &&
353                     *cmd_keyw_def_p == ethsw_id_key_end) {
354                         *argc_val += keyw_matched;
355                         parsed_cmd->cmd_function =
356                                         ethsw_cmd_def[i].keyword_function;
357                         return;
358                 }
359         }
360 }
361
362 /* find all the keywords in the command */
363 static int keywords_find(int argc, char * const argv[],
364                          struct ethsw_command_def *parsed_cmd)
365 {
366         int i;
367         int j;
368         int argc_val;
369         int rc = CMD_RET_SUCCESS;
370
371         for (i = 1; i < argc; i++) {
372                 for (j = 0; j < ethsw_id_count; j++) {
373                         if (keyword[j].match(j, argc, argv, &i, parsed_cmd))
374                                 break;
375                 }
376         }
377
378         /* if there is no keyword match for a word, the command is invalid */
379         for (i = 1; i < argc; i++)
380                 if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end)
381                         rc = CMD_RET_USAGE;
382
383         parsed_cmd->cmd_keywords_nr = argc;
384         argc_val = 1;
385
386         /* get optional parameters first */
387         cmd_keywords_opt_check(parsed_cmd, &argc_val);
388
389         if (argc_val == parsed_cmd->cmd_keywords_nr)
390                 return CMD_RET_USAGE;
391
392         /*
393          * check the keywords and if a match is found,
394          * get the function to call
395          */
396         cmd_keywords_check(parsed_cmd, &argc_val);
397
398         /* error if not all commands' parameters were matched */
399         if (argc_val == parsed_cmd->cmd_keywords_nr) {
400                 if (!parsed_cmd->cmd_function) {
401                         printf("Command not available for: %s\n", ethsw_name);
402                         rc = CMD_RET_FAILURE;
403                 }
404         } else {
405                 rc = CMD_RET_USAGE;
406         }
407
408         return rc;
409 }
410
411 static void command_def_init(struct ethsw_command_def *parsed_cmd)
412 {
413         int i;
414
415         for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++)
416                 parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end;
417
418         parsed_cmd->port = ETHSW_CMD_PORT_ALL;
419         parsed_cmd->cmd_function = NULL;
420 }
421
422 /* function to interpret commands starting with "ethsw " */
423 static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
424 {
425         struct ethsw_command_def parsed_cmd;
426         int rc = CMD_RET_SUCCESS;
427
428         if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS)
429                 return CMD_RET_USAGE;
430
431         command_def_init(&parsed_cmd);
432
433         rc = keywords_find(argc, argv, &parsed_cmd);
434
435         if (rc == CMD_RET_SUCCESS)
436                 rc = parsed_cmd.cmd_function(&parsed_cmd);
437
438         return rc;
439 }
440
441 #define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \
442 "- enable/disable a port; show shows a port's configuration"
443
444 U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw,
445            "Ethernet l2 switch commands",
446            ETHSW_PORT_CONF_HELP"\n"
447            ETHSW_PORT_STATS_HELP"\n"
448            ETHSW_LEARN_HELP"\n"
449 );