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