0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/device.h>
0010 #include <linux/init.h>
0011 #include <linux/led-class-flash.h>
0012 #include <linux/leds.h>
0013 #include <linux/module.h>
0014 #include <linux/slab.h>
0015 #include "leds.h"
0016
0017 #define has_flash_op(fled_cdev, op) \
0018 (fled_cdev && fled_cdev->ops->op)
0019
0020 #define call_flash_op(fled_cdev, op, args...) \
0021 ((has_flash_op(fled_cdev, op)) ? \
0022 (fled_cdev->ops->op(fled_cdev, args)) : \
0023 -EINVAL)
0024
0025 static const char * const led_flash_fault_names[] = {
0026 "led-over-voltage",
0027 "flash-timeout-exceeded",
0028 "controller-over-temperature",
0029 "controller-short-circuit",
0030 "led-power-supply-over-current",
0031 "indicator-led-fault",
0032 "led-under-voltage",
0033 "controller-under-voltage",
0034 "led-over-temperature",
0035 };
0036
0037 static ssize_t flash_brightness_store(struct device *dev,
0038 struct device_attribute *attr, const char *buf, size_t size)
0039 {
0040 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0041 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0042 unsigned long state;
0043 ssize_t ret;
0044
0045 mutex_lock(&led_cdev->led_access);
0046
0047 if (led_sysfs_is_disabled(led_cdev)) {
0048 ret = -EBUSY;
0049 goto unlock;
0050 }
0051
0052 ret = kstrtoul(buf, 10, &state);
0053 if (ret)
0054 goto unlock;
0055
0056 ret = led_set_flash_brightness(fled_cdev, state);
0057 if (ret < 0)
0058 goto unlock;
0059
0060 ret = size;
0061 unlock:
0062 mutex_unlock(&led_cdev->led_access);
0063 return ret;
0064 }
0065
0066 static ssize_t flash_brightness_show(struct device *dev,
0067 struct device_attribute *attr, char *buf)
0068 {
0069 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0070 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0071
0072
0073 led_update_flash_brightness(fled_cdev);
0074
0075 return sprintf(buf, "%u\n", fled_cdev->brightness.val);
0076 }
0077 static DEVICE_ATTR_RW(flash_brightness);
0078
0079 static ssize_t max_flash_brightness_show(struct device *dev,
0080 struct device_attribute *attr, char *buf)
0081 {
0082 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0083 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0084
0085 return sprintf(buf, "%u\n", fled_cdev->brightness.max);
0086 }
0087 static DEVICE_ATTR_RO(max_flash_brightness);
0088
0089 static ssize_t flash_strobe_store(struct device *dev,
0090 struct device_attribute *attr, const char *buf, size_t size)
0091 {
0092 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0093 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0094 unsigned long state;
0095 ssize_t ret = -EBUSY;
0096
0097 mutex_lock(&led_cdev->led_access);
0098
0099 if (led_sysfs_is_disabled(led_cdev))
0100 goto unlock;
0101
0102 ret = kstrtoul(buf, 10, &state);
0103 if (ret)
0104 goto unlock;
0105
0106 if (state > 1) {
0107 ret = -EINVAL;
0108 goto unlock;
0109 }
0110
0111 ret = led_set_flash_strobe(fled_cdev, state);
0112 if (ret < 0)
0113 goto unlock;
0114 ret = size;
0115 unlock:
0116 mutex_unlock(&led_cdev->led_access);
0117 return ret;
0118 }
0119
0120 static ssize_t flash_strobe_show(struct device *dev,
0121 struct device_attribute *attr, char *buf)
0122 {
0123 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0124 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0125 bool state;
0126 int ret;
0127
0128
0129 ret = led_get_flash_strobe(fled_cdev, &state);
0130 if (ret < 0)
0131 return ret;
0132
0133 return sprintf(buf, "%u\n", state);
0134 }
0135 static DEVICE_ATTR_RW(flash_strobe);
0136
0137 static ssize_t flash_timeout_store(struct device *dev,
0138 struct device_attribute *attr, const char *buf, size_t size)
0139 {
0140 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0141 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0142 unsigned long flash_timeout;
0143 ssize_t ret;
0144
0145 mutex_lock(&led_cdev->led_access);
0146
0147 if (led_sysfs_is_disabled(led_cdev)) {
0148 ret = -EBUSY;
0149 goto unlock;
0150 }
0151
0152 ret = kstrtoul(buf, 10, &flash_timeout);
0153 if (ret)
0154 goto unlock;
0155
0156 ret = led_set_flash_timeout(fled_cdev, flash_timeout);
0157 if (ret < 0)
0158 goto unlock;
0159
0160 ret = size;
0161 unlock:
0162 mutex_unlock(&led_cdev->led_access);
0163 return ret;
0164 }
0165
0166 static ssize_t flash_timeout_show(struct device *dev,
0167 struct device_attribute *attr, char *buf)
0168 {
0169 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0170 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0171
0172 return sprintf(buf, "%u\n", fled_cdev->timeout.val);
0173 }
0174 static DEVICE_ATTR_RW(flash_timeout);
0175
0176 static ssize_t max_flash_timeout_show(struct device *dev,
0177 struct device_attribute *attr, char *buf)
0178 {
0179 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0180 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0181
0182 return sprintf(buf, "%u\n", fled_cdev->timeout.max);
0183 }
0184 static DEVICE_ATTR_RO(max_flash_timeout);
0185
0186 static ssize_t flash_fault_show(struct device *dev,
0187 struct device_attribute *attr, char *buf)
0188 {
0189 struct led_classdev *led_cdev = dev_get_drvdata(dev);
0190 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0191 u32 fault, mask = 0x1;
0192 char *pbuf = buf;
0193 int i, ret, buf_len;
0194
0195 ret = led_get_flash_fault(fled_cdev, &fault);
0196 if (ret < 0)
0197 return -EINVAL;
0198
0199 *buf = '\0';
0200
0201 for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
0202 if (fault & mask) {
0203 buf_len = sprintf(pbuf, "%s ",
0204 led_flash_fault_names[i]);
0205 pbuf += buf_len;
0206 }
0207 mask <<= 1;
0208 }
0209
0210 return strlen(strcat(buf, "\n"));
0211 }
0212 static DEVICE_ATTR_RO(flash_fault);
0213
0214 static struct attribute *led_flash_strobe_attrs[] = {
0215 &dev_attr_flash_strobe.attr,
0216 NULL,
0217 };
0218
0219 static struct attribute *led_flash_timeout_attrs[] = {
0220 &dev_attr_flash_timeout.attr,
0221 &dev_attr_max_flash_timeout.attr,
0222 NULL,
0223 };
0224
0225 static struct attribute *led_flash_brightness_attrs[] = {
0226 &dev_attr_flash_brightness.attr,
0227 &dev_attr_max_flash_brightness.attr,
0228 NULL,
0229 };
0230
0231 static struct attribute *led_flash_fault_attrs[] = {
0232 &dev_attr_flash_fault.attr,
0233 NULL,
0234 };
0235
0236 static const struct attribute_group led_flash_strobe_group = {
0237 .attrs = led_flash_strobe_attrs,
0238 };
0239
0240 static const struct attribute_group led_flash_timeout_group = {
0241 .attrs = led_flash_timeout_attrs,
0242 };
0243
0244 static const struct attribute_group led_flash_brightness_group = {
0245 .attrs = led_flash_brightness_attrs,
0246 };
0247
0248 static const struct attribute_group led_flash_fault_group = {
0249 .attrs = led_flash_fault_attrs,
0250 };
0251
0252 static void led_flash_resume(struct led_classdev *led_cdev)
0253 {
0254 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0255
0256 call_flash_op(fled_cdev, flash_brightness_set,
0257 fled_cdev->brightness.val);
0258 call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val);
0259 }
0260
0261 static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev)
0262 {
0263 struct led_classdev *led_cdev = &fled_cdev->led_cdev;
0264 const struct led_flash_ops *ops = fled_cdev->ops;
0265 const struct attribute_group **flash_groups = fled_cdev->sysfs_groups;
0266
0267 int num_sysfs_groups = 0;
0268
0269 flash_groups[num_sysfs_groups++] = &led_flash_strobe_group;
0270
0271 if (ops->flash_brightness_set)
0272 flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
0273
0274 if (ops->timeout_set)
0275 flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
0276
0277 if (ops->fault_get)
0278 flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
0279
0280 led_cdev->groups = flash_groups;
0281 }
0282
0283 int led_classdev_flash_register_ext(struct device *parent,
0284 struct led_classdev_flash *fled_cdev,
0285 struct led_init_data *init_data)
0286 {
0287 struct led_classdev *led_cdev;
0288 const struct led_flash_ops *ops;
0289 int ret;
0290
0291 if (!fled_cdev)
0292 return -EINVAL;
0293
0294 led_cdev = &fled_cdev->led_cdev;
0295
0296 if (led_cdev->flags & LED_DEV_CAP_FLASH) {
0297 if (!led_cdev->brightness_set_blocking)
0298 return -EINVAL;
0299
0300 ops = fled_cdev->ops;
0301 if (!ops || !ops->strobe_set)
0302 return -EINVAL;
0303
0304 led_cdev->flash_resume = led_flash_resume;
0305
0306
0307 led_flash_init_sysfs_groups(fled_cdev);
0308 }
0309
0310
0311 ret = led_classdev_register_ext(parent, led_cdev, init_data);
0312 if (ret < 0)
0313 return ret;
0314
0315 return 0;
0316 }
0317 EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext);
0318
0319 void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev)
0320 {
0321 if (!fled_cdev)
0322 return;
0323
0324 led_classdev_unregister(&fled_cdev->led_cdev);
0325 }
0326 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
0327
0328 static void devm_led_classdev_flash_release(struct device *dev, void *res)
0329 {
0330 led_classdev_flash_unregister(*(struct led_classdev_flash **)res);
0331 }
0332
0333 int devm_led_classdev_flash_register_ext(struct device *parent,
0334 struct led_classdev_flash *fled_cdev,
0335 struct led_init_data *init_data)
0336 {
0337 struct led_classdev_flash **dr;
0338 int ret;
0339
0340 dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr),
0341 GFP_KERNEL);
0342 if (!dr)
0343 return -ENOMEM;
0344
0345 ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data);
0346 if (ret) {
0347 devres_free(dr);
0348 return ret;
0349 }
0350
0351 *dr = fled_cdev;
0352 devres_add(parent, dr);
0353
0354 return 0;
0355 }
0356 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext);
0357
0358 static int devm_led_classdev_flash_match(struct device *dev,
0359 void *res, void *data)
0360 {
0361 struct led_classdev_flash **p = res;
0362
0363 if (WARN_ON(!p || !*p))
0364 return 0;
0365
0366 return *p == data;
0367 }
0368
0369 void devm_led_classdev_flash_unregister(struct device *dev,
0370 struct led_classdev_flash *fled_cdev)
0371 {
0372 WARN_ON(devres_release(dev,
0373 devm_led_classdev_flash_release,
0374 devm_led_classdev_flash_match, fled_cdev));
0375 }
0376 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister);
0377
0378 static void led_clamp_align(struct led_flash_setting *s)
0379 {
0380 u32 v, offset;
0381
0382 v = s->val + s->step / 2;
0383 v = clamp(v, s->min, s->max);
0384 offset = v - s->min;
0385 offset = s->step * (offset / s->step);
0386 s->val = s->min + offset;
0387 }
0388
0389 int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout)
0390 {
0391 struct led_classdev *led_cdev = &fled_cdev->led_cdev;
0392 struct led_flash_setting *s = &fled_cdev->timeout;
0393
0394 s->val = timeout;
0395 led_clamp_align(s);
0396
0397 if (!(led_cdev->flags & LED_SUSPENDED))
0398 return call_flash_op(fled_cdev, timeout_set, s->val);
0399
0400 return 0;
0401 }
0402 EXPORT_SYMBOL_GPL(led_set_flash_timeout);
0403
0404 int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault)
0405 {
0406 return call_flash_op(fled_cdev, fault_get, fault);
0407 }
0408 EXPORT_SYMBOL_GPL(led_get_flash_fault);
0409
0410 int led_set_flash_brightness(struct led_classdev_flash *fled_cdev,
0411 u32 brightness)
0412 {
0413 struct led_classdev *led_cdev = &fled_cdev->led_cdev;
0414 struct led_flash_setting *s = &fled_cdev->brightness;
0415
0416 s->val = brightness;
0417 led_clamp_align(s);
0418
0419 if (!(led_cdev->flags & LED_SUSPENDED))
0420 return call_flash_op(fled_cdev, flash_brightness_set, s->val);
0421
0422 return 0;
0423 }
0424 EXPORT_SYMBOL_GPL(led_set_flash_brightness);
0425
0426 int led_update_flash_brightness(struct led_classdev_flash *fled_cdev)
0427 {
0428 struct led_flash_setting *s = &fled_cdev->brightness;
0429 u32 brightness;
0430
0431 if (has_flash_op(fled_cdev, flash_brightness_get)) {
0432 int ret = call_flash_op(fled_cdev, flash_brightness_get,
0433 &brightness);
0434 if (ret < 0)
0435 return ret;
0436
0437 s->val = brightness;
0438 }
0439
0440 return 0;
0441 }
0442 EXPORT_SYMBOL_GPL(led_update_flash_brightness);
0443
0444 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
0445 MODULE_DESCRIPTION("LED Flash class interface");
0446 MODULE_LICENSE("GPL v2");