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/property.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 BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0); 78 static struct bin_attribute *led_trigger_bin_attrs[] = { 79 &bin_attr_trigger, 80 NULL, 81 }; 82 static const struct attribute_group led_trigger_group = { 83 .bin_attrs = led_trigger_bin_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 led_classdev_next_name(const char *init_name, char *name, 218 size_t len) 219 { 220 unsigned int i = 0; 221 int ret = 0; 222 struct device *dev; 223 224 strlcpy(name, init_name, len); 225 226 while ((ret < len) && 227 (dev = class_find_device_by_name(leds_class, name))) { 228 put_device(dev); 229 ret = snprintf(name, len, "%s_%u", init_name, ++i); 230 } 231 232 if (ret >= len) 233 return -ENOMEM; 234 235 return i; 236 } 237 238 /** 239 * led_classdev_register_ext - register a new object of led_classdev class 240 * with init data. 241 * 242 * @parent: parent of LED device 243 * @led_cdev: the led_classdev structure for this device. 244 * @init_data: LED class device initialization data 245 */ 246 int led_classdev_register_ext(struct device *parent, 247 struct led_classdev *led_cdev, 248 struct led_init_data *init_data) 249 { 250 char composed_name[LED_MAX_NAME_SIZE]; 251 char final_name[LED_MAX_NAME_SIZE]; 252 const char *proposed_name = composed_name; 253 int ret; 254 255 if (init_data) { 256 if (init_data->devname_mandatory && !init_data->devicename) { 257 dev_err(parent, "Mandatory device name is missing"); 258 return -EINVAL; 259 } 260 ret = led_compose_name(parent, init_data, composed_name); 261 if (ret < 0) 262 return ret; 263 } else { 264 proposed_name = led_cdev->name; 265 } 266 267 ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name)); 268 if (ret < 0) 269 return ret; 270 271 mutex_init(&led_cdev->led_access); 272 mutex_lock(&led_cdev->led_access); 273 led_cdev->dev = device_create_with_groups(leds_class, parent, 0, 274 led_cdev, led_cdev->groups, "%s", final_name); 275 if (IS_ERR(led_cdev->dev)) { 276 mutex_unlock(&led_cdev->led_access); 277 return PTR_ERR(led_cdev->dev); 278 } 279 if (init_data && init_data->fwnode) 280 led_cdev->dev->fwnode = init_data->fwnode; 281 282 if (ret) 283 dev_warn(parent, "Led %s renamed to %s due to name collision", 284 led_cdev->name, dev_name(led_cdev->dev)); 285 286 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) { 287 ret = led_add_brightness_hw_changed(led_cdev); 288 if (ret) { 289 device_unregister(led_cdev->dev); 290 led_cdev->dev = NULL; 291 mutex_unlock(&led_cdev->led_access); 292 return ret; 293 } 294 } 295 296 led_cdev->work_flags = 0; 297 #ifdef CONFIG_LEDS_TRIGGERS 298 init_rwsem(&led_cdev->trigger_lock); 299 #endif 300 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED 301 led_cdev->brightness_hw_changed = -1; 302 #endif 303 /* add to the list of leds */ 304 down_write(&leds_list_lock); 305 list_add_tail(&led_cdev->node, &leds_list); 306 up_write(&leds_list_lock); 307 308 if (!led_cdev->max_brightness) 309 led_cdev->max_brightness = LED_FULL; 310 311 led_update_brightness(led_cdev); 312 313 led_init_core(led_cdev); 314 315 #ifdef CONFIG_LEDS_TRIGGERS 316 led_trigger_set_default(led_cdev); 317 #endif 318 319 mutex_unlock(&led_cdev->led_access); 320 321 dev_dbg(parent, "Registered led device: %s\n", 322 led_cdev->name); 323 324 return 0; 325 } 326 EXPORT_SYMBOL_GPL(led_classdev_register_ext); 327 328 /** 329 * led_classdev_unregister - unregisters a object of led_properties class. 330 * @led_cdev: the led device to unregister 331 * 332 * Unregisters a previously registered via led_classdev_register object. 333 */ 334 void led_classdev_unregister(struct led_classdev *led_cdev) 335 { 336 if (IS_ERR_OR_NULL(led_cdev->dev)) 337 return; 338 339 #ifdef CONFIG_LEDS_TRIGGERS 340 down_write(&led_cdev->trigger_lock); 341 if (led_cdev->trigger) 342 led_trigger_set(led_cdev, NULL); 343 up_write(&led_cdev->trigger_lock); 344 #endif 345 346 led_cdev->flags |= LED_UNREGISTERING; 347 348 /* Stop blinking */ 349 led_stop_software_blink(led_cdev); 350 351 led_set_brightness(led_cdev, LED_OFF); 352 353 flush_work(&led_cdev->set_brightness_work); 354 355 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) 356 led_remove_brightness_hw_changed(led_cdev); 357 358 device_unregister(led_cdev->dev); 359 360 down_write(&leds_list_lock); 361 list_del(&led_cdev->node); 362 up_write(&leds_list_lock); 363 364 mutex_destroy(&led_cdev->led_access); 365 } 366 EXPORT_SYMBOL_GPL(led_classdev_unregister); 367 368 static void devm_led_classdev_release(struct device *dev, void *res) 369 { 370 led_classdev_unregister(*(struct led_classdev **)res); 371 } 372 373 /** 374 * devm_led_classdev_register_ext - resource managed led_classdev_register_ext() 375 * 376 * @parent: parent of LED device 377 * @led_cdev: the led_classdev structure for this device. 378 * @init_data: LED class device initialization data 379 */ 380 int devm_led_classdev_register_ext(struct device *parent, 381 struct led_classdev *led_cdev, 382 struct led_init_data *init_data) 383 { 384 struct led_classdev **dr; 385 int rc; 386 387 dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL); 388 if (!dr) 389 return -ENOMEM; 390 391 rc = led_classdev_register_ext(parent, led_cdev, init_data); 392 if (rc) { 393 devres_free(dr); 394 return rc; 395 } 396 397 *dr = led_cdev; 398 devres_add(parent, dr); 399 400 return 0; 401 } 402 EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext); 403 404 static int devm_led_classdev_match(struct device *dev, void *res, void *data) 405 { 406 struct led_classdev **p = res; 407 408 if (WARN_ON(!p || !*p)) 409 return 0; 410 411 return *p == data; 412 } 413 414 /** 415 * devm_led_classdev_unregister() - resource managed led_classdev_unregister() 416 * @parent: The device to unregister. 417 * @led_cdev: the led_classdev structure for this device. 418 */ 419 void devm_led_classdev_unregister(struct device *dev, 420 struct led_classdev *led_cdev) 421 { 422 WARN_ON(devres_release(dev, 423 devm_led_classdev_release, 424 devm_led_classdev_match, led_cdev)); 425 } 426 EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); 427 428 static int __init leds_init(void) 429 { 430 leds_class = class_create(THIS_MODULE, "leds"); 431 if (IS_ERR(leds_class)) 432 return PTR_ERR(leds_class); 433 leds_class->pm = &leds_class_dev_pm_ops; 434 leds_class->dev_groups = led_groups; 435 return 0; 436 } 437 438 static void __exit leds_exit(void) 439 { 440 class_destroy(leds_class); 441 } 442 443 subsys_initcall(leds_init); 444 module_exit(leds_exit); 445 446 MODULE_AUTHOR("John Lenz, Richard Purdie"); 447 MODULE_LICENSE("GPL"); 448 MODULE_DESCRIPTION("LED Class Interface"); 449