1 /*
2  * Copyright (c) 2020 Seagate Technology LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/shell/shell.h>
8 #include <zephyr/drivers/led.h>
9 #include <zephyr/dt-bindings/led/led.h>
10 #include <stdlib.h>
11 
12 #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(led_shell);
15 
16 #define MAX_CHANNEL_ARGS 8
17 
18 enum {
19 	arg_idx_dev		= 1,
20 	arg_idx_led		= 2,
21 	arg_idx_value		= 3,
22 	arg_idx_delay_on	= 3,
23 	arg_idx_delay_off	= 4,
24 };
25 
parse_common_args(const struct shell * sh,char ** argv,const struct device ** dev,uint32_t * led)26 static int parse_common_args(const struct shell *sh, char **argv,
27 			     const struct device * *dev, uint32_t *led)
28 {
29 	char *end_ptr;
30 
31 	*dev = shell_device_get_binding(argv[arg_idx_dev]);
32 	if (*dev == NULL) {
33 		shell_error(sh,
34 			    "LED device %s not found", argv[arg_idx_dev]);
35 		return -ENODEV;
36 	}
37 
38 	*led = strtoul(argv[arg_idx_led], &end_ptr, 0);
39 	if (*end_ptr != '\0') {
40 		shell_error(sh, "Invalid LED number parameter %s",
41 			    argv[arg_idx_led]);
42 		return -EINVAL;
43 	}
44 
45 	return 0;
46 }
47 
cmd_off(const struct shell * sh,size_t argc,char ** argv)48 static int cmd_off(const struct shell *sh, size_t argc, char **argv)
49 {
50 	const struct device *dev;
51 	uint32_t led;
52 	int err;
53 
54 	err = parse_common_args(sh, argv, &dev, &led);
55 	if (err < 0) {
56 		return err;
57 	}
58 
59 	shell_print(sh, "%s: turning off LED %d", dev->name, led);
60 
61 	err = led_off(dev, led);
62 	if (err) {
63 		shell_error(sh, "Error: %d", err);
64 	}
65 
66 	return err;
67 }
68 
cmd_on(const struct shell * sh,size_t argc,char ** argv)69 static int cmd_on(const struct shell *sh, size_t argc, char **argv)
70 {
71 	const struct device *dev;
72 	uint32_t led;
73 	int err;
74 
75 	err = parse_common_args(sh, argv, &dev, &led);
76 	if (err < 0) {
77 		return err;
78 	}
79 
80 	shell_print(sh, "%s: turning on LED %d", dev->name, led);
81 
82 	err = led_on(dev, led);
83 	if (err) {
84 		shell_error(sh, "Error: %d", err);
85 	}
86 
87 	return err;
88 }
89 
cmd_blink(const struct shell * sh,size_t argc,char ** argv)90 static int cmd_blink(const struct shell *sh, size_t argc, char **argv)
91 {
92 	const struct device *dev;
93 	uint32_t led, delay_on, delay_off;
94 	char *end_ptr;
95 	int err;
96 
97 	err = parse_common_args(sh, argv, &dev, &led);
98 	if (err < 0) {
99 		return err;
100 	}
101 
102 	delay_on = strtoul(argv[arg_idx_delay_on], &end_ptr, 0);
103 	if (*end_ptr != '\0') {
104 		shell_error(sh, "Invalid delay_on parameter %s", argv[arg_idx_delay_on]);
105 		return -EINVAL;
106 	}
107 
108 	if (argc > arg_idx_delay_off) {
109 		delay_off = strtoul(argv[arg_idx_delay_off], &end_ptr, 0);
110 		if (*end_ptr != '\0') {
111 			shell_error(sh, "Invalid delay_off parameter %s", argv[arg_idx_delay_off]);
112 			return -EINVAL;
113 		}
114 	} else {
115 		delay_off = delay_on;
116 	}
117 
118 	shell_print(sh, "%s: blinking LED %d with %d ms on and %d ms off",
119 		    dev->name, led, delay_on, delay_off);
120 
121 	err = led_blink(dev, led, delay_on, delay_off);
122 	if (err) {
123 		shell_error(sh, "Error: %d", err);
124 	}
125 
126 	return err;
127 }
128 
led_color_to_str(uint8_t color)129 static const char *led_color_to_str(uint8_t color)
130 {
131 	switch (color) {
132 	case LED_COLOR_ID_WHITE:
133 		return "white";
134 	case LED_COLOR_ID_RED:
135 		return "red";
136 	case LED_COLOR_ID_GREEN:
137 		return "green";
138 	case LED_COLOR_ID_BLUE:
139 		return "blue";
140 	case LED_COLOR_ID_VIOLET:
141 		return "violet";
142 	case LED_COLOR_ID_YELLOW:
143 		return "yellow";
144 	case LED_COLOR_ID_IR:
145 		return "IR";
146 	default:
147 		return "unknown";
148 	}
149 }
150 
cmd_get_info(const struct shell * sh,size_t argc,char ** argv)151 static int cmd_get_info(const struct shell *sh, size_t argc, char **argv)
152 {
153 	const struct device *dev;
154 	uint32_t led;
155 	int err;
156 	const struct led_info *info;
157 	int i;
158 
159 	err = parse_common_args(sh, argv, &dev, &led);
160 	if (err < 0) {
161 		return err;
162 	}
163 
164 	shell_print(sh, "%s: getting LED %d information", dev->name, led);
165 
166 	err = led_get_info(dev, led, &info);
167 	if (err) {
168 		shell_error(sh, "Error: %d", err);
169 		return err;
170 	}
171 
172 	shell_print(sh, "Label      : %s", info->label ? : "<NULL>");
173 	shell_print(sh, "Index      : %d", info->index);
174 	shell_print(sh, "Num colors : %d", info->num_colors);
175 	if (info->color_mapping) {
176 		shell_fprintf(sh, SHELL_NORMAL, "Colors     : %s",
177 			      led_color_to_str(info->color_mapping[0]));
178 		for (i = 1; i < info->num_colors; i++) {
179 			shell_fprintf(sh, SHELL_NORMAL, ":%s",
180 				      led_color_to_str(info->color_mapping[i]));
181 		}
182 		shell_fprintf(sh, SHELL_NORMAL, "\n");
183 	}
184 
185 	return 0;
186 }
187 
cmd_set_brightness(const struct shell * sh,size_t argc,char ** argv)188 static int cmd_set_brightness(const struct shell *sh,
189 			      size_t argc, char **argv)
190 {
191 	const struct device *dev;
192 	uint32_t led;
193 	int err;
194 	char *end_ptr;
195 	unsigned long value;
196 
197 	err = parse_common_args(sh, argv, &dev, &led);
198 	if (err < 0) {
199 		return err;
200 	}
201 
202 	value = strtoul(argv[arg_idx_value], &end_ptr, 0);
203 	if (*end_ptr != '\0') {
204 		shell_error(sh, "Invalid LED brightness parameter %s",
205 			     argv[arg_idx_value]);
206 		return -EINVAL;
207 	}
208 	if (value > LED_BRIGHTNESS_MAX) {
209 		shell_error(sh, "Invalid LED brightness value %lu (max 100)",
210 			    value);
211 		return -EINVAL;
212 	}
213 
214 	shell_print(sh, "%s: setting LED %d brightness to %lu",
215 		    dev->name, led, value);
216 
217 	err = led_set_brightness(dev, led, (uint8_t) value);
218 	if (err) {
219 		shell_error(sh, "Error: %d", err);
220 	}
221 
222 	return err;
223 }
224 
cmd_set_color(const struct shell * sh,size_t argc,char ** argv)225 static int cmd_set_color(const struct shell *sh, size_t argc, char **argv)
226 {
227 	const struct device *dev;
228 	uint32_t led;
229 	int err;
230 	size_t num_colors;
231 	uint8_t i;
232 	uint8_t color[MAX_CHANNEL_ARGS];
233 
234 	err = parse_common_args(sh, argv, &dev, &led);
235 	if (err < 0) {
236 		return err;
237 	}
238 
239 	num_colors = argc - arg_idx_value;
240 	if (num_colors > MAX_CHANNEL_ARGS) {
241 		shell_error(sh,
242 			    "Invalid number of colors %d (max %d)",
243 			     num_colors, MAX_CHANNEL_ARGS);
244 		return -EINVAL;
245 	}
246 
247 	for (i = 0; i < num_colors; i++) {
248 		char *end_ptr;
249 		unsigned long col;
250 
251 		col = strtoul(argv[arg_idx_value + i], &end_ptr, 0);
252 		if (*end_ptr != '\0') {
253 			shell_error(sh, "Invalid LED color parameter %s",
254 				    argv[arg_idx_value + i]);
255 			return -EINVAL;
256 		}
257 		if (col > 255) {
258 			shell_error(sh,
259 				    "Invalid LED color value %lu (max 255)",
260 				    col);
261 			return -EINVAL;
262 		}
263 		color[i] = col;
264 	}
265 
266 	shell_fprintf(sh, SHELL_NORMAL, "%s: setting LED %d color to %d",
267 		      dev->name, led, color[0]);
268 	for (i = 1; i < num_colors; i++) {
269 		shell_fprintf(sh, SHELL_NORMAL, ":%d", color[i]);
270 	}
271 	shell_fprintf(sh, SHELL_NORMAL, "\n");
272 
273 	err = led_set_color(dev, led, num_colors, color);
274 	if (err) {
275 		shell_error(sh, "Error: %d", err);
276 	}
277 
278 	return err;
279 }
280 
cmd_set_channel(const struct shell * sh,size_t argc,char ** argv)281 static int cmd_set_channel(const struct shell *sh, size_t argc, char **argv)
282 {
283 	const struct device *dev;
284 	uint32_t channel;
285 	int err;
286 	char *end_ptr;
287 	unsigned long value;
288 
289 	err = parse_common_args(sh, argv, &dev, &channel);
290 	if (err < 0) {
291 		return err;
292 	}
293 
294 	value = strtoul(argv[arg_idx_value], &end_ptr, 0);
295 	if (*end_ptr != '\0') {
296 		shell_error(sh, "Invalid channel value parameter %s",
297 			     argv[arg_idx_value]);
298 		return -EINVAL;
299 	}
300 	if (value > 255) {
301 		shell_error(sh, "Invalid channel value %lu (max 255)",
302 			    value);
303 		return -EINVAL;
304 	}
305 
306 	shell_print(sh, "%s: setting channel %d to %lu",
307 		    dev->name, channel, value);
308 
309 	err = led_set_channel(dev, channel, (uint8_t) value);
310 	if (err) {
311 		shell_error(sh, "Error: %d", err);
312 	}
313 
314 	return err;
315 }
316 
317 static int
cmd_write_channels(const struct shell * sh,size_t argc,char ** argv)318 cmd_write_channels(const struct shell *sh, size_t argc, char **argv)
319 {
320 	const struct device *dev;
321 	uint32_t start_channel;
322 	int err;
323 	size_t num_channels;
324 	uint8_t i;
325 	uint8_t value[MAX_CHANNEL_ARGS];
326 
327 	err = parse_common_args(sh, argv, &dev, &start_channel);
328 	if (err < 0) {
329 		return err;
330 	}
331 
332 	num_channels = argc - arg_idx_value;
333 	if (num_channels > MAX_CHANNEL_ARGS) {
334 		shell_error(sh,
335 			    "Can't write %d channels (max %d)",
336 			     num_channels, MAX_CHANNEL_ARGS);
337 		return -EINVAL;
338 	}
339 
340 	for (i = 0; i < num_channels; i++) {
341 		char *end_ptr;
342 		unsigned long val;
343 
344 		val = strtoul(argv[arg_idx_value + i], &end_ptr, 0);
345 		if (*end_ptr != '\0') {
346 			shell_error(sh,
347 				    "Invalid channel value parameter %s",
348 				    argv[arg_idx_value + i]);
349 			return -EINVAL;
350 		}
351 		if (val > 255) {
352 			shell_error(sh,
353 				    "Invalid channel value %lu (max 255)", val);
354 			return -EINVAL;
355 		}
356 		value[i] = val;
357 	}
358 
359 	shell_fprintf(sh, SHELL_NORMAL, "%s: writing from channel %d: %d",
360 		      dev->name, start_channel, value[0]);
361 	for (i = 1; i < num_channels; i++) {
362 		shell_fprintf(sh, SHELL_NORMAL, " %d", value[i]);
363 	}
364 	shell_fprintf(sh, SHELL_NORMAL, "\n");
365 
366 	err = led_write_channels(dev, start_channel, num_channels, value);
367 	if (err) {
368 		shell_error(sh, "Error: %d", err);
369 	}
370 
371 	return err;
372 }
373 
device_is_led(const struct device * dev)374 static bool device_is_led(const struct device *dev)
375 {
376 	return DEVICE_API_IS(led, dev);
377 }
378 
device_name_get(size_t idx,struct shell_static_entry * entry)379 static void device_name_get(size_t idx, struct shell_static_entry *entry)
380 {
381 	const struct device *dev = shell_device_filter(idx, device_is_led);
382 
383 	entry->syntax = (dev != NULL) ? dev->name : NULL;
384 	entry->handler = NULL;
385 	entry->help = NULL;
386 	entry->subcmd = NULL;
387 }
388 
389 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
390 
391 SHELL_STATIC_SUBCMD_SET_CREATE(
392 	sub_led,
393 	SHELL_CMD_ARG(off, &dsub_device_name, SHELL_HELP("Turn off LED", "<device> <led>"), cmd_off,
394 		      3, 0),
395 	SHELL_CMD_ARG(on, &dsub_device_name, SHELL_HELP("Turn on LED", "<device> <led>"), cmd_on, 3,
396 		      0),
397 	SHELL_CMD_ARG(blink, &dsub_device_name,
398 		      SHELL_HELP("Blink LED on and off",
399 				 "<device> <led> <delay_on_in_ms> [<delay_off_in_ms>]"),
400 		      cmd_blink, 4, 1),
401 	SHELL_CMD_ARG(get_info, &dsub_device_name,
402 		      SHELL_HELP("Get LED information", "<device> <led>"), cmd_get_info, 3, 0),
403 	SHELL_CMD_ARG(set_brightness, &dsub_device_name,
404 		      SHELL_HELP("Set LED brightness",
405 				 "<device> <led> <value>\n"
406 				 "value: 0-100"),
407 		      cmd_set_brightness, 4, 0),
408 	SHELL_CMD_ARG(set_color, &dsub_device_name,
409 		      SHELL_HELP("Set LED color",
410 				 "<device> <led> <color0> ... <colorN>\n"
411 				 "colorN: raw value of the N-th color channel (0-255)"),
412 		      cmd_set_color, 4, MAX_CHANNEL_ARGS - 1),
413 	SHELL_CMD_ARG(set_channel, &dsub_device_name,
414 		      SHELL_HELP("Set LED channel",
415 				 "<device> <channel> <value>\n"
416 				  "value: raw channel value (0-255)"),
417 		      cmd_set_channel, 4, 0),
418 	SHELL_CMD_ARG(write_channels, &dsub_device_name,
419 		      SHELL_HELP("Write to LED channels",
420 				 "<device> <channel> <value0> ... <valueN>\n"
421 				 "valueN: raw value of the N-th channel (0-255)"),
422 		      cmd_write_channels, 4, MAX_CHANNEL_ARGS - 1),
423 	SHELL_SUBCMD_SET_END);
424 
425 SHELL_CMD_REGISTER(led, &sub_led, "LED commands", NULL);
426