diff options
author | Jacek Anaszewski <j.anaszewski@samsung.com> | 2014-09-22 11:21:04 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2014-11-14 17:29:35 -0500 |
commit | acd899e4f3066b6662f6047da5b795cc762093cb (patch) | |
tree | 8a0cdf6849c1aec46d741ddee463067951a942ed | |
parent | 3f6e42c808409c40dd0d0f8fe2022d197b27455e (diff) |
leds: implement sysfs interface locking mechanism
Add a mechanism for locking LED subsystem sysfs interface.
This patch prepares ground for addition of LED Flash Class
extension, whose API will be integrated with V4L2 Flash API.
Such a fusion enforces introducing a locking scheme, which
will secure consistent access to the LED Flash Class device.
The mechanism being introduced allows for disabling LED
subsystem sysfs interface by calling led_sysfs_disable function
and enabling it by calling led_sysfs_enable. The functions
alter the LED_SYSFS_DISABLE flag state and must be called
under mutex lock. The state of the lock is checked with use
of led_sysfs_is_disabled function. Such a design allows for
providing immediate feedback to the user space on whether
the LED Flash Class device is available or is under V4L2 Flash
sub-device control.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
-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 | */ |