[U-Boot] [PATCH15/23] Cmd: LED: rewrite to prepare non-static access

Benjamin Tietz uboot at dresden.micronet24.de
Sat Jun 18 14:56:42 CEST 2016


From: Benjamin Tietz <benjamin at micronet24.de>

Previously, all knwon LED were hold in a single array and computed at compile-time.
With the new variant, if all LEDs are known, these will also be computed at compile-time.
Using functions will allow additional dynamic (eg. DM-based) LED allocation.

Apart from that, the led-command becomes indipendent from the STATUS_LED setting.
---
 cmd/led.c |  337 ++++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 230 insertions(+), 107 deletions(-)
-------------- next part --------------
From: Benjamin Tietz <benjamin at micronet24.de>

Previously, all knwon LED were hold in a single array and computed at compile-time.
With the new variant, if all LEDs are known, these will also be computed at compile-time.
Using functions will allow additional dynamic (eg. DM-based) LED allocation.

Apart from that, the led-command becomes indipendent from the STATUS_LED setting.
---
 cmd/led.c |  337 ++++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 230 insertions(+), 107 deletions(-)

diff --git a/cmd/led.c b/cmd/led.c
index b0f1a61..e020b92 100644
--- a/cmd/led.c
+++ b/cmd/led.c
@@ -15,53 +15,241 @@
 #include <command.h>
 #include <status_led.h>
 
+enum led_cmd { LED_ON, LED_OFF, LED_TOGGLE, LED_BLINK, LED_LIST, LED_MAX_OP };
+
+struct led_tbl_s;
+
+typedef void (* led_op)(const struct led_tbl_s *, enum led_cmd, char *arg);
+
 struct led_tbl_s {
-	char		*string;	/* String for use in the command */
+	const char	*string;	/* String for use in the command */
 	led_id_t	mask;		/* Mask used for calling __led_set() */
-	void		(*off)(void);	/* Optional function for turning LED off */
-	void		(*on)(void);	/* Optional function for turning LED on */
-	void		(*toggle)(void);/* Optional function for toggling LED */
+	led_op op[LED_MAX_OP];		/* functions for handling LED commands */
 };
 
 typedef struct led_tbl_s led_tbl_t;
 
