1 /*
2  * Copyright (c) 2022-2023, Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * ARM SiP service shell command 'sip_svc'.
7  */
8 
9 #include <zephyr/shell/shell.h>
10 #include <zephyr/sip_svc/sip_svc.h>
11 #include <zephyr/sip_svc/sip_svc_controller.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 
15 #define MAX_TIMEOUT_MSECS (1 * 1000UL)
16 
17 struct private_data {
18 	struct k_sem semaphore;
19 	const struct shell *sh;
20 };
21 
parse_common_args(const struct shell * sh,char ** argv,void ** ctrl)22 static int parse_common_args(const struct shell *sh, char **argv, void **ctrl)
23 {
24 	*ctrl = sip_svc_get_controller(argv[1]);
25 
26 	if (!*ctrl) {
27 		shell_error(sh, "service %s not found", argv[1]);
28 		return -ENODEV;
29 	}
30 
31 	struct sip_svc_controller *ct = (struct sip_svc_controller *)(*ctrl);
32 
33 	if (!ct->init) {
34 		shell_error(sh, "ARM SiP services method %s not initialized", argv[1]);
35 		return -ENODEV;
36 	}
37 	return 0;
38 }
39 
cmd_reg(const struct shell * sh,size_t argc,char ** argv)40 static int cmd_reg(const struct shell *sh, size_t argc, char **argv)
41 {
42 	struct sip_svc_controller *ctrl;
43 	uint32_t c_token;
44 	int err;
45 
46 	err = parse_common_args(sh, argv, (void **)&ctrl);
47 	if (err < 0) {
48 		return err;
49 	}
50 
51 	c_token = sip_svc_register(ctrl, NULL);
52 	if (c_token == SIP_SVC_ID_INVALID) {
53 		shell_error(sh, "%s: register fail", ctrl->method);
54 		err = -1;
55 	} else {
56 		shell_print(sh, "%s: register success: client token %08x\n", ctrl->method, c_token);
57 		err = 0;
58 	}
59 
60 	return err;
61 }
62 
cmd_unreg(const struct shell * sh,size_t argc,char ** argv)63 static int cmd_unreg(const struct shell *sh, size_t argc, char **argv)
64 {
65 	struct sip_svc_controller *ctrl;
66 	uint32_t c_token;
67 	int err;
68 	char *endptr;
69 
70 	err = parse_common_args(sh, argv, (void **)&ctrl);
71 	if (err < 0) {
72 		return err;
73 	}
74 
75 	errno = 0;
76 	c_token = strtoul(argv[2], &endptr, 16);
77 	if (errno == ERANGE) {
78 		shell_error(sh, "Out of range value");
79 		return -ERANGE;
80 	} else if (errno || endptr == argv[2] || *endptr) {
81 		shell_error(sh, "Invalid argument");
82 		return -errno;
83 	}
84 
85 	err = sip_svc_unregister(ctrl, (uint32_t)c_token);
86 	if (err) {
87 		shell_error(sh, "%s: unregister fail (%d): client token %08x", ctrl->method, err,
88 			    (uint32_t)c_token);
89 	} else {
90 		shell_print(sh, "%s: unregister success: client token %08x", ctrl->method,
91 			    (uint32_t)c_token);
92 	}
93 
94 	return err;
95 }
96 
cmd_open(const struct shell * sh,size_t argc,char ** argv)97 static int cmd_open(const struct shell *sh, size_t argc, char **argv)
98 {
99 	struct sip_svc_controller *ctrl;
100 	uint32_t c_token;
101 	unsigned long mseconds = 0;
102 	int err;
103 	char *endptr;
104 	k_timeout_t timeout = K_MSEC(MAX_TIMEOUT_MSECS);
105 
106 	err = parse_common_args(sh, argv, (void **)&ctrl);
107 	if (err < 0) {
108 		return err;
109 	}
110 
111 	errno = 0;
112 	c_token = strtoul(argv[2], &endptr, 16);
113 	if (errno == ERANGE) {
114 		shell_error(sh, "Out of range value");
115 		return -ERANGE;
116 	} else if (errno || endptr == argv[2] || *endptr) {
117 		shell_error(sh, "Invalid argument");
118 		return -errno;
119 	}
120 
121 	if (argc > 3) {
122 		errno = 0;
123 		mseconds = strtoul(argv[3], &endptr, 10);
124 		if (errno == ERANGE) {
125 			shell_error(sh, "Out of range value");
126 			return -ERANGE;
127 		} else if (errno || endptr == argv[3] || *endptr) {
128 			shell_error(sh, "Invalid Argument");
129 			return -EINVAL;
130 		} else if (mseconds <= MAX_TIMEOUT_MSECS) {
131 			timeout = K_MSEC(mseconds);
132 		} else {
133 			timeout = K_MSEC(MAX_TIMEOUT_MSECS);
134 			shell_error(sh, "Setting timeout value to %lu milliseconds",
135 				    MAX_TIMEOUT_MSECS);
136 		}
137 	}
138 
139 	err = sip_svc_open(ctrl, (uint32_t)c_token, timeout);
140 	if (err) {
141 		shell_error(sh, "%s: open fail (%d): client token %08x", ctrl->method, err,
142 			    (uint32_t)c_token);
143 	} else {
144 		shell_print(sh, "%s: open success: client token %08x", ctrl->method,
145 			    (uint32_t)c_token);
146 	}
147 
148 	return err;
149 }
150 
cmd_close(const struct shell * sh,size_t argc,char ** argv)151 static int cmd_close(const struct shell *sh, size_t argc, char **argv)
152 {
153 	struct sip_svc_controller *ctrl;
154 	uint32_t c_token;
155 	int err;
156 	char *endptr;
157 
158 	err = parse_common_args(sh, argv, (void **)&ctrl);
159 	if (err < 0) {
160 		return err;
161 	}
162 
163 	errno = 0;
164 	c_token = strtoul(argv[2], &endptr, 16);
165 	if (errno == ERANGE) {
166 		shell_error(sh, "Out of range value");
167 		return -ERANGE;
168 	} else if (errno || endptr == argv[2] || *endptr) {
169 		shell_error(sh, "Invalid argument");
170 		return -errno;
171 	}
172 
173 	err = sip_svc_close(ctrl, (uint32_t)c_token, NULL);
174 	if (err) {
175 		shell_error(sh, "%s: close fail (%d): client token %08x", ctrl->method, err,
176 			    (uint32_t)c_token);
177 	} else {
178 		shell_print(sh, "%s: close success: client token %08x", ctrl->method,
179 			    (uint32_t)c_token);
180 	}
181 
182 	return err;
183 }
184 
cmd_send_callback(uint32_t c_token,struct sip_svc_response * response)185 static void cmd_send_callback(uint32_t c_token, struct sip_svc_response *response)
186 {
187 	if (response == NULL) {
188 		return;
189 	}
190 
191 	struct private_data *priv = (struct private_data *)response->priv_data;
192 	const struct shell *sh = priv->sh;
193 
194 	shell_print(sh, "\n\rsip_svc send callback response\n");
195 	shell_print(sh, "\theader=%08x\n", response->header);
196 	shell_print(sh, "\ta0=%016lx\n", response->a0);
197 	shell_print(sh, "\ta1=%016lx\n", response->a1);
198 	shell_print(sh, "\ta2=%016lx\n", response->a2);
199 	shell_print(sh, "\ta3=%016lx\n", response->a3);
200 
201 	k_sem_give(&(priv->semaphore));
202 }
203 
cmd_send(const struct shell * sh,size_t argc,char ** argv)204 static int cmd_send(const struct shell *sh, size_t argc, char **argv)
205 {
206 	struct sip_svc_controller *ctrl;
207 	uint32_t c_token;
208 	int trans_id;
209 	struct sip_svc_request request;
210 	struct private_data priv;
211 	char *endptr;
212 	int err;
213 
214 	err = parse_common_args(sh, argv, (void **)&ctrl);
215 	if (err < 0) {
216 		return err;
217 	}
218 
219 	errno = 0;
220 	c_token = strtoul(argv[2], &endptr, 16);
221 	if (errno == ERANGE) {
222 		shell_error(sh, "Out of range value");
223 		return -ERANGE;
224 	} else if (errno || endptr == argv[2] || *endptr) {
225 		shell_error(sh, "Invalid argument");
226 		return -errno;
227 	}
228 
229 	request.header = SIP_SVC_PROTO_HEADER(SIP_SVC_PROTO_CMD_SYNC, 0);
230 
231 	request.a0 = strtoul(argv[3], &endptr, 16);
232 	if (errno == ERANGE) {
233 		shell_error(sh, "Out of range value for a0");
234 		return -ERANGE;
235 	} else if (errno || endptr == argv[3] || *endptr) {
236 		shell_error(sh, "Invalid argument for a0");
237 		return -errno;
238 	}
239 
240 	if (argc > 4) {
241 		request.a1 = strtoul(argv[4], &endptr, 16);
242 		if (errno == ERANGE) {
243 			shell_error(sh, "Out of range value for a1");
244 			return -ERANGE;
245 		} else if (errno || endptr == argv[4] || *endptr) {
246 			shell_error(sh, "Invalid argument for a1");
247 			return -errno;
248 		}
249 	}
250 
251 	if (argc > 5) {
252 		request.a2 = strtoul(argv[5], &endptr, 16);
253 		if (errno == ERANGE) {
254 			shell_error(sh, "Out of range value for a2");
255 			return -ERANGE;
256 		} else if (errno || endptr == argv[5] || *endptr) {
257 			shell_error(sh, "Invalid argument for a2");
258 			return -errno;
259 		}
260 	}
261 
262 	if (argc > 6) {
263 		request.a3 = strtoul(argv[6], &endptr, 16);
264 		if (errno == ERANGE) {
265 			shell_error(sh, "Out of range value for a3");
266 			return -ERANGE;
267 		} else if (errno || endptr == argv[6] || *endptr) {
268 			shell_error(sh, "Invalid argument for a3");
269 			return -errno;
270 		}
271 	}
272 
273 	if (argc > 7) {
274 		request.a4 = strtoul(argv[7], &endptr, 16);
275 		if (errno == ERANGE) {
276 			shell_error(sh, "Out of range value for a4");
277 			return -ERANGE;
278 		} else if (errno || endptr == argv[7] || *endptr) {
279 			shell_error(sh, "Invalid argument for a4");
280 			return -errno;
281 		}
282 	}
283 
284 	if (argc > 8) {
285 		request.a5 = strtoul(argv[8], &endptr, 16);
286 		if (errno == ERANGE) {
287 			shell_error(sh, "Out of range value for a5");
288 			return -ERANGE;
289 		} else if (errno || endptr == argv[8] || *endptr) {
290 			shell_error(sh, "Invalid argument for a5");
291 			return -errno;
292 		}
293 	}
294 
295 	if (argc > 9) {
296 		request.a6 = strtoul(argv[9], &endptr, 16);
297 		if (errno == ERANGE) {
298 			shell_error(sh, "Out of range value for a6");
299 			return -ERANGE;
300 		} else if (errno || endptr == argv[9] || *endptr) {
301 			shell_error(sh, "Invalid argument for a6");
302 			return -errno;
303 		}
304 	}
305 
306 	if (argc > 10) {
307 		request.a7 = strtoul(argv[10], &endptr, 16);
308 		if (errno == ERANGE) {
309 			shell_error(sh, "Out of range value for a7");
310 			return -ERANGE;
311 		} else if (errno || endptr == argv[10] || *endptr) {
312 			shell_error(sh, "Invalid argument for a7");
313 			return -errno;
314 		}
315 	}
316 
317 	k_sem_init(&(priv.semaphore), 0, 1);
318 	priv.sh = sh;
319 
320 	request.resp_data_addr = 0;
321 	request.resp_data_size = 0;
322 	request.priv_data = (void *)&priv;
323 
324 	trans_id = sip_svc_send(ctrl, (uint32_t)c_token, &request, cmd_send_callback);
325 
326 	if (trans_id < 0) {
327 		shell_error(sh, "%s: send fail: client token %08x", ctrl->method,
328 			    (uint32_t)c_token);
329 		err = trans_id;
330 	} else {
331 		/*wait for callback*/
332 		k_sem_take(&(priv.semaphore), K_FOREVER);
333 
334 		shell_print(sh, "%s: send success: client token %08x, trans_id %d", ctrl->method,
335 			    (uint32_t)c_token, trans_id);
336 		err = 0;
337 	}
338 	return err;
339 }
340 
cmd_info(const struct shell * sh,size_t argc,char ** argv)341 static int cmd_info(const struct shell *sh, size_t argc, char **argv)
342 {
343 	struct sip_svc_controller *ctrl;
344 	int err;
345 	uint32_t i;
346 	static const char *const state_str_list[] = {"INVALID", "IDLE", "OPEN", "ABORT"};
347 	const char *state_str = "UNKNOWN";
348 
349 	err = parse_common_args(sh, argv, (void **)&ctrl);
350 	if (err < 0) {
351 		return err;
352 	}
353 
354 	shell_print(sh, "---------------------------------------\n");
355 	shell_print(sh, "sip_svc service information\n");
356 	shell_print(sh, "---------------------------------------\n");
357 
358 	shell_print(sh, "active job cnt         %d\n", ctrl->active_job_cnt);
359 	shell_print(sh, "active async job cnt   %d\n", ctrl->active_async_job_cnt);
360 
361 	shell_print(sh, "---------------------------------------\n");
362 	shell_print(sh, "Client Token\tState\tTrans Cnt\n");
363 	shell_print(sh, "---------------------------------------\n");
364 	for (i = 0; i < ctrl->num_clients; i++) {
365 		if (ctrl->clients[i].id != SIP_SVC_ID_INVALID) {
366 			if (ctrl->clients[i].state <= SIP_SVC_CLIENT_ST_ABORT) {
367 				state_str = state_str_list[ctrl->clients[i].state];
368 			}
369 
370 			shell_print(sh, "%08x    \t%-10s\t%-9d\n", ctrl->clients[i].token,
371 				    state_str, ctrl->clients[i].active_trans_cnt);
372 		}
373 	}
374 	return err;
375 }
376 
377 SHELL_STATIC_SUBCMD_SET_CREATE(
378 	sub_sip_svc, SHELL_CMD_ARG(reg, NULL, "<method>", cmd_reg, 2, 0),
379 	SHELL_CMD_ARG(unreg, NULL, "<method> <token>", cmd_unreg, 3, 0),
380 	SHELL_CMD_ARG(open, NULL, "<method> <token> <[timeout_msec]>", cmd_open, 3, 1),
381 	SHELL_CMD_ARG(close, NULL, "<method> <token>", cmd_close, 3, 0),
382 	SHELL_CMD_ARG(send, NULL, "<method> <token> <a0> [<a1> <a2> ... <a7>]", cmd_send, 4, 7),
383 	SHELL_CMD_ARG(info, NULL, "<method>", cmd_info, 2, 0), SHELL_SUBCMD_SET_END);
384 
385 SHELL_CMD_REGISTER(sip_svc, &sub_sip_svc, "ARM SiP services commands", NULL);
386