]>
Commit | Line | Data |
---|---|---|
4ea54e3f CC |
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 <ethsw.h> | |
13 | ||
14 | static const char *ethsw_name; | |
15 | ||
86719f0c CC |
16 | #define ETHSW_PORT_STATS_HELP "ethsw [port <port_no>] statistics " \ |
17 | "{ [help] | [clear] } - show an l2 switch port's statistics" | |
18 | ||
19 | static int ethsw_port_stats_help_key_func(struct ethsw_command_def *parsed_cmd) | |
20 | { | |
21 | printf(ETHSW_PORT_STATS_HELP"\n"); | |
22 | ||
23 | return CMD_RET_SUCCESS; | |
24 | } | |
25 | ||
4ea54e3f CC |
26 | static struct keywords_to_function { |
27 | enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS]; | |
28 | int cmd_func_offset; | |
29 | int (*keyword_function)(struct ethsw_command_def *parsed_cmd); | |
30 | } ethsw_cmd_def[] = { | |
31 | { | |
32 | .cmd_keyword = { | |
33 | ethsw_id_enable, | |
34 | ethsw_id_key_end, | |
35 | }, | |
36 | .cmd_func_offset = offsetof(struct ethsw_command_func, | |
37 | port_enable), | |
38 | .keyword_function = NULL, | |
39 | }, { | |
40 | .cmd_keyword = { | |
41 | ethsw_id_disable, | |
42 | ethsw_id_key_end, | |
43 | }, | |
44 | .cmd_func_offset = offsetof(struct ethsw_command_func, | |
45 | port_disable), | |
46 | .keyword_function = NULL, | |
47 | }, { | |
48 | .cmd_keyword = { | |
49 | ethsw_id_show, | |
50 | ethsw_id_key_end, | |
51 | }, | |
52 | .cmd_func_offset = offsetof(struct ethsw_command_func, | |
53 | port_show), | |
54 | .keyword_function = NULL, | |
86719f0c CC |
55 | }, { |
56 | .cmd_keyword = { | |
57 | ethsw_id_statistics, | |
58 | ethsw_id_help, | |
59 | ethsw_id_key_end, | |
60 | }, | |
61 | .cmd_func_offset = -1, | |
62 | .keyword_function = ðsw_port_stats_help_key_func, | |
63 | }, { | |
64 | .cmd_keyword = { | |
65 | ethsw_id_statistics, | |
66 | ethsw_id_key_end, | |
67 | }, | |
68 | .cmd_func_offset = offsetof(struct ethsw_command_func, | |
69 | port_stats), | |
70 | .keyword_function = NULL, | |
71 | }, { | |
72 | .cmd_keyword = { | |
73 | ethsw_id_statistics, | |
74 | ethsw_id_clear, | |
75 | ethsw_id_key_end, | |
76 | }, | |
77 | .cmd_func_offset = offsetof(struct ethsw_command_func, | |
78 | port_stats_clear), | |
79 | .keyword_function = NULL, | |
4ea54e3f CC |
80 | }, |
81 | }; | |
82 | ||
83 | struct keywords_optional { | |
84 | int cmd_keyword[ETHSW_MAX_CMD_PARAMS]; | |
85 | } cmd_opt_def[] = { | |
86 | { | |
87 | .cmd_keyword = { | |
88 | ethsw_id_port, | |
89 | ethsw_id_port_no, | |
90 | ethsw_id_key_end, | |
91 | }, | |
92 | }, | |
93 | }; | |
94 | ||
95 | static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char | |
96 | *const argv[], int *argc_nr, | |
97 | struct ethsw_command_def *parsed_cmd); | |
98 | static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, | |
99 | char *const argv[], int *argc_nr, | |
100 | struct ethsw_command_def *parsed_cmd); | |
101 | ||
102 | /* | |
103 | * Define properties for each keyword; | |
104 | * keep the order synced with enum ethsw_keyword_id | |
105 | */ | |
106 | struct keyword_def { | |
107 | const char *keyword_name; | |
108 | int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[], | |
109 | int *argc_nr, struct ethsw_command_def *parsed_cmd); | |
110 | } keyword[] = { | |
111 | { | |
112 | .keyword_name = "help", | |
113 | .match = &keyword_match_gen, | |
114 | }, { | |
115 | .keyword_name = "show", | |
116 | .match = &keyword_match_gen, | |
117 | }, { | |
118 | .keyword_name = "port", | |
119 | .match = &keyword_match_port | |
120 | }, { | |
121 | .keyword_name = "enable", | |
122 | .match = &keyword_match_gen, | |
123 | }, { | |
124 | .keyword_name = "disable", | |
125 | .match = &keyword_match_gen, | |
86719f0c CC |
126 | }, { |
127 | .keyword_name = "statistics", | |
128 | .match = &keyword_match_gen, | |
129 | }, { | |
130 | .keyword_name = "clear", | |
131 | .match = &keyword_match_gen, | |
4ea54e3f CC |
132 | }, |
133 | }; | |
134 | ||
135 | /* | |
136 | * Function used by an Ethernet Switch driver to set the functions | |
137 | * that must be called by the parser when an ethsw command is given | |
138 | */ | |
139 | int ethsw_define_functions(const struct ethsw_command_func *cmd_func) | |
140 | { | |
141 | int i; | |
142 | void **aux_p; | |
143 | int (*cmd_func_aux)(struct ethsw_command_def *); | |
144 | ||
145 | if (!cmd_func->ethsw_name) | |
146 | return -EINVAL; | |
147 | ||
148 | ethsw_name = cmd_func->ethsw_name; | |
149 | ||
150 | for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { | |
151 | /* | |
152 | * get the pointer to the function send by the Ethernet Switch | |
153 | * driver that corresponds to the proper ethsw command | |
154 | */ | |
155 | if (ethsw_cmd_def[i].keyword_function) | |
156 | continue; | |
157 | ||
158 | aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset; | |
159 | ||
160 | cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p; | |
161 | ethsw_cmd_def[i].keyword_function = cmd_func_aux; | |
162 | } | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
167 | /* Generic function used to match a keyword only by a string */ | |
168 | static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, | |
169 | char *const argv[], int *argc_nr, | |
170 | struct ethsw_command_def *parsed_cmd) | |
171 | { | |
172 | if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) { | |
173 | parsed_cmd->cmd_to_keywords[*argc_nr] = key_id; | |
174 | ||
175 | return 1; | |
176 | } | |
177 | return 0; | |
178 | } | |
179 | ||
180 | /* Function used to match the command's port */ | |
181 | static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, | |
182 | char *const argv[], int *argc_nr, | |
183 | struct ethsw_command_def *parsed_cmd) | |
184 | { | |
185 | unsigned long val; | |
186 | ||
187 | if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) | |
188 | return 0; | |
189 | ||
190 | if (*argc_nr + 1 >= argc) | |
191 | return 0; | |
192 | ||
193 | if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { | |
194 | parsed_cmd->port = val; | |
195 | (*argc_nr)++; | |
196 | parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no; | |
197 | return 1; | |
198 | } | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | /* Finds optional keywords and modifies *argc_va to skip them */ | |
204 | static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd, | |
205 | int *argc_val) | |
206 | { | |
207 | int i; | |
208 | int keyw_opt_matched; | |
209 | int argc_val_max; | |
210 | int const *cmd_keyw_p; | |
211 | int const *cmd_keyw_opt_p; | |
212 | ||
213 | /* remember the best match */ | |
214 | argc_val_max = *argc_val; | |
215 | ||
216 | /* | |
217 | * check if our command's optional keywords match the optional | |
218 | * keywords of an available command | |
219 | */ | |
220 | for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { | |
221 | keyw_opt_matched = 0; | |
222 | cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched]; | |
223 | cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched]; | |
224 | ||
225 | /* | |
226 | * increase the number of keywords that | |
227 | * matched with a command | |
228 | */ | |
229 | while (keyw_opt_matched + *argc_val < | |
230 | parsed_cmd->cmd_keywords_nr && | |
231 | *cmd_keyw_opt_p != ethsw_id_key_end && | |
232 | *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) { | |
233 | keyw_opt_matched++; | |
234 | cmd_keyw_p++; | |
235 | cmd_keyw_opt_p++; | |
236 | } | |
237 | ||
238 | /* | |
239 | * if all our optional command's keywords perfectly match an | |
240 | * optional pattern, then we can move to the next defined | |
241 | * keywords in our command; remember the one that matched the | |
242 | * greatest number of keywords | |
243 | */ | |
244 | if (keyw_opt_matched + *argc_val <= | |
245 | parsed_cmd->cmd_keywords_nr && | |
246 | *cmd_keyw_opt_p == ethsw_id_key_end && | |
247 | *argc_val + keyw_opt_matched > argc_val_max) | |
248 | argc_val_max = *argc_val + keyw_opt_matched; | |
249 | } | |
250 | ||
251 | *argc_val = argc_val_max; | |
252 | } | |
253 | ||
254 | /* | |
255 | * Finds the function to call based on keywords and | |
256 | * modifies *argc_va to skip them | |
257 | */ | |
258 | static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd, | |
259 | int *argc_val) | |
260 | { | |
261 | int i; | |
262 | int keyw_matched; | |
263 | int *cmd_keyw_p; | |
264 | int *cmd_keyw_def_p; | |
265 | ||
266 | /* | |
267 | * check if our command's keywords match the | |
268 | * keywords of an available command | |
269 | */ | |
270 | for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { | |
271 | keyw_matched = 0; | |
272 | cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched]; | |
273 | cmd_keyw_def_p = ðsw_cmd_def[i].cmd_keyword[keyw_matched]; | |
274 | ||
275 | /* | |
276 | * increase the number of keywords that | |
277 | * matched with a command | |
278 | */ | |
279 | while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr && | |
280 | *cmd_keyw_def_p != ethsw_id_key_end && | |
281 | *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) { | |
282 | keyw_matched++; | |
283 | cmd_keyw_p++; | |
284 | cmd_keyw_def_p++; | |
285 | } | |
286 | ||
287 | /* | |
288 | * if all our command's keywords perfectly match an | |
289 | * available command, then we get the function we need to call | |
290 | * to configure the Ethernet Switch | |
291 | */ | |
292 | if (keyw_matched && keyw_matched + *argc_val == | |
293 | parsed_cmd->cmd_keywords_nr && | |
294 | *cmd_keyw_def_p == ethsw_id_key_end) { | |
295 | *argc_val += keyw_matched; | |
296 | parsed_cmd->cmd_function = | |
297 | ethsw_cmd_def[i].keyword_function; | |
298 | return; | |
299 | } | |
300 | } | |
301 | } | |
302 | ||
303 | /* find all the keywords in the command */ | |
304 | static int keywords_find(int argc, char * const argv[], | |
305 | struct ethsw_command_def *parsed_cmd) | |
306 | { | |
307 | int i; | |
308 | int j; | |
309 | int argc_val; | |
310 | int rc = CMD_RET_SUCCESS; | |
311 | ||
312 | for (i = 1; i < argc; i++) { | |
313 | for (j = 0; j < ethsw_id_count; j++) { | |
314 | if (keyword[j].match(j, argc, argv, &i, parsed_cmd)) | |
315 | break; | |
316 | } | |
317 | } | |
318 | ||
319 | /* if there is no keyword match for a word, the command is invalid */ | |
320 | for (i = 1; i < argc; i++) | |
321 | if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end) | |
322 | rc = CMD_RET_USAGE; | |
323 | ||
324 | parsed_cmd->cmd_keywords_nr = argc; | |
325 | argc_val = 1; | |
326 | ||
327 | /* get optional parameters first */ | |
328 | cmd_keywords_opt_check(parsed_cmd, &argc_val); | |
329 | ||
330 | if (argc_val == parsed_cmd->cmd_keywords_nr) | |
331 | return CMD_RET_USAGE; | |
332 | ||
333 | /* | |
334 | * check the keywords and if a match is found, | |
335 | * get the function to call | |
336 | */ | |
337 | cmd_keywords_check(parsed_cmd, &argc_val); | |
338 | ||
339 | /* error if not all commands' parameters were matched */ | |
340 | if (argc_val == parsed_cmd->cmd_keywords_nr) { | |
341 | if (!parsed_cmd->cmd_function) { | |
342 | printf("Command not available for: %s\n", ethsw_name); | |
343 | rc = CMD_RET_FAILURE; | |
344 | } | |
345 | } else { | |
346 | rc = CMD_RET_USAGE; | |
347 | } | |
348 | ||
349 | return rc; | |
350 | } | |
351 | ||
352 | static void command_def_init(struct ethsw_command_def *parsed_cmd) | |
353 | { | |
354 | int i; | |
355 | ||
356 | for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++) | |
357 | parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end; | |
358 | ||
359 | parsed_cmd->port = ETHSW_CMD_PORT_ALL; | |
360 | parsed_cmd->cmd_function = NULL; | |
361 | } | |
362 | ||
363 | /* function to interpret commands starting with "ethsw " */ | |
364 | static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
365 | { | |
366 | struct ethsw_command_def parsed_cmd; | |
367 | int rc = CMD_RET_SUCCESS; | |
368 | ||
369 | if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS) | |
370 | return CMD_RET_USAGE; | |
371 | ||
372 | command_def_init(&parsed_cmd); | |
373 | ||
374 | rc = keywords_find(argc, argv, &parsed_cmd); | |
375 | ||
376 | if (rc == CMD_RET_SUCCESS) | |
377 | rc = parsed_cmd.cmd_function(&parsed_cmd); | |
378 | ||
379 | return rc; | |
380 | } | |
381 | ||
382 | #define ETHSW_PORT_CONF_HELP "[port <port_no>] { enable | disable | show } " \ | |
383 | "- enable/disable a port; show shows a port's configuration" | |
384 | ||
385 | U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, | |
386 | "Ethernet l2 switch commands", | |
387 | ETHSW_PORT_CONF_HELP"\n" | |
86719f0c | 388 | ETHSW_PORT_STATS_HELP"\n" |
4ea54e3f | 389 | ); |