PATCH: Integrate asus_acpi LED's with new LED subsystem

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here is a patch I wrote that allows the LED's that the asus_acpi
driver exposes via a /proc interface to be accessed via the LED
subsystem.  It creates a new config option, CONFIG_ACPI_ASUS_NEW_LED,
that will enable this support.

It registers some subset of LED's named asus:mail, asus:wireless, and
asus:touchpad, depending on the LED's available on the laptop.  LED
updates are scheduled in a workqueue to avoid messing with ACPI stuff
(needed to update the LED's) during a timer interrupt.

I've tested it on my Asus M2400Ne, and it appears to work with the
timer, ide-disk, and cpu triggers.  (I wrote the cpu one, and will
submit it in a future patch.)

This is the first (working) kernel code I've ever written, so I
apologize if it's screwed up in any way.  It's diffed against
2.6.17.1, not 2.6.17.3, but, from a quick glance at the ChangeLogs, it
doesn't appear that any of the files in question have changed between
those versions.  I tried to adhere to any guidelines I found.  I also
apologize, for it appears that Gmail is sending the patch with a type
of text/x-patch, rather than text/plain.  It should still work,
though--it is plain text.

--
Thomas Tuttle
https://www.thomastuttle.mooo.com/
[email protected] (for non-LKML stuff, remove +lkml)
AIM: thinkinginbinary
diff -udr linux-2.6.17.1/drivers/acpi/asus_acpi.c linux-2.6.17.1-mine/drivers/acpi/asus_acpi.c
--- linux-2.6.17.1/drivers/acpi/asus_acpi.c	2006-07-05 19:39:25.000000000 -0400
+++ linux-2.6.17.1-mine/drivers/acpi/asus_acpi.c	2006-07-05 20:54:34.000000000 -0400
@@ -33,11 +33,15 @@
  *  Complete display switching -- may require dirty hacks or calling _DOS?
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/proc_fs.h>
+#ifdef CONFIG_ACPI_ASUS_NEW_LED
+#include <linux/leds.h>
+#endif
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
 #include <asm/uaccess.h>
@@ -544,6 +548,145 @@
 	return count;
 }
 