-static const led_tbl_t led_commands[] = {
+static int call_led(const char *name, enum led_cmd cmd, char *arg);
+
+static void _led_list_name(const led_tbl_t *led, enum led_cmd cmd, char *arg) {
+	printf("%s\n", led->string);
+}
+
+#define LED_MASK_FUNC(fnc)	((led_op) fnc)
+#define LED_TEST_RET(name, ret)	if(strcmp(name, ret.string) == 0) return &ret;
+#define LED_TBL_COLOURED(clr)	static const led_tbl_t _led_##clr = { \
+	.string = #clr, \
+	.op = { \
+		[LED_ON] = LED_MASK_FUNC(clr ## _led_on), \
+		[LED_OFF] = LED_MASK_FUNC(clr ## _led_off), \
+		[LED_LIST] = _led_list_name, \
+	}, \
+}
+
+#ifdef STATUS_LED_GREEN
+LED_TBL_COLOURED(green);
+#define LED_NAME_GREEN		_led_green.string
+#define LED_TEST_GREEN(name)	LED_TEST_RET(name, _led_green)
+#else
+#define LED_NAME_GREEN		NULL
+#define LED_TEST_GREEN(name)	while(0)
+#endif
+
+#ifdef STATUS_LED_YELLOW
+LED_TBL_COLOURED(yellow);
+#define LED_NAME_YELLOW		_led_yellow.string
+#define LED_TEST_YELLOW(name)	LED_TEST_RET(name, _led_yellow)
+#else
+#define LED_NAME_YELLOW		NULL
+#define LED_TEST_YELLOW(name)	while(0)
+#endif
+
+#ifdef STATUS_LED_RED
+LED_TBL_COLOURED(red);
+#define LED_NAME_RED		_led_red.string
+#define LED_TEST_RED(name)	LED_TEST_RET(name, _led_red)
+#else
+#define LED_NAME_RED		NULL
+#define LED_TEST_RED(name)	while(0)
+#endif
+
+#ifdef STATUS_LED_BLUE
+LED_TBL_COLOURED(blue);
+#define LED_NAME_BLUE		_led_blue.string
+#define LED_TEST_BLUE(name)	LED_TEST_RET(name, _led_blue)
+#else
+#define LED_NAME_BLUE		NULL
+#define LED_TEST_BLUE(name)	while(0)
+#endif
+
 #ifdef CONFIG_BOARD_SPECIFIC_LED
+/*
+ * LED drivers providing a blinking LED functionality, like the
+ * PCA9551, can override this empty weak function
+ */
+void __weak __led_blink(led_id_t mask, int freq)
+{
+}
+
+static void _led_status_onoff(const led_tbl_t *led, enum led_cmd cmd, char *arg) {
+	__led_set(led->mask, cmd != LED_ON ? STATUS_LED_OFF : STATUS_LED_ON);
+}
+static void _led_status_toggle(const led_tbl_t *led, enum led_cmd cmd, char *arg) {
+	__led_toggle(led->mask);
+}
+static void _led_status_blink(const led_tbl_t *led, enum led_cmd cmd, char *freq) {
+	if (!freq)
+		return;
+
+	__led_blink(led->mask, simple_strtoul(freq, NULL, 10));
+}
+
+#define LED_TBL_STATUS(name, _mask)	static const led_tbl_t _led_##name = { \
+	.string = #name, \
+	.mask = _mask, \
+	.op = { \
+		[LED_ON] = _led_status_onoff, \
+		[LED_OFF] = _led_status_onoff, \
+		[LED_TOGGLE] = _led_status_toggle, \
+		[LED_BLINK] = _led_status_blink, \
+		[LED_LIST] = _led_list_name, \
+	}, \
+}
+
 #ifdef STATUS_LED_BIT
-	{ "0", STATUS_LED_BIT, NULL, NULL, NULL },
+LED_TBL_STATUS(0, STATUS_LED_BIT);
+#define LED_NAME_0		_led_0.string
+#define LED_TEST_0(name)	LED_TEST_RET(name, _led_0)
 #endif
+
 #ifdef STATUS_LED_BIT1
-	{ "1", STATUS_LED_BIT1, NULL, NULL, NULL },
+LED_TBL_STATUS(1, STATUS_LED_BIT1);
+#define LED_NAME_1		_led_1.string
+#define LED_TEST_1(name)	LED_TEST_RET(name, _led_1)
 #endif
+
 #ifdef STATUS_LED_BIT2
-	{ "2", STATUS_LED_BIT2, NULL, NULL, NULL },
+LED_TBL_STATUS(2, STATUS_LED_BIT2);
+#define LED_NAME_2		_led_2.string
+#define LED_TEST_2(name)	LED_TEST_RET(name, _led_2)
 #endif
+
 #ifdef STATUS_LED_BIT3
-	{ "3", STATUS_LED_BIT3, NULL, NULL, NULL },
+LED_TBL_STATUS(3, STATUS_LED_BIT3);
+#define LED_NAME_3		_led_3.string
+#define LED_TEST_3(name)	LED_TEST_RET(name, _led_3)
 #endif
+
 #ifdef STATUS_LED_BIT4
-	{ "4", STATUS_LED_BIT4, NULL, NULL, NULL },
+LED_TBL_STATUS(4, STATUS_LED_BIT4);
+#define LED_NAME_4		_led_4.string
+#define LED_TEST_4(name)	LED_TEST_RET(name, _led_4)
 #endif
+
 #ifdef STATUS_LED_BIT5
-	{ "5", STATUS_LED_BIT5, NULL, NULL, NULL },
+LED_TBL_STATUS(5, STATUS_LED_BIT5);
+#define LED_NAME_5		_led_5.string
+#define LED_TEST_5(name)	LED_TEST_RET(name, _led_5)
 #endif
+#endif  /* CONFIG_BOARD_SPECIFIC_LED */
+
+#ifndef LED_NAME_0
+#define LED_NAME_0		NULL
+#define LED_TEST_0(name)	while(0)
 #endif
-#ifdef STATUS_LED_GREEN
-	{ "green", STATUS_LED_GREEN, green_led_off, green_led_on, NULL },
+
+#ifndef LED_NAME_1
+#define LED_NAME_1		NULL
+#define LED_TEST_1(name)	while(0)
 #endif
-#ifdef STATUS_LED_YELLOW
-	{ "yellow", STATUS_LED_YELLOW, yellow_led_off, yellow_led_on, NULL },
+
+#ifndef LED_NAME_2
+#define LED_NAME_2		NULL
+#define LED_TEST_2(name)	while(0)
 #endif
-#ifdef STATUS_LED_RED
-	{ "red", STATUS_LED_RED, red_led_off, red_led_on, NULL },
+
+#ifndef LED_NAME_3
+#define LED_NAME_3		NULL
+#define LED_TEST_3(name)	while(0)
 #endif
-#ifdef STATUS_LED_BLUE
-	{ "blue", STATUS_LED_BLUE, blue_led_off, blue_led_on, NULL },
+
+#ifndef LED_NAME_4
+#define LED_NAME_4		NULL
+#define LED_TEST_4(name)	while(0)
 #endif
-	{ NULL, 0, NULL, NULL, NULL }
+
+#ifndef LED_NAME_5
+#define LED_NAME_5		NULL
+#define LED_TEST_5(name)	while(0)
+#endif
+
+static int _led_count(void) {
+	int i = 0;
+	if(LED_NAME_GREEN) i++;
+	if(LED_NAME_RED) i++;
+	if(LED_NAME_BLUE) i++;
+	if(LED_NAME_YELLOW) i++;
+	if(LED_NAME_0) i++;
+	if(LED_NAME_1) i++;
+	if(LED_NAME_2) i++;
+	if(LED_NAME_3) i++;
+	if(LED_NAME_4) i++;
+	if(LED_NAME_5) i++;
+	return i;
+}
+
+#define TEST_ADD_NAME(name, tbl, size) do { if((name) && ((size) > 0)) { *tbl++ = name; size--; } } while(0)
+static int _led_list(const char **tbl, int size) {
+	int init_size = size;
+	TEST_ADD_NAME(LED_NAME_GREEN, tbl, size);
+	TEST_ADD_NAME(LED_NAME_RED, tbl, size);
+	TEST_ADD_NAME(LED_NAME_BLUE, tbl, size);
+	TEST_ADD_NAME(LED_NAME_YELLOW, tbl, size);
+	TEST_ADD_NAME(LED_NAME_0, tbl, size);
+	TEST_ADD_NAME(LED_NAME_1, tbl, size);
+	TEST_ADD_NAME(LED_NAME_2, tbl, size);
+	TEST_ADD_NAME(LED_NAME_3, tbl, size);
+	TEST_ADD_NAME(LED_NAME_4, tbl, size);
+	TEST_ADD_NAME(LED_NAME_5, tbl, size);
+	return init_size - size;
+}
+
+static void _led_all_cmd(const led_tbl_t *led, enum led_cmd cmd, char *arg) {
+	int cnt = _led_count();
+	const char *leds[cnt];
+	int i;
+
+	_led_list(leds, cnt);
+	for(i=0; i < cnt; i++)
+		call_led(leds[i], cmd, arg);
+}
+
+static const led_tbl_t _led_all = {
+	.string = "all",
+	.op = {
+		[LED_ON] = _led_all_cmd,
+		[LED_OFF] = _led_all_cmd,
+		[LED_TOGGLE] = _led_all_cmd,
+		[LED_BLINK] = _led_all_cmd,
+		[LED_LIST] = _led_all_cmd,
+	},
 };
+#define LED_TEST_ALL(name)	LED_TEST_RET(name, _led_all)
 
-enum led_cmd { LED_ON, LED_OFF, LED_TOGGLE, LED_BLINK };
+static const led_tbl_t *get_led(const char *name) {
+	LED_TEST_ALL(name);
+	LED_TEST_GREEN(name);
+	LED_TEST_YELLOW(name);
+	LED_TEST_RED(name);
+	LED_TEST_BLUE(name);
+	LED_TEST_0(name);
+	LED_TEST_1(name);
+	LED_TEST_2(name);
+	LED_TEST_3(name);
+	LED_TEST_4(name);
+	LED_TEST_5(name);
+	return NULL;
+}
 
 enum led_cmd get_led_cmd(char *var)
 {
@@ -73,23 +261,29 @@ enum led_cmd get_led_cmd(char *var)
 		return LED_TOGGLE;
 	if (strcmp(var, "blink") == 0)
 		return LED_BLINK;
+	if (strcmp(var, "list") == 0)
+		return LED_LIST;
 
 	return -1;
 }
 
-/*
- * LED drivers providing a blinking LED functionality, like the
- * PCA9551, can override this empty weak function
- */
-void __weak __led_blink(led_id_t mask, int freq)
+static int call_led(const char *name, enum led_cmd cmd, char *arg)
 {
+	const led_tbl_t *led = get_led(name);
+	if(!led)
+		return CMD_RET_USAGE;
+
+	if(cmd >= LED_MAX_OP)
+		return CMD_RET_USAGE;
+	if(led->op[cmd])
+		led->op[cmd](led, cmd, arg);
+	return 0;
+
 }
 
 int do_led (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-	int i, match = 0;
 	enum led_cmd cmd;
-	int freq;
 
 	/* Validate arguments */
 	if ((argc < 3) || (argc > 4))
@@ -100,87 +294,16 @@ int do_led (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		return CMD_RET_USAGE;
 	}
 
-	for (i = 0; led_commands[i].string; i++) {
-		if ((strcmp("all", argv[1]) == 0) ||
-		    (strcmp(led_commands[i].string, argv[1]) == 0)) {
-			match = 1;
-			switch (cmd) {
-			case LED_ON:
-				if (led_commands[i].on)
-					led_commands[i].on();
-				else
-					__led_set(led_commands[i].mask,
-							  STATUS_LED_ON);
-				break;
-			case LED_OFF:
-				if (led_commands[i].off)
-					led_commands[i].off();
-				else
-					__led_set(led_commands[i].mask,
-							  STATUS_LED_OFF);
-				break;
-			case LED_TOGGLE:
-				if (led_commands[i].toggle)
-					led_commands[i].toggle();
-				else
-					__led_toggle(led_commands[i].mask);
-				break;
-			case LED_BLINK:
-				if (argc != 4)
-					return CMD_RET_USAGE;
-
-				freq = simple_strtoul(argv[3], NULL, 10);
-				__led_blink(led_commands[i].mask, freq);
-			}
-			/* Need to set only 1 led if led_name wasn't 'all' */
-			if (strcmp("all", argv[1]) != 0)
-				break;
-		}
-	}
-
-	/* If we ran out of matches, print Usage */
-	if (!match) {
-		return CMD_RET_USAGE;
-	}
-
-	return 0;
+	return call_led(argv[1], cmd, argc != 4 ? NULL : argv[3]);
 }
 
 U_BOOT_CMD(
 	led, 4, 1, do_led,
-	"["
-#ifdef CONFIG_BOARD_SPECIFIC_LED
-#ifdef STATUS_LED_BIT
-	"0|"
-#endif
-#ifdef STATUS_LED_BIT1
-	"1|"
-#endif
-#ifdef STATUS_LED_BIT2
-	"2|"
-#endif
-#ifdef STATUS_LED_BIT3
-	"3|"
-#endif
-#ifdef STATUS_LED_BIT4
-	"4|"
-#endif
-#ifdef STATUS_LED_BIT5
-	"5|"
-#endif
-#endif
-#ifdef STATUS_LED_GREEN
-	"green|"
-#endif
-#ifdef STATUS_LED_YELLOW
-	"yellow|"
-#endif
-#ifdef STATUS_LED_RED
-	"red|"
-#endif
-#ifdef STATUS_LED_BLUE
-	"blue|"
-#endif
-	"all] [on|off|toggle|blink] [blink-freq in ms]",
-	"[led_name] [on|off|toggle|blink] sets or clears led(s)"
+	"[led_name|all] [on|off|toggle|blink|list] [blink-freq in ms]",
+	"if 'all' is given as led-name, all known leds will be affected by the command\n"
+	"[on] sets an led\n"
+	"[off] clears an led\n"
+	"[toggle] inverts an led\n"
+	"[blink freq] let an led blink, if supported by the controller\n"
+	"[list] shows the name of known led\n"
 );


More information about the U-Boot mailing list