* [PATCH 1/3] LED: add support to leds with readable status
2008-02-07 23:49 [RFC] LED brightness_get and thinkpad-acpi led-class support Henrique de Moraes Holschuh
@ 2008-02-07 23:57 ` Henrique de Moraes Holschuh
2008-02-07 23:57 ` [PATCH 2/3] ACPI: thinkpad-acpi: add sysfs led class support for thinklight Henrique de Moraes Holschuh
2008-02-07 23:57 ` [PATCH 3/3] ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds Henrique de Moraes Holschuh
2 siblings, 0 replies; 4+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-02-07 23:57 UTC (permalink / raw)
To: Richard Purdie
Cc: linux-kernel, ibm-acpi-devel, linux-acpi,
Henrique de Moraes Holschuh, Richard Purdie
Some led hardware allows drivers to query the led state, and this patch
adds a hook to let the led class take advantage of that information when
available.
Without this functionality, when access to the led hardware is not
exclusive (i.e. firmware or hardware might change its state behind the
kernel's back), reality goes out of sync with the led class' idea of what
the led is doing, which is annoying at best.
Behaviour for drivers that do not or cannot read the led status is
unchanged.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
drivers/leds/led-class.c | 9 +++++++++
include/linux/leds.h | 2 ++
2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 835f939..3bb38a8 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -24,6 +24,12 @@
static struct class *leds_class;
+static void led_update_brightness(struct led_classdev *led_cdev)
+{
+ if (led_cdev->brightness_get)
+ led_cdev->brightness = led_cdev->brightness_get(led_cdev);
+}
+
static ssize_t led_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -31,6 +37,7 @@ static ssize_t led_brightness_show(struct device *dev,
ssize_t ret = 0;
/* no lock needed for this */
+ led_update_brightness(led_cdev);
sprintf(buf, "%u\n", led_cdev->brightness);
ret = strlen(buf) + 1;
@@ -112,6 +119,8 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
list_add_tail(&led_cdev->node, &leds_list);
up_write(&leds_list_lock);
+ led_update_brightness(led_cdev);
+
#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 4cb5927..dd5969e 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -37,6 +37,8 @@ struct led_classdev {
/* Set LED brightness level */
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightness brightness);
+ /* Get LED brightness level */
+ enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
/* Activate hardware accelerated blink */
int (*blink_set)(struct led_classdev *led_cdev,
--
1.5.3.8
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/3] ACPI: thinkpad-acpi: add sysfs led class support for thinklight
2008-02-07 23:49 [RFC] LED brightness_get and thinkpad-acpi led-class support Henrique de Moraes Holschuh
2008-02-07 23:57 ` [PATCH 1/3] LED: add support to leds with readable status Henrique de Moraes Holschuh
@ 2008-02-07 23:57 ` Henrique de Moraes Holschuh
2008-02-07 23:57 ` [PATCH 3/3] ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds Henrique de Moraes Holschuh
2 siblings, 0 replies; 4+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-02-07 23:57 UTC (permalink / raw)
To: Richard Purdie
Cc: linux-kernel, ibm-acpi-devel, linux-acpi,
Henrique de Moraes Holschuh, Richard Purdie
Add a sysfs led class interface to the thinklight (light subdriver).
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
Documentation/thinkpad-acpi.txt | 25 ++++++++++++++++++++-----
drivers/misc/thinkpad_acpi.c | 39 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 58 insertions(+), 6 deletions(-)
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt
index 204161e..0301394 100644
--- a/Documentation/thinkpad-acpi.txt
+++ b/Documentation/thinkpad-acpi.txt
@@ -640,16 +640,31 @@ while others are still having problems. For more information:
https://bugs.freedesktop.org/show_bug.cgi?id=2000
-ThinkLight control -- /proc/acpi/ibm/light
-------------------------------------------
+ThinkLight control
+------------------
+
+procfs: /proc/acpi/ibm/light
+sysfs attributes: as per led class, for the "tp::thinklight" led
+
+procfs notes:
-The current status of the ThinkLight can be found in this file. A few
-models which do not make the status available will show it as
-"unknown". The available commands are:
+The ThinkLight status can be read and set through the procfs interface. A
+few models which do not make the status available will show the ThinkLight
+status as "unknown". The available commands are:
echo on > /proc/acpi/ibm/light
echo off > /proc/acpi/ibm/light
+sysfs notes:
+
+The ThinkLight sysfs interface is documented by the led class
+documentation, in Documentation/leds-class.txt. The ThinkLight led name
+is "tp::thinklight".
+
+Due to limitations in the sysfs led class, if the status of the thinklight
+cannot be read or if it is unknown, thinkpad-acpi will report it as "off".
+It is impossible to know if the status returned through sysfs is valid.
+
Docking / undocking -- /proc/acpi/ibm/dock
------------------------------------------
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index c2cb796..68bd8a1 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -67,6 +67,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/input.h>
+#include <linux/leds.h>
#include <asm/uaccess.h>
#include <linux/dmi.h>
@@ -3095,8 +3096,27 @@ static int light_set_status(int status)
return -ENXIO;
}
+static void light_sysfs_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ light_set_status((brightness != LED_OFF));
+}
+
+static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
+{
+ return (light_get_status() == 1)? LED_FULL : LED_OFF;
+}
+
+static struct led_classdev tpacpi_led_thinklight = {
+ .name = "tp::thinklight",
+ .brightness_set = &light_sysfs_set,
+ .brightness_get = &light_sysfs_get,
+};
+
static int __init light_init(struct ibm_init_struct *iibm)
{
+ int rc = 0;
+
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
TPACPI_ACPIHANDLE_INIT(ledb);
@@ -3115,7 +3135,23 @@ static int __init light_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "light is %s\n",
str_supported(tp_features.light));
- return (tp_features.light)? 0 : 1;
+ if (tp_features.light) {
+ rc = led_classdev_register(&tpacpi_pdev->dev,
+ &tpacpi_led_thinklight);
+ }
+
+ if (rc < 0) {
+ tp_features.light = 0;
+ tp_features.light_status = 0;
+ } else {
+ rc = (tp_features.light)? 0 : 1;
+ }
+ return rc;
+}
+
+static void light_exit(void)
+{
+ led_classdev_unregister(&tpacpi_led_thinklight);
}
static int light_read(char *p)
@@ -3163,6 +3199,7 @@ static struct ibm_struct light_driver_data = {
.name = "light",
.read = light_read,
.write = light_write,
+ .exit = light_exit,
};
/*************************************************************************
--
1.5.3.8
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 3/3] ACPI: thinkpad-acpi: add sysfs led class support to thinkpad leds
2008-02-07 23:49 [RFC] LED brightness_get and thinkpad-acpi led-class support Henrique de Moraes Holschuh
2008-02-07 23:57 ` [PATCH 1/3] LED: add support to leds with readable status Henrique de Moraes Holschuh
2008-02-07 23:57 ` [PATCH 2/3] ACPI: thinkpad-acpi: add sysfs led class support for thinklight Henrique de Moraes Holschuh
@ 2008-02-07 23:57 ` Henrique de Moraes Holschuh
2 siblings, 0 replies; 4+ messages in thread
From: Henrique de Moraes Holschuh @ 2008-02-07 23:57 UTC (permalink / raw)
To: Richard Purdie
Cc: linux-kernel, ibm-acpi-devel, linux-acpi,
Henrique de Moraes Holschuh, Richard Purdie
Add a sysfs led class interface to the led subdriver.
Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
Documentation/thinkpad-acpi.txt | 46 +++++++++++++--
drivers/misc/thinkpad_acpi.c | 120 +++++++++++++++++++++++++++++++++++++++
2 files changed, 160 insertions(+), 6 deletions(-)
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt
index 0301394..53aa620 100644
--- a/Documentation/thinkpad-acpi.txt
+++ b/Documentation/thinkpad-acpi.txt
@@ -823,28 +823,62 @@ The cmos command interface is prone to firmware split-brain problems, as
in newer ThinkPads it is just a compatibility layer. Do not use it, it is
exported just as a debug tool.
-LED control -- /proc/acpi/ibm/led
----------------------------------
+LED control
+-----------
-Some of the LED indicators can be controlled through this feature. The
-available commands are:
+procfs: /proc/acpi/ibm/led
+sysfs attributes: as per led class, see below for names
+
+Some of the LED indicators can be controlled through this feature. On
+some older ThinkPad models, it is possible to query the status of the
+LED indicators as well. Newer ThinkPads cannot query the real status
+of the LED indicators.
+
+procfs notes:
+
+The available commands are:
echo '<led number> on' >/proc/acpi/ibm/led
echo '<led number> off' >/proc/acpi/ibm/led
echo '<led number> blink' >/proc/acpi/ibm/led
The <led number> range is 0 to 7. The set of LEDs that can be
-controlled varies from model to model. Here is the mapping on the X40:
+controlled varies from model to model. Here is the common ThinkPad
+mapping:
0 - power
1 - battery (orange)
2 - battery (green)
- 3 - UltraBase
+ 3 - UltraBase/dock
4 - UltraBay
+ 5 - UltraBase battery slot
+ 6 - (unknown)
7 - standby
All of the above can be turned on and off and can be made to blink.
+sysfs notes:
+
+The ThinkPad LED sysfs interface is described in detail by the led class
+documentation, in Documentation/leds-class.txt.
+
+The leds are named: "tp::power", "tp:orange:battery", "tp:green:battery",
+"tp::ultrabase", "tp::ultrabay", "tp::ultrabase_batt", "tp::unknown_led",
+"tp::standby".
+
+Due to limitations in the sysfs led class, if the status of the LED
+indicators cannot be read due to an error, thinkpad-acpi will report it as
+a brightness of zero (same as LED off).
+
+If the thinkpad firmware doesn't support reading the current status, trying
+to read the current LED brightness will just return whatever brightness was
+last written to that attribute.
+
+These LEDs can blink using hardware acceleration. To request that a
+ThinkPad indicator LED blinks in hardware, use the "timer" trigger, and
+leave the delay_on and delay_off parameters set to zero (to request
+hardware acceleration autodetection).
+
ACPI sounds -- /proc/acpi/ibm/beep
----------------------------------
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index 68bd8a1..8f0ff2f 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -3233,6 +3233,19 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
"LED", /* all others */
); /* R30, R31 */
+#define TPACPI_LED_NUMLEDS 8
+static struct led_classdev *tpacpi_leds;
+static const char const *tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
+ "tp::power",
+ "tp:orange:battery",
+ "tp:green:battery",
+ "tp::ultrabase",
+ "tp::ultrabay",
+ "tp::ultrabase_batt", /* 19 char + NULL limit on sysfs */
+ "tp::unknown_led",
+ "tp::standby",
+};
+
static int led_get_status(unsigned int led)
{
int status;
@@ -3296,10 +3309,93 @@ static int led_set_status(unsigned int led, enum led_status_t ledstatus)
return rc;
}
+/*
+ * This is highly dependent on the tpacpi_leds array
+ */
+static void led_sysfs_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ unsigned int led;
+ enum led_status_t newstatus;
+
+ led = led_cdev - tpacpi_leds;
+ BUG_ON(led >= TPACPI_LED_NUMLEDS);
+
+ if (brightness == LED_OFF)
+ newstatus = TPACPI_LED_OFF;
+ else
+ newstatus = TPACPI_LED_ON;
+
+ led_set_status(led, newstatus);
+}
+
+/*
+ * This is highly dependent on the tpacpi_leds array
+ */
+static int led_sysfs_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ unsigned int led;
+
+ led = led_cdev - tpacpi_leds;
+ BUG_ON(led >= TPACPI_LED_NUMLEDS);
+
+ /* Can we choose the flash rate? */
+ if (*delay_on == 0 && *delay_off == 0) {
+ /* yes. set them to the hardware blink rate (1 Hz) */
+ *delay_on = 500; /* ms */
+ *delay_off = 500; /* ms */
+ } else if ((*delay_on != 500) || (*delay_off != 500))
+ return -EINVAL;
+
+ led_set_status(led, TPACPI_LED_BLINK);
+
+ return 0;
+}
+
+/*
+ * This is highly dependent on the tpacpi_leds array
+ */
+static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
+{
+ unsigned int led;
+ int rc;
+
+ led = led_cdev - tpacpi_leds;
+ BUG_ON(led >= TPACPI_LED_NUMLEDS);
+
+ rc = led_get_status(led);
+
+ if (rc == TPACPI_LED_OFF || rc < 0)
+ rc = LED_OFF; /* no error handling in led class :( */
+ else
+ rc = LED_FULL;
+
+ return rc;
+}
+
+static void led_exit(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+ if (tpacpi_leds[i].name)
+ led_classdev_unregister(&tpacpi_leds[i]);
+ }
+
+ kfree(tpacpi_leds);
+ tpacpi_leds = NULL;
+}
+
static int __init led_init(struct ibm_init_struct *iibm)
{
+ unsigned int i;
+ int rc;
+
vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
+ tpacpi_leds = NULL;
+
TPACPI_ACPIHANDLE_INIT(led);
if (!led_handle)
@@ -3318,6 +3414,29 @@ static int __init led_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
str_supported(led_supported), led_supported);
+ tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
+ GFP_KERNEL);
+ if (!tpacpi_leds) {
+ printk(TPACPI_ERR "Out of memory for LED data\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+ tpacpi_leds[i].brightness_set = &led_sysfs_set;
+ tpacpi_leds[i].blink_set = &led_sysfs_blink_set;
+ if (led_supported == TPACPI_LED_570)
+ tpacpi_leds[i].brightness_get = &led_sysfs_get;
+ tpacpi_leds[i].name = tpacpi_led_names[i];
+
+ rc = led_classdev_register(&tpacpi_pdev->dev,
+ &tpacpi_leds[i]);
+ if (rc < 0) {
+ tpacpi_leds[i].name = NULL;
+ led_exit();
+ return rc;
+ }
+ }
+
return (led_supported != TPACPI_LED_NONE)? 0 : 1;
}
@@ -3388,6 +3507,7 @@ static struct ibm_struct led_driver_data = {
.name = "led",
.read = led_read,
.write = led_write,
+ .exit = led_exit,
};
/*************************************************************************
--
1.5.3.8
^ permalink raw reply related [flat|nested] 4+ messages in thread