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