1 From af116101924914a9655dfad108548d0db58c40f9 Mon Sep 17 00:00:00 2001
2 From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
3 Date: Sat, 26 Apr 2008 01:02:25 -0300
4 Subject: ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds (v3.2)
6 Add a sysfs led class interface to the led subdriver.
8 Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
9 Cc: Richard Purdie <rpurdie@rpsys.net>
10 Signed-off-by: Len Brown <len.brown@intel.com>
12 Documentation/laptops/thinkpad-acpi.txt | 47 +++++++++--
13 drivers/misc/thinkpad_acpi.c | 136 ++++++++++++++++++++++++++++++-
14 2 files changed, 176 insertions(+), 7 deletions(-)
16 diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
17 index af1f2bc..73b80a7 100644
18 --- a/Documentation/laptops/thinkpad-acpi.txt
19 +++ b/Documentation/laptops/thinkpad-acpi.txt
20 @@ -876,28 +876,63 @@ The cmos command interface is prone to firmware split-brain problems, as
21 in newer ThinkPads it is just a compatibility layer. Do not use it, it is
22 exported just as a debug tool.
24 -LED control -- /proc/acpi/ibm/led
25 ----------------------------------
29 -Some of the LED indicators can be controlled through this feature. The
30 -available commands are:
31 +procfs: /proc/acpi/ibm/led
32 +sysfs attributes: as per led class, see below for names
34 +Some of the LED indicators can be controlled through this feature. On
35 +some older ThinkPad models, it is possible to query the status of the
36 +LED indicators as well. Newer ThinkPads cannot query the real status
37 +of the LED indicators.
41 +The available commands are:
43 echo '<led number> on' >/proc/acpi/ibm/led
44 echo '<led number> off' >/proc/acpi/ibm/led
45 echo '<led number> blink' >/proc/acpi/ibm/led
47 The <led number> range is 0 to 7. The set of LEDs that can be
48 -controlled varies from model to model. Here is the mapping on the X40:
49 +controlled varies from model to model. Here is the common ThinkPad
58 + 5 - UltraBase battery slot
62 All of the above can be turned on and off and can be made to blink.
66 +The ThinkPad LED sysfs interface is described in detail by the led class
67 +documentation, in Documentation/leds-class.txt.
69 +The leds are named (in LED ID order, from 0 to 7):
70 +"tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt",
71 +"tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt",
72 +"tpacpi::unknown_led", "tpacpi::standby".
74 +Due to limitations in the sysfs led class, if the status of the LED
75 +indicators cannot be read due to an error, thinkpad-acpi will report it as
76 +a brightness of zero (same as LED off).
78 +If the thinkpad firmware doesn't support reading the current status,
79 +trying to read the current LED brightness will just return whatever
80 +brightness was last written to that attribute.
82 +These LEDs can blink using hardware acceleration. To request that a
83 +ThinkPad indicator LED should blink in hardware accelerated mode, use the
84 +"timer" trigger, and leave the delay_on and delay_off parameters set to
85 +zero (to request hardware acceleration autodetection).
87 ACPI sounds -- /proc/acpi/ibm/beep
88 ----------------------------------
90 diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
91 index 38a119b..2ab3633 100644
92 --- a/drivers/misc/thinkpad_acpi.c
93 +++ b/drivers/misc/thinkpad_acpi.c
94 @@ -277,6 +277,7 @@ struct tpacpi_led_classdev {
95 struct led_classdev led_classdev;
96 struct work_struct work;
97 enum led_brightness new_brightness;
101 /****************************************************************************
102 @@ -3814,20 +3815,38 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
103 "LED", /* all others */
106 +#define TPACPI_LED_NUMLEDS 8
107 +static struct tpacpi_led_classdev *tpacpi_leds;
108 +static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
109 +static const char const *tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
110 + /* there's a limit of 19 chars + NULL before 2.6.26 */
112 + "tpacpi:orange:batt",
113 + "tpacpi:green:batt",
114 + "tpacpi::dock_active",
115 + "tpacpi::bay_active",
116 + "tpacpi::dock_batt",
117 + "tpacpi::unknown_led",
121 static int led_get_status(unsigned int led)
124 + enum led_status_t led_s;
126 switch (led_supported) {
128 if (!acpi_evalf(ec_handle,
129 &status, "GLED", "dd", 1 << led))
131 - return (status == 0)?
132 + led_s = (status == 0)?
137 + tpacpi_led_state_cache[led] = led_s;
142 @@ -3874,11 +3893,96 @@ static int led_set_status(unsigned int led, enum led_status_t ledstatus)
147 + tpacpi_led_state_cache[led] = ledstatus;
152 +static void led_sysfs_set_status(unsigned int led,
153 + enum led_brightness brightness)
155 + led_set_status(led,
156 + (brightness == LED_OFF) ?
158 + (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
159 + TPACPI_LED_BLINK : TPACPI_LED_ON);
162 +static void led_set_status_worker(struct work_struct *work)
164 + struct tpacpi_led_classdev *data =
165 + container_of(work, struct tpacpi_led_classdev, work);
167 + if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
168 + led_sysfs_set_status(data->led, data->new_brightness);
171 +static void led_sysfs_set(struct led_classdev *led_cdev,
172 + enum led_brightness brightness)
174 + struct tpacpi_led_classdev *data = container_of(led_cdev,
175 + struct tpacpi_led_classdev, led_classdev);
177 + data->new_brightness = brightness;
178 + schedule_work(&data->work);
181 +static int led_sysfs_blink_set(struct led_classdev *led_cdev,
182 + unsigned long *delay_on, unsigned long *delay_off)
184 + struct tpacpi_led_classdev *data = container_of(led_cdev,
185 + struct tpacpi_led_classdev, led_classdev);
187 + /* Can we choose the flash rate? */
188 + if (*delay_on == 0 && *delay_off == 0) {
189 + /* yes. set them to the hardware blink rate (1 Hz) */
190 + *delay_on = 500; /* ms */
191 + *delay_off = 500; /* ms */
192 + } else if ((*delay_on != 500) || (*delay_off != 500))
195 + data->new_brightness = TPACPI_LED_BLINK;
196 + schedule_work(&data->work);
201 +static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
205 + struct tpacpi_led_classdev *data = container_of(led_cdev,
206 + struct tpacpi_led_classdev, led_classdev);
208 + rc = led_get_status(data->led);
210 + if (rc == TPACPI_LED_OFF || rc < 0)
211 + rc = LED_OFF; /* no error handling in led class :( */
218 +static void led_exit(void)
222 + for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
223 + if (tpacpi_leds[i].led_classdev.name)
224 + led_classdev_unregister(&tpacpi_leds[i].led_classdev);
227 + kfree(tpacpi_leds);
228 + tpacpi_leds = NULL;
231 static int __init led_init(struct ibm_init_struct *iibm)
236 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
238 TPACPI_ACPIHANDLE_INIT(led);
239 @@ -3899,6 +4003,35 @@ static int __init led_init(struct ibm_init_struct *iibm)
240 vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
241 str_supported(led_supported), led_supported);
243 + tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
245 + if (!tpacpi_leds) {
246 + printk(TPACPI_ERR "Out of memory for LED data\n");
250 + for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
251 + tpacpi_leds[i].led = i;
253 + tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set;
254 + tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set;
255 + if (led_supported == TPACPI_LED_570)
256 + tpacpi_leds[i].led_classdev.brightness_get =
259 + tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
261 + INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
263 + rc = led_classdev_register(&tpacpi_pdev->dev,
264 + &tpacpi_leds[i].led_classdev);
266 + tpacpi_leds[i].led_classdev.name = NULL;
272 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
275 @@ -3969,6 +4102,7 @@ static struct ibm_struct led_driver_data = {
282 /*************************************************************************