1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/comparator.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/kernel.h>
10 
11 #include <stdlib.h>
12 
13 #define AWAIT_TRIGGER_DEFAULT_TIMEOUT \
14 	CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_DEFAULT_TIMEOUT
15 
16 #define AWAIT_TRIGGER_MAX_TIMEOUT \
17 	CONFIG_COMPARATOR_SHELL_AWAIT_TRIGGER_MAX_TIMEOUT
18 
19 /* Mapped 1-1 to enum comparator_trigger */
20 static const char *const trigger_lookup[] = {
21 	"NONE",
22 	"RISING_EDGE",
23 	"FALLING_EDGE",
24 	"BOTH_EDGES",
25 };
26 
27 static K_SEM_DEFINE(triggered_sem, 0, 1);
28 
get_device_from_str(const struct shell * sh,const char * dev_str,const struct device ** dev)29 static int get_device_from_str(const struct shell *sh,
30 			       const char *dev_str,
31 			       const struct device **dev)
32 {
33 	*dev = shell_device_get_binding(dev_str);
34 
35 	if (*dev == NULL) {
36 		shell_error(sh, "%s not %s", dev_str, "found");
37 		return -ENODEV;
38 	}
39 
40 	if (!device_is_ready(*dev)) {
41 		shell_error(sh, "%s not %s", dev_str, "ready");
42 		return -ENODEV;
43 	}
44 
45 	return 0;
46 }
47 
cmd_get_output(const struct shell * sh,size_t argc,char ** argv)48 static int cmd_get_output(const struct shell *sh, size_t argc, char **argv)
49 {
50 	int ret;
51 	const char *dev_str;
52 	const struct device *dev;
53 
54 	ARG_UNUSED(argc);
55 
56 	dev_str = argv[1];
57 	ret = get_device_from_str(sh, dev_str, &dev);
58 	if (ret < 0) {
59 		return ret;
60 	}
61 
62 	ret = comparator_get_output(dev);
63 	if (ret < 0) {
64 		shell_error(sh, "failed to %s %s", "get", "output");
65 		return -EIO;
66 	}
67 
68 	shell_print(sh, "%i", ret);
69 	return 0;
70 }
71 
get_trigger_from_str(const struct shell * sh,const char * trigger_str,enum comparator_trigger * trigger)72 static int get_trigger_from_str(const struct shell *sh,
73 				const char *trigger_str,
74 				enum comparator_trigger *trigger)
75 {
76 	ARRAY_FOR_EACH(trigger_lookup, i) {
77 		if (strcmp(trigger_lookup[i], trigger_str) == 0) {
78 			*trigger = (enum comparator_trigger)i;
79 			return 0;
80 		}
81 	}
82 
83 	shell_error(sh, "%s not %s", trigger_str, "valid");
84 	return -EINVAL;
85 }
86 
cmd_set_trigger(const struct shell * sh,size_t argc,char ** argv)87 static int cmd_set_trigger(const struct shell *sh, size_t argc, char **argv)
88 {
89 	const char *dev_str;
90 	const char *trigger_str;
91 	int ret;
92 	const struct device *dev;
93 	enum comparator_trigger trigger;
94 
95 	ARG_UNUSED(argc);
96 
97 	dev_str = argv[1];
98 	ret = get_device_from_str(sh, dev_str, &dev);
99 	if (ret < 0) {
100 		return ret;
101 	}
102 
103 	trigger_str = argv[2];
104 	ret = get_trigger_from_str(sh, trigger_str, &trigger);
105 	if (ret < 0) {
106 		return ret;
107 	}
108 
109 	ret = comparator_set_trigger(dev, trigger);
110 	if (ret < 0) {
111 		shell_error(sh, "failed to %s %s", "set", "trigger");
112 		return -EIO;
113 	}
114 
115 	return 0;
116 }
117 
get_timeout_from_str(const struct shell * sh,const char * timeout_str,k_timeout_t * timeout)118 static int get_timeout_from_str(const struct shell *sh,
119 				const char *timeout_str,
120 				k_timeout_t *timeout)
121 {
122 	long seconds;
123 	char *end;
124 
125 	seconds = strtol(timeout_str, &end, 10);
126 	if ((*end != '\0') ||
127 	    (seconds < 1) ||
128 	    (seconds > AWAIT_TRIGGER_MAX_TIMEOUT)) {
129 		shell_error(sh, "%s not %s", timeout_str, "valid");
130 		return -EINVAL;
131 	}
132 
133 	*timeout = K_SECONDS(seconds);
134 	return 0;
135 }
136 
trigger_cb(const struct device * dev,void * user_data)137 static void trigger_cb(const struct device *dev, void *user_data)
138 {
139 	ARG_UNUSED(dev);
140 	ARG_UNUSED(user_data);
141 
142 	k_sem_give(&triggered_sem);
143 }
144 
cmd_await_trigger(const struct shell * sh,size_t argc,char ** argv)145 static int cmd_await_trigger(const struct shell *sh, size_t argc, char **argv)
146 {
147 	const char *dev_str;
148 	const char *timeout_str;
149 	int ret;
150 	const struct device *dev;
151 	k_timeout_t timeout;
152 
153 	dev_str = argv[1];
154 	ret = get_device_from_str(sh, dev_str, &dev);
155 	if (ret < 0) {
156 		return ret;
157 	}
158 
159 	if (argc == 3) {
160 		timeout_str = argv[2];
161 		ret = get_timeout_from_str(sh, timeout_str, &timeout);
162 		if (ret < 0) {
163 			return ret;
164 		}
165 	} else {
166 		timeout = K_SECONDS(AWAIT_TRIGGER_DEFAULT_TIMEOUT);
167 	}
168 
169 	k_sem_reset(&triggered_sem);
170 
171 	ret = comparator_set_trigger_callback(dev, trigger_cb, NULL);
172 	if (ret < 0) {
173 		shell_error(sh, "failed to %s %s", "set", "trigger callback");
174 		return -EIO;
175 	}
176 
177 	ret = k_sem_take(&triggered_sem, timeout);
178 	if (ret == 0) {
179 		shell_print(sh, "triggered");
180 	} else if (ret == -EAGAIN) {
181 		shell_print(sh, "timed out");
182 	} else {
183 		shell_error(sh, "internal error");
184 	}
185 
186 	ret = comparator_set_trigger_callback(dev, NULL, NULL);
187 	if (ret < 0) {
188 		shell_error(sh, "failed to %s %s", "clear", "trigger callback");
189 		return -EIO;
190 	}
191 
192 	return 0;
193 }
194 
cmd_trigger_is_pending(const struct shell * sh,size_t argc,char ** argv)195 static int cmd_trigger_is_pending(const struct shell *sh, size_t argc, char **argv)
196 {
197 	int ret;
198 	const char *dev_str;
199 	const struct device *dev;
200 
201 	ARG_UNUSED(argc);
202 
203 	dev_str = argv[1];
204 	ret = get_device_from_str(sh, dev_str, &dev);
205 	if (ret < 0) {
206 		return ret;
207 	}
208 
209 	ret = comparator_trigger_is_pending(dev);
210 	if (ret < 0) {
211 		shell_error(sh, "failed to %s %s", "get", "trigger status");
212 		return -EIO;
213 	}
214 
215 	shell_print(sh, "%i", ret);
216 	return 0;
217 }
218 
device_is_comp_and_ready(const struct device * dev)219 static bool device_is_comp_and_ready(const struct device *dev)
220 {
221 	return device_is_ready(dev) && DEVICE_API_IS(comparator, dev);
222 }
223 
dsub_set_trigger_lookup_1(size_t idx,struct shell_static_entry * entry)224 static void dsub_set_trigger_lookup_1(size_t idx, struct shell_static_entry *entry)
225 {
226 	entry->syntax = (idx < ARRAY_SIZE(trigger_lookup)) ? trigger_lookup[idx] : NULL;
227 	entry->handler = NULL;
228 	entry->help = NULL;
229 	entry->subcmd = NULL;
230 }
231 
232 SHELL_DYNAMIC_CMD_CREATE(dsub_set_trigger_1, dsub_set_trigger_lookup_1);
233 
dsub_set_trigger_lookup_0(size_t idx,struct shell_static_entry * entry)234 static void dsub_set_trigger_lookup_0(size_t idx, struct shell_static_entry *entry)
235 {
236 	const struct device *dev = shell_device_filter(idx, device_is_comp_and_ready);
237 
238 	entry->syntax = dev != NULL ? dev->name : NULL;
239 	entry->handler = NULL;
240 	entry->help = NULL;
241 	entry->subcmd = &dsub_set_trigger_1;
242 }
243 
244 SHELL_DYNAMIC_CMD_CREATE(dsub_set_trigger_0, dsub_set_trigger_lookup_0);
245 
dsub_device_lookup_0(size_t idx,struct shell_static_entry * entry)246 static void dsub_device_lookup_0(size_t idx, struct shell_static_entry *entry)
247 {
248 	const struct device *dev = shell_device_filter(idx, device_is_comp_and_ready);
249 
250 	entry->syntax = (dev != NULL) ? dev->name : NULL;
251 	entry->handler = NULL;
252 	entry->help = NULL;
253 	entry->subcmd = NULL;
254 }
255 
256 SHELL_DYNAMIC_CMD_CREATE(dsub_device_0, dsub_device_lookup_0);
257 
258 #define GET_OUTPUT_HELP \
259 	("comp get_output <device>")
260 
261 #define SET_TRIGGER_HELP \
262 	("comp set_trigger <device> <NONE | RISING_EDGE | FALLING_EDGE | BOTH_EDGES>")
263 
264 #define AWAIT_TRIGGER_HELP								\
265 	("comp await_trigger <device> [timeout] (default "				\
266 	 STRINGIFY(AWAIT_TRIGGER_DEFAULT_TIMEOUT)					\
267 	 "s, max "									\
268 	 STRINGIFY(AWAIT_TRIGGER_MAX_TIMEOUT)						\
269 	 "s)")
270 
271 #define TRIGGER_PENDING_HELP \
272 	("comp trigger_is_pending <device>")
273 
274 SHELL_STATIC_SUBCMD_SET_CREATE(
275 	sub_comp,
276 	SHELL_CMD_ARG(get_output, &dsub_device_0, GET_OUTPUT_HELP, cmd_get_output, 2, 0),
277 	SHELL_CMD_ARG(set_trigger, &dsub_set_trigger_0, SET_TRIGGER_HELP, cmd_set_trigger, 3, 0),
278 	SHELL_CMD_ARG(await_trigger, &dsub_device_0, AWAIT_TRIGGER_HELP, cmd_await_trigger, 2, 1),
279 	SHELL_CMD_ARG(trigger_is_pending, &dsub_device_0, TRIGGER_PENDING_HELP,
280 		      cmd_trigger_is_pending, 2, 1),
281 	SHELL_SUBCMD_SET_END
282 );
283 
284 SHELL_CMD_REGISTER(comp, &sub_comp, "Comparator device commands", NULL);
285