1/* 2 * LED Class Core 3 * 4 * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu> 5 * Copyright (C) 2005-2006 Richard Purdie <rpurdie@openedhand.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/module.h> 13#include <linux/kernel.h> 14#include <linux/init.h> 15#include <linux/list.h> 16#include <linux/spinlock.h> 17#include <linux/device.h> 18#include <linux/sysdev.h> 19#include <linux/timer.h> 20#include <linux/err.h> 21#include <linux/ctype.h> 22#include <linux/leds.h> 23#include "leds.h" 24 25static struct class *leds_class; 26 27static ssize_t led_brightness_show(struct class_device *dev, char *buf) 28{ 29 struct led_classdev *led_cdev = class_get_devdata(dev); 30 ssize_t ret = 0; 31 32 /* no lock needed for this */ 33 sprintf(buf, "%u\n", led_cdev->brightness); 34 ret = strlen(buf) + 1; 35 36 return ret; 37} 38 39static ssize_t led_brightness_store(struct class_device *dev, 40 const char *buf, size_t size) 41{ 42 struct led_classdev *led_cdev = class_get_devdata(dev); 43 ssize_t ret = -EINVAL; 44 char *after; 45 unsigned long state = simple_strtoul(buf, &after, 10); 46 size_t count = after - buf; 47 48 if (*after && isspace(*after)) 49 count++; 50 51 if (count == size) { 52 ret = count; 53 led_set_brightness(led_cdev, state); 54 } 55 56 return ret; 57} 58 59static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show, 60 led_brightness_store); 61#ifdef CONFIG_LEDS_TRIGGERS 62static CLASS_DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); 63#endif 64 65/** 66 * led_classdev_suspend - suspend an led_classdev. 67 * @led_cdev: the led_classdev to suspend. 68 */ 69void led_classdev_suspend(struct led_classdev *led_cdev) 70{ 71 led_cdev->flags |= LED_SUSPENDED; 72 led_cdev->brightness_set(led_cdev, 0); 73} 74EXPORT_SYMBOL_GPL(led_classdev_suspend); 75 76/** 77 * led_classdev_resume - resume an led_classdev. 78 * @led_cdev: the led_classdev to resume. 79 */ 80void led_classdev_resume(struct led_classdev *led_cdev) 81{ 82 led_cdev->brightness_set(led_cdev, led_cdev->brightness); 83 led_cdev->flags &= ~LED_SUSPENDED; 84} 85EXPORT_SYMBOL_GPL(led_classdev_resume); 86 87/** 88 * led_classdev_register - register a new object of led_classdev class. 89 * @dev: The device to register. 90 * @led_cdev: the led_classdev structure for this device. 91 */ 92int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) 93{ 94 int rc; 95 96 led_cdev->class_dev = class_device_create(leds_class, NULL, 0, 97 parent, "%s", led_cdev->name); 98 if (unlikely(IS_ERR(led_cdev->class_dev))) 99 return PTR_ERR(led_cdev->class_dev); 100 101 class_set_devdata(led_cdev->class_dev, led_cdev); 102 103 /* register the attributes */ 104 rc = class_device_create_file(led_cdev->class_dev, 105 &class_device_attr_brightness); 106 if (rc) 107 goto err_out; 108 109 /* add to the list of leds */ 110 write_lock(&leds_list_lock); 111 list_add_tail(&led_cdev->node, &leds_list); 112 write_unlock(&leds_list_lock); 113 114#ifdef CONFIG_LEDS_TRIGGERS 115 rwlock_init(&led_cdev->trigger_lock); 116 117 rc = class_device_create_file(led_cdev->class_dev, 118 &class_device_attr_trigger); 119 if (rc) 120 goto err_out_led_list; 121 122 led_trigger_set_default(led_cdev); 123#endif 124 125 printk(KERN_INFO "Registered led device: %s\n", 126 led_cdev->class_dev->class_id); 127 128 return 0; 129 130#ifdef CONFIG_LEDS_TRIGGERS 131err_out_led_list: 132 class_device_remove_file(led_cdev->class_dev, 133 &class_device_attr_brightness); 134 list_del(&led_cdev->node); 135#endif 136err_out: 137 class_device_unregister(led_cdev->class_dev); 138 return rc; 139} 140EXPORT_SYMBOL_GPL(led_classdev_register); 141 142/** 143 * led_classdev_unregister - unregisters a object of led_properties class. 144 * @led_cdev: the led device to unregister 145 * 146 * Unregisters a previously registered via led_classdev_register object. 147 */ 148void led_classdev_unregister(struct led_classdev *led_cdev) 149{ 150 class_device_remove_file(led_cdev->class_dev, 151 &class_device_attr_brightness); 152#ifdef CONFIG_LEDS_TRIGGERS 153 class_device_remove_file(led_cdev->class_dev, 154 &class_device_attr_trigger); 155 write_lock(&led_cdev->trigger_lock); 156 if (led_cdev->trigger) 157 led_trigger_set(led_cdev, NULL); 158 write_unlock(&led_cdev->trigger_lock); 159#endif 160 161 class_device_unregister(led_cdev->class_dev); 162 163 write_lock(&leds_list_lock); 164 list_del(&led_cdev->node); 165 write_unlock(&leds_list_lock); 166} 167EXPORT_SYMBOL_GPL(led_classdev_unregister); 168 169static int __init leds_init(void) 170{ 171 leds_class = class_create(THIS_MODULE, "leds"); 172 if (IS_ERR(leds_class)) 173 return PTR_ERR(leds_class); 174 return 0; 175} 176 177static void __exit leds_exit(void) 178{ 179 class_destroy(leds_class); 180} 181 182subsys_initcall(leds_init); 183module_exit(leds_exit); 184 185MODULE_AUTHOR("John Lenz, Richard Purdie"); 186MODULE_LICENSE("GPL"); 187MODULE_DESCRIPTION("LED Class Interface"); 188