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