blob: 9ba680a9b909f77311e9b469bdc1bfafce5d1c66 [file] [log] [blame]
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +03001/*
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>
Codrin Ciubotariu22449852015-09-09 18:00:52 +030012#include <env_flags.h>
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +030013#include <ethsw.h>
14
15static const char *ethsw_name;
16
Codrin Ciubotariu86719f02015-07-24 16:55:29 +030017#define ETHSW_PORT_STATS_HELP "ethsw [port <port_no>] statistics " \
18"{ [help] | [clear] } - show an l2 switch port's statistics"
19
20static int ethsw_port_stats_help_key_func(struct ethsw_command_def *parsed_cmd)
21{
22 printf(ETHSW_PORT_STATS_HELP"\n");
23
24 return CMD_RET_SUCCESS;
25}
26
Codrin Ciubotariu68c929d2015-07-24 16:55:30 +030027#define ETHSW_LEARN_HELP "ethsw [port <port_no>] learning " \
28"{ [help] | show | auto | disable } " \
29"- enable/disable/show learning configuration on a port"
30
31static int ethsw_learn_help_key_func(struct ethsw_command_def *parsed_cmd)
32{
33 printf(ETHSW_LEARN_HELP"\n");
34
35 return CMD_RET_SUCCESS;
36}
37
Codrin Ciubotariu22449852015-09-09 18:00:52 +030038#define ETHSW_FDB_HELP "ethsw [port <port_no>] [vlan <vid>] fdb " \
39"{ [help] | show | flush | { add | del } <mac> } " \
40"- Add/delete a mac entry in FDB; use show to see FDB entries; " \
41"if vlan <vid> is missing, VID 1 will be used"
42
43static int ethsw_fdb_help_key_func(struct ethsw_command_def *parsed_cmd)
44{
45 printf(ETHSW_FDB_HELP"\n");
46
47 return CMD_RET_SUCCESS;
48}
49
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +030050static struct keywords_to_function {
51 enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS];
52 int cmd_func_offset;
53 int (*keyword_function)(struct ethsw_command_def *parsed_cmd);
54} ethsw_cmd_def[] = {
55 {
56 .cmd_keyword = {
57 ethsw_id_enable,
58 ethsw_id_key_end,
59 },
60 .cmd_func_offset = offsetof(struct ethsw_command_func,
61 port_enable),
62 .keyword_function = NULL,
63 }, {
64 .cmd_keyword = {
65 ethsw_id_disable,
66 ethsw_id_key_end,
67 },
68 .cmd_func_offset = offsetof(struct ethsw_command_func,
69 port_disable),
70 .keyword_function = NULL,
71 }, {
72 .cmd_keyword = {
73 ethsw_id_show,
74 ethsw_id_key_end,
75 },
76 .cmd_func_offset = offsetof(struct ethsw_command_func,
77 port_show),
78 .keyword_function = NULL,
Codrin Ciubotariu86719f02015-07-24 16:55:29 +030079 }, {
80 .cmd_keyword = {
81 ethsw_id_statistics,
82 ethsw_id_help,
83 ethsw_id_key_end,
84 },
85 .cmd_func_offset = -1,
86 .keyword_function = &ethsw_port_stats_help_key_func,
87 }, {
88 .cmd_keyword = {
89 ethsw_id_statistics,
90 ethsw_id_key_end,
91 },
92 .cmd_func_offset = offsetof(struct ethsw_command_func,
93 port_stats),
94 .keyword_function = NULL,
95 }, {
96 .cmd_keyword = {
97 ethsw_id_statistics,
98 ethsw_id_clear,
99 ethsw_id_key_end,
100 },
101 .cmd_func_offset = offsetof(struct ethsw_command_func,
102 port_stats_clear),
103 .keyword_function = NULL,
Codrin Ciubotariu68c929d2015-07-24 16:55:30 +0300104 }, {
105 .cmd_keyword = {
106 ethsw_id_learning,
107 ethsw_id_key_end,
108 },
109 .cmd_func_offset = -1,
110 .keyword_function = &ethsw_learn_help_key_func,
111 }, {
112 .cmd_keyword = {
113 ethsw_id_learning,
114 ethsw_id_help,
115 ethsw_id_key_end,
116 },
117 .cmd_func_offset = -1,
118 .keyword_function = &ethsw_learn_help_key_func,
119 }, {
120 .cmd_keyword = {
121 ethsw_id_learning,
122 ethsw_id_show,
123 ethsw_id_key_end,
124 },
125 .cmd_func_offset = offsetof(struct ethsw_command_func,
126 port_learn_show),
127 .keyword_function = NULL,
128 }, {
129 .cmd_keyword = {
130 ethsw_id_learning,
131 ethsw_id_auto,
132 ethsw_id_key_end,
133 },
134 .cmd_func_offset = offsetof(struct ethsw_command_func,
135 port_learn),
136 .keyword_function = NULL,
137 }, {
138 .cmd_keyword = {
139 ethsw_id_learning,
140 ethsw_id_disable,
141 ethsw_id_key_end,
142 },
143 .cmd_func_offset = offsetof(struct ethsw_command_func,
144 port_learn),
145 .keyword_function = NULL,
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300146 }, {
147 .cmd_keyword = {
148 ethsw_id_fdb,
149 ethsw_id_key_end,
150 },
151 .cmd_func_offset = -1,
152 .keyword_function = &ethsw_fdb_help_key_func,
153 }, {
154 .cmd_keyword = {
155 ethsw_id_fdb,
156 ethsw_id_help,
157 ethsw_id_key_end,
158 },
159 .cmd_func_offset = -1,
160 .keyword_function = &ethsw_fdb_help_key_func,
161 }, {
162 .cmd_keyword = {
163 ethsw_id_fdb,
164 ethsw_id_show,
165 ethsw_id_key_end,
166 },
167 .cmd_func_offset = offsetof(struct ethsw_command_func,
168 fdb_show),
169 .keyword_function = NULL,
170 }, {
171 .cmd_keyword = {
172 ethsw_id_fdb,
173 ethsw_id_flush,
174 ethsw_id_key_end,
175 },
176 .cmd_func_offset = offsetof(struct ethsw_command_func,
177 fdb_flush),
178 .keyword_function = NULL,
179 }, {
180 .cmd_keyword = {
181 ethsw_id_fdb,
182 ethsw_id_add,
183 ethsw_id_add_del_mac,
184 ethsw_id_key_end,
185 },
186 .cmd_func_offset = offsetof(struct ethsw_command_func,
187 fdb_entry_add),
188 .keyword_function = NULL,
189 }, {
190 .cmd_keyword = {
191 ethsw_id_fdb,
192 ethsw_id_del,
193 ethsw_id_add_del_mac,
194 ethsw_id_key_end,
195 },
196 .cmd_func_offset = offsetof(struct ethsw_command_func,
197 fdb_entry_del),
198 .keyword_function = NULL,
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300199 },
200};
201
202struct keywords_optional {
203 int cmd_keyword[ETHSW_MAX_CMD_PARAMS];
204} cmd_opt_def[] = {
205 {
206 .cmd_keyword = {
207 ethsw_id_port,
208 ethsw_id_port_no,
209 ethsw_id_key_end,
210 },
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300211 }, {
212 .cmd_keyword = {
213 ethsw_id_vlan,
214 ethsw_id_vlan_no,
215 ethsw_id_key_end,
216 },
217 }, {
218 .cmd_keyword = {
219 ethsw_id_port,
220 ethsw_id_port_no,
221 ethsw_id_vlan,
222 ethsw_id_vlan_no,
223 ethsw_id_key_end,
224 },
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300225 },
226};
227
228static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char
229 *const argv[], int *argc_nr,
230 struct ethsw_command_def *parsed_cmd);
231static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
232 char *const argv[], int *argc_nr,
233 struct ethsw_command_def *parsed_cmd);
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300234static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
235 char *const argv[], int *argc_nr,
236 struct ethsw_command_def *parsed_cmd);
237static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc,
238 char *const argv[], int *argc_nr,
239 struct ethsw_command_def *parsed_cmd);
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300240
241/*
242 * Define properties for each keyword;
243 * keep the order synced with enum ethsw_keyword_id
244 */
245struct keyword_def {
246 const char *keyword_name;
247 int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[],
248 int *argc_nr, struct ethsw_command_def *parsed_cmd);
249} keyword[] = {
250 {
251 .keyword_name = "help",
252 .match = &keyword_match_gen,
253 }, {
254 .keyword_name = "show",
255 .match = &keyword_match_gen,
256 }, {
257 .keyword_name = "port",
258 .match = &keyword_match_port
259 }, {
260 .keyword_name = "enable",
261 .match = &keyword_match_gen,
262 }, {
263 .keyword_name = "disable",
264 .match = &keyword_match_gen,
Codrin Ciubotariu86719f02015-07-24 16:55:29 +0300265 }, {
266 .keyword_name = "statistics",
267 .match = &keyword_match_gen,
268 }, {
269 .keyword_name = "clear",
270 .match = &keyword_match_gen,
Codrin Ciubotariu68c929d2015-07-24 16:55:30 +0300271 }, {
272 .keyword_name = "learning",
273 .match = &keyword_match_gen,
274 }, {
275 .keyword_name = "auto",
276 .match = &keyword_match_gen,
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300277 }, {
278 .keyword_name = "vlan",
279 .match = &keyword_match_vlan,
280 }, {
281 .keyword_name = "fdb",
282 .match = &keyword_match_gen,
283 }, {
284 .keyword_name = "add",
285 .match = &keyword_match_mac_addr,
286 }, {
287 .keyword_name = "del",
288 .match = &keyword_match_mac_addr,
289 }, {
290 .keyword_name = "flush",
291 .match = &keyword_match_gen,
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300292 },
293};
294
295/*
296 * Function used by an Ethernet Switch driver to set the functions
297 * that must be called by the parser when an ethsw command is given
298 */
299int ethsw_define_functions(const struct ethsw_command_func *cmd_func)
300{
301 int i;
302 void **aux_p;
303 int (*cmd_func_aux)(struct ethsw_command_def *);
304
305 if (!cmd_func->ethsw_name)
306 return -EINVAL;
307
308 ethsw_name = cmd_func->ethsw_name;
309
310 for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
311 /*
312 * get the pointer to the function send by the Ethernet Switch
313 * driver that corresponds to the proper ethsw command
314 */
315 if (ethsw_cmd_def[i].keyword_function)
316 continue;
317
318 aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset;
319
320 cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p;
321 ethsw_cmd_def[i].keyword_function = cmd_func_aux;
322 }
323
324 return 0;
325}
326
327/* Generic function used to match a keyword only by a string */
328static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc,
329 char *const argv[], int *argc_nr,
330 struct ethsw_command_def *parsed_cmd)
331{
332 if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) {
333 parsed_cmd->cmd_to_keywords[*argc_nr] = key_id;
334
335 return 1;
336 }
337 return 0;
338}
339
340/* Function used to match the command's port */
341static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
342 char *const argv[], int *argc_nr,
343 struct ethsw_command_def *parsed_cmd)
344{
345 unsigned long val;
346
347 if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
348 return 0;
349
350 if (*argc_nr + 1 >= argc)
351 return 0;
352
353 if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
354 parsed_cmd->port = val;
355 (*argc_nr)++;
356 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no;
357 return 1;
358 }
359
360 return 0;
361}
362
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300363/* Function used to match the command's vlan */
364static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
365 char *const argv[], int *argc_nr,
366 struct ethsw_command_def *parsed_cmd)
367{
368 unsigned long val;
369 int aux;
370
371 if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
372 return 0;
373
374 if (*argc_nr + 1 >= argc)
375 return 0;
376
377 if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
378 parsed_cmd->vid = val;
379 (*argc_nr)++;
380 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_vlan_no;
381 return 1;
382 }
383
384 aux = *argc_nr + 1;
385
386 if (keyword_match_gen(ethsw_id_add, argc, argv, &aux, parsed_cmd))
387 parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add;
388 else if (keyword_match_gen(ethsw_id_del, argc, argv, &aux, parsed_cmd))
389 parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_del;
390 else
391 return 0;
392
393 if (*argc_nr + 2 >= argc)
394 return 0;
395
396 if (strict_strtoul(argv[*argc_nr + 2], 10, &val) != -EINVAL) {
397 parsed_cmd->vid = val;
398 (*argc_nr) += 2;
399 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_add_del_no;
400 return 1;
401 }
402
403 return 0;
404}
405
406/* Function used to match the command's MAC address */
407static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc,
408 char *const argv[], int *argc_nr,
409 struct ethsw_command_def *parsed_cmd)
410{
411 if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
412 return 0;
413
414 if ((*argc_nr + 1 >= argc) ||
415 !is_broadcast_ethaddr(parsed_cmd->ethaddr))
416 return 1;
417
418 if (eth_validate_ethaddr_str(argv[*argc_nr + 1])) {
419 printf("Invalid MAC address: %s\n", argv[*argc_nr + 1]);
420 return 0;
421 }
422
423 eth_parse_enetaddr(argv[*argc_nr + 1], parsed_cmd->ethaddr);
424
425 if (is_broadcast_ethaddr(parsed_cmd->ethaddr)) {
426 memset(parsed_cmd->ethaddr, 0xFF, sizeof(parsed_cmd->ethaddr));
427 return 0;
428 }
429
430 parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add_del_mac;
431
432 return 1;
433}
434
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300435/* Finds optional keywords and modifies *argc_va to skip them */
436static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd,
437 int *argc_val)
438{
439 int i;
440 int keyw_opt_matched;
441 int argc_val_max;
442 int const *cmd_keyw_p;
443 int const *cmd_keyw_opt_p;
444
445 /* remember the best match */
446 argc_val_max = *argc_val;
447
448 /*
449 * check if our command's optional keywords match the optional
450 * keywords of an available command
451 */
452 for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
453 keyw_opt_matched = 0;
454 cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched];
455 cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched];
456
457 /*
458 * increase the number of keywords that
459 * matched with a command
460 */
461 while (keyw_opt_matched + *argc_val <
462 parsed_cmd->cmd_keywords_nr &&
463 *cmd_keyw_opt_p != ethsw_id_key_end &&
464 *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) {
465 keyw_opt_matched++;
466 cmd_keyw_p++;
467 cmd_keyw_opt_p++;
468 }
469
470 /*
471 * if all our optional command's keywords perfectly match an
472 * optional pattern, then we can move to the next defined
473 * keywords in our command; remember the one that matched the
474 * greatest number of keywords
475 */
476 if (keyw_opt_matched + *argc_val <=
477 parsed_cmd->cmd_keywords_nr &&
478 *cmd_keyw_opt_p == ethsw_id_key_end &&
479 *argc_val + keyw_opt_matched > argc_val_max)
480 argc_val_max = *argc_val + keyw_opt_matched;
481 }
482
483 *argc_val = argc_val_max;
484}
485
486/*
487 * Finds the function to call based on keywords and
488 * modifies *argc_va to skip them
489 */
490static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd,
491 int *argc_val)
492{
493 int i;
494 int keyw_matched;
495 int *cmd_keyw_p;
496 int *cmd_keyw_def_p;
497
498 /*
499 * check if our command's keywords match the
500 * keywords of an available command
501 */
502 for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
503 keyw_matched = 0;
504 cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched];
505 cmd_keyw_def_p = &ethsw_cmd_def[i].cmd_keyword[keyw_matched];
506
507 /*
508 * increase the number of keywords that
509 * matched with a command
510 */
511 while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr &&
512 *cmd_keyw_def_p != ethsw_id_key_end &&
513 *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) {
514 keyw_matched++;
515 cmd_keyw_p++;
516 cmd_keyw_def_p++;
517 }
518
519 /*
520 * if all our command's keywords perfectly match an
521 * available command, then we get the function we need to call
522 * to configure the Ethernet Switch
523 */
524 if (keyw_matched && keyw_matched + *argc_val ==
525 parsed_cmd->cmd_keywords_nr &&
526 *cmd_keyw_def_p == ethsw_id_key_end) {
527 *argc_val += keyw_matched;
528 parsed_cmd->cmd_function =
529 ethsw_cmd_def[i].keyword_function;
530 return;
531 }
532 }
533}
534
535/* find all the keywords in the command */
536static int keywords_find(int argc, char * const argv[],
537 struct ethsw_command_def *parsed_cmd)
538{
539 int i;
540 int j;
541 int argc_val;
542 int rc = CMD_RET_SUCCESS;
543
544 for (i = 1; i < argc; i++) {
545 for (j = 0; j < ethsw_id_count; j++) {
546 if (keyword[j].match(j, argc, argv, &i, parsed_cmd))
547 break;
548 }
549 }
550
551 /* if there is no keyword match for a word, the command is invalid */
552 for (i = 1; i < argc; i++)
553 if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end)
554 rc = CMD_RET_USAGE;
555
556 parsed_cmd->cmd_keywords_nr = argc;
557 argc_val = 1;
558
559 /* get optional parameters first */
560 cmd_keywords_opt_check(parsed_cmd, &argc_val);
561
562 if (argc_val == parsed_cmd->cmd_keywords_nr)
563 return CMD_RET_USAGE;
564
565 /*
566 * check the keywords and if a match is found,
567 * get the function to call
568 */
569 cmd_keywords_check(parsed_cmd, &argc_val);
570
571 /* error if not all commands' parameters were matched */
572 if (argc_val == parsed_cmd->cmd_keywords_nr) {
573 if (!parsed_cmd->cmd_function) {
574 printf("Command not available for: %s\n", ethsw_name);
575 rc = CMD_RET_FAILURE;
576 }
577 } else {
578 rc = CMD_RET_USAGE;
579 }
580
581 return rc;
582}
583
584static void command_def_init(struct ethsw_command_def *parsed_cmd)
585{
586 int i;
587
588 for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++)
589 parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end;
590
591 parsed_cmd->port = ETHSW_CMD_PORT_ALL;
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300592 parsed_cmd->vid = ETHSW_CMD_VLAN_ALL;
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300593 parsed_cmd->cmd_function = NULL;
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300594
595 /* We initialize the MAC address with the Broadcast address */
596 memset(parsed_cmd->ethaddr, 0xff, sizeof(parsed_cmd->ethaddr));
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300597}
598
599/* function to interpret commands starting with "ethsw " */
600static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
601{
602 struct ethsw_command_def parsed_cmd;
603 int rc = CMD_RET_SUCCESS;
604
605 if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS)
606 return CMD_RET_USAGE;
607
608 command_def_init(&parsed_cmd);
609
610 rc = keywords_find(argc, argv, &parsed_cmd);
611
612 if (rc == CMD_RET_SUCCESS)
613 rc = parsed_cmd.cmd_function(&parsed_cmd);
614
615 return rc;
616}
617
618#define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \
619"- enable/disable a port; show shows a port's configuration"
620
621U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw,
622 "Ethernet l2 switch commands",
623 ETHSW_PORT_CONF_HELP"\n"
Codrin Ciubotariu86719f02015-07-24 16:55:29 +0300624 ETHSW_PORT_STATS_HELP"\n"
Codrin Ciubotariu68c929d2015-07-24 16:55:30 +0300625 ETHSW_LEARN_HELP"\n"
Codrin Ciubotariu22449852015-09-09 18:00:52 +0300626 ETHSW_FDB_HELP"\n"
Codrin Ciubotariu4ea54e32015-07-24 16:55:27 +0300627);