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