Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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