1 /* 2 * LED Flash class interface 3 * 4 * Copyright (C) 2015 Samsung Electronics Co., Ltd. 5 * Author: Jacek Anaszewski <j.anaszewski@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #include <linux/device.h> 13 #include <linux/init.h> 14 #include <linux/led-class-flash.h> 15 #include <linux/leds.h> 16 #include <linux/module.h> 17 #include <linux/slab.h> 18 #include "leds.h" 19 20 #define has_flash_op(fled_cdev, op) \ 21 (fled_cdev && fled_cdev->ops->op) 22 23 #define call_flash_op(fled_cdev, op, args...) \ 24 ((has_flash_op(fled_cdev, op)) ? \ 25 (fled_cdev->ops->op(fled_cdev, args)) : \ 26 -EINVAL) 27 28 static const char * const led_flash_fault_names[] = { 29 "led-over-voltage", 30 "flash-timeout-exceeded", 31 "controller-over-temperature", 32 "controller-short-circuit", 33 "led-power-supply-over-current", 34 "indicator-led-fault", 35 "led-under-voltage", 36 "controller-under-voltage", 37 "led-over-temperature", 38 }; 39 40 static ssize_t flash_brightness_store(struct device *dev, 41 struct device_attribute *attr, const char *buf, size_t size) 42 { 43 struct led_classdev *led_cdev = dev_get_drvdata(dev); 44 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 45 unsigned long state; 46 ssize_t ret; 47 48 mutex_lock(&led_cdev->led_access); 49 50 if (led_sysfs_is_disabled(led_cdev)) { 51 ret = -EBUSY; 52 goto unlock; 53 } 54 55 ret = kstrtoul(buf, 10, &state); 56 if (ret) 57 goto unlock; 58 59 ret = led_set_flash_brightness(fled_cdev, state); 60 if (ret < 0) 61 goto unlock; 62 63 ret = size; 64 unlock: 65 mutex_unlock(&led_cdev->led_access); 66 return ret; 67 } 68 69 static ssize_t flash_brightness_show(struct device *dev, 70 struct device_attribute *attr, char *buf) 71 { 72 struct led_classdev *led_cdev = dev_get_drvdata(dev); 73 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 74 75 /* no lock needed for this */ 76 led_update_flash_brightness(fled_cdev); 77 78 return sprintf(buf, "%u\n", fled_cdev->brightness.val); 79 } 80 static DEVICE_ATTR_RW(flash_brightness); 81 82 static ssize_t max_flash_brightness_show(struct device *dev, 83 struct device_attribute *attr, char *buf) 84 { 85 struct led_classdev *led_cdev = dev_get_drvdata(dev); 86 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 87 88 return sprintf(buf, "%u\n", fled_cdev->brightness.max); 89 } 90 static DEVICE_ATTR_RO(max_flash_brightness); 91 92 static ssize_t flash_strobe_store(struct device *dev, 93 struct device_attribute *attr, const char *buf, size_t size) 94 { 95 struct led_classdev *led_cdev = dev_get_drvdata(dev); 96 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 97 unsigned long state; 98 ssize_t ret = -EINVAL; 99 100 mutex_lock(&led_cdev->led_access); 101 102 if (led_sysfs_is_disabled(led_cdev)) { 103 ret = -EBUSY; 104 goto unlock; 105 } 106 107 ret = kstrtoul(buf, 10, &state); 108 if (ret) 109 goto unlock; 110 111 if (state < 0 || state > 1) { 112 ret = -EINVAL; 113 goto unlock; 114 } 115 116 ret = led_set_flash_strobe(fled_cdev, state); 117 if (ret < 0) 118 goto unlock; 119 ret = size; 120 unlock: 121 mutex_unlock(&led_cdev->led_access); 122 return ret; 123 } 124 125 static ssize_t flash_strobe_show(struct device *dev, 126 struct device_attribute *attr, char *buf) 127 { 128 struct led_classdev *led_cdev = dev_get_drvdata(dev); 129 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 130 bool state; 131 int ret; 132 133 /* no lock needed for this */ 134 ret = led_get_flash_strobe(fled_cdev, &state); 135 if (ret < 0) 136 return ret; 137 138 return sprintf(buf, "%u\n", state); 139 } 140 static DEVICE_ATTR_RW(flash_strobe); 141 142 static ssize_t flash_timeout_store(struct device *dev, 143 struct device_attribute *attr, const char *buf, size_t size) 144 { 145 struct led_classdev *led_cdev = dev_get_drvdata(dev); 146 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 147 unsigned long flash_timeout; 148 ssize_t ret; 149 150 mutex_lock(&led_cdev->led_access); 151 152 if (led_sysfs_is_disabled(led_cdev)) { 153 ret = -EBUSY; 154 goto unlock; 155 } 156 157 ret = kstrtoul(buf, 10, &flash_timeout); 158 if (ret) 159 goto unlock; 160 161 ret = led_set_flash_timeout(fled_cdev, flash_timeout); 162 if (ret < 0) 163 goto unlock; 164 165 ret = size; 166 unlock: 167 mutex_unlock(&led_cdev->led_access); 168 return ret; 169 } 170 171 static ssize_t flash_timeout_show(struct device *dev, 172 struct device_attribute *attr, char *buf) 173 { 174 struct led_classdev *led_cdev = dev_get_drvdata(dev); 175 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 176 177 return sprintf(buf, "%u\n", fled_cdev->timeout.val); 178 } 179 static DEVICE_ATTR_RW(flash_timeout); 180 181 static ssize_t max_flash_timeout_show(struct device *dev, 182 struct device_attribute *attr, char *buf) 183 { 184 struct led_classdev *led_cdev = dev_get_drvdata(dev); 185 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 186 187 return sprintf(buf, "%u\n", fled_cdev->timeout.max); 188 } 189 static DEVICE_ATTR_RO(max_flash_timeout); 190 191 static ssize_t flash_fault_show(struct device *dev, 192 struct device_attribute *attr, char *buf) 193 { 194 struct led_classdev *led_cdev = dev_get_drvdata(dev); 195 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 196 u32 fault, mask = 0x1; 197 char *pbuf = buf; 198 int i, ret, buf_len; 199 200 ret = led_get_flash_fault(fled_cdev, &fault); 201 if (ret < 0) 202 return -EINVAL; 203 204 *buf = '\0'; 205 206 for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) { 207 if (fault & mask) { 208 buf_len = sprintf(pbuf, "%s ", 209 led_flash_fault_names[i]); 210 pbuf += buf_len; 211 } 212 mask <<= 1; 213 } 214 215 return sprintf(buf, "%s\n", buf); 216 } 217 static DEVICE_ATTR_RO(flash_fault); 218 219 static ssize_t available_sync_leds_show(struct device *dev, 220 struct device_attribute *attr, char *buf) 221 { 222 struct led_classdev *led_cdev = dev_get_drvdata(dev); 223 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 224 char *pbuf = buf; 225 int i, buf_len; 226 227 buf_len = sprintf(pbuf, "[0: none] "); 228 pbuf += buf_len; 229 230 for (i = 0; i < fled_cdev->num_sync_leds; ++i) { 231 buf_len = sprintf(pbuf, "[%d: %s] ", i + 1, 232 fled_cdev->sync_leds[i]->led_cdev.name); 233 pbuf += buf_len; 234 } 235 236 return sprintf(buf, "%s\n", buf); 237 } 238 static DEVICE_ATTR_RO(available_sync_leds); 239 240 static ssize_t flash_sync_strobe_store(struct device *dev, 241 struct device_attribute *attr, const char *buf, size_t size) 242 { 243 struct led_classdev *led_cdev = dev_get_drvdata(dev); 244 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 245 unsigned long led_id; 246 ssize_t ret; 247 248 mutex_lock(&led_cdev->led_access); 249 250 if (led_sysfs_is_disabled(led_cdev)) { 251 ret = -EBUSY; 252 goto unlock; 253 } 254 255 ret = kstrtoul(buf, 10, &led_id); 256 if (ret) 257 goto unlock; 258 259 if (led_id > fled_cdev->num_sync_leds) { 260 ret = -ERANGE; 261 goto unlock; 262 } 263 264 fled_cdev->sync_led_id = led_id; 265 266 ret = size; 267 unlock: 268 mutex_unlock(&led_cdev->led_access); 269 return ret; 270 } 271 272 static ssize_t flash_sync_strobe_show(struct device *dev, 273 struct device_attribute *attr, char *buf) 274 { 275 struct led_classdev *led_cdev = dev_get_drvdata(dev); 276 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 277 int sled_id = fled_cdev->sync_led_id; 278 char *sync_led_name = "none"; 279 280 if (fled_cdev->sync_led_id > 0) 281 sync_led_name = (char *) 282 fled_cdev->sync_leds[sled_id - 1]->led_cdev.name; 283 284 return sprintf(buf, "[%d: %s]\n", sled_id, sync_led_name); 285 } 286 static DEVICE_ATTR_RW(flash_sync_strobe); 287 288 static struct attribute *led_flash_strobe_attrs[] = { 289 &dev_attr_flash_strobe.attr, 290 NULL, 291 }; 292 293 static struct attribute *led_flash_timeout_attrs[] = { 294 &dev_attr_flash_timeout.attr, 295 &dev_attr_max_flash_timeout.attr, 296 NULL, 297 }; 298 299 static struct attribute *led_flash_brightness_attrs[] = { 300 &dev_attr_flash_brightness.attr, 301 &dev_attr_max_flash_brightness.attr, 302 NULL, 303 }; 304 305 static struct attribute *led_flash_fault_attrs[] = { 306 &dev_attr_flash_fault.attr, 307 NULL, 308 }; 309 310 static struct attribute *led_flash_sync_strobe_attrs[] = { 311 &dev_attr_available_sync_leds.attr, 312 &dev_attr_flash_sync_strobe.attr, 313 NULL, 314 }; 315 316 static const struct attribute_group led_flash_strobe_group = { 317 .attrs = led_flash_strobe_attrs, 318 }; 319 320 static const struct attribute_group led_flash_timeout_group = { 321 .attrs = led_flash_timeout_attrs, 322 }; 323 324 static const struct attribute_group led_flash_brightness_group = { 325 .attrs = led_flash_brightness_attrs, 326 }; 327 328 static const struct attribute_group led_flash_fault_group = { 329 .attrs = led_flash_fault_attrs, 330 }; 331 332 static const struct attribute_group led_flash_sync_strobe_group = { 333 .attrs = led_flash_sync_strobe_attrs, 334 }; 335 336 static void led_flash_resume(struct led_classdev *led_cdev) 337 { 338 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 339 340 call_flash_op(fled_cdev, flash_brightness_set, 341 fled_cdev->brightness.val); 342 call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val); 343 } 344 345 static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev) 346 { 347 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 348 const struct led_flash_ops *ops = fled_cdev->ops; 349 const struct attribute_group **flash_groups = fled_cdev->sysfs_groups; 350 351 int num_sysfs_groups = 0; 352 353 flash_groups[num_sysfs_groups++] = &led_flash_strobe_group; 354 355 if (ops->flash_brightness_set) 356 flash_groups[num_sysfs_groups++] = &led_flash_brightness_group; 357 358 if (ops->timeout_set) 359 flash_groups[num_sysfs_groups++] = &led_flash_timeout_group; 360 361 if (ops->fault_get) 362 flash_groups[num_sysfs_groups++] = &led_flash_fault_group; 363 364 if (led_cdev->flags & LED_DEV_CAP_SYNC_STROBE) 365 flash_groups[num_sysfs_groups++] = &led_flash_sync_strobe_group; 366 367 led_cdev->groups = flash_groups; 368 } 369 370 int led_classdev_flash_register(struct device *parent, 371 struct led_classdev_flash *fled_cdev) 372 { 373 struct led_classdev *led_cdev; 374 const struct led_flash_ops *ops; 375 int ret; 376 377 if (!fled_cdev) 378 return -EINVAL; 379 380 led_cdev = &fled_cdev->led_cdev; 381 382 if (led_cdev->flags & LED_DEV_CAP_FLASH) { 383 if (!led_cdev->brightness_set_sync) 384 return -EINVAL; 385 386 ops = fled_cdev->ops; 387 if (!ops || !ops->strobe_set) 388 return -EINVAL; 389 390 led_cdev->flash_resume = led_flash_resume; 391 392 /* Select the sysfs attributes to be created for the device */ 393 led_flash_init_sysfs_groups(fled_cdev); 394 } 395 396 /* Register led class device */ 397 ret = led_classdev_register(parent, led_cdev); 398 if (ret < 0) 399 return ret; 400 401 /* Setting a torch brightness needs to have immediate effect */ 402 led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC; 403 led_cdev->flags |= SET_BRIGHTNESS_SYNC; 404 405 return 0; 406 } 407 EXPORT_SYMBOL_GPL(led_classdev_flash_register); 408 409 void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) 410 { 411 if (!fled_cdev) 412 return; 413 414 led_classdev_unregister(&fled_cdev->led_cdev); 415 } 416 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister); 417 418 static void led_clamp_align(struct led_flash_setting *s) 419 { 420 u32 v, offset; 421 422 v = s->val + s->step / 2; 423 v = clamp(v, s->min, s->max); 424 offset = v - s->min; 425 offset = s->step * (offset / s->step); 426 s->val = s->min + offset; 427 } 428 429 int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout) 430 { 431 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 432 struct led_flash_setting *s = &fled_cdev->timeout; 433 434 s->val = timeout; 435 led_clamp_align(s); 436 437 if (!(led_cdev->flags & LED_SUSPENDED)) 438 return call_flash_op(fled_cdev, timeout_set, s->val); 439 440 return 0; 441 } 442 EXPORT_SYMBOL_GPL(led_set_flash_timeout); 443 444 int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault) 445 { 446 return call_flash_op(fled_cdev, fault_get, fault); 447 } 448 EXPORT_SYMBOL_GPL(led_get_flash_fault); 449 450 int led_set_flash_brightness(struct led_classdev_flash *fled_cdev, 451 u32 brightness) 452 { 453 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 454 struct led_flash_setting *s = &fled_cdev->brightness; 455 456 s->val = brightness; 457 led_clamp_align(s); 458 459 if (!(led_cdev->flags & LED_SUSPENDED)) 460 return call_flash_op(fled_cdev, flash_brightness_set, s->val); 461 462 return 0; 463 } 464 EXPORT_SYMBOL_GPL(led_set_flash_brightness); 465 466 int led_update_flash_brightness(struct led_classdev_flash *fled_cdev) 467 { 468 struct led_flash_setting *s = &fled_cdev->brightness; 469 u32 brightness; 470 471 if (has_flash_op(fled_cdev, flash_brightness_get)) { 472 int ret = call_flash_op(fled_cdev, flash_brightness_get, 473 &brightness); 474 if (ret < 0) 475 return ret; 476 477 s->val = brightness; 478 } 479 480 return 0; 481 } 482 EXPORT_SYMBOL_GPL(led_update_flash_brightness); 483 484 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); 485 MODULE_DESCRIPTION("LED Flash class interface"); 486 MODULE_LICENSE("GPL v2"); 487