Hi,

I've been converting the various acpi drivers to use the input subsystem
rather than using acpi events or other random proc interfaces.

So far sony-laptop and thinkpad_acpi have been converted, and I would
like to also suggest asus-acpi does the same as well.

Doing so have the following advantages:

* we can get rid of vendor specific acpi event daemons that have to be
configured
* we can simplify and have one single reporting stack
* we can remap keys using the existing infrastructure at the quirk
pages: http://people.freedesktop.org/~hughsient/quirk/
* the buttons "just work" without configuration

I've attached a patch for review. I've compile tested it, but I haven't
got any asus hardware to test it on. Comments, suggestions and
criticisms welcome.

Making asus acpi using input allows us to use HAL to report some
buttons, and eventually use a evdev X to automatically make buttons
work, no matter if they are on a true keyboard or acpi generated events.

Thanks for review,

Richard.

--- asus-laptop-orig.c	2007-07-30 10:01:26.000000000 +0100
+++ asus-laptop.c	2007-07-30 09:34:58.000000000 +0100
@@ -44,6 +44,8 @@
 #include <linux/fb.h>
 #include <linux/leds.h>
 #include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/pci_ids.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_bus.h>
 #include <asm/uaccess.h>
@@ -175,6 +177,7 @@ ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX
 struct asus_hotk {
 	char *name;		//laptop name
 	struct acpi_device *device;	//the device we are in
+	struct input_dev *inputdev;	//the input device we emit from
 	acpi_handle handle;	//the handle of the hotk device
 	char status;		//status of the hotk, for LEDs, ...
 	u32 ledd_status;	//status of the LED display
@@ -183,6 +186,53 @@ struct asus_hotk {
 	u16 event_count[128];	//count for each event TODO make this better
 };
 
+
+/* The MSC_SCAN numbers emitted from the driver.
+ * We remap them to 15 from 128 sparse states to keep the size of the
+ * keymap table sane */
+#define ASUS_SCAN_UNKNOWN		0x00
+#define ASUS_SCAN_SUSPEND		0x01
+#define ASUS_SCAN_WLAN			0x02
+#define ASUS_SCAN_BRIGHTNESS_UP		0x03
+#define ASUS_SCAN_BRIGHTNESS_DOWN	0x04
+#define ASUS_SCAN_VOLUME_DOWN		0x05
+#define ASUS_SCAN_LCD_TOGGLE		0x06
+#define ASUS_SCAN_VOLUME_UP		0x07
+#define ASUS_SCAN_MUTE_TOGGLE		0x08
+#define ASUS_SCAN_CDROM			0x09
+#define ASUS_SCAN_EMAIL			0x0a
+#define ASUS_SCAN_INTERNET		0x0b
+#define ASUS_SCAN_MESSAGING		0x0c
+#define ASUS_SCAN_VIDEO_TOGGLE		0x0d
+#define ASUS_SCAN_TRACKPAD_TOGGLE	0x0e
+#define ASUS_SCAN_NEXT			0x0f
+#define ASUS_SCAN_PREV			0x10
+#define ASUS_SCAN_STOP			0x11
+#define ASUS_SCAN_PLAYPAUSE		0x12
+
+/* we don't yet know what event id corresponds to each scancode */
+static u16 hotkey_keycode_map[] = {
+	KEY_RESERVED,
+	KEY_SUSPEND,		/* 0x01 */
+	KEY_WLAN,		/* 0x02 */
+	KEY_BRIGHTNESSUP,	/* 0x03 */
+	KEY_BRIGHTNESSDOWN,	/* 0x04 */
+	KEY_VOLUMEDOWN,		/* 0x05 */
+	KEY_SWITCHVIDEOMODE,	/* 0x06 */
+	KEY_VOLUMEUP,		/* 0x07 */
+	KEY_MUTE,		/* 0x08 */
+	KEY_PLAYCD,		/* 0x09 */
+	KEY_MAIL,		/* 0x0a */
+	KEY_NEWS,		/* 0x0b */
+	KEY_MESSENGER,		/* 0x0c */
+	KEY_SWITCHVIDEOMODE,	/* 0x0d */
+	KEY_F22,		/* 0x0e */
+	KEY_NEXTSONG,		/* 0x0f */
+	KEY_PREVIOUSSONG,	/* 0x10 */
+	KEY_STOP,		/* 0x11 */
+	KEY_PLAYPAUSE,		/* 0x12 */
+};
+
 /*
  * This header is made available to allow proper configuration given model,
  * revision number , ... this info cannot go in struct asus_hotk because it is
@@ -714,8 +764,92 @@ static ssize_t store_gps(struct device *
 	return store_status(buf, count, NULL, GPS_ON);
 }
 
+static void asus_input_send_key(unsigned int scancode, unsigned int keycode)
+{
+	if (keycode != KEY_RESERVED) {
+		input_report_key(hotk->inputdev, keycode, 1);
+		input_event(hotk->inputdev, EV_MSC, MSC_SCAN, scancode);
+		input_sync(hotk->inputdev);
+
+		input_report_key(hotk->inputdev, keycode, 0);
+		input_event(hotk->inputdev, EV_MSC, MSC_SCAN, scancode);
+		input_sync(hotk->inputdev);
+	}
+}
+
+/* Convert the hardware scancode table to ids that we can export as ABI
+ * stable. This means we can add more events as they are discovered without
+ * breaking userspace. */
+static unsigned int asus_convert_scancode_to_id(u32 event)
+{
+	unsigned int id;
+
+	/* these change every timethe brightnes level is altered */
+	if (event >= 0x11 && event <= 0x1f)
+		return ASUS_SCAN_BRIGHTNESS_UP;
+	if (event >= 0x20 && event <= 0x2e)
+		return ASUS_SCAN_BRIGHTNESS_DOWN;
+
+	switch (event) {
+	case 0x30:
+		id = ASUS_SCAN_VOLUME_UP;
+		break;
+	case 0x31:
+		id = ASUS_SCAN_VOLUME_DOWN;
+		break;
+	case 0x33:
+	case 0x34:
+		id = ASUS_SCAN_LCD_TOGGLE;
+		break;
+	case 0x32:
+		id = ASUS_SCAN_MUTE_TOGGLE;
+		break;
+	case 0x40:
+		id = ASUS_SCAN_PREV;
+		break;
+	case 0x41:
+		id = ASUS_SCAN_NEXT;
+		break;
+	case 0x43:
+		id = ASUS_SCAN_STOP;
+		break;
+	case 0x45:
+		id = ASUS_SCAN_PLAYPAUSE;
+		break;
+	case 0x4c:
+		id = ASUS_SCAN_CDROM;
+		break;
+	case 0x50:
+		id = ASUS_SCAN_EMAIL;
+		break;
+	case 0x51:
+		id = ASUS_SCAN_INTERNET;
+		break;
+	case 0x5c:
+		id = ASUS_SCAN_MESSAGING;
+		break;
+	case 0x5f:
+		id = ASUS_SCAN_WLAN;
+		break;
+	case 0x61:
+		id = ASUS_SCAN_VIDEO_TOGGLE;
+		break;
+	case 0x6a:
+	case 0x6b:
+		id = ASUS_SCAN_TRACKPAD_TOGGLE;
+		break;
+	default:
+		id = ASUS_SCAN_UNKNOWN;
+	}
+	return id;
+}
+
 static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
 {
+	unsigned int scancode;
+	unsigned int keycode;
+	int sendacpi = 1;
+
 	/* TODO Find a better way to handle events count. */
 	if (!hotk)
 		return;
@@ -732,8 +866,24 @@ static void asus_hotk_notify(acpi_handle
 		lcd_blank(FB_BLANK_POWERDOWN);
 	}
 
-	acpi_bus_generate_event(hotk->device, event,
-				hotk->event_count[event % 128]++);
+	/* convert the sparse 128 byte map to a 16 byte map */
+	scancode = asus_convert_scancode_to_id(event);
+	if (scancode == ASUS_SCAN_UNKNOWN) {
+		printk(KERN_WARNING " Scancode '0x%x' cannot be mapped to keycode.\n", event);
+	} else {
+		/* We have to emit a keycode via INPUT */
+		keycode = hotkey_keycode_map[scancode];
+		printk(KERN_INFO " Scancode '0x%x' mapped to keycode '%d'.\n", event, keycode);
+		if (keycode != KEY_UNKNOWN) {
+			asus_input_send_key(scancode, keycode);
+			sendacpi = 0;
+		}
+	}
+
+	/* we have not emitted an input event so do the event as normal */
+	if (sendacpi == 1)
+		acpi_bus_generate_event(hotk->device, event,
+					hotk->event_count[event % 128]++);
 
 	return;
 }
@@ -1059,6 +1209,11 @@ static int asus_hotk_remove(struct acpi_
 	if (ACPI_FAILURE(status))
 		printk(ASUS_ERR "Error removing notify handler\n");
 
+	if (hotk->inputdev) {
+		input_unregister_device(hotk->inputdev);
+		input_free_device(hotk->inputdev);
+	}
+
 	kfree(hotk->name);
 	kfree(hotk);
 
@@ -1179,6 +1334,7 @@ static int __init asus_laptop_init(void)
 {
 	struct device *dev;
 	int result;
+	int i;
 
 	if (acpi_disabled)
 		return -ENODEV;
@@ -1229,8 +1385,46 @@ static int __init asus_laptop_init(void)
 	if (result)
 		goto fail_sysfs;
 
+	/* registe an input event source */
+	hotk->inputdev = input_allocate_device();
+	if (!hotk->inputdev) {
+		printk(ASUS_ERR "Unable to allocate input device\n");
+		result = -ENOMEM;
+		goto fail_sysfs;
+	}
+
+	/* Prepare input device, but don't register */
+	hotk->inputdev->name = "ASUS Extra Buttons";
+	hotk->inputdev->phys = "asus_acpi/input0";
+	hotk->inputdev->id.bustype = BUS_HOST;
+	hotk->inputdev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
+	hotk->inputdev->id.product = 0x5054; /* "TP" */
+	hotk->inputdev->id.version = 0x4101;
+
+	result = input_register_device(hotk->inputdev);
+	if (result < 0) {
+		printk(ASUS_ERR "Unable to register input device\n");
+		goto fail_register;
+	}
+
+	/* set up the setkeycodes map */
+	set_bit(EV_KEY, hotk->inputdev->evbit);
+	set_bit(EV_MSC, hotk->inputdev->evbit);
+	set_bit(MSC_SCAN, hotk->inputdev->mscbit);
+	hotk->inputdev->keycodesize = sizeof(hotkey_keycode_map[0]);
+	hotk->inputdev->keycodemax = ARRAY_SIZE(hotkey_keycode_map);
+	hotk->inputdev->keycode = &hotkey_keycode_map;
+	for (i = 0; i < ARRAY_SIZE(hotkey_keycode_map); i++) {
+		if (hotkey_keycode_map[i] != KEY_RESERVED) {
+			set_bit(hotkey_keycode_map[i], hotk->inputdev->keybit);
+		}
+	}
+
 	return 0;
 
+      fail_register:
+	input_free_device(hotk->inputdev);
+
       fail_sysfs:
 	platform_device_del(asuspf_device);
 
-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
Acpi4asus-user mailing list
Acpi4asus-user@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/acpi4asus-user

Reply via email to