+#ifdef CONFIG_ACPI_ASUS_NEW_LED
+
+/* These functions are called by the LED subsystem to update the desired
+ * state of the LED's. */
+static void led_set_mled(struct led_classdev *led_cdev,
+				enum led_brightness value);
+static void led_set_wled(struct led_classdev *led_cdev,
+				enum led_brightness value);
+static void led_set_tled(struct led_classdev *led_cdev,
+				enum led_brightness value);
+
+/* LED class devices. */
+static struct led_classdev led_cdev_mled =
+	{ .name = "asus:mail",     .brightness_set = led_set_mled };
+static struct led_classdev led_cdev_wled =
+	{ .name = "asus:wireless", .brightness_set = led_set_wled };
+static struct led_classdev led_cdev_tled =
+	{ .name = "asus:touchpad", .brightness_set = led_set_tled };
+
+/* These functions actually update the LED's, and are called from a
+ * workqueue.  By doing this as separate work rather than when the LED
+ * subsystem asks, I avoid messing with the Asus ACPI stuff during a
+ * potentially bad time, such as a timer interrupt. */
+static void led_update_mled(void *private);
+static void led_update_wled(void *private);
+static void led_update_tled(void *private);
+
+/* Desired values of LED's. */
+static int led_mled_value = 0;
+static int led_wled_value = 0;
+static int led_tled_value = 0;
+
+/* LED workqueue. */
+static struct workqueue_struct *led_workqueue;
+
+/* LED update work structs. */
+DECLARE_WORK(led_mled_work, led_update_mled, NULL);
+DECLARE_WORK(led_wled_work, led_update_wled, NULL);
+DECLARE_WORK(led_tled_work, led_update_tled, NULL);
+
+/* LED subsystem callbacks. */
+static void led_set_mled(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	led_mled_value = value;
+	queue_work(led_workqueue, &led_mled_work);
+}
+
+static void led_set_wled(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	led_wled_value = value;
+	queue_work(led_workqueue, &led_wled_work);
+}
+
+static void led_set_tled(struct led_classdev *led_cdev,
+	enum led_brightness value)
+{
+	led_tled_value = value;
+	queue_work(led_workqueue, &led_tled_work);
+}
+
+/* LED work functions. */
+static void led_update_mled(void *private) {
+	char *ledname = hotk->methods->mt_mled;
+	int led_out = led_mled_value ? 1 : 0;
+	hotk->status = (led_out) ? (hotk->status | MLED_ON) : (hotk->status & ~MLED_ON);
+	led_out = 1 - led_out;
+	if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
+		printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n",
+		       ledname);
+}
+
+static void led_update_wled(void *private) {
+	char *ledname = hotk->methods->mt_wled;
+	int led_out = led_wled_value ? 1 : 0;
+	hotk->status = (led_out) ? (hotk->status | WLED_ON) : (hotk->status & ~WLED_ON);
+	if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
+		printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n",
+		       ledname);
+}
+
+static void led_update_tled(void *private) {
+	char *ledname = hotk->methods->mt_tled;
+	int led_out = led_tled_value ? 1 : 0;
+	hotk->status = (led_out) ? (hotk->status | TLED_ON) : (hotk->status & ~TLED_ON);
+	if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
+		printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n",
+		       ledname);
+}
+
+/* Registers LED class devices and sets up workqueue. */
+static int led_initialize(struct device *parent)
+{
+	int result;
+
+	if (hotk->methods->mt_mled) {
+		result = led_classdev_register(parent, &led_cdev_mled);
+		if (result)
+			return result;
+	}
+
+	if (hotk->methods->mt_wled) {
+		result = led_classdev_register(parent, &led_cdev_wled);
+		if (result)
+			return result;
+	}
+
+	if (hotk->methods->mt_tled) {
+		result = led_classdev_register(parent, &led_cdev_tled);
+		if (result)
+			return result;
+	}
+
+	led_workqueue = create_singlethread_workqueue("led_workqueue");
+
+	return 0;
+}
+
+/* Destroys the workqueue and unregisters the LED class devices. */
+static void led_terminate(void)
+{
+	destroy_workqueue(led_workqueue);
+
+	if (hotk->methods->mt_tled) {
+		led_classdev_unregister(&led_cdev_tled);
+	}
+
+	if (hotk->methods->mt_wled) {
+		led_classdev_unregister(&led_cdev_wled);
+	}
+
+	if (hotk->methods->mt_mled) {
+		led_classdev_unregister(&led_cdev_mled);
+	}
+}
+
+#endif
+
 /*
  * Proc handlers for MLED
  */
@@ -1180,6 +1323,10 @@
 		}
 	}
 
+#ifdef CONFIG_ACPI_ASUS_NEW_LED
+	result = led_initialize(acpi_get_physical_device(device->handle));
+#endif
+
       end:
 	if (result) {
 		kfree(hotk);
@@ -1192,6 +1339,10 @@
 {
 	acpi_status status = 0;
 
+#ifdef CONFIG_ACPI_ASUS_NEW_LED
+	led_terminate();
+#endif
+
 	if (!device || !acpi_driver_data(device))
 		return -EINVAL;
 
diff -udr linux-2.6.17.1/drivers/acpi/Kconfig linux-2.6.17.1-mine/drivers/acpi/Kconfig
--- linux-2.6.17.1/drivers/acpi/Kconfig	2006-07-05 19:39:25.000000000 -0400
+++ linux-2.6.17.1-mine/drivers/acpi/Kconfig	2006-07-05 20:44:00.000000000 -0400
@@ -192,6 +192,15 @@
           driver is still under development, so if your laptop is unsupported or
           something works not quite as expected, please use the mailing list
           available on the above page ([email protected])
+
+config ACPI_ASUS_NEW_LED
+	bool "ASUS/Medion LED subsystem integration"
+	depends on ACPI_ASUS
+	depends on LEDS_CLASS
+	---help---
+	  This adds support for the new LED subsystem to the asus_acpi
+	  driver.  The LED's will show up as asus:mail, asus:wireless,
+	  and asus:touchpad, as applicable to your laptop.
           
 config ACPI_IBM
 	tristate "IBM ThinkPad Laptop Extras"

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux