diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-class-led | 17 | ||||
| -rw-r--r-- | Documentation/leds/leds-class.txt | 15 | ||||
| -rw-r--r-- | drivers/leds/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/leds/led-class.c | 76 | ||||
| -rw-r--r-- | include/linux/leds.h | 15 |
5 files changed, 132 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led index 491cdeedc195..5f67f7ab277b 100644 --- a/Documentation/ABI/testing/sysfs-class-led +++ b/Documentation/ABI/testing/sysfs-class-led | |||
| @@ -23,6 +23,23 @@ Description: | |||
| 23 | If the LED does not support different brightness levels, this | 23 | If the LED does not support different brightness levels, this |
| 24 | should be 1. | 24 | should be 1. |
| 25 | 25 | ||
| 26 | What: /sys/class/leds/<led>/brightness_hw_changed | ||
| 27 | Date: January 2017 | ||
| 28 | KernelVersion: 4.11 | ||
| 29 | Description: | ||
| 30 | Last hardware set brightness level for this LED. Some LEDs | ||
| 31 | may be changed autonomously by hardware/firmware. Only LEDs | ||
| 32 | where this happens and the driver can detect this, will have | ||
| 33 | this file. | ||
| 34 | |||
| 35 | This file supports poll() to detect when the hardware changes | ||
| 36 | the brightness. | ||
| 37 | |||
| 38 | Reading this file will return the last brightness level set | ||
| 39 | by the hardware, this may be different from the current | ||
| 40 | brightness. Reading this file when no hw brightness change | ||
| 41 | event has happened will return an ENODATA error. | ||
| 42 | |||
| 26 | What: /sys/class/leds/<led>/trigger | 43 | What: /sys/class/leds/<led>/trigger |
| 27 | Date: March 2006 | 44 | Date: March 2006 |
| 28 | KernelVersion: 2.6.17 | 45 | KernelVersion: 2.6.17 |
diff --git a/Documentation/leds/leds-class.txt b/Documentation/leds/leds-class.txt index f1f7ec9f5cc5..836cb16d6f09 100644 --- a/Documentation/leds/leds-class.txt +++ b/Documentation/leds/leds-class.txt | |||
| @@ -65,6 +65,21 @@ LED subsystem core exposes following API for setting brightness: | |||
| 65 | blinking, returns -EBUSY if software blink fallback is enabled. | 65 | blinking, returns -EBUSY if software blink fallback is enabled. |
| 66 | 66 | ||
| 67 | 67 | ||
| 68 | LED registration API | ||
| 69 | ==================== | ||
| 70 | |||
| 71 | A driver wanting to register a LED classdev for use by other drivers / | ||
| 72 | userspace needs to allocate and fill a led_classdev struct and then call | ||
| 73 | [devm_]led_classdev_register. If the non devm version is used the driver | ||
| 74 | must call led_classdev_unregister from its remove function before | ||
| 75 | free-ing the led_classdev struct. | ||
| 76 | |||
| 77 | If the driver can detect hardware initiated brightness changes and thus | ||
| 78 | wants to have a brightness_hw_changed attribute then the LED_BRIGHT_HW_CHANGED | ||
| 79 | flag must be set in flags before registering. Calling | ||
| 80 | led_classdev_notify_brightness_hw_changed on a classdev not registered with | ||
| 81 | the LED_BRIGHT_HW_CHANGED flag is a bug and will trigger a WARN_ON. | ||
| 82 | |||
| 68 | Hardware accelerated blink of LEDs | 83 | Hardware accelerated blink of LEDs |
| 69 | ================================== | 84 | ================================== |
| 70 | 85 | ||
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index c621cbbb5768..275f467956ee 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
| @@ -29,6 +29,15 @@ config LEDS_CLASS_FLASH | |||
| 29 | for the flash related features of a LED device. It can be built | 29 | for the flash related features of a LED device. It can be built |
| 30 | as a module. | 30 | as a module. |
| 31 | 31 | ||
| 32 | config LEDS_BRIGHTNESS_HW_CHANGED | ||
| 33 | bool "LED Class brightness_hw_changed attribute support" | ||
| 34 | depends on LEDS_CLASS | ||
| 35 | help | ||
| 36 | This option enables support for the brightness_hw_changed attribute | ||
| 37 | for led sysfs class devices under /sys/class/leds. | ||
| 38 | |||
| 39 | See Documentation/ABI/testing/sysfs-class-led for details. | ||
| 40 | |||
| 32 | comment "LED drivers" | 41 | comment "LED drivers" |
| 33 | 42 | ||
| 34 | config LEDS_88PM860X | 43 | config LEDS_88PM860X |
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 326ee6e925a2..f2b0a80a62b4 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
| @@ -103,6 +103,68 @@ static const struct attribute_group *led_groups[] = { | |||
| 103 | NULL, | 103 | NULL, |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED | ||
| 107 | static ssize_t brightness_hw_changed_show(struct device *dev, | ||
| 108 | struct device_attribute *attr, char *buf) | ||
| 109 | { | ||
| 110 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 111 | |||
| 112 | if (led_cdev->brightness_hw_changed == -1) | ||
| 113 | return -ENODATA; | ||
| 114 | |||
| 115 | return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed); | ||
| 116 | } | ||
| 117 | |||
| 118 | static DEVICE_ATTR_RO(brightness_hw_changed); | ||
| 119 | |||
| 120 | static int led_add_brightness_hw_changed(struct led_classdev *led_cdev) | ||
| 121 | { | ||
| 122 | struct device *dev = led_cdev->dev; | ||
| 123 | int ret; | ||
| 124 | |||
| 125 | ret = device_create_file(dev, &dev_attr_brightness_hw_changed); | ||
| 126 | if (ret) { | ||
| 127 | dev_err(dev, "Error creating brightness_hw_changed\n"); | ||
| 128 | return ret; | ||
| 129 | } | ||
| 130 | |||
| 131 | led_cdev->brightness_hw_changed_kn = | ||
| 132 | sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed"); | ||
| 133 | if (!led_cdev->brightness_hw_changed_kn) { | ||
| 134 | dev_err(dev, "Error getting brightness_hw_changed kn\n"); | ||
| 135 | device_remove_file(dev, &dev_attr_brightness_hw_changed); | ||
| 136 | return -ENXIO; | ||
| 137 | } | ||
| 138 | |||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev) | ||
| 143 | { | ||
| 144 | sysfs_put(led_cdev->brightness_hw_changed_kn); | ||
| 145 | device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed); | ||
| 146 | } | ||
| 147 | |||
| 148 | void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev, | ||
| 149 | enum led_brightness brightness) | ||
| 150 | { | ||
| 151 | if (WARN_ON(!led_cdev->brightness_hw_changed_kn)) | ||
| 152 | return; | ||
| 153 | |||
| 154 | led_cdev->brightness_hw_changed = brightness; | ||
| 155 | sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn); | ||
| 156 | } | ||
| 157 | EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed); | ||
| 158 | #else | ||
| 159 | static int led_add_brightness_hw_changed(struct led_classdev *led_cdev) | ||
| 160 | { | ||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev) | ||
| 164 | { | ||
| 165 | } | ||
| 166 | #endif | ||
| 167 | |||
| 106 | /** | 168 | /** |
| 107 | * led_classdev_suspend - suspend an led_classdev. | 169 | * led_classdev_suspend - suspend an led_classdev. |
| 108 | * @led_cdev: the led_classdev to suspend. | 170 | * @led_cdev: the led_classdev to suspend. |
| @@ -204,10 +266,21 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | |||
| 204 | dev_warn(parent, "Led %s renamed to %s due to name collision", | 266 | dev_warn(parent, "Led %s renamed to %s due to name collision", |
| 205 | led_cdev->name, dev_name(led_cdev->dev)); | 267 | led_cdev->name, dev_name(led_cdev->dev)); |
| 206 | 268 | ||
| 269 | if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) { | ||
| 270 | ret = led_add_brightness_hw_changed(led_cdev); | ||
| 271 | if (ret) { | ||
| 272 | device_unregister(led_cdev->dev); | ||
| 273 | return ret; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 207 | led_cdev->work_flags = 0; | 277 | led_cdev->work_flags = 0; |
| 208 | #ifdef CONFIG_LEDS_TRIGGERS | 278 | #ifdef CONFIG_LEDS_TRIGGERS |
| 209 | init_rwsem(&led_cdev->trigger_lock); | 279 | init_rwsem(&led_cdev->trigger_lock); |
| 210 | #endif | 280 | #endif |
| 281 | #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED | ||
| 282 | led_cdev->brightness_hw_changed = -1; | ||
| 283 | #endif | ||
| 211 | mutex_init(&led_cdev->led_access); | 284 | mutex_init(&led_cdev->led_access); |
| 212 | /* add to the list of leds */ | 285 | /* add to the list of leds */ |
| 213 | down_write(&leds_list_lock); | 286 | down_write(&leds_list_lock); |
| @@ -256,6 +329,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev) | |||
| 256 | 329 | ||
| 257 | flush_work(&led_cdev->set_brightness_work); | 330 | flush_work(&led_cdev->set_brightness_work); |
| 258 | 331 | ||
| 332 | if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) | ||
| 333 | led_remove_brightness_hw_changed(led_cdev); | ||
| 334 | |||
| 259 | device_unregister(led_cdev->dev); | 335 | device_unregister(led_cdev->dev); |
| 260 | 336 | ||
| 261 | down_write(&leds_list_lock); | 337 | down_write(&leds_list_lock); |
diff --git a/include/linux/leds.h b/include/linux/leds.h index bb50d0151e75..38c0bd7ca107 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/device.h> | 15 | #include <linux/device.h> |
| 16 | #include <linux/kernfs.h> | ||
| 16 | #include <linux/list.h> | 17 | #include <linux/list.h> |
| 17 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
| 18 | #include <linux/rwsem.h> | 19 | #include <linux/rwsem.h> |
| @@ -47,6 +48,7 @@ struct led_classdev { | |||
| 47 | #define LED_DEV_CAP_FLASH (1 << 18) | 48 | #define LED_DEV_CAP_FLASH (1 << 18) |
| 48 | #define LED_HW_PLUGGABLE (1 << 19) | 49 | #define LED_HW_PLUGGABLE (1 << 19) |
| 49 | #define LED_PANIC_INDICATOR (1 << 20) | 50 | #define LED_PANIC_INDICATOR (1 << 20) |
| 51 | #define LED_BRIGHT_HW_CHANGED (1 << 21) | ||
| 50 | 52 | ||
| 51 | /* set_brightness_work / blink_timer flags, atomic, private. */ | 53 | /* set_brightness_work / blink_timer flags, atomic, private. */ |
| 52 | unsigned long work_flags; | 54 | unsigned long work_flags; |
| @@ -111,6 +113,11 @@ struct led_classdev { | |||
| 111 | bool activated; | 113 | bool activated; |
| 112 | #endif | 114 | #endif |
| 113 | 115 | ||
| 116 | #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED | ||
| 117 | int brightness_hw_changed; | ||
| 118 | struct kernfs_node *brightness_hw_changed_kn; | ||
| 119 | #endif | ||
| 120 | |||
| 114 | /* Ensures consistent access to the LED Flash Class device */ | 121 | /* Ensures consistent access to the LED Flash Class device */ |
| 115 | struct mutex led_access; | 122 | struct mutex led_access; |
| 116 | }; | 123 | }; |
| @@ -423,4 +430,12 @@ static inline void ledtrig_cpu(enum cpu_led_event evt) | |||
| 423 | } | 430 | } |
| 424 | #endif | 431 | #endif |
| 425 | 432 | ||
| 433 | #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED | ||
| 434 | extern void led_classdev_notify_brightness_hw_changed( | ||
| 435 | struct led_classdev *led_cdev, enum led_brightness brightness); | ||
| 436 | #else | ||
| 437 | static inline void led_classdev_notify_brightness_hw_changed( | ||
| 438 | struct led_classdev *led_cdev, enum led_brightness brightness) { } | ||
| 439 | #endif | ||
| 440 | |||
| 426 | #endif /* __LINUX_LEDS_H_INCLUDED */ | 441 | #endif /* __LINUX_LEDS_H_INCLUDED */ |
