1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * LED Class Core 4 * 5 * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu> 6 * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com> 7 */ 8 9 #include <linux/ctype.h> 10 #include <linux/device.h> 11 #include <linux/err.h> 12 #include <linux/init.h> 13 #include <linux/kernel.h> 14 #include <linux/leds.h> 15 #include <linux/list.h> 16 #include <linux/module.h> 17 #include <linux/slab.h> 18 #include <linux/spinlock.h> 19 #include <linux/timer.h> 20 #include <uapi/linux/uleds.h> 21 #include "leds.h" 22 23 static struct class *leds_class; 24 25 static ssize_t brightness_show(struct device *dev, 26 struct device_attribute *attr, char *buf) 27 { 28 struct led_classdev *led_cdev = dev_get_drvdata(dev); 29 30 /* no lock needed for this */ 31 led_update_brightness(led_cdev); 32 33 return sprintf(buf, "%u\n", led_cdev->brightness); 34 } 35 36 static ssize_t brightness_store(struct device *dev, 37 struct device_attribute *attr, const char *buf, size_t size) 38 { 39 struct led_classdev *led_cdev = dev_get_drvdata(dev); 40 unsigned long state; 41 ssize_t ret; 42 43 mutex_lock(&led_cdev->led_access); 44 45 if (led_sysfs_is_disabled(led_cdev)) { 46 ret = -EBUSY; 47 goto unlock; 48 } 49 50 ret = kstrtoul(buf, 10, &state); 51 if (ret) 52 goto unlock; 53 54 if (state == LED_OFF) 55 led_trigger_remove(led_cdev); 56 led_set_brightness(led_cdev, state); 57 flush_work(&led_cdev->set_brightness_work); 58 59 ret = size; 60 unlock: 61 mutex_unlock(&led_cdev->led_access); 62 return ret; 63 } 64 static DEVICE_ATTR_RW(brightness); 65 66 static ssize_t max_brightness_show(struct device *dev, 67 struct device_attribute *attr, char *buf) 68 { 69 struct led_classdev *led_cdev = dev_get_drvdata(dev); 70 71 return sprintf(buf, "%u\n", led_cdev->max_brightness); 72 } 73 static DEVICE_ATTR_RO(max_brightness); 74 75 #ifdef CONFIG_LEDS_TRIGGERS 76 static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); 77 static struct attribute *led_trigger_attrs[] = { 78 &dev_attr_trigger.attr, 79 NULL, 80 }; 81 static const struct attribute_group led_trigger_group = { 82 .attrs = led_trigger_attrs, 83 }; 84 #endif 85 86 static struct attribute *led_class_attrs[] = { 87 &dev_attr_brightness.attr, 88 &dev_attr_max_brightness.attr, 89 NULL, 90 }; 91 92 static const struct attribute_group led_group = { 93 .attrs = led_class_attrs, 94 }; 95 96 static const struct attribute_group *led_groups[] = { 97 &led_group, 98 #ifdef CONFIG_LEDS_TRIGGERS 99 &led_trigger_group, 100 #endif 101 NULL, 102 }; 103 104 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED 105 static ssize_t brightness_hw_changed_show(struct device *dev, 106 struct device_attribute *attr, char *buf) 107 { 108 struct led_classdev *led_cdev = dev_get_drvdata(dev); 109 110 if (led_cdev->brightness_hw_changed == -1) 111 return -ENODATA; 112 113 return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed); 114 } 115 116 static DEVICE_ATTR_RO(brightness_hw_changed); 117 118 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev) 119 { 120 struct device *dev = led_cdev->dev; 121 int ret; 122 123 ret = device_create_file(dev, &dev_attr_brightness_hw_changed); 124 if (ret) { 125 dev_err(dev, "Error creating brightness_hw_changed\n"); 126 return ret; 127 } 128 129 led_cdev->brightness_hw_changed_kn = 130 sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed"); 131 if (!led_cdev->brightness_hw_changed_kn) { 132 dev_err(dev, "Error getting brightness_hw_changed kn\n"); 133 device_remove_file(dev, &dev_attr_brightness_hw_changed); 134 return -ENXIO; 135 } 136 137 return 0; 138 } 139 140 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev) 141 { 142 sysfs_put(led_cdev->brightness_hw_changed_kn); 143 device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed); 144 } 145 146 void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev, 147 enum led_brightness brightness) 148 { 149 if (WARN_ON(!led_cdev->brightness_hw_changed_kn)) 150 return; 151 152 led_cdev->brightness_hw_changed = brightness; 153 sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn); 154 } 155 EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed); 156 #else 157 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev) 158 { 159 return 0; 160 } 161 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev) 162 { 163 } 164 #endif 165 166 /** 167 * led_classdev_suspend - suspend an led_classdev. 168 * @led_cdev: the led_classdev to suspend. 169 */ 170 void led_classdev_suspend(struct led_classdev *led_cdev) 171 { 172 led_cdev->flags |= LED_SUSPENDED; 173 led_set_brightness_nopm(led_cdev, 0); 174 } 175 EXPORT_SYMBOL_GPL(led_classdev_suspend); 176 177 /** 178 * led_classdev_resume - resume an led_classdev. 179 * @led_cdev: the led_classdev to resume. 180 */ 181 void led_classdev_resume(struct led_classdev *led_cdev) 182 { 183 led_set_brightness_nopm(led_cdev, led_cdev->brightness); 184 185 if (led_cdev->flash_resume) 186 led_cdev->flash_resume(led_cdev); 187 188 led_cdev->flags &= ~LED_SUSPENDED; 189 } 190 EXPORT_SYMBOL_GPL(led_classdev_resume); 191 192 #ifdef CONFIG_PM_SLEEP 193 static int led_suspend(struct device *dev) 194 { 195 struct led_classdev *led_cdev = dev_get_drvdata(dev); 196 197 if (led_cdev->flags & LED_CORE_SUSPENDRESUME) 198 led_classdev_suspend(led_cdev); 199 200 return 0; 201 } 202 203 static int led_resume(struct device *dev) 204 { 205 struct led_classdev *led_cdev = dev_get_drvdata(dev); 206 207 if (led_cdev->flags & LED_CORE_SUSPENDRESUME) 208 led_classdev_resume(led_cdev); 209 210 return 0; 211 } 212 #endif 213 214 static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume); 215 216 static int match_name(struct device *dev, const void *data) 217 { 218 if (!dev_name(dev)) 219 return 0; 220 return !strcmp(dev_name(dev), (char *)data); 221 } 222 223 static int led_classdev_next_name(const char *init_name, char *name, 224 size_t len) 225 { 226 unsigned int i = 0; 227 int ret = 0; 228 struct device *dev; 229 230 strlcpy(name, init_name, len); 231 232 while ((ret < len) && 233 (dev = class_find_device(leds_class, NULL, name, match_name))) { 234 put_device(dev); 235 ret = snprintf(name, len, "%s_%u", init_name, ++i); 236 } 237 238 if (ret >= len) 239 return -ENOMEM; 240 241 return i; 242 } 243 244 /** 245 * of_led_classdev_register - register a new object of led_classdev class. 246 * 247 * @parent: parent of LED device 248 * @led_cdev: the led_classdev structure for this device. 249 * @np: DT node describing this LED 250 */ 251 int of_led_classdev_register(struct device *parent, struct device_node *np, 252 struct led_classdev *led_cdev) 253 { 254 char name[LED_MAX_NAME_SIZE]; 255 int ret; 256 257 ret = led_classdev_next_name(led_cdev->name, name, sizeof(name)); 258 if (ret < 0) 259 return ret; 260 261 mutex_init(&led_cdev->led_access); 262 mutex_lock(&led_cdev->led_access); 263 led_cdev->dev = device_create_with_groups(leds_class, parent, 0, 264 led_cdev, led_cdev->groups, "%s", name); 265 if (IS_ERR(led_cdev->dev)) { 266 mutex_unlock(&led_cdev->led_access); 267 return PTR_ERR(led_cdev->dev); 268 } 269 led_cdev->dev->of_node = np; 270 271 if (ret) 272 dev_warn(parent, "Led %s renamed to %s due to name collision", 273 led_cdev->name, dev_name(led_cdev->dev)); 274 275 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) { 276 ret = led_add_brightness_hw_changed(led_cdev); 277 if (ret) { 278 device_unregister(led_cdev->dev); 279 mutex_unlock(&led_cdev->led_access); 280 return ret; 281 } 282 } 283 284 led_cdev->work_flags = 0; 285 #ifdef CONFIG_LEDS_TRIGGERS 286 init_rwsem(&led_cdev->trigger_lock); 287 #endif 288 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED 289 led_cdev->brightness_hw_changed = -1; 290 #endif 291 /* add to the list of leds */ 292 down_write(&leds_list_lock); 293 list_add_tail(&led_cdev->node, &leds_list); 294 up_write(&leds_list_lock); 295 296 if (!led_cdev->max_brightness) 297 led_cdev->max_brightness = LED_FULL; 298 299 led_update_brightness(led_cdev); 300 301 led_init_core(led_cdev); 302 303 #ifdef CONFIG_LEDS_TRIGGERS 304 led_trigger_set_default(led_cdev); 305 #endif 306 307 mutex_unlock(&led_cdev->led_access); 308 309 dev_dbg(parent, "Registered led device: %s\n", 310 led_cdev->name); 311 312 return 0; 313 } 314 EXPORT_SYMBOL_GPL(of_led_classdev_register); 315 316 /** 317 * led_classdev_unregister - unregisters a object of led_properties class. 318 * @led_cdev: the led device to unregister 319 * 320 * Unregisters a previously registered via led_classdev_register object. 321 */ 322 void led_classdev_unregister(struct led_classdev *led_cdev) 323 { 324 #ifdef CONFIG_LEDS_TRIGGERS 325 down_write(&led_cdev->trigger_lock); 326 if (led_cdev->trigger) 327 led_trigger_set(led_cdev, NULL); 328 up_write(&led_cdev->trigger_lock); 329 #endif 330 331 led_cdev->flags |= LED_UNREGISTERING; 332 333 /* Stop blinking */ 334 led_stop_software_blink(led_cdev); 335 336 led_set_brightness(led_cdev, LED_OFF); 337 338 flush_work(&led_cdev->set_brightness_work); 339 340 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) 341 led_remove_brightness_hw_changed(led_cdev); 342 343 device_unregister(led_cdev->dev); 344 345 down_write(&leds_list_lock); 346 list_del(&led_cdev->node); 347 up_write(&leds_list_lock); 348 349 mutex_destroy(&led_cdev->led_access); 350 } 351 EXPORT_SYMBOL_GPL(led_classdev_unregister); 352 353 static void devm_led_classdev_release(struct device *dev, void *res) 354 { 355 led_classdev_unregister(*(struct led_classdev **)res); 356 } 357 358 /** 359 * devm_of_led_classdev_register - resource managed led_classdev_register() 360 * 361 * @parent: parent of LED device 362 * @led_cdev: the led_classdev structure for this device. 363 */ 364 int devm_of_led_classdev_register(struct device *parent, 365 struct device_node *np, 366 struct led_classdev *led_cdev) 367 { 368 struct led_classdev **dr; 369 int rc; 370 371 dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL); 372 if (!dr) 373 return -ENOMEM; 374 375 rc = of_led_classdev_register(parent, np, led_cdev); 376 if (rc) { 377 devres_free(dr); 378 return rc; 379 } 380 381 *dr = led_cdev; 382 devres_add(parent, dr); 383 384 return 0; 385 } 386 EXPORT_SYMBOL_GPL(devm_of_led_classdev_register); 387 388 static int devm_led_classdev_match(struct device *dev, void *res, void *data) 389 { 390 struct led_cdev **p = res; 391 392 if (WARN_ON(!p || !*p)) 393 return 0; 394 395 return *p == data; 396 } 397 398 /** 399 * devm_led_classdev_unregister() - resource managed led_classdev_unregister() 400 * @parent: The device to unregister. 401 * @led_cdev: the led_classdev structure for this device. 402 */ 403 void devm_led_classdev_unregister(struct device *dev, 404 struct led_classdev *led_cdev) 405 { 406 WARN_ON(devres_release(dev, 407 devm_led_classdev_release, 408 devm_led_classdev_match, led_cdev)); 409 } 410 EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); 411 412 static int __init leds_init(void) 413 { 414 leds_class = class_create(THIS_MODULE, "leds"); 415 if (IS_ERR(leds_class)) 416 return PTR_ERR(leds_class); 417 leds_class->pm = &leds_class_dev_pm_ops; 418 leds_class->dev_groups = led_groups; 419 return 0; 420 } 421 422 static void __exit leds_exit(void) 423 { 424 class_destroy(leds_class); 425 } 426 427 subsys_initcall(leds_init); 428 module_exit(leds_exit); 429 430 MODULE_AUTHOR("John Lenz, Richard Purdie"); 431 MODULE_LICENSE("GPL"); 432 MODULE_DESCRIPTION("LED Class Interface"); 433