xref: /openbmc/u-boot/cmd/ethsw.c (revision d521197d)
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 );
1028