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