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 };
23 
parse_common_args(const struct shell * sh,char ** argv,const struct device ** dev,uint32_t * led)24 static int parse_common_args(const struct shell *sh, char **argv,
25 			     const struct device * *dev, uint32_t *led)
26 {
27 	char *end_ptr;
28 
29 	*dev = shell_device_get_binding(argv[arg_idx_dev]);
30 	if (!*dev) {
31 		shell_error(sh,
32 			    "LED device %s not found", argv[arg_idx_dev]);
33 		return -ENODEV;
34 	}
35 
36 	*led = strtoul(argv[arg_idx_led], &end_ptr, 0);
37 	if (*end_ptr != '\0') {
38 		shell_error(sh, "Invalid LED number parameter %s",
39 			    argv[arg_idx_led]);
40 		return -EINVAL;
41 	}
42 
43 	return 0;
44 }
45 
cmd_off(const struct shell * sh,size_t argc,char ** argv)46 static int cmd_off(const struct shell *sh, size_t argc, char **argv)
47 {
48 	const struct device *dev;
49 	uint32_t led;
50 	int err;
51 
52 	err = parse_common_args(sh, argv, &dev, &led);
53 	if (err < 0) {
54 		return err;
55 	}
56 
57 	shell_print(sh, "%s: turning off LED %d", dev->name, led);
58 
59 	err = led_off(dev, led);
60 	if (err) {
61 		shell_error(sh, "Error: %d", err);
62 	}
63 
64 	return err;
65 }
66 
cmd_on(const struct shell * sh,size_t argc,char ** argv)67 static int cmd_on(const struct shell *sh, size_t argc, char **argv)
68 {
69 	const struct device *dev;
70 	uint32_t led;
71 	int err;
72 
73 	err = parse_common_args(sh, argv, &dev, &led);
74 	if (err < 0) {
75 		return err;
76 	}
77 
78 	shell_print(sh, "%s: turning on LED %d", dev->name, led);
79 
80 	err = led_on(dev, led);
81 	if (err) {
82 		shell_error(sh, "Error: %d", err);
83 	}
84 
85 	return err;
86 }
87 
led_color_to_str(uint8_t color)88 static const char *led_color_to_str(uint8_t color)
89 {
90 	switch (color) {
91 	case LED_COLOR_ID_WHITE:
92 		return "white";
93 	case LED_COLOR_ID_RED:
94 		return "red";
95 	case LED_COLOR_ID_GREEN:
96 		return "green";
97 	case LED_COLOR_ID_BLUE:
98 		return "blue";
99 	case LED_COLOR_ID_VIOLET:
100 		return "violet";
101 	case LED_COLOR_ID_YELLOW:
102 		return "yellow";
103 	case LED_COLOR_ID_IR:
104 		return "IR";
105 	default:
106 		return "unknown";
107 	}
108 }
109 
cmd_get_info(const struct shell * sh,size_t argc,char ** argv)110 static int cmd_get_info(const struct shell *sh, size_t argc, char **argv)
111 {
112 	const struct device *dev;
113 	uint32_t led;
114 	int err;
115 	const struct led_info *info;
116 	int i;
117 
118 	err = parse_common_args(sh, argv, &dev, &led);
119 	if (err < 0) {
120 		return err;
121 	}
122 
123 	shell_print(sh, "%s: getting LED %d information", dev->name, led);
124 
125 	err = led_get_info(dev, led, &info);
126 	if (err) {
127 		shell_error(sh, "Error: %d", err);
128 		return err;
129 	}
130 
131 	shell_print(sh, "Label      : %s", info->label ? : "<NULL>");
132 	shell_print(sh, "Index      : %d", info->index);
133 	shell_print(sh, "Num colors : %d", info->num_colors);
134 	if (info->color_mapping) {
135 		shell_fprintf(sh, SHELL_NORMAL, "Colors     : %s",
136 			      led_color_to_str(info->color_mapping[0]));
137 		for (i = 1; i < info->num_colors; i++) {
138 			shell_fprintf(sh, SHELL_NORMAL, ":%s",
139 				      led_color_to_str(info->color_mapping[i]));
140 		}
141 		shell_fprintf(sh, SHELL_NORMAL, "\n");
142 	}
143 
144 	return 0;
145 }
146 
cmd_set_brightness(const struct shell * sh,size_t argc,char ** argv)147 static int cmd_set_brightness(const struct shell *sh,
148 			      size_t argc, char **argv)
149 {
150 	const struct device *dev;
151 	uint32_t led;
152 	int err;
153 	char *end_ptr;
154 	unsigned long value;
155 
156 	err = parse_common_args(sh, argv, &dev, &led);
157 	if (err < 0) {
158 		return err;
159 	}
160 
161 	value = strtoul(argv[arg_idx_value], &end_ptr, 0);
162 	if (*end_ptr != '\0') {
163 		shell_error(sh, "Invalid LED brightness parameter %s",
164 			     argv[arg_idx_value]);
165 		return -EINVAL;
166 	}
167 	if (value > 100) {
168 		shell_error(sh, "Invalid LED brightness value %lu (max 100)",
169 			    value);
170 		return -EINVAL;
171 	}
172 
173 	shell_print(sh, "%s: setting LED %d brightness to %lu",
174 		    dev->name, led, value);
175 
176 	err = led_set_brightness(dev, led, (uint8_t) value);
177 	if (err) {
178 		shell_error(sh, "Error: %d", err);
179 	}
180 
181 	return err;
182 }
183 
cmd_set_color(const struct shell * sh,size_t argc,char ** argv)184 static int cmd_set_color(const struct shell *sh, size_t argc, char **argv)
185 {
186 	const struct device *dev;
187 	uint32_t led;
188 	int err;
189 	size_t num_colors;
190 	uint8_t i;
191 	uint8_t color[MAX_CHANNEL_ARGS];
192 
193 	err = parse_common_args(sh, argv, &dev, &led);
194 	if (err < 0) {
195 		return err;
196 	}
197 
198 	num_colors = argc - arg_idx_value;
199 	if (num_colors > MAX_CHANNEL_ARGS) {
200 		shell_error(sh,
201 			    "Invalid number of colors %d (max %d)",
202 			     num_colors, MAX_CHANNEL_ARGS);
203 		return -EINVAL;
204 	}
205 
206 	for (i = 0; i < num_colors; i++) {
207 		char *end_ptr;
208 		unsigned long col;
209 
210 		col = strtoul(argv[arg_idx_value + i], &end_ptr, 0);
211 		if (*end_ptr != '\0') {
212 			shell_error(sh, "Invalid LED color parameter %s",
213 				    argv[arg_idx_value + i]);
214 			return -EINVAL;
215 		}
216 		if (col > 255) {
217 			shell_error(sh,
218 				    "Invalid LED color value %lu (max 255)",
219 				    col);
220 			return -EINVAL;
221 		}
222 		color[i] = col;
223 	}
224 
225 	shell_fprintf(sh, SHELL_NORMAL, "%s: setting LED %d color to %d",
226 		      dev->name, led, color[0]);
227 	for (i = 1; i < num_colors; i++) {
228 		shell_fprintf(sh, SHELL_NORMAL, ":%d", color[i]);
229 	}
230 	shell_fprintf(sh, SHELL_NORMAL, "\n");
231 
232 	err = led_set_color(dev, led, num_colors, color);
233 	if (err) {
234 		shell_error(sh, "Error: %d", err);
235 	}
236 
237 	return err;
238 }
239 
cmd_set_channel(const struct shell * sh,size_t argc,char ** argv)240 static int cmd_set_channel(const struct shell *sh, size_t argc, char **argv)
241 {
242 	const struct device *dev;
243 	uint32_t channel;
244 	int err;
245 	char *end_ptr;
246 	unsigned long value;
247 
248 	err = parse_common_args(sh, argv, &dev, &channel);
249 	if (err < 0) {
250 		return err;
251 	}
252 
253 	value = strtoul(argv[arg_idx_value], &end_ptr, 0);
254 	if (*end_ptr != '\0') {
255 		shell_error(sh, "Invalid channel value parameter %s",
256 			     argv[arg_idx_value]);
257 		return -EINVAL;
258 	}
259 	if (value > 255) {
260 		shell_error(sh, "Invalid channel value %lu (max 255)",
261 			    value);
262 		return -EINVAL;
263 	}
264 
265 	shell_print(sh, "%s: setting channel %d to %lu",
266 		    dev->name, channel, value);
267 
268 	err = led_set_channel(dev, channel, (uint8_t) value);
269 	if (err) {
270 		shell_error(sh, "Error: %d", err);
271 	}
272 
273 	return err;
274 }
275 
276 static int
cmd_write_channels(const struct shell * sh,size_t argc,char ** argv)277 cmd_write_channels(const struct shell *sh, size_t argc, char **argv)
278 {
279 	const struct device *dev;
280 	uint32_t start_channel;
281 	int err;
282 	size_t num_channels;
283 	uint8_t i;
284 	uint8_t value[MAX_CHANNEL_ARGS];
285 
286 	err = parse_common_args(sh, argv, &dev, &start_channel);
287 	if (err < 0) {
288 		return err;
289 	}
290 
291 	num_channels = argc - arg_idx_value;
292 	if (num_channels > MAX_CHANNEL_ARGS) {
293 		shell_error(sh,
294 			    "Can't write %d channels (max %d)",
295 			     num_channels, MAX_CHANNEL_ARGS);
296 		return -EINVAL;
297 	}
298 
299 	for (i = 0; i < num_channels; i++) {
300 		char *end_ptr;
301 		unsigned long val;
302 
303 		val = strtoul(argv[arg_idx_value + i], &end_ptr, 0);
304 		if (*end_ptr != '\0') {
305 			shell_error(sh,
306 				    "Invalid channel value parameter %s",
307 				    argv[arg_idx_value + i]);
308 			return -EINVAL;
309 		}
310 		if (val > 255) {
311 			shell_error(sh,
312 				    "Invalid channel value %lu (max 255)", val);
313 			return -EINVAL;
314 		}
315 		value[i] = val;
316 	}
317 
318 	shell_fprintf(sh, SHELL_NORMAL, "%s: writing from channel %d: %d",
319 		      dev->name, start_channel, value[0]);
320 	for (i = 1; i < num_channels; i++) {
321 		shell_fprintf(sh, SHELL_NORMAL, " %d", value[i]);
322 	}
323 	shell_fprintf(sh, SHELL_NORMAL, "\n");
324 
325 	err = led_write_channels(dev, start_channel, num_channels, value);
326 	if (err) {
327 		shell_error(sh, "Error: %d", err);
328 	}
329 
330 	return err;
331 }
332 
device_is_led_and_ready(const struct device * dev)333 static bool device_is_led_and_ready(const struct device *dev)
334 {
335 	return device_is_ready(dev) && DEVICE_API_IS(led, dev);
336 }
337 
device_name_get(size_t idx,struct shell_static_entry * entry)338 static void device_name_get(size_t idx, struct shell_static_entry *entry)
339 {
340 	const struct device *dev = shell_device_filter(idx, device_is_led_and_ready);
341 
342 	entry->syntax = (dev != NULL) ? dev->name : NULL;
343 	entry->handler = NULL;
344 	entry->help = NULL;
345 	entry->subcmd = NULL;
346 }
347 
348 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
349 
350 SHELL_STATIC_SUBCMD_SET_CREATE(
351 	sub_led, SHELL_CMD_ARG(off, &dsub_device_name, "<device> <led>", cmd_off, 3, 0),
352 	SHELL_CMD_ARG(on, &dsub_device_name, "<device> <led>", cmd_on, 3, 0),
353 	SHELL_CMD_ARG(get_info, &dsub_device_name, "<device> <led>", cmd_get_info, 3, 0),
354 	SHELL_CMD_ARG(set_brightness, &dsub_device_name, "<device> <led> <value [0-100]>",
355 		      cmd_set_brightness, 4, 0),
356 	SHELL_CMD_ARG(set_color, &dsub_device_name,
357 		      "<device> <led> <color 0 [0-255]> ... <color N>", cmd_set_color, 4,
358 		      MAX_CHANNEL_ARGS - 1),
359 	SHELL_CMD_ARG(set_channel, &dsub_device_name, "<device> <channel> <value [0-255]>",
360 		      cmd_set_channel, 4, 0),
361 	SHELL_CMD_ARG(write_channels, &dsub_device_name,
362 		      "<device> <chan> <value 0 [0-255]> ... <value N>", cmd_write_channels, 4,
363 		      MAX_CHANNEL_ARGS - 1),
364 	SHELL_SUBCMD_SET_END);
365 
366 SHELL_CMD_REGISTER(led, &sub_led, "LED commands", NULL);
367