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