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 
16 static 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 
ethsw_port_stats_help_key_func(struct ethsw_command_def * parsed_cmd)21 static 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 
ethsw_learn_help_key_func(struct ethsw_command_def * parsed_cmd)32 static 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 
ethsw_fdb_help_key_func(struct ethsw_command_def * parsed_cmd)44 static 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 
ethsw_pvid_help_key_func(struct ethsw_command_def * parsed_cmd)55 static 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 
ethsw_vlan_help_key_func(struct ethsw_command_def * parsed_cmd)66 static 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 
ethsw_port_untag_help_key_func(struct ethsw_command_def * parsed_cmd)77 static 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 
ethsw_egr_tag_help_key_func(struct ethsw_command_def * parsed_cmd)89 static 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 
ethsw_vlan_learn_help_key_func(struct ethsw_command_def * parsed_cmd)100 static 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 
ethsw_ingr_fltr_help_key_func(struct ethsw_command_def * parsed_cmd)111 static 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 
ethsw_port_aggr_help_key_func(struct ethsw_command_def * parsed_cmd)122 static 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 
129 static 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 
583 struct 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 
609 static 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);
612 static 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);
615 static 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);
618 static 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);
621 static 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);
624 static 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  */
632 struct 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  */
ethsw_define_functions(const struct ethsw_command_func * cmd_func)722 int 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 */
keyword_match_gen(enum ethsw_keyword_id key_id,int argc,char * const argv[],int * argc_nr,struct ethsw_command_def * parsed_cmd)751 static 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 */
keyword_match_port(enum ethsw_keyword_id key_id,int argc,char * const argv[],int * argc_nr,struct ethsw_command_def * parsed_cmd)764 static 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 */
keyword_match_vlan(enum ethsw_keyword_id key_id,int argc,char * const argv[],int * argc_nr,struct ethsw_command_def * parsed_cmd)787 static 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 */
keyword_match_pvid(enum ethsw_keyword_id key_id,int argc,char * const argv[],int * argc_nr,struct ethsw_command_def * parsed_cmd)830 static 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 */
keyword_match_mac_addr(enum ethsw_keyword_id key_id,int argc,char * const argv[],int * argc_nr,struct ethsw_command_def * parsed_cmd)852 static 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 */
keyword_match_aggr(enum ethsw_keyword_id key_id,int argc,char * const argv[],int * argc_nr,struct ethsw_command_def * parsed_cmd)881 static 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 */
cmd_keywords_opt_check(const struct ethsw_command_def * parsed_cmd,int * argc_val)903 static 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  */
cmd_keywords_check(struct ethsw_command_def * parsed_cmd,int * argc_val)957 static 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 */
keywords_find(int argc,char * const argv[],struct ethsw_command_def * parsed_cmd)1003 static 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 
command_def_init(struct ethsw_command_def * parsed_cmd)1051 static 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 " */
do_ethsw(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])1068 static 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 
1090 U_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