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