drivers/net/vsc9953: Add VLAN commands for VSC9953
[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 <env_flags.h>
13 #include <ethsw.h>
14
15 static const char *ethsw_name;
16
17 #define ETHSW_PORT_STATS_HELP "ethsw [port <port_no>] statistics " \
18 "{ [help] | [clear] } - show an l2 switch port's statistics"
19
20 static 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
27 #define ETHSW_LEARN_HELP "ethsw [port <port_no>] learning " \
28 "{ [help] | show | auto | disable } " \
29 "- enable/disable/show learning configuration on a port"
30
31 static 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
38 #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
43 static 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
50 #define ETHSW_PVID_HELP "ethsw [port <port_no>] " \
51 "pvid { [help] | show | <pvid> } " \
52 "- set/show PVID (ingress and egress VLAN tagging) for a port"
53
54 static int ethsw_pvid_help_key_func(struct ethsw_command_def *parsed_cmd)
55 {
56         printf(ETHSW_PVID_HELP"\n");
57
58         return CMD_RET_SUCCESS;
59 }
60
61 #define ETHSW_VLAN_HELP "ethsw [port <port_no>] vlan " \
62 "{ [help] | show | add <vid> | del <vid> } " \
63 "- add a VLAN to a port (VLAN members)"
64
65 static int ethsw_vlan_help_key_func(struct ethsw_command_def *parsed_cmd)
66 {
67         printf(ETHSW_VLAN_HELP"\n");
68
69         return CMD_RET_SUCCESS;
70 }
71
72 #define ETHSW_PORT_UNTAG_HELP "ethsw [port <port_no>] untagged " \
73 "{ [help] | show | all | none | pvid } " \
74 " - set egress tagging mod for a port"
75
76 static int ethsw_port_untag_help_key_func(struct ethsw_command_def *parsed_cmd)
77 {
78         printf(ETHSW_PORT_UNTAG_HELP"\n");
79
80         return CMD_RET_SUCCESS;
81 }
82
83 #define ETHSW_EGR_VLAN_TAG_HELP "ethsw [port <port_no>] egress tag " \
84 "{ [help] | show | pvid | classified } " \
85 "- Configure VID source for egress tag. " \
86 "Tag's VID could be the frame's classified VID or the PVID of the port"
87
88 static int ethsw_egr_tag_help_key_func(struct ethsw_command_def *parsed_cmd)
89 {
90         printf(ETHSW_EGR_VLAN_TAG_HELP"\n");
91
92         return CMD_RET_SUCCESS;
93 }
94
95 static struct keywords_to_function {
96         enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS];
97         int cmd_func_offset;
98         int (*keyword_function)(struct ethsw_command_def *parsed_cmd);
99 } ethsw_cmd_def[] = {
100                 {
101                         .cmd_keyword = {
102                                         ethsw_id_enable,
103                                         ethsw_id_key_end,
104                         },
105                         .cmd_func_offset = offsetof(struct ethsw_command_func,
106                                                     port_enable),
107                         .keyword_function = NULL,
108                 }, {
109                         .cmd_keyword = {
110                                         ethsw_id_disable,
111                                         ethsw_id_key_end,
112                         },
113                         .cmd_func_offset = offsetof(struct ethsw_command_func,
114                                                     port_disable),
115                         .keyword_function = NULL,
116                 }, {
117                         .cmd_keyword = {
118                                         ethsw_id_show,
119                                         ethsw_id_key_end,
120                         },
121                         .cmd_func_offset = offsetof(struct ethsw_command_func,
122                                                     port_show),
123                         .keyword_function = NULL,
124                 }, {
125                         .cmd_keyword = {
126                                         ethsw_id_statistics,
127                                         ethsw_id_help,
128                                         ethsw_id_key_end,
129                         },
130                         .cmd_func_offset = -1,
131                         .keyword_function = &ethsw_port_stats_help_key_func,
132                 }, {
133                         .cmd_keyword = {
134                                         ethsw_id_statistics,
135                                         ethsw_id_key_end,
136                         },
137                         .cmd_func_offset = offsetof(struct ethsw_command_func,
138                                                     port_stats),
139                         .keyword_function = NULL,
140                 }, {
141                         .cmd_keyword = {
142                                         ethsw_id_statistics,
143                                         ethsw_id_clear,
144                                         ethsw_id_key_end,
145                         },
146                         .cmd_func_offset = offsetof(struct ethsw_command_func,
147                                                     port_stats_clear),
148                         .keyword_function = NULL,
149                 }, {
150                         .cmd_keyword = {
151                                         ethsw_id_learning,
152                                         ethsw_id_key_end,
153                         },
154                         .cmd_func_offset = -1,
155                         .keyword_function = &ethsw_learn_help_key_func,
156                 }, {
157                         .cmd_keyword = {
158                                         ethsw_id_learning,
159                                         ethsw_id_help,
160                                         ethsw_id_key_end,
161                         },
162                         .cmd_func_offset = -1,
163                         .keyword_function = &ethsw_learn_help_key_func,
164                 }, {
165                         .cmd_keyword = {
166                                         ethsw_id_learning,
167                                         ethsw_id_show,
168                                         ethsw_id_key_end,
169                         },
170                         .cmd_func_offset = offsetof(struct ethsw_command_func,
171                                                     port_learn_show),
172                         .keyword_function = NULL,
173                 }, {
174                         .cmd_keyword = {
175                                         ethsw_id_learning,
176                                         ethsw_id_auto,
177                                         ethsw_id_key_end,
178                         },
179                         .cmd_func_offset = offsetof(struct ethsw_command_func,
180                                                     port_learn),
181                         .keyword_function = NULL,
182                 }, {
183                         .cmd_keyword = {
184                                         ethsw_id_learning,
185                                         ethsw_id_disable,
186                                         ethsw_id_key_end,
187                         },
188                         .cmd_func_offset = offsetof(struct ethsw_command_func,
189                                                     port_learn),
190                         .keyword_function = NULL,
191                 }, {
192                         .cmd_keyword = {
193                                         ethsw_id_fdb,
194                                         ethsw_id_key_end,
195                         },
196                         .cmd_func_offset = -1,
197                         .keyword_function = &ethsw_fdb_help_key_func,
198                 }, {
199                         .cmd_keyword = {
200                                         ethsw_id_fdb,
201                                         ethsw_id_help,
202                                         ethsw_id_key_end,
203                         },
204                         .cmd_func_offset = -1,
205                         .keyword_function = &ethsw_fdb_help_key_func,
206                 }, {
207                         .cmd_keyword = {
208                                         ethsw_id_fdb,
209                                         ethsw_id_show,
210                                         ethsw_id_key_end,
211                         },
212                         .cmd_func_offset = offsetof(struct ethsw_command_func,
213                                                     fdb_show),
214                         .keyword_function = NULL,
215                 }, {
216                         .cmd_keyword = {
217                                         ethsw_id_fdb,
218                                         ethsw_id_flush,
219                                         ethsw_id_key_end,
220                         },
221                         .cmd_func_offset = offsetof(struct ethsw_command_func,
222                                                     fdb_flush),
223                         .keyword_function = NULL,
224                 }, {
225                         .cmd_keyword = {
226                                         ethsw_id_fdb,
227                                         ethsw_id_add,
228                                         ethsw_id_add_del_mac,
229                                         ethsw_id_key_end,
230                         },
231                         .cmd_func_offset = offsetof(struct ethsw_command_func,
232                                                     fdb_entry_add),
233                         .keyword_function = NULL,
234                 }, {
235                         .cmd_keyword = {
236                                         ethsw_id_fdb,
237                                         ethsw_id_del,
238                                         ethsw_id_add_del_mac,
239                                         ethsw_id_key_end,
240                         },
241                         .cmd_func_offset = offsetof(struct ethsw_command_func,
242                                                     fdb_entry_del),
243                         .keyword_function = NULL,
244                 }, {
245                         .cmd_keyword = {
246                                         ethsw_id_pvid,
247                                         ethsw_id_key_end,
248                         },
249                         .cmd_func_offset = -1,
250                         .keyword_function = &ethsw_pvid_help_key_func,
251                 }, {
252                         .cmd_keyword = {
253                                         ethsw_id_pvid,
254                                         ethsw_id_help,
255                                         ethsw_id_key_end,
256                         },
257                         .cmd_func_offset = -1,
258                         .keyword_function = &ethsw_pvid_help_key_func,
259                 }, {
260                         .cmd_keyword = {
261                                         ethsw_id_pvid,
262                                         ethsw_id_show,
263                                         ethsw_id_key_end,
264                         },
265                         .cmd_func_offset = offsetof(struct ethsw_command_func,
266                                                     pvid_show),
267                         .keyword_function = NULL,
268                 }, {
269                         .cmd_keyword = {
270                                         ethsw_id_pvid,
271                                         ethsw_id_pvid_no,
272                                         ethsw_id_key_end,
273                         },
274                         .cmd_func_offset = offsetof(struct ethsw_command_func,
275                                                     pvid_set),
276                         .keyword_function = NULL,
277                 }, {
278                         .cmd_keyword = {
279                                         ethsw_id_vlan,
280                                         ethsw_id_key_end,
281                         },
282                         .cmd_func_offset = -1,
283                         .keyword_function = &ethsw_vlan_help_key_func,
284                 }, {
285                         .cmd_keyword = {
286                                         ethsw_id_vlan,
287                                         ethsw_id_help,
288                                         ethsw_id_key_end,
289                         },
290                         .cmd_func_offset = -1,
291                         .keyword_function = &ethsw_vlan_help_key_func,
292                 }, {
293                         .cmd_keyword = {
294                                         ethsw_id_vlan,
295                                         ethsw_id_show,
296                                         ethsw_id_key_end,
297                         },
298                         .cmd_func_offset = offsetof(struct ethsw_command_func,
299                                                     vlan_show),
300                         .keyword_function = NULL,
301                 }, {
302                         .cmd_keyword = {
303                                         ethsw_id_vlan,
304                                         ethsw_id_add,
305                                         ethsw_id_add_del_no,
306                                         ethsw_id_key_end,
307                         },
308                         .cmd_func_offset = offsetof(struct ethsw_command_func,
309                                                     vlan_set),
310                         .keyword_function = NULL,
311                 }, {
312                         .cmd_keyword = {
313                                         ethsw_id_vlan,
314                                         ethsw_id_del,
315                                         ethsw_id_add_del_no,
316                                         ethsw_id_key_end,
317                         },
318                         .cmd_func_offset = offsetof(struct ethsw_command_func,
319                                                     vlan_set),
320                         .keyword_function = NULL,
321                 }, {
322                         .cmd_keyword = {
323                                         ethsw_id_untagged,
324                                         ethsw_id_key_end,
325                         },
326                         .cmd_func_offset = -1,
327                         .keyword_function = &ethsw_port_untag_help_key_func,
328                 }, {
329                         .cmd_keyword = {
330                                         ethsw_id_untagged,
331                                         ethsw_id_help,
332                                         ethsw_id_key_end,
333                         },
334                         .cmd_func_offset = -1,
335                         .keyword_function = &ethsw_port_untag_help_key_func,
336                 }, {
337                         .cmd_keyword = {
338                                         ethsw_id_untagged,
339                                         ethsw_id_show,
340                                         ethsw_id_key_end,
341                         },
342                         .cmd_func_offset = offsetof(struct ethsw_command_func,
343                                                     port_untag_show),
344                         .keyword_function = NULL,
345                 }, {
346                         .cmd_keyword = {
347                                         ethsw_id_untagged,
348                                         ethsw_id_all,
349                                         ethsw_id_key_end,
350                         },
351                         .cmd_func_offset = offsetof(struct ethsw_command_func,
352                                                     port_untag_set),
353                         .keyword_function = NULL,
354                 }, {
355                         .cmd_keyword = {
356                                         ethsw_id_untagged,
357                                         ethsw_id_none,
358                                         ethsw_id_key_end,
359                         },
360                         .cmd_func_offset = offsetof(struct ethsw_command_func,
361                                                     port_untag_set),
362                         .keyword_function = NULL,
363                 }, {
364                         .cmd_keyword = {
365                                         ethsw_id_untagged,
366                                         ethsw_id_pvid,
367                                         ethsw_id_key_end,
368                         },
369                         .cmd_func_offset = offsetof(struct ethsw_command_func,
370                                                     port_untag_set),
371                         .keyword_function = NULL,
372                 }, {
373                         .cmd_keyword = {
374                                         ethsw_id_egress,
375                                         ethsw_id_tag,
376                                         ethsw_id_key_end,
377                         },
378                         .cmd_func_offset = -1,
379                         .keyword_function = &ethsw_egr_tag_help_key_func,
380                 }, {
381                         .cmd_keyword = {
382                                         ethsw_id_egress,
383                                         ethsw_id_tag,
384                                         ethsw_id_help,
385                                         ethsw_id_key_end,
386                         },
387                         .cmd_func_offset = -1,
388                         .keyword_function = &ethsw_egr_tag_help_key_func,
389                 }, {
390                         .cmd_keyword = {
391                                         ethsw_id_egress,
392                                         ethsw_id_tag,
393                                         ethsw_id_show,
394                                         ethsw_id_key_end,
395                         },
396                         .cmd_func_offset = offsetof(struct ethsw_command_func,
397                                                     port_egr_vlan_show),
398                         .keyword_function = NULL,
399                 }, {
400                         .cmd_keyword = {
401                                         ethsw_id_egress,
402                                         ethsw_id_tag,
403                                         ethsw_id_pvid,
404                                         ethsw_id_key_end,
405                         },
406                         .cmd_func_offset = offsetof(struct ethsw_command_func,
407                                                     port_egr_vlan_set),
408                         .keyword_function = NULL,
409                 }, {
410                         .cmd_keyword = {
411                                         ethsw_id_egress,
412                                         ethsw_id_tag,
413                                         ethsw_id_classified,
414                                         ethsw_id_key_end,
415                         },
416                         .cmd_func_offset = offsetof(struct ethsw_command_func,
417                                                     port_egr_vlan_set),
418                         .keyword_function = NULL,
419                 },
420 };
421
422 struct keywords_optional {
423         int cmd_keyword[ETHSW_MAX_CMD_PARAMS];
424 } cmd_opt_def[] = {
425                 {
426                                 .cmd_keyword = {
427                                                 ethsw_id_port,
428                                                 ethsw_id_port_no,
429                                                 ethsw_id_key_end,
430                                 },
431                 }, {
432                                 .cmd_keyword = {
433                                                 ethsw_id_vlan,
434                                                 ethsw_id_vlan_no,
435                                                 ethsw_id_key_end,
436                                 },
437                 }, {
438                                 .cmd_keyword = {
439                                                 ethsw_id_port,
440                                                 ethsw_id_port_no,
441                                                 ethsw_id_vlan,
442                                                 ethsw_id_vlan_no,
443                                                 ethsw_id_key_end,
444                                 },
445                 },
446 };
447
448 static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char
449                              *const argv[], int *argc_nr,
450                              struct ethsw_command_def *parsed_cmd);
451 static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
452                               char *const argv[], int *argc_nr,
453                               struct ethsw_command_def *parsed_cmd);
454 static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
455                               char *const argv[], int *argc_nr,
456                               struct ethsw_command_def *parsed_cmd);
457 static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc,
458                               char *const argv[], int *argc_nr,
459                               struct ethsw_command_def *parsed_cmd);
460 static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc,
461                                   char *const argv[], int *argc_nr,
462                                   struct ethsw_command_def *parsed_cmd);
463
464 /*
465  * Define properties for each keyword;
466  * keep the order synced with enum ethsw_keyword_id
467  */
468 struct keyword_def {
469         const char *keyword_name;
470         int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[],
471                      int *argc_nr, struct ethsw_command_def *parsed_cmd);
472 } keyword[] = {
473                 {
474                                 .keyword_name = "help",
475                                 .match = &keyword_match_gen,
476                 }, {
477                                 .keyword_name = "show",
478                                 .match = &keyword_match_gen,
479                 }, {
480                                 .keyword_name = "port",
481                                 .match = &keyword_match_port
482                 },  {
483                                 .keyword_name = "enable",
484                                 .match = &keyword_match_gen,
485                 }, {
486                                 .keyword_name = "disable",
487                                 .match = &keyword_match_gen,
488                 }, {
489                                 .keyword_name = "statistics",
490                                 .match = &keyword_match_gen,
491                 }, {
492                                 .keyword_name = "clear",
493                                 .match = &keyword_match_gen,
494                 }, {
495                                 .keyword_name = "learning",
496                                 .match = &keyword_match_gen,
497                 }, {
498                                 .keyword_name = "auto",
499                                 .match = &keyword_match_gen,
500                 }, {
501                                 .keyword_name = "vlan",
502                                 .match = &keyword_match_vlan,
503                 }, {
504                                 .keyword_name = "fdb",
505                                 .match = &keyword_match_gen,
506                 }, {
507                                 .keyword_name = "add",
508                                 .match = &keyword_match_mac_addr,
509                 }, {
510                                 .keyword_name = "del",
511                                 .match = &keyword_match_mac_addr,
512                 }, {
513                                 .keyword_name = "flush",
514                                 .match = &keyword_match_gen,
515                 }, {
516                                 .keyword_name = "pvid",
517                                 .match = &keyword_match_pvid,
518                 }, {
519                                 .keyword_name = "untagged",
520                                 .match = &keyword_match_gen,
521                 }, {
522                                 .keyword_name = "all",
523                                 .match = &keyword_match_gen,
524                 }, {
525                                 .keyword_name = "none",
526                                 .match = &keyword_match_gen,
527                 }, {
528                                 .keyword_name = "egress",
529                                 .match = &keyword_match_gen,
530                 }, {
531                                 .keyword_name = "tag",
532                                 .match = &keyword_match_gen,
533                 }, {
534                                 .keyword_name = "classified",
535                                 .match = &keyword_match_gen,
536                 },
537 };
538
539 /*
540  * Function used by an Ethernet Switch driver to set the functions
541  * that must be called by the parser when an ethsw command is given
542  */
543 int ethsw_define_functions(const struct ethsw_command_func *cmd_func)
544 {
545         int i;
546         void **aux_p;
547         int (*cmd_func_aux)(struct ethsw_command_def *);
548
549         if (!cmd_func->ethsw_name)
550                 return -EINVAL;
551
552         ethsw_name = cmd_func->ethsw_name;
553
554         for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
555                 /*
556                  * get the pointer to the function send by the Ethernet Switch
557                  * driver that corresponds to the proper ethsw command
558                  */
559                 if (ethsw_cmd_def[i].keyword_function)
560                         continue;
561
562                 aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset;
563
564                 cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p;
565                 ethsw_cmd_def[i].keyword_function = cmd_func_aux;
566         }
567
568         return 0;
569 }
570
571 /* Generic function used to match a keyword only by a string */
572 static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc,
573                              char *const argv[], int *argc_nr,
574                              struct ethsw_command_def *parsed_cmd)
575 {
576         if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) {
577                 parsed_cmd->cmd_to_keywords[*argc_nr] = key_id;
578
579                 return 1;
580         }
581         return 0;
582 }
583
584 /* Function used to match the command's port */
585 static int keyword_match_port(enum ethsw_keyword_id key_id, int argc,
586                               char *const argv[], int *argc_nr,
587                               struct ethsw_command_def *parsed_cmd)
588 {
589         unsigned long val;
590
591         if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
592                 return 0;
593
594         if (*argc_nr + 1 >= argc)
595                 return 0;
596
597         if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
598                 parsed_cmd->port = val;
599                 (*argc_nr)++;
600                 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no;
601                 return 1;
602         }
603
604         return 0;
605 }
606
607 /* Function used to match the command's vlan */
608 static int keyword_match_vlan(enum ethsw_keyword_id key_id, int argc,
609                               char *const argv[], int *argc_nr,
610                               struct ethsw_command_def *parsed_cmd)
611 {
612         unsigned long val;
613         int aux;
614
615         if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
616                 return 0;
617
618         if (*argc_nr + 1 >= argc)
619                 return 0;
620
621         if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
622                 parsed_cmd->vid = val;
623                 (*argc_nr)++;
624                 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_vlan_no;
625                 return 1;
626         }
627
628         aux = *argc_nr + 1;
629
630         if (keyword_match_gen(ethsw_id_add, argc, argv, &aux, parsed_cmd))
631                 parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add;
632         else if (keyword_match_gen(ethsw_id_del, argc, argv, &aux, parsed_cmd))
633                 parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_del;
634         else
635                 return 0;
636
637         if (*argc_nr + 2 >= argc)
638                 return 0;
639
640         if (strict_strtoul(argv[*argc_nr + 2], 10, &val) != -EINVAL) {
641                 parsed_cmd->vid = val;
642                 (*argc_nr) += 2;
643                 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_add_del_no;
644                 return 1;
645         }
646
647         return 0;
648 }
649
650 /* Function used to match the command's pvid */
651 static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc,
652                               char *const argv[], int *argc_nr,
653                               struct ethsw_command_def *parsed_cmd)
654 {
655         unsigned long val;
656
657         if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
658                 return 0;
659
660         if (*argc_nr + 1 >= argc)
661                 return 1;
662
663         if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) {
664                 parsed_cmd->vid = val;
665                 (*argc_nr)++;
666                 parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_pvid_no;
667         }
668
669         return 1;
670 }
671
672 /* Function used to match the command's MAC address */
673 static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc,
674                                      char *const argv[], int *argc_nr,
675                                      struct ethsw_command_def *parsed_cmd)
676 {
677         if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd))
678                 return 0;
679
680         if ((*argc_nr + 1 >= argc) ||
681             !is_broadcast_ethaddr(parsed_cmd->ethaddr))
682                 return 1;
683
684         if (eth_validate_ethaddr_str(argv[*argc_nr + 1])) {
685                 printf("Invalid MAC address: %s\n", argv[*argc_nr + 1]);
686                 return 0;
687         }
688
689         eth_parse_enetaddr(argv[*argc_nr + 1], parsed_cmd->ethaddr);
690
691         if (is_broadcast_ethaddr(parsed_cmd->ethaddr)) {
692                 memset(parsed_cmd->ethaddr, 0xFF, sizeof(parsed_cmd->ethaddr));
693                 return 0;
694         }
695
696         parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add_del_mac;
697
698         return 1;
699 }
700
701 /* Finds optional keywords and modifies *argc_va to skip them */
702 static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd,
703                                    int *argc_val)
704 {
705         int i;
706         int keyw_opt_matched;
707         int argc_val_max;
708         int const *cmd_keyw_p;
709         int const *cmd_keyw_opt_p;
710
711         /* remember the best match */
712         argc_val_max = *argc_val;
713
714         /*
715          * check if our command's optional keywords match the optional
716          * keywords of an available command
717          */
718         for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
719                 keyw_opt_matched = 0;
720                 cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched];
721                 cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched];
722
723                 /*
724                  * increase the number of keywords that
725                  * matched with a command
726                  */
727                 while (keyw_opt_matched + *argc_val <
728                        parsed_cmd->cmd_keywords_nr &&
729                        *cmd_keyw_opt_p != ethsw_id_key_end &&
730                        *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) {
731                         keyw_opt_matched++;
732                         cmd_keyw_p++;
733                         cmd_keyw_opt_p++;
734                 }
735
736                 /*
737                  * if all our optional command's keywords perfectly match an
738                  * optional pattern, then we can move to the next defined
739                  * keywords in our command; remember the one that matched the
740                  * greatest number of keywords
741                  */
742                 if (keyw_opt_matched + *argc_val <=
743                     parsed_cmd->cmd_keywords_nr &&
744                     *cmd_keyw_opt_p == ethsw_id_key_end &&
745                     *argc_val + keyw_opt_matched > argc_val_max)
746                         argc_val_max = *argc_val + keyw_opt_matched;
747         }
748
749         *argc_val = argc_val_max;
750 }
751
752 /*
753  * Finds the function to call based on keywords and
754  * modifies *argc_va to skip them
755  */
756 static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd,
757                                int *argc_val)
758 {
759         int i;
760         int keyw_matched;
761         int *cmd_keyw_p;
762         int *cmd_keyw_def_p;
763
764         /*
765          * check if our command's keywords match the
766          * keywords of an available command
767          */
768         for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) {
769                 keyw_matched = 0;
770                 cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched];
771                 cmd_keyw_def_p = &ethsw_cmd_def[i].cmd_keyword[keyw_matched];
772
773                 /*
774                  * increase the number of keywords that
775                  * matched with a command
776                  */
777                 while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr &&
778                        *cmd_keyw_def_p != ethsw_id_key_end &&
779                        *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) {
780                         keyw_matched++;
781                         cmd_keyw_p++;
782                         cmd_keyw_def_p++;
783                 }
784
785                 /*
786                  * if all our command's keywords perfectly match an
787                  * available command, then we get the function we need to call
788                  * to configure the Ethernet Switch
789                  */
790                 if (keyw_matched && keyw_matched + *argc_val ==
791                     parsed_cmd->cmd_keywords_nr &&
792                     *cmd_keyw_def_p == ethsw_id_key_end) {
793                         *argc_val += keyw_matched;
794                         parsed_cmd->cmd_function =
795                                         ethsw_cmd_def[i].keyword_function;
796                         return;
797                 }
798         }
799 }
800
801 /* find all the keywords in the command */
802 static int keywords_find(int argc, char * const argv[],
803                          struct ethsw_command_def *parsed_cmd)
804 {
805         int i;
806         int j;
807         int argc_val;
808         int rc = CMD_RET_SUCCESS;
809
810         for (i = 1; i < argc; i++) {
811                 for (j = 0; j < ethsw_id_count; j++) {
812                         if (keyword[j].match(j, argc, argv, &i, parsed_cmd))
813                                 break;
814                 }
815         }
816
817         /* if there is no keyword match for a word, the command is invalid */
818         for (i = 1; i < argc; i++)
819                 if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end)
820                         rc = CMD_RET_USAGE;
821
822         parsed_cmd->cmd_keywords_nr = argc;
823         argc_val = 1;
824
825         /* get optional parameters first */
826         cmd_keywords_opt_check(parsed_cmd, &argc_val);
827
828         if (argc_val == parsed_cmd->cmd_keywords_nr)
829                 return CMD_RET_USAGE;
830
831         /*
832          * check the keywords and if a match is found,
833          * get the function to call
834          */
835         cmd_keywords_check(parsed_cmd, &argc_val);
836
837         /* error if not all commands' parameters were matched */
838         if (argc_val == parsed_cmd->cmd_keywords_nr) {
839                 if (!parsed_cmd->cmd_function) {
840                         printf("Command not available for: %s\n", ethsw_name);
841                         rc = CMD_RET_FAILURE;
842                 }
843         } else {
844                 rc = CMD_RET_USAGE;
845         }
846
847         return rc;
848 }
849
850 static void command_def_init(struct ethsw_command_def *parsed_cmd)
851 {
852         int i;
853
854         for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++)
855                 parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end;
856
857         parsed_cmd->port = ETHSW_CMD_PORT_ALL;
858         parsed_cmd->vid = ETHSW_CMD_VLAN_ALL;
859         parsed_cmd->cmd_function = NULL;
860
861         /* We initialize the MAC address with the Broadcast address */
862         memset(parsed_cmd->ethaddr, 0xff, sizeof(parsed_cmd->ethaddr));
863 }
864
865 /* function to interpret commands starting with "ethsw " */
866 static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
867 {
868         struct ethsw_command_def parsed_cmd;
869         int rc = CMD_RET_SUCCESS;
870
871         if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS)
872                 return CMD_RET_USAGE;
873
874         command_def_init(&parsed_cmd);
875
876         rc = keywords_find(argc, argv, &parsed_cmd);
877
878         if (rc == CMD_RET_SUCCESS)
879                 rc = parsed_cmd.cmd_function(&parsed_cmd);
880
881         return rc;
882 }
883
884 #define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \
885 "- enable/disable a port; show shows a port's configuration"
886
887 U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw,
888            "Ethernet l2 switch commands",
889            ETHSW_PORT_CONF_HELP"\n"
890            ETHSW_PORT_STATS_HELP"\n"
891            ETHSW_LEARN_HELP"\n"
892            ETHSW_FDB_HELP"\n"
893            ETHSW_PVID_HELP"\n"
894            ETHSW_VLAN_HELP"\n"
895            ETHSW_PORT_UNTAG_HELP"\n"
896            ETHSW_EGR_VLAN_TAG_HELP"\n"
897 );