aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorentin Chary <corentincj@iksaif.net>2007-01-26 08:04:35 -0500
committerLen Brown <len.brown@intel.com>2007-01-30 01:37:00 -0500
commitbe18cdabb8ed40ff4b8a240e0d6f4e6c30ff866d (patch)
treed0ca8b148430db06d3b428a3cdf905c4434fab2e
parent85091b718969be7b8e6f795af7e264b8afcd7a6d (diff)
asus-laptop: add led support
Add led support, using generic led class. Thomas Tuttle's patch <http://lkml.org/lkml/2006/7/6/247> was very usefull. We use hotk->status to store led status because it's very hard to find acpi method to get the right status... To reduce the code, I use a lot of macro (ASUS_LED, ASUS_LED_REGISTER, etc ...), because the code is the same for all leds ... Signed-off-by: Corentin Chary <corentincj@iksaif.net> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/asus-laptop.c145
2 files changed, 146 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4b1e367e8feb..87e1db8ffd47 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -74,6 +74,7 @@ config ASUS_LAPTOP
74 depends on X86 74 depends on X86
75 depends on ACPI 75 depends on ACPI
76 depends on EXPERIMENTAL && !ACPI_ASUS 76 depends on EXPERIMENTAL && !ACPI_ASUS
77 depends on LEDS_CLASS
77 ---help--- 78 ---help---
78 This is the new Linux driver for Asus laptops. It may also support some 79 This is the new Linux driver for Asus laptops. It may also support some
79 MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate 80 MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c
index 959b20f5604a..d0d5ee9b358f 100644
--- a/drivers/misc/asus-laptop.c
+++ b/drivers/misc/asus-laptop.c
@@ -40,6 +40,7 @@
40#include <linux/types.h> 40#include <linux/types.h>
41#include <linux/err.h> 41#include <linux/err.h>
42#include <linux/proc_fs.h> 42#include <linux/proc_fs.h>
43#include <linux/leds.h>
43#include <linux/platform_device.h> 44#include <linux/platform_device.h>
44#include <acpi/acpi_drivers.h> 45#include <acpi/acpi_drivers.h>
45#include <acpi/acpi_bus.h> 46#include <acpi/acpi_bus.h>
@@ -54,6 +55,14 @@
54#define ASUS_HOTK_FILE "asus-laptop" 55#define ASUS_HOTK_FILE "asus-laptop"
55#define ASUS_HOTK_PREFIX "\\_SB.ATKD." 56#define ASUS_HOTK_PREFIX "\\_SB.ATKD."
56 57
58/*
59 * Flags for hotk status
60 */
61#define MLED_ON 0x04 //mail LED
62#define TLED_ON 0x08 //touchpad LED
63#define RLED_ON 0x10 //Record LED
64#define PLED_ON 0x20 //Phone LED
65
57#define ASUS_LOG ASUS_HOTK_FILE ": " 66#define ASUS_LOG ASUS_HOTK_FILE ": "
58#define ASUS_ERR KERN_ERR ASUS_LOG 67#define ASUS_ERR KERN_ERR ASUS_LOG
59#define ASUS_WARNING KERN_WARNING ASUS_LOG 68#define ASUS_WARNING KERN_WARNING ASUS_LOG
@@ -69,6 +78,12 @@ MODULE_LICENSE("GPL");
69 static acpi_handle object##_handle = NULL; \ 78 static acpi_handle object##_handle = NULL; \
70 static char *object##_paths[] = { paths } 79 static char *object##_paths[] = { paths }
71 80
81/* LED */
82ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
83ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
84ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */
85ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */
86
72/* 87/*
73 * This is the main structure, we can use it to store anything interesting 88 * This is the main structure, we can use it to store anything interesting
74 * about the hotk device 89 * about the hotk device
@@ -106,6 +121,28 @@ static struct acpi_driver asus_hotk_driver = {
106 }, 121 },
107}; 122};
108 123
124/* These functions actually update the LED's, and are called from a
125 * workqueue. By doing this as separate work rather than when the LED
126 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
127 * potentially bad time, such as a timer interrupt. */
128static struct workqueue_struct *led_workqueue;
129
130#define ASUS_LED(object, ledname) \
131 static void object##_led_set(struct led_classdev *led_cdev, \
132 enum led_brightness value); \
133 static void object##_led_update(struct work_struct *ignored); \
134 static int object##_led_wk; \
135 DECLARE_WORK(object##_led_work, object##_led_update); \
136 static struct led_classdev object##_led = { \
137 .name = "asus:" ledname, \
138 .brightness_set = object##_led_set, \
139 }
140
141ASUS_LED(mled, "mail");
142ASUS_LED(tled, "touchpad");
143ASUS_LED(rled, "record");
144ASUS_LED(pled, "phone");
145
109/* 146/*
110 * This function evaluates an ACPI method, given an int as parameter, the 147 * This function evaluates an ACPI method, given an int as parameter, the
111 * method is searched within the scope of the handle, can be NULL. The output 148 * method is searched within the scope of the handle, can be NULL. The output
@@ -144,6 +181,43 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val,
144 return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); 181 return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
145} 182}
146 183
184/* Generic LED functions */
185static int read_status(int mask)
186{
187 return (hotk->status & mask) ? 1 : 0;
188}
189
190static void write_status(acpi_handle handle, int out, int mask,
191 int invert)
192{
193 hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
194
195 if (invert) /* invert target value */
196 out = !out & 0x1;
197
198 if (handle && !write_acpi_int(handle, NULL, out, NULL))
199 printk(ASUS_WARNING " write failed\n");
200}
201
202/* /sys/class/led handlers */
203#define ASUS_LED_HANDLER(object, mask, invert) \
204 static void object##_led_set(struct led_classdev *led_cdev, \
205 enum led_brightness value) \
206 { \
207 object##_led_wk = value; \
208 queue_work(led_workqueue, &object##_led_work); \
209 } \
210 static void object##_led_update(struct work_struct *ignored) \
211 { \
212 int value = object##_led_wk; \
213 write_status(object##_set_handle, value, (mask), (invert)); \
214 }
215
216ASUS_LED_HANDLER(mled, MLED_ON, 1);
217ASUS_LED_HANDLER(pled, PLED_ON, 0);
218ASUS_LED_HANDLER(rled, RLED_ON, 0);
219ASUS_LED_HANDLER(tled, TLED_ON, 0);
220
147/* 221/*
148 * Platform device handlers 222 * Platform device handlers
149 */ 223 */
@@ -361,6 +435,11 @@ static int asus_hotk_get_info(void)
361 if(*string) 435 if(*string)
362 printk(ASUS_NOTICE " %s model detected\n", string); 436 printk(ASUS_NOTICE " %s model detected\n", string);
363 437
438 ASUS_HANDLE_INIT(mled_set);
439 ASUS_HANDLE_INIT(tled_set);
440 ASUS_HANDLE_INIT(rled_set);
441 ASUS_HANDLE_INIT(pled_set);
442
364 kfree(model); 443 kfree(model);
365 444
366 return AE_OK; 445 return AE_OK;
@@ -452,8 +531,25 @@ static int asus_hotk_remove(struct acpi_device *device, int type)
452 return 0; 531 return 0;
453} 532}
454 533
534#define ASUS_LED_UNREGISTER(object) \
535 if(object##_led.class_dev \
536 && !IS_ERR(object##_led.class_dev)) \
537 led_classdev_unregister(&object##_led)
538
539static void asus_led_exit(void)
540{
541 ASUS_LED_UNREGISTER(mled);
542 ASUS_LED_UNREGISTER(tled);
543 ASUS_LED_UNREGISTER(pled);
544 ASUS_LED_UNREGISTER(rled);
545
546 destroy_workqueue(led_workqueue);
547}
548
455static void __exit asus_laptop_exit(void) 549static void __exit asus_laptop_exit(void)
456{ 550{
551 asus_led_exit();
552
457 acpi_bus_unregister_driver(&asus_hotk_driver); 553 acpi_bus_unregister_driver(&asus_hotk_driver);
458 sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group); 554 sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group);
459 platform_device_unregister(asuspf_device); 555 platform_device_unregister(asuspf_device);
@@ -462,8 +558,48 @@ static void __exit asus_laptop_exit(void)
462 kfree(asus_info); 558 kfree(asus_info);
463} 559}
464 560
561static int asus_led_register(acpi_handle handle,
562 struct led_classdev * ldev,
563 struct device * dev)
564{
565 if(!handle)
566 return 0;
567
568 return led_classdev_register(dev, ldev);
569}
570#define ASUS_LED_REGISTER(object, device) \
571 asus_led_register(object##_set_handle, &object##_led, device)
572
573static int asus_led_init(struct device * dev)
574{
575 int rv;
576
577 rv = ASUS_LED_REGISTER(mled, dev);
578 if(rv)
579 return rv;
580
581 rv = ASUS_LED_REGISTER(tled, dev);
582 if(rv)
583 return rv;
584
585 rv = ASUS_LED_REGISTER(rled, dev);
586 if(rv)
587 return rv;
588
589 rv = ASUS_LED_REGISTER(pled, dev);
590 if(rv)
591 return rv;
592
593 led_workqueue = create_singlethread_workqueue("led_workqueue");
594 if(!led_workqueue)
595 return -ENOMEM;
596
597 return 0;
598}
599
465static int __init asus_laptop_init(void) 600static int __init asus_laptop_init(void)
466{ 601{
602 struct device *dev;
467 int result; 603 int result;
468 604
469 if (acpi_disabled) 605 if (acpi_disabled)
@@ -490,6 +626,12 @@ static int __init asus_laptop_init(void)
490 return -ENODEV; 626 return -ENODEV;
491 } 627 }
492 628
629 dev = acpi_get_physical_device(hotk->device->handle);
630
631 result = asus_led_init(dev);
632 if(result)
633 goto fail_led;
634
493 /* Register platform stuff */ 635 /* Register platform stuff */
494 result = platform_driver_register(&asuspf_driver); 636 result = platform_driver_register(&asuspf_driver);
495 if (result) 637 if (result)
@@ -522,6 +664,9 @@ fail_platform_device1:
522 platform_driver_unregister(&asuspf_driver); 664 platform_driver_unregister(&asuspf_driver);
523 665
524fail_platform_driver: 666fail_platform_driver:
667 asus_led_exit();
668
669fail_led:
525 670
526 return result; 671 return result;
527} 672}