diff options
| -rw-r--r-- | drivers/leds/led-class.c | 19 | ||||
| -rw-r--r-- | drivers/leds/led-core.c | 18 | ||||
| -rw-r--r-- | drivers/leds/led-triggers.c | 16 | ||||
| -rw-r--r-- | include/linux/leds.h | 32 |
4 files changed, 79 insertions, 6 deletions
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 7440c58b8e6f..65722deb8d28 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
| @@ -40,17 +40,27 @@ static ssize_t brightness_store(struct device *dev, | |||
| 40 | { | 40 | { |
| 41 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 41 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
| 42 | unsigned long state; | 42 | unsigned long state; |
| 43 | ssize_t ret = -EINVAL; | 43 | ssize_t ret; |
| 44 | |||
| 45 | mutex_lock(&led_cdev->led_access); | ||
| 46 | |||
| 47 | if (led_sysfs_is_disabled(led_cdev)) { | ||
| 48 | ret = -EBUSY; | ||
| 49 | goto unlock; | ||
| 50 | } | ||
| 44 | 51 | ||
| 45 | ret = kstrtoul(buf, 10, &state); | 52 | ret = kstrtoul(buf, 10, &state); |
| 46 | if (ret) | 53 | if (ret) |
| 47 | return ret; | 54 | goto unlock; |
| 48 | 55 | ||
| 49 | if (state == LED_OFF) | 56 | if (state == LED_OFF) |
| 50 | led_trigger_remove(led_cdev); | 57 | led_trigger_remove(led_cdev); |
| 51 | __led_set_brightness(led_cdev, state); | 58 | __led_set_brightness(led_cdev, state); |
| 52 | 59 | ||
| 53 | return size; | 60 | ret = size; |
| 61 | unlock: | ||
| 62 | mutex_unlock(&led_cdev->led_access); | ||
| 63 | return ret; | ||
| 54 | } | 64 | } |
| 55 | static DEVICE_ATTR_RW(brightness); | 65 | static DEVICE_ATTR_RW(brightness); |
| 56 | 66 | ||
| @@ -214,6 +224,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | |||
| 214 | #ifdef CONFIG_LEDS_TRIGGERS | 224 | #ifdef CONFIG_LEDS_TRIGGERS |
| 215 | init_rwsem(&led_cdev->trigger_lock); | 225 | init_rwsem(&led_cdev->trigger_lock); |
| 216 | #endif | 226 | #endif |
| 227 | mutex_init(&led_cdev->led_access); | ||
| 217 | /* add to the list of leds */ | 228 | /* add to the list of leds */ |
| 218 | down_write(&leds_list_lock); | 229 | down_write(&leds_list_lock); |
| 219 | list_add_tail(&led_cdev->node, &leds_list); | 230 | list_add_tail(&led_cdev->node, &leds_list); |
| @@ -267,6 +278,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev) | |||
| 267 | down_write(&leds_list_lock); | 278 | down_write(&leds_list_lock); |
| 268 | list_del(&led_cdev->node); | 279 | list_del(&led_cdev->node); |
| 269 | up_write(&leds_list_lock); | 280 | up_write(&leds_list_lock); |
| 281 | |||
| 282 | mutex_destroy(&led_cdev->led_access); | ||
| 270 | } | 283 | } |
| 271 | EXPORT_SYMBOL_GPL(led_classdev_unregister); | 284 | EXPORT_SYMBOL_GPL(led_classdev_unregister); |
| 272 | 285 | ||
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index aaa8eba9099f..be6d9fa5e971 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c | |||
| @@ -143,3 +143,21 @@ int led_update_brightness(struct led_classdev *led_cdev) | |||
| 143 | return ret; | 143 | return ret; |
| 144 | } | 144 | } |
| 145 | EXPORT_SYMBOL(led_update_brightness); | 145 | EXPORT_SYMBOL(led_update_brightness); |
| 146 | |||
| 147 | /* Caller must ensure led_cdev->led_access held */ | ||
| 148 | void led_sysfs_disable(struct led_classdev *led_cdev) | ||
| 149 | { | ||
| 150 | lockdep_assert_held(&led_cdev->led_access); | ||
| 151 | |||
| 152 | led_cdev->flags |= LED_SYSFS_DISABLE; | ||
| 153 | } | ||
| 154 | EXPORT_SYMBOL_GPL(led_sysfs_disable); | ||
| 155 | |||
| 156 | /* Caller must ensure led_cdev->led_access held */ | ||
| 157 | void led_sysfs_enable(struct led_classdev *led_cdev) | ||
| 158 | { | ||
| 159 | lockdep_assert_held(&led_cdev->led_access); | ||
| 160 | |||
| 161 | led_cdev->flags &= ~LED_SYSFS_DISABLE; | ||
| 162 | } | ||
| 163 | EXPORT_SYMBOL_GPL(led_sysfs_enable); | ||
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index c3734f10fdd5..e8b1120f486d 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c | |||
| @@ -37,6 +37,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | |||
| 37 | char trigger_name[TRIG_NAME_MAX]; | 37 | char trigger_name[TRIG_NAME_MAX]; |
| 38 | struct led_trigger *trig; | 38 | struct led_trigger *trig; |
| 39 | size_t len; | 39 | size_t len; |
| 40 | int ret = count; | ||
| 41 | |||
| 42 | mutex_lock(&led_cdev->led_access); | ||
| 43 | |||
| 44 | if (led_sysfs_is_disabled(led_cdev)) { | ||
| 45 | ret = -EBUSY; | ||
| 46 | goto unlock; | ||
| 47 | } | ||
| 40 | 48 | ||
| 41 | trigger_name[sizeof(trigger_name) - 1] = '\0'; | 49 | trigger_name[sizeof(trigger_name) - 1] = '\0'; |
| 42 | strncpy(trigger_name, buf, sizeof(trigger_name) - 1); | 50 | strncpy(trigger_name, buf, sizeof(trigger_name) - 1); |
| @@ -47,7 +55,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | |||
| 47 | 55 | ||
| 48 | if (!strcmp(trigger_name, "none")) { | 56 | if (!strcmp(trigger_name, "none")) { |
| 49 | led_trigger_remove(led_cdev); | 57 | led_trigger_remove(led_cdev); |
| 50 | return count; | 58 | goto unlock; |
| 51 | } | 59 | } |
| 52 | 60 | ||
| 53 | down_read(&triggers_list_lock); | 61 | down_read(&triggers_list_lock); |
| @@ -58,12 +66,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, | |||
| 58 | up_write(&led_cdev->trigger_lock); | 66 | up_write(&led_cdev->trigger_lock); |
| 59 | 67 | ||
| 60 | up_read(&triggers_list_lock); | 68 | up_read(&triggers_list_lock); |
| 61 | return count; | 69 | goto unlock; |
| 62 | } | 70 | } |
| 63 | } | 71 | } |
| 64 | up_read(&triggers_list_lock); | 72 | up_read(&triggers_list_lock); |
| 65 | 73 | ||
| 66 | return -EINVAL; | 74 | unlock: |
| 75 | mutex_unlock(&led_cdev->led_access); | ||
| 76 | return ret; | ||
| 67 | } | 77 | } |
| 68 | EXPORT_SYMBOL_GPL(led_trigger_store); | 78 | EXPORT_SYMBOL_GPL(led_trigger_store); |
| 69 | 79 | ||
diff --git a/include/linux/leds.h b/include/linux/leds.h index a57611d0c94e..737f9b1051f2 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #define __LINUX_LEDS_H_INCLUDED | 13 | #define __LINUX_LEDS_H_INCLUDED |
| 14 | 14 | ||
| 15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
| 16 | #include <linux/mutex.h> | ||
| 16 | #include <linux/rwsem.h> | 17 | #include <linux/rwsem.h> |
| 17 | #include <linux/spinlock.h> | 18 | #include <linux/spinlock.h> |
| 18 | #include <linux/timer.h> | 19 | #include <linux/timer.h> |
| @@ -42,6 +43,7 @@ struct led_classdev { | |||
| 42 | #define LED_BLINK_ONESHOT (1 << 17) | 43 | #define LED_BLINK_ONESHOT (1 << 17) |
| 43 | #define LED_BLINK_ONESHOT_STOP (1 << 18) | 44 | #define LED_BLINK_ONESHOT_STOP (1 << 18) |
| 44 | #define LED_BLINK_INVERT (1 << 19) | 45 | #define LED_BLINK_INVERT (1 << 19) |
| 46 | #define LED_SYSFS_DISABLE (1 << 20) | ||
| 45 | 47 | ||
| 46 | /* Set LED brightness level */ | 48 | /* Set LED brightness level */ |
| 47 | /* Must not sleep, use a workqueue if needed */ | 49 | /* Must not sleep, use a workqueue if needed */ |
| @@ -85,6 +87,9 @@ struct led_classdev { | |||
| 85 | /* true if activated - deactivate routine uses it to do cleanup */ | 87 | /* true if activated - deactivate routine uses it to do cleanup */ |
| 86 | bool activated; | 88 | bool activated; |
| 87 | #endif | 89 | #endif |
| 90 | |||
| 91 | /* Ensures consistent access to the LED Flash Class device */ | ||
| 92 | struct mutex led_access; | ||
| 88 | }; | 93 | }; |
| 89 | 94 | ||
| 90 | extern int led_classdev_register(struct device *parent, | 95 | extern int led_classdev_register(struct device *parent, |
| @@ -151,6 +156,33 @@ extern void led_set_brightness(struct led_classdev *led_cdev, | |||
| 151 | */ | 156 | */ |
| 152 | extern int led_update_brightness(struct led_classdev *led_cdev); | 157 | extern int led_update_brightness(struct led_classdev *led_cdev); |
| 153 | 158 | ||
| 159 | /** | ||
| 160 | * led_sysfs_disable - disable LED sysfs interface | ||
| 161 | * @led_cdev: the LED to set | ||
| 162 | * | ||
| 163 | * Disable the led_cdev's sysfs interface. | ||
| 164 | */ | ||
| 165 | extern void led_sysfs_disable(struct led_classdev *led_cdev); | ||
| 166 | |||
| 167 | /** | ||
| 168 | * led_sysfs_enable - enable LED sysfs interface | ||
| 169 | * @led_cdev: the LED to set | ||
| 170 | * | ||
| 171 | * Enable the led_cdev's sysfs interface. | ||
| 172 | */ | ||
| 173 | extern void led_sysfs_enable(struct led_classdev *led_cdev); | ||
| 174 | |||
| 175 | /** | ||
| 176 | * led_sysfs_is_disabled - check if LED sysfs interface is disabled | ||
| 177 | * @led_cdev: the LED to query | ||
| 178 | * | ||
| 179 | * Returns: true if the led_cdev's sysfs interface is disabled. | ||
| 180 | */ | ||
| 181 | static inline bool led_sysfs_is_disabled(struct led_classdev *led_cdev) | ||
| 182 | { | ||
| 183 | return led_cdev->flags & LED_SYSFS_DISABLE; | ||
| 184 | } | ||
| 185 | |||
| 154 | /* | 186 | /* |
| 155 | * LED Triggers | 187 | * LED Triggers |
| 156 | */ | 188 | */ |
