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