aboutsummaryrefslogtreecommitdiff
path: root/drivers/leds/led-class-3g.c
diff options
context:
space:
mode:
authorDavid Barksdale <amatus@amatus.name>2014-08-13 18:32:56 -0500
committerDavid Barksdale <amatus@amatus.name>2014-08-13 18:32:56 -0500
commitcddfc3baae08e24185c7716434452ebbd404cd39 (patch)
tree01e1f933fd8724bf2ad34db47730c98feefc0a7c /drivers/leds/led-class-3g.c
parent3918522a523adc3f001cf9c5da327d8fb383c26e (diff)
Diffstat (limited to 'drivers/leds/led-class-3g.c')
-rw-r--r--drivers/leds/led-class-3g.c445
1 files changed, 341 insertions, 104 deletions
diff --git a/drivers/leds/led-class-3g.c b/drivers/leds/led-class-3g.c
index c0c1c11cbdb..7b8e0fa45ed 100644
--- a/drivers/leds/led-class-3g.c
+++ b/drivers/leds/led-class-3g.c
@@ -20,59 +20,296 @@
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/leds.h>
+#include <linux/errno.h>
#include "leds.h"
+#define MAX_USERS 32
+#define N_COLORS 4
+#define N_EVENTS 3
+#define USR_LEN 81
+#define EVENT_LEN 81
+#define INDEX_LEN 8
+
+
+/* LED users */
+#define EV_NAS_SYSTEM 0 /* Overall system: NAS ready, booting, shutdown... */
+#define EV_DISK_SMART 1 /* Disk SMART including temp., error lba, ...*/
+#define EV_DISK_IO 2 /* Disk read/write error */
+#define EV_RAID_CFG 3 /* RAID setup failure: assembling, formatting, rebuild ...*/
+#define EV_FW_UPDATE 4 /* NAS firmware update */
+#define EV_NETWORK 5 /* Network connectivity error */
+#define EV_VM 6 /* Volume manager */
+
+char Led_user_arr[MAX_USERS][USR_LEN] = { "EV_NAS_SYSTEM", \
+ "EV_DISK_SMART", \
+ "EV_DISK_IO" , \
+ "EV_RAID_CFG" , \
+ "EV_FW_UPDATE" , \
+ "EV_NETWORK" , \
+ "EV_VM", \
+ };
+/* LED event types */
+#define LED_STAT_OK 0 /* Happy user, normal operation */
+#define LED_STAT_ERR 1 /* User error, needs led indication */
+#define LED_STAT_IN_PROG 2 /* User doing something important, needs led indication */
+
+char *Led_ev_arr[] = { "LED_STAT_OK", "LED_STAT_ERR", "LED_STAT_IN_PROG" };
+
+char Color_map[MAX_USERS][N_EVENTS] = { {'g','r','w'}, /* EV_NAS_SYSTEM */ \
+ {'g','y','w'}, /* EV_DISK_SMART */ \
+ {'g','r','w'}, /* EV_DISK_IO */ \
+ {'g','r','w'}, /* EV_RAID_CFG */ \
+ {'g','r','w'}, /* EV_FW_UPDATE */ \
+ {'g','y','w'}, /* EV_NETWORK */ \
+ {'g','r','w'}, /* EV_VM */ \
+ };
+
+char Blink_map[MAX_USERS][N_EVENTS] = { {'n','n','n'}, /* EV_NAS_SYSTEM */ \
+ {'n','y','n'}, /* EV_DISK_SMART */ \
+ {'n','n','n'}, /* EV_DISK_IO */ \
+ {'n','n','n'}, /* EV_RAID_CFG */ \
+ {'n','n','n'}, /* EV_FW_UPDATE */ \
+ {'n','y','n'}, /* EV_NETWORK */ \
+ {'n','n','n'}, /* EV_VM */ \
+ };
+
+u32 Led_error_bits = 0;
+int N_USERS = 7; /* default number of users */
+
static struct class *leds_class;
static void led_update_color(struct led_classdev *led_cdev)
{
- if (led_cdev->color_get)
- led_cdev->color = led_cdev->color_get(led_cdev);
+ if (led_cdev->color_get)
+ led_cdev->color = led_cdev->color_get(led_cdev);
}
static ssize_t led_color_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+ struct device_attribute *attr, const char *buf, size_t size)
{
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- unsigned long state = 9;
-
- switch (buf[0]) {
- case 'r': /* red */
- state = 1;
- break;
- case 'g': /* green */
- state = 2;
- break;
- case 'b': /* blue */
- state = 3;
- break;
- case 'y': /* yellow */
- state = 4;
- break;
- case 'w': /* white */
- state = 5;
- break;
- case 'o': /* off */
- state = 0;
- break;
- default:
- break;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ unsigned long state = 9;
+ char user[USR_LEN], event[EVENT_LEN], index_str[INDEX_LEN], color;
+ int i = 0, j = 0, found = 0, tmp = 0, edit_policy = 0;
+ int reg_user = -1, reg_event = -1, reg_color = -1;
+ const char * cptr = NULL;
+ long int index = -1;
+ char blink;
+ int reg_blink = 'n';
+
+ cptr = &buf[0];
+
+ /* check for 'register' event */
+ // NB: Format of register event is
+ // register:event,status,color
+ if( cptr[8] == ':' ) {
+ if( !memcmp("register", cptr, 8) ) {
+ edit_policy = 1;
+ cptr = &buf[9];
+ }
+ }
+
+ /* parse user name */
+ for( i = 0; i < (USR_LEN -1) && cptr[i]; i++ ) {
+ if( cptr[i] == ',' ) {
+ break;
+ }
+ user[i] = cptr[i];
+ }
+
+ /* null terminate user buf */
+ user[i] = '\0';
+ i++; /* skips the ',' delimiter */
+
+
+ for( j = 0; (j < EVENT_LEN -1) && cptr[i] ; j++,i++ ) {
+ if( (cptr[i] == ',') || (cptr[i] == '\0') || (cptr[i] == '\n') ) {
+ if( cptr[i] == ',' ) {
+ cptr = &cptr[i+1];
+ }
+ break;
+ }
+ event[j] = cptr[i];
+ }
+ /* null terminate event buf */
+ event[j] = '\0';
+
+ /* if editing policy, parse the color */
+ if( edit_policy ) {
+ if( cptr != NULL ) {
+ reg_color = cptr[0]; /* r,g,b,y,w */
+ if( reg_color != 'r' && reg_color != 'g' &&
+ reg_color != 'b' && reg_color != 'y' && reg_color != 'w' ) {
+ reg_color = -1; /* invalid color */
+ }
+
+ /** TBD: Get the value of reg_blink from cptr */
+ }
+ }
+ else {
+ /* scan index for some users */
+ if( !strcmp(user, Led_user_arr[EV_DISK_SMART]) ||
+ !strcmp(user, Led_user_arr[EV_DISK_IO]) ) {
+ if( cptr != NULL ) {
+ for( i = 0; (i < INDEX_LEN -1) && cptr[i] ; i++ ) {
+ if( (cptr[i] == ',') || (cptr[i] == '\0') || (cptr[i] == '\n') ) {
+ break;
+ }
+ index_str[i] = cptr[i];
+ }
+ }
+ }
+
+ /* null terminate index_str */
+ index_str[i] = '\0';
+ if( i ) {
+ tmp = strict_strtol(index_str, 10, &index);
+ if( !tmp && (index >= 0) ) {
+ /*
+ * TODO: insert code to fulfill req's. Currently not required.
+ */
+ /*printk(KERN_INFO "\nindex %ld\n", index);*/
+ }
+ }
+ } /* if( !edit_policy ) */
+
+ /* Validate user and event */
+ found = 0;
+ for( i = 0; i < N_USERS; i++ ) {
+ if( !strcmp( Led_user_arr[i], user ) ) {
+ found = 1;
+ break;
}
+ }
- led_set_color(led_cdev, state);
+ if( found || edit_policy) {
+ reg_user = i;
+ /* new user registration */
+ if( ! found ) {
+ if( N_USERS == MAX_USERS ) {
+ /* only support up to 32 users */
+ return (ssize_t)size;
+ }
+ reg_user = N_USERS++;
+
+ strcpy(Led_user_arr[reg_user], user);
+ }
+ found = 0;
+ for( j = 0; j < N_EVENTS; j++ ) {
+ if( ! strcmp(Led_ev_arr[j], event) ) {
+ if( j == LED_STAT_ERR ) {
+ Led_error_bits |= (1 << i); /* register error for this user */
+ }
+ else if( j == LED_STAT_OK ) {
+ Led_error_bits &= ~(1 << i); /* clear error for this user */
+ }
+ found = 1;
+ reg_event = j;
+ break;
+ }
+ }
+ }
+
+ /* if this is a register event, do just that */
+ if( edit_policy ) {
+ /* valid event above and color */
+ if( (reg_event != -1) && (reg_color != -1) ) {
+ Color_map[reg_user][reg_event] = reg_color;
+
+ /** TBD: Add support for registering blink with register: interface*/
+ reg_blink = 'n';
+ Blink_map[reg_user][reg_event] = reg_blink;
+ }
+ /*printk( KERN_INFO "reg_user = %d, reg_event= %d, reg_color = %c\n", reg_user, reg_event, reg_color, reg_blink);*/
+ return (ssize_t)size;
+ }
+
+ /* Be nice ! support older led mechanism */
+ color = buf[0];
+ blink = 'x';
+
+ /* If valid user and event, retrieve color & blink map */
+ if( found ) {
+ /* if a canceling event and other error(s) existing, don't do anything */
+ if( (j == LED_STAT_OK) && (Led_error_bits != 0) ) {
+ }
+ else {
+ color = Color_map[i][j];
+ blink = Blink_map[i][j];
+ }
+ /*printk(KERN_INFO "\nUser= %s, event= %s, color %c, %08x\n", user, event, color, blink, Led_error_bits);*/
+ }
+
+ switch (color) {
+ case 'r': /* red */
+ state = 1;
+ break;
+ case 'g': /* green */
+ state = 2;
+ break;
+ case 'b': /* blue */
+ state = 3;
+ break;
+ case 'y': /* yellow */
+ state = 4;
+ break;
+ case 'w': /* white */
+ state = 5;
+ break;
+ case 'o': /* off */
+ state = 0;
+ break;
+ default:
+ state = -1;
+ break;
+ }
+
+ /** do nothing if no color change is required */
+ if( state == -1 ) {
+ return (ssize_t)size;
+ }
+
+ // printk(KERN_DEBUG "Calling led_set_color with value %c, blink is %c\n", color, blink);
+ led_set_color( led_cdev, state );
+
+ /** blink the led */
+ {
+ int val = -1;
+
+ printk(KERN_DEBUG "Calling led_set_blink with value %c\n", blink);
+
+ switch( blink ) {
+ case 'y': /** yes */
+ val = 1;
+ break;
+ case 'n': /** no */
+ val = 0;
+ break;
+ case 'f': /** forced */
+ val = 2;
+ break;
+ default:
+ break;
+ }
+
+ if( val >= 0 )
+ {
+ led_set_blink( led_cdev, val );
+ }
+ }
- return (ssize_t)size;
+ return (ssize_t)size;
}
static ssize_t led_color_show(struct device *dev,
- struct device_attribute *attr, char *buf) {
+ struct device_attribute *attr, char *buf) {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
char * readbuf[] = {"off", "red", "green", "blue", "yellow", "white"} ;
- /* no lock needed for this */
- led_update_color(led_cdev);
+ /* no lock needed for this */
+ led_update_color(led_cdev);
- return sprintf(buf, "%s\n", readbuf[led_cdev->color]);
+ return sprintf(buf, "%s\n", readbuf[led_cdev->color]);
}
static ssize_t led_blink_show(struct device *dev, struct device_attribute *attr,
@@ -86,11 +323,11 @@ static ssize_t led_blink_show(struct device *dev, struct device_attribute *attr,
else if (led_cdev->blink == 1 ){
blinkStr = "yes";
}
- return sprintf(buf, "%s\n", blinkStr);
+ return sprintf(buf, "%s\n", blinkStr);
}
static ssize_t led_blink_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size) {
+ struct device_attribute *attr, const char *buf, size_t size) {
int val = 0;
struct led_classdev * led_cdev = dev_get_drvdata(dev);
@@ -109,11 +346,11 @@ static ssize_t led_blink_store(struct device *dev,
}
static ssize_t led_max_brightness_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
- return sprintf(buf, "%u\n", led_cdev->max_brightness);
+ return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
/*static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);*/
@@ -130,8 +367,8 @@ static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
*/
void led_classdev_suspend(struct led_classdev *led_cdev)
{
- led_cdev->flags |= LED_SUSPENDED;
- led_cdev->color_set(led_cdev, 0);
+ led_cdev->flags |= LED_SUSPENDED;
+ led_cdev->color_set(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);
@@ -141,29 +378,29 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
*/
void led_classdev_resume(struct led_classdev *led_cdev)
{
- led_cdev->color_set(led_cdev, led_cdev->color);
- led_cdev->flags &= ~LED_SUSPENDED;
+ led_cdev->color_set(led_cdev, led_cdev->color);
+ led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);
static int led_suspend(struct device *dev, pm_message_t state)
{
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
- if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
- led_classdev_suspend(led_cdev);
+ if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
+ led_classdev_suspend(led_cdev);
- return 0;
+ return 0;
}
static int led_resume(struct device *dev)
{
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
- if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
- led_classdev_resume(led_cdev);
+ if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
+ led_classdev_resume(led_cdev);
- return 0;
+ return 0;
}
/**
@@ -173,62 +410,62 @@ static int led_resume(struct device *dev)
*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
- int rc;
+ int rc;
- led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
- "%s", led_cdev->name);
- if (IS_ERR(led_cdev->dev))
- return PTR_ERR(led_cdev->dev);
+ led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
+ "%s", led_cdev->name);
+ if (IS_ERR(led_cdev->dev))
+ return PTR_ERR(led_cdev->dev);
- /* register the attributes */
- rc = device_create_file(led_cdev->dev, &dev_attr_color);
- if (rc)
- goto err_out;
+ /* register the attributes */
+ rc = device_create_file(led_cdev->dev, &dev_attr_color);
+ if (rc)
+ goto err_out;
- rc = device_create_file(led_cdev->dev, &dev_attr_blink);
- if (rc)
- goto err_out;
+ rc = device_create_file(led_cdev->dev, &dev_attr_blink);
+ if (rc)
+ goto err_out;
#ifdef CONFIG_LEDS_TRIGGERS
- init_rwsem(&led_cdev->trigger_lock);
+ init_rwsem(&led_cdev->trigger_lock);
#endif
- /* add to the list of leds */
- down_write(&leds_list_lock);
- list_add_tail(&led_cdev->node, &leds_list);
- up_write(&leds_list_lock);
+ /* add to the list of leds */
+ down_write(&leds_list_lock);
+ list_add_tail(&led_cdev->node, &leds_list);
+ up_write(&leds_list_lock);
- if (!led_cdev->max_brightness)
- led_cdev->max_brightness = LED_FULL;
+ if (!led_cdev->max_brightness)
+ led_cdev->max_brightness = LED_FULL;
- rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
- if (rc)
- goto err_out_attr_max;
+ rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
+ if (rc)
+ goto err_out_attr_max;
- led_update_color(led_cdev);
+ led_update_color(led_cdev);
#ifdef CONFIG_LEDS_TRIGGERS
- rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
- if (rc)
- goto err_out_led_list;
+ rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
+ if (rc)
+ goto err_out_led_list;
- led_trigger_set_default(led_cdev);
+ led_trigger_set_default(led_cdev);
#endif
- printk(KERN_INFO "Registered led device: %s\n",
- led_cdev->name);
+ printk(KERN_INFO "Registered led device: %s\n",
+ led_cdev->name);
- return 0;
+ return 0;
#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
- device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
+ device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
#endif
err_out_attr_max:
- device_remove_file(led_cdev->dev, &dev_attr_color);
- list_del(&led_cdev->node);
+ device_remove_file(led_cdev->dev, &dev_attr_color);
+ list_del(&led_cdev->node);
err_out:
- device_unregister(led_cdev->dev);
- return rc;
+ device_unregister(led_cdev->dev);
+ return rc;
}
EXPORT_SYMBOL_GPL(led_classdev_register);
@@ -240,37 +477,37 @@ EXPORT_SYMBOL_GPL(led_classdev_register);
*/
void led_classdev_unregister(struct led_classdev *led_cdev)
{
- device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
- device_remove_file(led_cdev->dev, &dev_attr_color);
+ device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
+ device_remove_file(led_cdev->dev, &dev_attr_color);
#ifdef CONFIG_LEDS_TRIGGERS
- device_remove_file(led_cdev->dev, &dev_attr_trigger);
- down_write(&led_cdev->trigger_lock);
- if (led_cdev->trigger)
- led_trigger_set(led_cdev, NULL);
- up_write(&led_cdev->trigger_lock);
+ device_remove_file(led_cdev->dev, &dev_attr_trigger);
+ down_write(&led_cdev->trigger_lock);
+ if (led_cdev->trigger)
+ led_trigger_set(led_cdev, NULL);
+ up_write(&led_cdev->trigger_lock);
#endif
- device_unregister(led_cdev->dev);
+ device_unregister(led_cdev->dev);
- down_write(&leds_list_lock);
- list_del(&led_cdev->node);
- up_write(&leds_list_lock);
+ down_write(&leds_list_lock);
+ list_del(&led_cdev->node);
+ up_write(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);
static int __init leds_init(void)
{
- leds_class = class_create(THIS_MODULE, "leds");
- if (IS_ERR(leds_class))
- return PTR_ERR(leds_class);
- leds_class->suspend = led_suspend;
- leds_class->resume = led_resume;
- return 0;
+ leds_class = class_create(THIS_MODULE, "leds");
+ if (IS_ERR(leds_class))
+ return PTR_ERR(leds_class);
+ leds_class->suspend = led_suspend;
+ leds_class->resume = led_resume;
+ return 0;
}
static void __exit leds_exit(void)
{
- class_destroy(leds_class);
+ class_destroy(leds_class);
}
subsys_initcall(leds_init);