--- asus-laptop-orig.c 2007-07-11 10:22:56.000000000 +0100 +++ asus-laptop.c 2007-07-11 10:37:48.000000000 +0100 @@ -44,6 +44,8 @@ #include #include #include +#include +#include #include #include #include @@ -176,6 +178,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 @@ -184,6 +187,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 @@ -709,8 +759,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; @@ -727,8 +861,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; } @@ -1056,6 +1206,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); @@ -1166,6 +1321,7 @@ static int __init asus_laptop_init(void) { struct device *dev; int result; + int i; if (acpi_disabled) return -ENODEV; @@ -1216,8 +1372,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);