From 97490f1cf82cccf2e088aafffb0517802f0ee336 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 4 Aug 2010 22:29:57 -0700 Subject: topstar-laptop - switch to using sparse keymap library Instead of implementing its own version of keymap hanlding switch over to using sparse keymap library. Acked-by: Herton Ronaldo Krzesinski Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index cff7cc2..0b11b41 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -478,6 +478,7 @@ config TOPSTAR_LAPTOP tristate "Topstar Laptop Extras" depends on ACPI depends on INPUT + select INPUT_SPARSEKMAP ---help--- This driver adds support for hotkeys found on Topstar laptops. diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index ff4b476..1d07d6d 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -19,6 +19,7 @@ #include #include #include +#include #define ACPI_TOPSTAR_CLASS "topstar" @@ -26,52 +27,37 @@ struct topstar_hkey { struct input_dev *inputdev; }; -struct tps_key_entry { - u8 code; - u16 keycode; -}; - -static struct tps_key_entry topstar_keymap[] = { - { 0x80, KEY_BRIGHTNESSUP }, - { 0x81, KEY_BRIGHTNESSDOWN }, - { 0x83, KEY_VOLUMEUP }, - { 0x84, KEY_VOLUMEDOWN }, - { 0x85, KEY_MUTE }, - { 0x86, KEY_SWITCHVIDEOMODE }, - { 0x87, KEY_F13 }, /* touchpad enable/disable key */ - { 0x88, KEY_WLAN }, - { 0x8a, KEY_WWW }, - { 0x8b, KEY_MAIL }, - { 0x8c, KEY_MEDIA }, - { 0x96, KEY_F14 }, /* G key? */ - { } -}; - -static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code) -{ - struct tps_key_entry *key; - - for (key = topstar_keymap; key->code; key++) - if (code == key->code) - return key; +static const struct key_entry topstar_keymap[] = { + { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x83, { KEY_VOLUMEUP } }, + { KE_KEY, 0x84, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x85, { KEY_MUTE } }, + { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */ + { KE_KEY, 0x88, { KEY_WLAN } }, + { KE_KEY, 0x8a, { KEY_WWW } }, + { KE_KEY, 0x8b, { KEY_MAIL } }, + { KE_KEY, 0x8c, { KEY_MEDIA } }, - return NULL; -} - -static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code) -{ - struct tps_key_entry *key; + /* Known non hotkey events don't handled or that we don't care yet */ + { KE_IGNORE, 0x8e, }, + { KE_IGNORE, 0x8f, }, + { KE_IGNORE, 0x90, }, - for (key = topstar_keymap; key->code; key++) - if (code == key->keycode) - return key; + /* + * 'G key' generate two event codes, convert to only + * one event/key code for now, consider replacing by + * a switch (3G switch - SW_3G?) + */ + { KE_KEY, 0x96, { KEY_F14 } }, + { KE_KEY, 0x97, { KEY_F14 } }, - return NULL; -} + { KE_END, 0 } +}; static void acpi_topstar_notify(struct acpi_device *device, u32 event) { - struct tps_key_entry *key; static bool dup_evnt[2]; bool *dup; struct topstar_hkey *hkey = acpi_driver_data(device); @@ -86,27 +72,8 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event) *dup = true; } - /* - * 'G key' generate two event codes, convert to only - * one event/key code for now (3G switch?) - */ - if (event == 0x97) - event = 0x96; - - key = tps_get_key_by_scancode(event); - if (key) { - input_report_key(hkey->inputdev, key->keycode, 1); - input_sync(hkey->inputdev); - input_report_key(hkey->inputdev, key->keycode, 0); - input_sync(hkey->inputdev); - return; - } - - /* Known non hotkey events don't handled or that we don't care yet */ - if (event == 0x8e || event == 0x8f || event == 0x90) - return; - - pr_info("unknown event = 0x%02x\n", event); + if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true)) + pr_info("unknown event = 0x%02x\n", event); } static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) @@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) return 0; } -static int topstar_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct tps_key_entry *key = tps_get_key_by_scancode(scancode); - - if (!key) - return -EINVAL; - - *keycode = key->keycode; - return 0; -} - -static int topstar_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct tps_key_entry *key; - int old_keycode; - - key = tps_get_key_by_scancode(scancode); - - if (!key) - return -EINVAL; - - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!tps_get_key_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; -} - static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) { - struct tps_key_entry *key; + struct input_dev *input; + int error; - hkey->inputdev = input_allocate_device(); - if (!hkey->inputdev) { + input = input_allocate_device(); + if (!input) { pr_err("Unable to allocate input device\n"); - return -ENODEV; + return -ENOMEM; } - hkey->inputdev->name = "Topstar Laptop extra buttons"; - hkey->inputdev->phys = "topstar/input0"; - hkey->inputdev->id.bustype = BUS_HOST; - hkey->inputdev->getkeycode = topstar_getkeycode; - hkey->inputdev->setkeycode = topstar_setkeycode; - for (key = topstar_keymap; key->code; key++) { - set_bit(EV_KEY, hkey->inputdev->evbit); - set_bit(key->keycode, hkey->inputdev->keybit); + + input->name = "Topstar Laptop extra buttons"; + input->phys = "topstar/input0"; + input->id.bustype = BUS_HOST; + + error = sparse_keymap_setup(input, topstar_keymap, NULL); + if (error) { + pr_err("Unable to setup input device keymap\n"); + goto err_free_dev; } - if (input_register_device(hkey->inputdev)) { + + error = input_register_device(input); + if (error) { pr_err("Unable to register input device\n"); - input_free_device(hkey->inputdev); - return -ENODEV; + goto err_free_keymap; } + hkey->inputdev = input; return 0; + + err_free_keymap: + sparse_keymap_free(input); + err_free_dev: + input_free_device(input); + return error; } static int acpi_topstar_add(struct acpi_device *device) @@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type) acpi_topstar_fncx_switch(device, false); + sparse_keymap_free(tps_hkey->inputdev); input_unregister_device(tps_hkey->inputdev); kfree(tps_hkey); -- cgit v0.10.2 From 1a765cac9a241380511a3b1dd4edf74a41cbfdf9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 4 Aug 2010 22:30:02 -0700 Subject: panasonic-laptop - switch to using sparse keymap library nstead of implementing its own version of keymap hanlding switch over to using sparse keymap library. Cc: Harald Welte Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0b11b41..c0446d6 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -171,6 +171,7 @@ config PANASONIC_LAPTOP tristate "Panasonic Laptop Extras" depends on INPUT && ACPI depends on BACKLIGHT_CLASS_DEVICE + select INPUT_SPARSEKMAP ---help--- This driver adds support for access to backlight control and hotkeys on Panasonic Let's Note laptops. diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index ec01c3d..b3b9e04 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -128,6 +128,7 @@ #include #include #include +#include #ifndef ACPI_HOTKEY_COMPONENT @@ -200,30 +201,29 @@ static struct acpi_driver acpi_pcc_driver = { }, }; -#define KEYMAP_SIZE 11 -static const unsigned int initial_keymap[KEYMAP_SIZE] = { - /* 0 */ KEY_RESERVED, - /* 1 */ KEY_BRIGHTNESSDOWN, - /* 2 */ KEY_BRIGHTNESSUP, - /* 3 */ KEY_DISPLAYTOGGLE, - /* 4 */ KEY_MUTE, - /* 5 */ KEY_VOLUMEDOWN, - /* 6 */ KEY_VOLUMEUP, - /* 7 */ KEY_SLEEP, - /* 8 */ KEY_PROG1, /* Change CPU boost */ - /* 9 */ KEY_BATTERY, - /* 10 */ KEY_SUSPEND, +static const struct key_entry panasonic_keymap[] = { + { KE_KEY, 0, { KEY_RESERVED } }, + { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 2, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 3, { KEY_DISPLAYTOGGLE } }, + { KE_KEY, 4, { KEY_MUTE } }, + { KE_KEY, 5, { KEY_VOLUMEDOWN } }, + { KE_KEY, 6, { KEY_VOLUMEUP } }, + { KE_KEY, 7, { KEY_SLEEP } }, + { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ + { KE_KEY, 9, { KEY_BATTERY } }, + { KE_KEY, 10, { KEY_SUSPEND } }, + { KE_END, 0 } }; struct pcc_acpi { acpi_handle handle; unsigned long num_sifr; int sticky_mode; - u32 *sinf; + u32 *sinf; struct acpi_device *device; struct input_dev *input_dev; struct backlight_device *backlight; - unsigned int keymap[KEYMAP_SIZE]; }; struct pcc_keyinput { @@ -446,56 +446,10 @@ static struct attribute_group pcc_attr_group = { /* hotkey input device driver */ -static int pcc_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct pcc_acpi *pcc = input_get_drvdata(dev); - - if (scancode >= ARRAY_SIZE(pcc->keymap)) - return -EINVAL; - - *keycode = pcc->keymap[scancode]; - - return 0; -} - -static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) { - if (pcc->keymap[i] == keycode) - return i+1; - } - - return 0; -} - -static int pcc_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct pcc_acpi *pcc = input_get_drvdata(dev); - int oldkeycode; - - if (scancode >= ARRAY_SIZE(pcc->keymap)) - return -EINVAL; - - oldkeycode = pcc->keymap[scancode]; - pcc->keymap[scancode] = keycode; - - set_bit(keycode, dev->keybit); - - if (!keymap_get_by_keycode(pcc, oldkeycode)) - clear_bit(oldkeycode, dev->keybit); - - return 0; -} - static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) { struct input_dev *hotk_input_dev = pcc->input_dev; int rc; - int key_code, hkey_num; unsigned long long result; rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, @@ -508,25 +462,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result); - hkey_num = result & 0xf; - - if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) { + if (!sparse_keymap_report_event(hotk_input_dev, + result & 0xf, result & 0x80, false)) ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "hotkey number out of range: %d\n", - hkey_num)); - return; - } - - key_code = pcc->keymap[hkey_num]; - - if (key_code != KEY_RESERVED) { - int pushed = (result & 0x80) ? TRUE : FALSE; - - input_report_key(hotk_input_dev, key_code, pushed); - input_sync(hotk_input_dev); - } - - return; + "Unknown hotkey event: %d\n", result)); } static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) @@ -545,40 +484,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) static int acpi_pcc_init_input(struct pcc_acpi *pcc) { - int i, rc; + struct input_dev *input_dev; + int error; - pcc->input_dev = input_allocate_device(); - if (!pcc->input_dev) { + input_dev = input_allocate_device(); + if (!input_dev) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate input device for hotkey")); return -ENOMEM; } - pcc->input_dev->evbit[0] = BIT(EV_KEY); - - pcc->input_dev->name = ACPI_PCC_DRIVER_NAME; - pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS; - pcc->input_dev->id.bustype = BUS_HOST; - pcc->input_dev->id.vendor = 0x0001; - pcc->input_dev->id.product = 0x0001; - pcc->input_dev->id.version = 0x0100; - pcc->input_dev->getkeycode = pcc_getkeycode; - pcc->input_dev->setkeycode = pcc_setkeycode; + input_dev->name = ACPI_PCC_DRIVER_NAME; + input_dev->phys = ACPI_PCC_INPUT_PHYS; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; - /* load initial keymap */ - memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap)); + error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL); + if (error) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to setup input device keymap\n")); + goto err_free_dev; + } - for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) - __set_bit(pcc->keymap[i], pcc->input_dev->keybit); - __clear_bit(KEY_RESERVED, pcc->input_dev->keybit); + error = input_register_device(input_dev); + if (error) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to register input device\n")); + goto err_free_keymap; + } - input_set_drvdata(pcc->input_dev, pcc); + pcc->input_dev = input_dev; + return 0; - rc = input_register_device(pcc->input_dev); - if (rc < 0) - input_free_device(pcc->input_dev); + err_free_keymap: + sparse_keymap_free(input_dev); + err_free_dev: + input_free_device(input_dev); + return error; +} - return rc; +static void acpi_pcc_destroy_input(struct pcc_acpi *pcc) +{ + sparse_keymap_free(pcc->input_dev); + input_unregister_device(pcc->input_dev); + /* + * No need to input_free_device() since core input API refcounts + * and free()s the device. + */ } /* kernel module interface */ @@ -636,7 +590,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing keyinput handler\n")); - goto out_hotkey; + goto out_sinf; } if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { @@ -651,7 +605,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) &pcc_backlight_ops, &props); if (IS_ERR(pcc->backlight)) { result = PTR_ERR(pcc->backlight); - goto out_sinf; + goto out_input; } /* read the initial brightness setting from the hardware */ @@ -669,12 +623,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) out_backlight: backlight_device_unregister(pcc->backlight); +out_input: + acpi_pcc_destroy_input(pcc); out_sinf: kfree(pcc->sinf); -out_input: - input_unregister_device(pcc->input_dev); - /* no need to input_free_device() since core input API refcount and - * free()s the device */ out_hotkey: kfree(pcc); @@ -709,9 +661,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) backlight_device_unregister(pcc->backlight); - input_unregister_device(pcc->input_dev); - /* no need to input_free_device() since core input API refcount and - * free()s the device */ + acpi_pcc_destroy_input(pcc); kfree(pcc->sinf); kfree(pcc); -- cgit v0.10.2 From 890a7c8e8dc2d0890e795bda9ad3484ebac42c0b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 4 Aug 2010 22:30:08 -0700 Subject: Input: dell-wmi - switch to using sparse keymap library Instead of implementing its own version of keymap hanlding switch over to using sparse keymap library. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c0446d6..ee8b09e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -92,6 +92,7 @@ config DELL_WMI tristate "Dell WMI extras" depends on ACPI_WMI depends on INPUT + select INPUT_SPARSEKMAP ---help--- Say Y here if you want to support WMI-based hotkeys on Dell laptops. diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 08fb70f..77f1d55 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -44,78 +45,70 @@ static int acpi_video; MODULE_ALIAS("wmi:"DELL_EVENT_GUID); -struct key_entry { - char type; /* See KE_* below */ - u16 code; - u16 keycode; -}; - -enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; - /* * Certain keys are flagged as KE_IGNORE. All of these are either * notifications (rather than requests for change) or are also sent * via the keyboard controller so should not be sent again. */ -static struct key_entry dell_legacy_wmi_keymap[] = { - {KE_KEY, 0xe045, KEY_PROG1}, - {KE_KEY, 0xe009, KEY_EJECTCD}, +static const struct key_entry dell_wmi_legacy_keymap[] __initconst = { + { KE_KEY, 0xe045, { KEY_PROG1 } }, + { KE_KEY, 0xe009, { KEY_EJECTCD } }, /* These also contain the brightness level at offset 6 */ - {KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, - {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, + { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, /* Battery health status button */ - {KE_KEY, 0xe007, KEY_BATTERY}, + { KE_KEY, 0xe007, { KEY_BATTERY } }, /* This is actually for all radios. Although physically a * switch, the notification does not provide an indication of * state and so it should be reported as a key */ - {KE_KEY, 0xe008, KEY_WLAN}, + { KE_KEY, 0xe008, { KEY_WLAN } }, /* The next device is at offset 6, the active devices are at offset 8 and the attached devices at offset 10 */ - {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, + { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, - {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, + { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } }, /* BIOS error detected */ - {KE_IGNORE, 0xe00d, KEY_RESERVED}, + { KE_IGNORE, 0xe00d, { KEY_RESERVED } }, /* Wifi Catcher */ - {KE_KEY, 0xe011, KEY_PROG2}, + { KE_KEY, 0xe011, {KEY_PROG2 } }, /* Ambient light sensor toggle */ - {KE_IGNORE, 0xe013, KEY_RESERVED}, - - {KE_IGNORE, 0xe020, KEY_MUTE}, - {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, - {KE_IGNORE, 0xe030, KEY_VOLUMEUP}, - {KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, - {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, - {KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, - {KE_IGNORE, 0xe045, KEY_NUMLOCK}, - {KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, - {KE_END, 0} + { KE_IGNORE, 0xe013, { KEY_RESERVED } }, + + { KE_IGNORE, 0xe020, { KEY_MUTE } }, + { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } }, + { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } }, + { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } }, + { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } }, + { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } }, + { KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, + { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, + { KE_END, 0 } }; static bool dell_new_hk_type; -struct dell_new_keymap_entry { +struct dell_bios_keymap_entry { u16 scancode; u16 keycode; }; -struct dell_hotkey_table { +struct dell_bios_hotkey_table { struct dmi_header header; - struct dell_new_keymap_entry keymap[]; + struct dell_bios_keymap_entry keymap[]; }; -static struct key_entry *dell_new_wmi_keymap; +static const struct dell_bios_hotkey_table *dell_bios_hotkey_table; -static u16 bios_to_linux_keycode[256] = { +static const u16 bios_to_linux_keycode[256] __initconst = { KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, @@ -138,68 +131,11 @@ static u16 bios_to_linux_keycode[256] = { KEY_PROG3 }; - -static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; - static struct input_dev *dell_wmi_input_dev; -static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code) -{ - struct key_entry *key; - - for (key = dell_wmi_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode) -{ - struct key_entry *key; - - for (key = dell_wmi_keymap; key->type != KE_END; key++) - if (key->type == KE_KEY && keycode == key->keycode) - return key; - - return NULL; -} - -static int dell_wmi_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int dell_wmi_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct key_entry *key; - unsigned int old_keycode; - - key = dell_wmi_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!dell_wmi_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - return -EINVAL; -} - static void dell_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - static struct key_entry *key; union acpi_object *obj; acpi_status status; @@ -212,8 +148,10 @@ static void dell_wmi_notify(u32 value, void *context) obj = (union acpi_object *)response.pointer; if (obj && obj->type == ACPI_TYPE_BUFFER) { + const struct key_entry *key; int reported_key; u16 *buffer_entry = (u16 *)obj->buffer.pointer; + if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { printk(KERN_INFO "dell-wmi: Received unknown WMI event" " (0x%x)\n", buffer_entry[1]); @@ -226,8 +164,8 @@ static void dell_wmi_notify(u32 value, void *context) else reported_key = (int)buffer_entry[1] & 0xffff; - key = dell_wmi_get_entry_by_scancode(reported_key); - + key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, + reported_key); if (!key) { printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", reported_key); @@ -237,92 +175,98 @@ static void dell_wmi_notify(u32 value, void *context) * come via ACPI */ ; } else { - input_report_key(dell_wmi_input_dev, key->keycode, 1); - input_sync(dell_wmi_input_dev); - input_report_key(dell_wmi_input_dev, key->keycode, 0); - input_sync(dell_wmi_input_dev); + sparse_keymap_report_entry(dell_wmi_input_dev, key, + 1, true); } } kfree(obj); } - -static void setup_new_hk_map(const struct dmi_header *dm) +static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) { - + int hotkey_num = (dell_bios_hotkey_table->header.length - 4) / + sizeof(struct dell_bios_keymap_entry); + struct key_entry *keymap; int i; - int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry); - struct dell_hotkey_table *table = - container_of(dm, struct dell_hotkey_table, header); - dell_new_wmi_keymap = kzalloc((hotkey_num+1) * - sizeof(struct key_entry), GFP_KERNEL); + keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL); + if (!keymap) + return NULL; for (i = 0; i < hotkey_num; i++) { - dell_new_wmi_keymap[i].type = KE_KEY; - dell_new_wmi_keymap[i].code = table->keymap[i].scancode; - dell_new_wmi_keymap[i].keycode = - (table->keymap[i].keycode > 255) ? 0 : - bios_to_linux_keycode[table->keymap[i].keycode]; + const struct dell_bios_keymap_entry *bios_entry = + &dell_bios_hotkey_table->keymap[i]; + keymap[i].type = KE_KEY; + keymap[i].code = bios_entry->scancode; + keymap[i].keycode = bios_entry->keycode < 256 ? + bios_to_linux_keycode[bios_entry->keycode] : + KEY_RESERVED; } - dell_new_wmi_keymap[i].type = KE_END; - dell_new_wmi_keymap[i].code = 0; - dell_new_wmi_keymap[i].keycode = 0; - - dell_wmi_keymap = dell_new_wmi_keymap; + keymap[hotkey_num].type = KE_END; + return keymap; } - -static void find_hk_type(const struct dmi_header *dm, void *dummy) -{ - - if ((dm->type == 0xb2) && (dm->length > 6)) { - dell_new_hk_type = true; - setup_new_hk_map(dm); - } - -} - - static int __init dell_wmi_input_setup(void) { - struct key_entry *key; int err; dell_wmi_input_dev = input_allocate_device(); - if (!dell_wmi_input_dev) return -ENOMEM; dell_wmi_input_dev->name = "Dell WMI hotkeys"; dell_wmi_input_dev->phys = "wmi/input0"; dell_wmi_input_dev->id.bustype = BUS_HOST; - dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode; - dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; - - for (key = dell_wmi_keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, dell_wmi_input_dev->evbit); - set_bit(key->keycode, dell_wmi_input_dev->keybit); - break; - case KE_SW: - set_bit(EV_SW, dell_wmi_input_dev->evbit); - set_bit(key->keycode, dell_wmi_input_dev->swbit); - break; + + if (dell_new_hk_type) { + const struct key_entry *keymap = dell_wmi_prepare_new_keymap(); + if (!keymap) { + err = -ENOMEM; + goto err_free_dev; } - } - err = input_register_device(dell_wmi_input_dev); + err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); - if (err) { - input_free_device(dell_wmi_input_dev); - return err; + /* + * Sparse keymap library makes a copy of keymap so we + * don't need the original one that was allocated. + */ + kfree(keymap); + } else { + err = sparse_keymap_setup(dell_wmi_input_dev, + dell_wmi_legacy_keymap, NULL); } + if (err) + goto err_free_dev; + + err = input_register_device(dell_wmi_input_dev); + if (err) + goto err_free_keymap; return 0; + + err_free_keymap: + sparse_keymap_free(dell_wmi_input_dev); + err_free_dev: + input_free_device(dell_wmi_input_dev); + return err; +} + +static void dell_wmi_input_destroy(void) +{ + sparse_keymap_free(dell_wmi_input_dev); + input_unregister_device(dell_wmi_input_dev); +} + +static void __init find_hk_type(const struct dmi_header *dm, void *dummy) +{ + if (dm->type == 0xb2 && dm->length > 6) { + dell_new_hk_type = true; + dell_bios_hotkey_table = + container_of(dm, struct dell_bios_hotkey_table, header); + } } static int __init dell_wmi_init(void) @@ -339,18 +283,13 @@ static int __init dell_wmi_init(void) acpi_video = acpi_video_backlight_support(); err = dell_wmi_input_setup(); - if (err) { - if (dell_new_hk_type) - kfree(dell_wmi_keymap); + if (err) return err; - } status = wmi_install_notify_handler(DELL_EVENT_GUID, dell_wmi_notify, NULL); if (ACPI_FAILURE(status)) { - input_unregister_device(dell_wmi_input_dev); - if (dell_new_hk_type) - kfree(dell_wmi_keymap); + dell_wmi_input_destroy(); printk(KERN_ERR "dell-wmi: Unable to register notify handler - %d\n", status); @@ -359,14 +298,11 @@ static int __init dell_wmi_init(void) return 0; } +module_init(dell_wmi_init); static void __exit dell_wmi_exit(void) { wmi_remove_notify_handler(DELL_EVENT_GUID); - input_unregister_device(dell_wmi_input_dev); - if (dell_new_hk_type) - kfree(dell_wmi_keymap); + dell_wmi_input_destroy(); } - -module_init(dell_wmi_init); module_exit(dell_wmi_exit); -- cgit v0.10.2 From 4d291ed7217d617dacbf54b4bd35819b0d08b981 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 4 Aug 2010 22:30:13 -0700 Subject: Input: hp-wmi - switch to using sparse keymap library Instead of implementing its own version of keymap hanlding switch over to using sparse keymap library. Also make sure that we install notify handler only after we allocated input device and that we remove notify handler before unregistering input device. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ee8b09e..1ca392f 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -141,6 +141,7 @@ config HP_WMI depends on ACPI_WMI depends on INPUT depends on RFKILL || RFKILL = n + select INPUT_SPARSEKMAP help Say Y here if you want to support WMI-based hotkeys on HP laptops and to read data from WMI such as docking or ambient light sensor state. diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c174114..1dac659 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -88,24 +89,16 @@ struct bios_return { u32 value; }; -struct key_entry { - char type; /* See KE_* below */ - u16 code; - u16 keycode; -}; - -enum { KE_KEY, KE_END }; - -static struct key_entry hp_wmi_keymap[] = { - {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, - {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, - {KE_KEY, 0x20e6, KEY_PROG1}, - {KE_KEY, 0x20e8, KEY_MEDIA}, - {KE_KEY, 0x2142, KEY_MEDIA}, - {KE_KEY, 0x213b, KEY_INFO}, - {KE_KEY, 0x2169, KEY_DIRECTION}, - {KE_KEY, 0x231b, KEY_HELP}, - {KE_END, 0} +static const struct key_entry hp_wmi_keymap[] = { + { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x20e6, { KEY_PROG1 } }, + { KE_KEY, 0x20e8, { KEY_MEDIA } }, + { KE_KEY, 0x2142, { KEY_MEDIA } }, + { KE_KEY, 0x213b, { KEY_INFO } }, + { KE_KEY, 0x2169, { KEY_DIRECTION } }, + { KE_KEY, 0x231b, { KEY_HELP } }, + { KE_END, 0 } }; static struct input_dev *hp_wmi_input_dev; @@ -347,64 +340,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); -static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) -{ - struct key_entry *key; - - for (key = hp_wmi_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) -{ - struct key_entry *key; - - for (key = hp_wmi_keymap; key->type != KE_END; key++) - if (key->type == KE_KEY && keycode == key->keycode) - return key; - - return NULL; -} - -static int hp_wmi_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int hp_wmi_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct key_entry *key; - unsigned int old_keycode; - - key = hp_wmi_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!hp_wmi_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - - return -EINVAL; -} - static void hp_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - static struct key_entry *key; union acpi_object *obj; u32 event_id, event_data; int key_code = 0, ret; @@ -465,19 +403,9 @@ static void hp_wmi_notify(u32 value, void *context) sizeof(key_code)); if (ret) break; - key = hp_wmi_get_entry_by_scancode(key_code); - if (key) { - switch (key->type) { - case KE_KEY: - input_report_key(hp_wmi_input_dev, - key->keycode, 1); - input_sync(hp_wmi_input_dev); - input_report_key(hp_wmi_input_dev, - key->keycode, 0); - input_sync(hp_wmi_input_dev); - break; - } - } else + + if (!sparse_keymap_report_event(hp_wmi_input_dev, + key_code, 1, true)) printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", key_code); break; @@ -510,7 +438,7 @@ static void hp_wmi_notify(u32 value, void *context) static int __init hp_wmi_input_setup(void) { - struct key_entry *key; + acpi_status status; int err; hp_wmi_input_dev = input_allocate_device(); @@ -520,21 +448,14 @@ static int __init hp_wmi_input_setup(void) hp_wmi_input_dev->name = "HP WMI hotkeys"; hp_wmi_input_dev->phys = "wmi/input0"; hp_wmi_input_dev->id.bustype = BUS_HOST; - hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; - hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; - - for (key = hp_wmi_keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, hp_wmi_input_dev->evbit); - set_bit(key->keycode, hp_wmi_input_dev->keybit); - break; - } - } - set_bit(EV_SW, hp_wmi_input_dev->evbit); - set_bit(SW_DOCK, hp_wmi_input_dev->swbit); - set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + __set_bit(EV_SW, hp_wmi_input_dev->evbit); + __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); + __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + + err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); + if (err) + goto err_free_dev; /* Set initial hardware state */ input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); @@ -542,14 +463,32 @@ static int __init hp_wmi_input_setup(void) hp_wmi_tablet_state()); input_sync(hp_wmi_input_dev); - err = input_register_device(hp_wmi_input_dev); - - if (err) { - input_free_device(hp_wmi_input_dev); - return err; + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_keymap; } + err = input_register_device(hp_wmi_input_dev); + if (err) + goto err_uninstall_notifier; + return 0; + + err_uninstall_notifier: + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + err_free_keymap: + sparse_keymap_free(hp_wmi_input_dev); + err_free_dev: + input_free_device(hp_wmi_input_dev); + return err; +} + +static void hp_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(HPWMI_EVENT_GUID); + sparse_keymap_free(hp_wmi_input_dev); + input_unregister_device(hp_wmi_input_dev); } static void cleanup_sysfs(struct platform_device *device) @@ -704,15 +643,9 @@ static int __init hp_wmi_init(void) int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); if (event_capable) { - err = wmi_install_notify_handler(HPWMI_EVENT_GUID, - hp_wmi_notify, NULL); - if (ACPI_FAILURE(err)) - return -EINVAL; err = hp_wmi_input_setup(); - if (err) { - wmi_remove_notify_handler(HPWMI_EVENT_GUID); + if (err) return err; - } } if (bios_capable) { @@ -739,20 +672,17 @@ err_device_add: err_device_alloc: platform_driver_unregister(&hp_wmi_driver); err_driver_reg: - if (wmi_has_guid(HPWMI_EVENT_GUID)) { - input_unregister_device(hp_wmi_input_dev); - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - } + if (event_capable) + hp_wmi_input_destroy(); return err; } static void __exit hp_wmi_exit(void) { - if (wmi_has_guid(HPWMI_EVENT_GUID)) { - wmi_remove_notify_handler(HPWMI_EVENT_GUID); - input_unregister_device(hp_wmi_input_dev); - } + if (wmi_has_guid(HPWMI_EVENT_GUID)) + hp_wmi_input_destroy(); + if (hp_wmi_platform_dev) { platform_device_unregister(hp_wmi_platform_dev); platform_driver_unregister(&hp_wmi_driver); -- cgit v0.10.2 From 384a7cd9ace5b37a17ffea436f09170cdf671c88 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 4 Aug 2010 22:30:19 -0700 Subject: toshiba-acpi - switch to using sparse keymap Instead of implementing its own version of keymap hanlding switch over to using sparse keymap library. Also, install notify handler only after we allocated input device, otherwise we may risk getting event too early and crash. Similarly, notify handler should be removed before we unregister input device. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1ca392f..b96db80 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -496,6 +496,8 @@ config ACPI_TOSHIBA depends on INPUT depends on RFKILL || RFKILL = n select INPUT_POLLDEV + select INPUT_SPARSEKMAP + select BACKLIGHT_CLASS_DEVICE ---help--- This driver adds support for access to certain system settings on "legacy free" Toshiba laptops. These laptops can be recognized by diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 7d67a45..06f304f 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -121,36 +122,28 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); -struct key_entry { - char type; - u16 code; - u16 keycode; -}; - -enum {KE_KEY, KE_END}; - -static struct key_entry toshiba_acpi_keymap[] = { - {KE_KEY, 0x101, KEY_MUTE}, - {KE_KEY, 0x102, KEY_ZOOMOUT}, - {KE_KEY, 0x103, KEY_ZOOMIN}, - {KE_KEY, 0x13b, KEY_COFFEE}, - {KE_KEY, 0x13c, KEY_BATTERY}, - {KE_KEY, 0x13d, KEY_SLEEP}, - {KE_KEY, 0x13e, KEY_SUSPEND}, - {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, - {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, - {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, - {KE_KEY, 0x142, KEY_WLAN}, - {KE_KEY, 0x143, KEY_PROG1}, - {KE_KEY, 0xb05, KEY_PROG2}, - {KE_KEY, 0xb06, KEY_WWW}, - {KE_KEY, 0xb07, KEY_MAIL}, - {KE_KEY, 0xb30, KEY_STOP}, - {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, - {KE_KEY, 0xb32, KEY_NEXTSONG}, - {KE_KEY, 0xb33, KEY_PLAYPAUSE}, - {KE_KEY, 0xb5a, KEY_MEDIA}, - {KE_END, 0, 0}, +static const struct key_entry toshiba_acpi_keymap[] __initconst = { + { KE_KEY, 0x101, { KEY_MUTE } }, + { KE_KEY, 0x102, { KEY_ZOOMOUT } }, + { KE_KEY, 0x103, { KEY_ZOOMIN } }, + { KE_KEY, 0x13b, { KEY_COFFEE } }, + { KE_KEY, 0x13c, { KEY_BATTERY } }, + { KE_KEY, 0x13d, { KEY_SLEEP } }, + { KE_KEY, 0x13e, { KEY_SUSPEND } }, + { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, + { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, + { KE_KEY, 0x142, { KEY_WLAN } }, + { KE_KEY, 0x143, { KEY_PROG1 } }, + { KE_KEY, 0xb05, { KEY_PROG2 } }, + { KE_KEY, 0xb06, { KEY_WWW } }, + { KE_KEY, 0xb07, { KEY_MAIL } }, + { KE_KEY, 0xb30, { KEY_STOP } }, + { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, + { KE_KEY, 0xb32, { KEY_NEXTSONG } }, + { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, + { KE_KEY, 0xb5a, { KEY_MEDIA } }, + { KE_END, 0 }, }; /* utility @@ -852,64 +845,9 @@ static struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; -static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code) -{ - struct key_entry *key; - - for (key = toshiba_acpi_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code) -{ - struct key_entry *key; - - for (key = toshiba_acpi_keymap; key->type != KE_END; key++) - if (code == key->keycode && key->type == KE_KEY) - return key; - - return NULL; -} - -static int toshiba_acpi_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int toshiba_acpi_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct key_entry *key; - unsigned int old_keycode; - - key = toshiba_acpi_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - - return -EINVAL; -} - static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) { u32 hci_result, value; - struct key_entry *key; if (event != 0x80) return; @@ -922,19 +860,11 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) if (value & 0x80) continue; - key = toshiba_acpi_get_entry_by_scancode - (value); - if (!key) { + if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, + value, 1, true)) { printk(MY_INFO "Unknown key %x\n", value); - continue; } - input_report_key(toshiba_acpi.hotkey_dev, - key->keycode, 1); - input_sync(toshiba_acpi.hotkey_dev); - input_report_key(toshiba_acpi.hotkey_dev, - key->keycode, 0); - input_sync(toshiba_acpi.hotkey_dev); } else if (hci_result == HCI_NOT_SUPPORTED) { /* This is a workaround for an unresolved issue on * some machines where system events sporadically @@ -945,34 +875,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) } while (hci_result != HCI_EMPTY); } -static int toshiba_acpi_setup_keyboard(char *device) +static int __init toshiba_acpi_setup_keyboard(char *device) { acpi_status status; - acpi_handle handle; - int result; - const struct key_entry *key; + int error; - status = acpi_get_handle(NULL, device, &handle); + status = acpi_get_handle(NULL, device, &toshiba_acpi.handle); if (ACPI_FAILURE(status)) { printk(MY_INFO "Unable to get notification device\n"); return -ENODEV; } - toshiba_acpi.handle = handle; - - status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); - if (ACPI_FAILURE(status)) { - printk(MY_INFO "Unable to enable hotkeys\n"); - return -ENODEV; - } - - status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, - toshiba_acpi_notify, NULL); - if (ACPI_FAILURE(status)) { - printk(MY_INFO "Unable to install hotkey notification\n"); - return -ENODEV; - } - toshiba_acpi.hotkey_dev = input_allocate_device(); if (!toshiba_acpi.hotkey_dev) { printk(MY_INFO "Unable to register input device\n"); @@ -982,27 +895,54 @@ static int toshiba_acpi_setup_keyboard(char *device) toshiba_acpi.hotkey_dev->name = "Toshiba input device"; toshiba_acpi.hotkey_dev->phys = device; toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; - toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; - toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; - for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { - set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); - set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); + error = sparse_keymap_setup(toshiba_acpi.hotkey_dev, + toshiba_acpi_keymap, NULL); + if (error) + goto err_free_dev; + + status = acpi_install_notify_handler(toshiba_acpi.handle, + ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to install hotkey notification\n"); + error = -ENODEV; + goto err_free_keymap; + } + + status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to enable hotkeys\n"); + error = -ENODEV; + goto err_remove_notify; } - result = input_register_device(toshiba_acpi.hotkey_dev); - if (result) { + error = input_register_device(toshiba_acpi.hotkey_dev); + if (error) { printk(MY_INFO "Unable to register input device\n"); - return result; + goto err_remove_notify; } return 0; + + err_remove_notify: + acpi_remove_notify_handler(toshiba_acpi.handle, + ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); + err_free_keymap: + sparse_keymap_free(toshiba_acpi.hotkey_dev); + err_free_dev: + input_free_device(toshiba_acpi.hotkey_dev); + toshiba_acpi.hotkey_dev = NULL; + return error; } static void toshiba_acpi_exit(void) { - if (toshiba_acpi.hotkey_dev) + if (toshiba_acpi.hotkey_dev) { + acpi_remove_notify_handler(toshiba_acpi.handle, + ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); + sparse_keymap_free(toshiba_acpi.hotkey_dev); input_unregister_device(toshiba_acpi.hotkey_dev); + } if (toshiba_acpi.bt_rfk) { rfkill_unregister(toshiba_acpi.bt_rfk); @@ -1017,9 +957,6 @@ static void toshiba_acpi_exit(void) if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); - acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, - toshiba_acpi_notify); - if (toshiba_acpi.illumination_installed) led_classdev_unregister(&toshiba_led); -- cgit v0.10.2 From 71e687dc499819caa0d6ee0f80dcda1d5c24b5b2 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 24 Aug 2010 09:30:44 +0200 Subject: platform-x86: sync eeepc-laptop and asus-laptop Makes asus-laptop and eeepc-laptop _init/_exit functions looks exactly the same as they do the same thing. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b756e07..ffab9b4 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -639,29 +639,29 @@ static int asus_backlight_notify(struct asus_laptop *asus) static int asus_backlight_init(struct asus_laptop *asus) { struct backlight_device *bd; - struct device *dev = &asus->platform_device->dev; struct backlight_properties props; - if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && - !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && - lcd_switch_handle) { - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 15; - - bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, - asus, &asusbl_ops, &props); - if (IS_ERR(bd)) { - pr_err("Could not register asus backlight device\n"); - asus->backlight_device = NULL; - return PTR_ERR(bd); - } + if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) || + acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) || + !lcd_switch_handle) + return 0; - asus->backlight_device = bd; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = 15; - bd->props.power = FB_BLANK_UNBLANK; - bd->props.brightness = asus_read_brightness(bd); - backlight_update_status(bd); + bd = backlight_device_register(ASUS_LAPTOP_FILE, + &asus->platform_device->dev, asus, + &asusbl_ops, &props); + if (IS_ERR(bd)) { + pr_err("Could not register asus backlight device\n"); + asus->backlight_device = NULL; + return PTR_ERR(bd); } + + asus->backlight_device = bd; + bd->props.brightness = asus_read_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); return 0; } @@ -1130,7 +1130,6 @@ static int asus_input_init(struct asus_laptop *asus) input->phys = ASUS_LAPTOP_FILE "/input0"; input->id.bustype = BUS_HOST; input->dev.parent = &asus->platform_device->dev; - input_set_drvdata(input, asus); error = sparse_keymap_setup(input, asus_keymap, NULL); if (error) { @@ -1159,6 +1158,7 @@ static void asus_input_exit(struct asus_laptop *asus) sparse_keymap_free(asus->inputdev); input_unregister_device(asus->inputdev); } + asus->inputdev = NULL; } /* @@ -1278,19 +1278,19 @@ static int asus_sysfs_init(struct asus_laptop *asus) static int asus_platform_init(struct asus_laptop *asus) { - int err; + int result; asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); if (!asus->platform_device) return -ENOMEM; platform_set_drvdata(asus->platform_device, asus); - err = platform_device_add(asus->platform_device); - if (err) + result = platform_device_add(asus->platform_device); + if (result) goto fail_platform_device; - err = asus_sysfs_init(asus); - if (err) + result = asus_sysfs_init(asus); + if (result) goto fail_sysfs; return 0; @@ -1299,7 +1299,7 @@ fail_sysfs: platform_device_del(asus->platform_device); fail_platform_device: platform_device_put(asus->platform_device); - return err; + return result; } static void asus_platform_exit(struct asus_laptop *asus) @@ -1428,8 +1428,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus) return AE_OK; } -static bool asus_device_present; - static int __devinit asus_acpi_init(struct asus_laptop *asus) { int result = 0; @@ -1474,6 +1472,8 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) return result; } +static bool asus_device_present; + static int __devinit asus_acpi_add(struct acpi_device *device) { struct asus_laptop *asus; diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 6b8e062..b2edfdc 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -165,6 +165,7 @@ struct eeepc_laptop { u16 event_count[128]; /* count for each event */ struct platform_device *platform_device; + struct acpi_device *device; /* the device we are in */ struct device *hwmon_device; struct backlight_device *backlight_device; @@ -1193,9 +1194,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) eeepc->inputdev = input; return 0; - err_free_keymap: +err_free_keymap: sparse_keymap_free(input); - err_free_dev: +err_free_dev: input_free_device(input); return error; } @@ -1206,6 +1207,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc) sparse_keymap_free(eeepc->inputdev); input_unregister_device(eeepc->inputdev); } + eeepc->inputdev = NULL; } /* @@ -1326,16 +1328,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc) cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); } -static int eeepc_acpi_init(struct eeepc_laptop *eeepc, - struct acpi_device *device) +static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc) { unsigned int init_flags; int result; - result = acpi_bus_get_status(device); + result = acpi_bus_get_status(eeepc->device); if (result) return result; - if (!device->status.present) { + if (!eeepc->device->status.present) { pr_err("Hotkey device not present, aborting\n"); return -ENODEV; } @@ -1384,12 +1385,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device) strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); device->driver_data = eeepc; + eeepc->device = device; eeepc->hotplug_disabled = hotplug_disabled; eeepc_dmi_check(eeepc); - result = eeepc_acpi_init(eeepc, device); + result = eeepc_acpi_init(eeepc); if (result) goto fail_platform; eeepc_enable_camera(eeepc); -- cgit v0.10.2 From b58baecddee634903f339b7f7e9db7fc2a1449f1 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 24 Aug 2010 09:30:45 +0200 Subject: asus-laptop: Add key found on Asus N61JQ Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index ffab9b4..27298a4 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -278,6 +278,7 @@ static const struct key_entry asus_keymap[] = { {KE_KEY, 0x99, { KEY_PHONE } }, {KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, + {KE_KEY, 0xb5, { KEY_CALC } }, {KE_END, 0}, }; -- cgit v0.10.2 From 23f45c3a76e715217f40ac397c15815c774cad7f Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 24 Aug 2010 09:30:46 +0200 Subject: asus-laptop: fix gps rfkill The GPS rfkill crappy code. The ops_data argument wasn't set, and was totally misused. The fix have been tested on an Asus R2H. Cc: stable@kernel.org Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 27298a4..67d9228 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1066,9 +1066,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, */ static int asus_gps_rfkill_set(void *data, bool blocked) { - acpi_handle handle = data; + struct asus_laptop *asus = data; - return asus_gps_switch(handle, !blocked); + return asus_gps_switch(asus, !blocked); } static const struct rfkill_ops asus_gps_rfkill_ops = { @@ -1095,7 +1095,7 @@ static int asus_rfkill_init(struct asus_laptop *asus) asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, RFKILL_TYPE_GPS, - &asus_gps_rfkill_ops, NULL); + &asus_gps_rfkill_ops, asus); if (!asus->gps_rfkill) return -EINVAL; -- cgit v0.10.2 From b404ecbf91a5c19c0f448b513aa26696eee09392 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 25 Aug 2010 07:45:45 -0700 Subject: asus-laptop: remove no longer used keycode_map field The driver uses sparse keymap library and does not use this field anymore. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 67d9228..e3b5e06 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -236,7 +236,6 @@ struct asus_laptop { u8 light_level; /* light sensor level */ u8 light_switch; /* light sensor switch value */ u16 event_count[128]; /* count for each event TODO make this better */ - u16 *keycode_map; }; static const struct key_entry asus_keymap[] = { -- cgit v0.10.2 From af9902e130d30ce46b5827d18f8dd56e2accbaf1 Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Mon, 30 Aug 2010 09:09:53 +0200 Subject: Don't show error if Acer WMI is not found Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 2badee2..c8c6537 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1314,7 +1314,7 @@ static int __init acer_wmi_init(void) AMW0_find_mailled(); if (!interface) { - printk(ACER_ERR "No or unsupported WMI interface, unable to " + printk(ACER_INFO "No or unsupported WMI interface, unable to " "load\n"); return -ENODEV; } -- cgit v0.10.2 From 91e5d284a7f14c70187914056b0466163a5e2f03 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 28 Aug 2010 10:10:50 -0700 Subject: acpi_toshiba: fix kconfig error Fix kconfig recursive dependency error in ACPI_TOSHIBA: it uses both select and depends on for BACKLIGHT_CLASS_DEVICE. drivers/video/backlight/Kconfig:117:error: recursive dependency detected! drivers/video/backlight/Kconfig:117: symbol BACKLIGHT_CLASS_DEVICE is selected by ACPI_TOSHIBA drivers/platform/x86/Kconfig:490: symbol ACPI_TOSHIBA depends on LEDS_CLASS drivers/leds/Kconfig:12: symbol LEDS_CLASS is selected by BACKLIGHT_ADP8860 drivers/video/backlight/Kconfig:285: symbol BACKLIGHT_ADP8860 depends on BACKLIGHT_CLASS_DEVICE Signed-off-by: Randy Dunlap Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b96db80..55579f1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -497,7 +497,6 @@ config ACPI_TOSHIBA depends on RFKILL || RFKILL = n select INPUT_POLLDEV select INPUT_SPARSEKMAP - select BACKLIGHT_CLASS_DEVICE ---help--- This driver adds support for access to certain system settings on "legacy free" Toshiba laptops. These laptops can be recognized by -- cgit v0.10.2 From 392bd8b58414106b129d72a33fa99b999b720237 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 11 Sep 2010 16:31:02 +0300 Subject: platform: x86: throw away custom methods In 2.6.35 the hex_to_bin() was introduced. Signed-off-by: Andy Shevchenko Cc: Carlos Corbacho Cc: Matthew Garrett Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b2978a0..4980682 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = { */ static int wmi_parse_hexbyte(const u8 *src) { - unsigned int x; /* For correct wrapping */ int h; + int value; /* high part */ - x = src[0]; - if (x - '0' <= '9' - '0') { - h = x - '0'; - } else if (x - 'a' <= 'f' - 'a') { - h = x - 'a' + 10; - } else if (x - 'A' <= 'F' - 'A') { - h = x - 'A' + 10; - } else { + h = value = hex_to_bin(src[0]); + if (value < 0) return -1; - } - h <<= 4; /* low part */ - x = src[1]; - if (x - '0' <= '9' - '0') - return h | (x - '0'); - if (x - 'a' <= 'f' - 'a') - return h | (x - 'a' + 10); - if (x - 'A' <= 'F' - 'A') - return h | (x - 'A' + 10); + value = hex_to_bin(src[1]); + if (value >= 0) + return (h << 4) | value; return -1; } -- cgit v0.10.2 From 209009b2cb29124ad707fbb3ba4c95d3d100a1c4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Sep 2010 15:55:05 +0100 Subject: scu_ipc: Fix warning caused by include changes We need to include the SFI headers. This is fine as the SCU is only relevant to x86 platforms with SFI. Fixes the -next warning report. Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 6abe18e..41a9e34 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From ac9b1e5b63d5d9829ecf2294f0486ccd270c5db4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:12:19 -0700 Subject: asus-laptop: use attribute group to manage attributes Instead of registering (and removing) every attribute individually switch to using sysfs attribute group. This makes sure that we properly unwind and do not try to remove non-existent attributes which may not be safe to do in the future. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index e3b5e06..60a5a5c 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1200,82 +1200,70 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL); static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan); -static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, - store_bluetooth); +static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, + show_bluetooth, store_bluetooth); static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); -static void asus_sysfs_exit(struct asus_laptop *asus) -{ - struct platform_device *device = asus->platform_device; - - device_remove_file(&device->dev, &dev_attr_infos); - device_remove_file(&device->dev, &dev_attr_wlan); - device_remove_file(&device->dev, &dev_attr_bluetooth); - device_remove_file(&device->dev, &dev_attr_display); - device_remove_file(&device->dev, &dev_attr_ledd); - device_remove_file(&device->dev, &dev_attr_ls_switch); - device_remove_file(&device->dev, &dev_attr_ls_level); - device_remove_file(&device->dev, &dev_attr_gps); -} +static struct attribute *asus_attributes[] = { + &dev_attr_infos.attr, + &dev_attr_wlan.attr, + &dev_attr_bluetooth.attr, + &dev_attr_display.attr, + &dev_attr_ledd.attr, + &dev_attr_ls_level.attr, + &dev_attr_ls_switch.attr, + &dev_attr_gps.attr, + NULL +}; -static int asus_sysfs_init(struct asus_laptop *asus) +static mode_t asus_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, + int idx) { - struct platform_device *device = asus->platform_device; - int err; + struct device *dev = container_of(kobj, struct device, kobj); + struct platform_device *pdev = to_platform_device(dev); + struct asus_laptop *asus = platform_get_drvdata(pdev); + acpi_handle handle = asus->handle; + bool supported; - err = device_create_file(&device->dev, &dev_attr_infos); - if (err) - return err; + if (attr == &dev_attr_wlan.attr) { + supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); - if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) { - err = device_create_file(&device->dev, &dev_attr_wlan); - if (err) - return err; - } - - if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { - err = device_create_file(&device->dev, &dev_attr_bluetooth); - if (err) - return err; - } + } else if (attr == &dev_attr_bluetooth.attr) { + supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL); - if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { - err = device_create_file(&device->dev, &dev_attr_display); - if (err) - return err; - } + } else if (attr == &dev_attr_display.attr) { + supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL); - if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { - err = device_create_file(&device->dev, &dev_attr_ledd); - if (err) - return err; - } + } else if (attr == &dev_attr_ledd.attr) { + supported = !acpi_check_handle(handle, METHOD_LEDD, NULL); - if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && - !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { - err = device_create_file(&device->dev, &dev_attr_ls_switch); - if (err) - return err; - err = device_create_file(&device->dev, &dev_attr_ls_level); - if (err) - return err; - } + } else if (attr == &dev_attr_ls_switch.attr || + attr == &dev_attr_ls_level.attr) { + supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && + !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); - if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && - !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && - !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) { - err = device_create_file(&device->dev, &dev_attr_gps); - if (err) - return err; + } else if (attr == &dev_attr_gps.attr) { + supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && + !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && + !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL); + } else { + supported = true; } - return err; + return supported ? attr->mode : 0; } + +static const struct attribute_group asus_attr_group = { + .is_visible = asus_sysfs_is_visible, + .attrs = asus_attributes, +}; + static int asus_platform_init(struct asus_laptop *asus) { int result; @@ -1289,13 +1277,14 @@ static int asus_platform_init(struct asus_laptop *asus) if (result) goto fail_platform_device; - result = asus_sysfs_init(asus); + result = sysfs_create_group(&asus->platform_device->dev.kobj, + &asus_attr_group); if (result) goto fail_sysfs; + return 0; fail_sysfs: - asus_sysfs_exit(asus); platform_device_del(asus->platform_device); fail_platform_device: platform_device_put(asus->platform_device); @@ -1304,7 +1293,7 @@ fail_platform_device: static void asus_platform_exit(struct asus_laptop *asus) { - asus_sysfs_exit(asus); + sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group); platform_device_unregister(asus->platform_device); } -- cgit v0.10.2 From 5212cd678af82fef00f6d60d14de01c1211aad56 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:14:42 -0700 Subject: WMI: remove EC region handler when _WDG parsing fails Driver initialization was forgetting to remove EC address space handler in cases when parse_wdg() method failed. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 4980682..63d0b65 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -956,12 +956,17 @@ static int acpi_wmi_add(struct acpi_device *device) ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler, NULL, NULL); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Error installing EC region handler\n"); return -ENODEV; + } status = parse_wdg(device->handle); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + acpi_remove_address_space_handler(device->handle, + ACPI_ADR_SPACE_EC, + &acpi_wmi_ec_space_handler); + printk(KERN_ERR PREFIX "Failed to parse WDG method\n"); return -ENODEV; } -- cgit v0.10.2 From 2d5ab5551f38793f8977114d78a98aad138cfb4e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:14:48 -0700 Subject: WMI: free wmi blocks when parse_wdg() fails Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 63d0b65..8d59699 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -795,6 +795,16 @@ static bool guid_already_parsed(const char *guid_string) return false; } +static void free_wmi_blocks(void) +{ + struct wmi_block *wblock, *next; + + list_for_each_entry_safe(wblock, next, &wmi_blocks.list, list) { + list_del(&wblock->list); + kfree(wblock); + } +} + /* * Parse the _WDG method for the GUID data blocks */ @@ -853,7 +863,7 @@ static acpi_status parse_wdg(acpi_handle handle) wblock->handle = handle; if (debug_event) { wblock->handler = wmi_notify_debug; - status = wmi_method_enable(wblock, 1); + wmi_method_enable(wblock, 1); } list_add_tail(&wblock->list, &wmi_blocks.list); } @@ -863,6 +873,9 @@ out_free_gblock: out_free_pointer: kfree(out.pointer); + if (ACPI_FAILURE(status)) + free_wmi_blocks(); + return status; } @@ -1002,19 +1015,11 @@ static int __init acpi_wmi_init(void) static void __exit acpi_wmi_exit(void) { - struct list_head *p, *tmp; - struct wmi_block *wblock; - wmi_class_exit(); acpi_bus_unregister_driver(&acpi_wmi_driver); - list_for_each_safe(p, tmp, &wmi_blocks.list) { - wblock = list_entry(p, struct wmi_block, list); - - list_del(p); - kfree(wblock); - } + free_wmi_blocks(); printk(KERN_INFO PREFIX "Mapper unloaded\n"); } -- cgit v0.10.2 From 4e4304d7498c85f6ea798ee5fcb5d3bd3741e74f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:14:53 -0700 Subject: WMI: fix wmi_gtoa() to actully terminate the string Courtesy of sparse... Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 8d59699..88ce87c 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -220,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out) for (i = 10; i <= 15; i++) out += sprintf(out, "%02X", in[i] & 0xFF); - out = '\0'; + *out = '\0'; return 0; } -- cgit v0.10.2 From 64ed0ab8d060d28a7787de29c76178c2efc1dd65 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:14:58 -0700 Subject: WMI: do not leak memory in parse_wdg() If we _WDG returned object that is not buffer we were forgetting to free memory allocated for that object. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 88ce87c..a24a4b5 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -825,8 +825,10 @@ static acpi_status parse_wdg(acpi_handle handle) obj = (union acpi_object *) out.pointer; - if (obj->type != ACPI_TYPE_BUFFER) - return AE_ERROR; + if (obj->type != ACPI_TYPE_BUFFER) { + status = AE_ERROR; + goto out_free_pointer; + } total = obj->buffer.length / sizeof(struct guid_block); -- cgit v0.10.2 From 3d2c63eb5e0abfd06b19928c8ed43020b3451a3c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:15:03 -0700 Subject: WMI: fix potential NULL pointer dereference Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index a24a4b5..81552bc 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -824,6 +824,8 @@ static acpi_status parse_wdg(acpi_handle handle) return status; obj = (union acpi_object *) out.pointer; + if (!obj) + return AE_ERROR; if (obj->type != ACPI_TYPE_BUFFER) { status = AE_ERROR; -- cgit v0.10.2 From 378306628e26d2945a1ed245f177a693c314e68d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:15:09 -0700 Subject: WMI: simplify handling of returned WMI blocks in parse_wdg() There is no reason why we allocate memory and copy data into an intermediate buffer, it is not like we are working with data coming from userspace. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 81552bc..f6e85c8 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -475,7 +475,7 @@ const struct acpi_buffer *in) } EXPORT_SYMBOL_GPL(wmi_set_block); -static void wmi_dump_wdg(struct guid_block *g) +static void wmi_dump_wdg(const struct guid_block *g) { char guid_string[37]; @@ -812,7 +812,7 @@ static acpi_status parse_wdg(acpi_handle handle) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; - struct guid_block *gblock; + const struct guid_block *gblock; struct wmi_block *wblock; char guid_string[37]; acpi_status status; @@ -832,14 +832,9 @@ static acpi_status parse_wdg(acpi_handle handle) goto out_free_pointer; } + gblock = (const struct guid_block *)obj->buffer.pointer; total = obj->buffer.length / sizeof(struct guid_block); - gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); - if (!gblock) { - status = AE_NO_MEMORY; - goto out_free_pointer; - } - for (i = 0; i < total; i++) { /* Some WMI devices, like those for nVidia hooks, have a @@ -860,7 +855,7 @@ static acpi_status parse_wdg(acpi_handle handle) wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); if (!wblock) { status = AE_NO_MEMORY; - goto out_free_gblock; + goto out_free_pointer; } wblock->gblock = gblock[i]; @@ -872,8 +867,6 @@ static acpi_status parse_wdg(acpi_handle handle) list_add_tail(&wblock->list, &wmi_blocks.list); } -out_free_gblock: - kfree(gblock); out_free_pointer: kfree(out.pointer); -- cgit v0.10.2 From 762e1a2ff6c1116d8f6f175994753fa2319097f2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:15:14 -0700 Subject: WMI: use separate list head for storing wmi blocks Do not abuse wmi_block structure to hold the head of list of blocks, use separate list_head for that. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index f6e85c8..dc528ba 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -47,6 +47,7 @@ MODULE_LICENSE("GPL"); #define PREFIX "ACPI: WMI: " static DEFINE_MUTEX(wmi_data_lock); +static LIST_HEAD(wmi_block_list); struct guid_block { char guid[16]; @@ -70,7 +71,6 @@ struct wmi_block { struct device *dev; }; -static struct wmi_block wmi_blocks; /* * If the GUID data block is marked as expensive, we must enable and @@ -234,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) wmi_parse_guid(guid_string, tmp); wmi_swap_bytes(tmp, guid_input); - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -621,7 +621,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = event; - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); gblock = &wblock->gblock; @@ -709,7 +709,7 @@ static int wmi_create_devs(void) struct device *guid_dev; /* Create devices for all the GUIDs */ - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); @@ -746,7 +746,7 @@ static void wmi_remove_devs(void) struct device *guid_dev; /* Delete devices for all the GUIDs */ - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); guid_dev = wblock->dev; @@ -785,7 +785,7 @@ static bool guid_already_parsed(const char *guid_string) struct wmi_block *wblock; struct list_head *p; - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); gblock = &wblock->gblock; @@ -799,7 +799,7 @@ static void free_wmi_blocks(void) { struct wmi_block *wblock, *next; - list_for_each_entry_safe(wblock, next, &wmi_blocks.list, list) { + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { list_del(&wblock->list); kfree(wblock); } @@ -864,7 +864,7 @@ static acpi_status parse_wdg(acpi_handle handle) wblock->handler = wmi_notify_debug; wmi_method_enable(wblock, 1); } - list_add_tail(&wblock->list, &wmi_blocks.list); + list_add_tail(&wblock->list, &wmi_block_list); } out_free_pointer: @@ -927,7 +927,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) struct list_head *p; char guid_string[37]; - list_for_each(p, &wmi_blocks.list) { + list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; @@ -987,8 +987,6 @@ static int __init acpi_wmi_init(void) { int result; - INIT_LIST_HEAD(&wmi_blocks.list); - if (acpi_disabled) return -ENODEV; -- cgit v0.10.2 From 8e07514db8d037a8454e81cd529a9844c9fa7075 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:15:19 -0700 Subject: WMI: use pr_err() and friends This makes source more concise and easier to read. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index dc528ba..dd30b2a 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -27,6 +27,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -44,8 +46,6 @@ MODULE_LICENSE("GPL"); #define ACPI_WMI_CLASS "wmi" -#define PREFIX "ACPI: WMI: " - static DEFINE_MUTEX(wmi_data_lock); static LIST_HEAD(wmi_block_list); @@ -480,25 +480,24 @@ static void wmi_dump_wdg(const struct guid_block *g) char guid_string[37]; wmi_gtoa(g->guid, guid_string); - printk(KERN_INFO PREFIX "%s:\n", guid_string); - printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", - g->object_id[0], g->object_id[1]); - printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); - printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); - printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); - printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); + + pr_info("%s:\n", guid_string); + pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); + pr_info("\tnotify_id: %02X\n", g->notify_id); + pr_info("\treserved: %02X\n", g->reserved); + pr_info("\tinstance_count: %d\n", g->instance_count); + pr_info("\tflags: %#x ", g->flags); if (g->flags) { - printk(" "); if (g->flags & ACPI_WMI_EXPENSIVE) - printk("ACPI_WMI_EXPENSIVE "); + pr_cont("ACPI_WMI_EXPENSIVE "); if (g->flags & ACPI_WMI_METHOD) - printk("ACPI_WMI_METHOD "); + pr_cont("ACPI_WMI_METHOD "); if (g->flags & ACPI_WMI_STRING) - printk("ACPI_WMI_STRING "); + pr_cont("ACPI_WMI_STRING "); if (g->flags & ACPI_WMI_EVENT) - printk("ACPI_WMI_EVENT "); + pr_cont("ACPI_WMI_EVENT "); } - printk("\n"); + pr_cont("\n"); } @@ -510,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context) status = wmi_get_event_data(value, &response); if (status != AE_OK) { - printk(KERN_INFO "wmi: bad event status 0x%x\n", status); + pr_info("bad event status 0x%x\n", status); return; } @@ -519,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context) if (!obj) return; - printk(KERN_INFO PREFIX "DEBUG Event "); + pr_info("DEBUG Event "); switch(obj->type) { case ACPI_TYPE_BUFFER: - printk("BUFFER_TYPE - length %d\n", obj->buffer.length); + pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); break; case ACPI_TYPE_STRING: - printk("STRING_TYPE - %s\n", obj->string.pointer); + pr_cont("STRING_TYPE - %s\n", obj->string.pointer); break; case ACPI_TYPE_INTEGER: - printk("INTEGER_TYPE - %llu\n", obj->integer.value); + pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); break; case ACPI_TYPE_PACKAGE: - printk("PACKAGE_TYPE - %d elements\n", obj->package.count); + pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); break; default: - printk("object type 0x%X\n", obj->type); + pr_cont("object type 0x%X\n", obj->type); } kfree(obj); } @@ -845,8 +844,7 @@ static acpi_status parse_wdg(acpi_handle handle) */ if (guid_already_parsed(gblock[i].guid) == true) { wmi_gtoa(gblock[i].guid, guid_string); - printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", - guid_string); + pr_info("Skipping duplicate GUID %s\n", guid_string); continue; } if (debug_dump_wdg) @@ -937,8 +935,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) wblock->handler(event, wblock->handler_data); if (debug_event) { wmi_gtoa(wblock->gblock.guid, guid_string); - printk(KERN_INFO PREFIX "DEBUG Event GUID:" - " %s\n", guid_string); + pr_info("DEBUG Event GUID: %s\n", guid_string); } acpi_bus_generate_netlink_event( @@ -967,7 +964,7 @@ static int acpi_wmi_add(struct acpi_device *device) &acpi_wmi_ec_space_handler, NULL, NULL); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + pr_err("Error installing EC region handler\n"); return -ENODEV; } @@ -976,7 +973,7 @@ static int acpi_wmi_add(struct acpi_device *device) acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); - printk(KERN_ERR PREFIX "Failed to parse WDG method\n"); + pr_err("Failed to parse WDG method\n"); return -ENODEV; } @@ -991,9 +988,8 @@ static int __init acpi_wmi_init(void) return -ENODEV; result = acpi_bus_register_driver(&acpi_wmi_driver); - if (result < 0) { - printk(KERN_INFO PREFIX "Error loading mapper\n"); + pr_err("Error loading mapper\n"); return -ENODEV; } @@ -1003,20 +999,18 @@ static int __init acpi_wmi_init(void) return result; } - printk(KERN_INFO PREFIX "Mapper loaded\n"); + pr_info("Mapper loaded\n"); - return result; + return 0; } static void __exit acpi_wmi_exit(void) { wmi_class_exit(); - acpi_bus_unregister_driver(&acpi_wmi_driver); - free_wmi_blocks(); - printk(KERN_INFO PREFIX "Mapper unloaded\n"); + pr_info("Mapper unloaded\n"); } subsys_initcall(acpi_wmi_init); -- cgit v0.10.2 From 614ef4322200086447d5e1f79e8876213c94f499 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:15:25 -0700 Subject: WMI: make use of class device's attributres Instead of adding modalias attribute manually set it up as class's device attribute so driver core will create and remove it for us. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index dd30b2a..b9a60a0 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -649,7 +649,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); /* * sysfs interface */ -static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { char guid_string[37]; @@ -663,7 +663,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, return sprintf(buf, "wmi:%s\n", guid_string); } -static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static struct device_attribute wmi_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL +}; static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -696,6 +700,7 @@ static struct class wmi_class = { .name = "wmi", .dev_release = wmi_dev_free, .dev_uevent = wmi_dev_uevent, + .dev_attrs = wmi_dev_attrs, }; static int wmi_create_devs(void) @@ -728,10 +733,6 @@ static int wmi_create_devs(void) result = device_register(guid_dev); if (result) return result; - - result = device_create_file(guid_dev, &dev_attr_modalias); - if (result) - return result; } return 0; @@ -751,8 +752,6 @@ static void wmi_remove_devs(void) guid_dev = wblock->dev; gblock = &wblock->gblock; - device_remove_file(guid_dev, &dev_attr_modalias); - device_unregister(guid_dev); } } -- cgit v0.10.2 From c64eefd48c44fa8145ad1f96edabf4a053fffc49 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 26 Aug 2010 00:15:30 -0700 Subject: WMI: embed struct device directly into wmi_block Instead of creating wmi_blocks and then register corresponding devices on a separate pass do it all in one shot, since lifetime rules for both objects are the same. This also takes care of leaking devices when device_create fails for one of them. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b9a60a0..104b77c 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -68,7 +68,7 @@ struct wmi_block { acpi_handle handle; wmi_notify_handler handler; void *handler_data; - struct device *dev; + struct device dev; }; @@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = { .add = acpi_wmi_add, .remove = acpi_wmi_remove, .notify = acpi_wmi_notify, - }, + }, }; /* @@ -693,7 +693,9 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) static void wmi_dev_free(struct device *dev) { - kfree(dev); + struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); + + kfree(wmi_block); } static struct class wmi_class = { @@ -703,104 +705,60 @@ static struct class wmi_class = { .dev_attrs = wmi_dev_attrs, }; -static int wmi_create_devs(void) +static struct wmi_block *wmi_create_device(const struct guid_block *gblock, + acpi_handle handle) { - int result; - char guid_string[37]; - struct guid_block *gblock; struct wmi_block *wblock; - struct list_head *p; - struct device *guid_dev; + int error; + char guid_string[37]; - /* Create devices for all the GUIDs */ - list_for_each(p, &wmi_block_list) { - wblock = list_entry(p, struct wmi_block, list); + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) { + error = -ENOMEM; + goto err_out; + } - guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!guid_dev) - return -ENOMEM; + wblock->handle = handle; + wblock->gblock = *gblock; - wblock->dev = guid_dev; + wblock->dev.class = &wmi_class; - guid_dev->class = &wmi_class; - dev_set_drvdata(guid_dev, wblock); + wmi_gtoa(gblock->guid, guid_string); + dev_set_name(&wblock->dev, guid_string); - gblock = &wblock->gblock; + dev_set_drvdata(&wblock->dev, wblock); - wmi_gtoa(gblock->guid, guid_string); - dev_set_name(guid_dev, guid_string); + error = device_register(&wblock->dev); + if (error) + goto err_free; - result = device_register(guid_dev); - if (result) - return result; - } + list_add_tail(&wblock->list, &wmi_block_list); + return wblock; - return 0; +err_free: + kfree(wblock); +err_out: + return ERR_PTR(error); } -static void wmi_remove_devs(void) +static void wmi_free_devices(void) { - struct guid_block *gblock; - struct wmi_block *wblock; - struct list_head *p; - struct device *guid_dev; + struct wmi_block *wblock, *next; /* Delete devices for all the GUIDs */ - list_for_each(p, &wmi_block_list) { - wblock = list_entry(p, struct wmi_block, list); - - guid_dev = wblock->dev; - gblock = &wblock->gblock; - - device_unregister(guid_dev); - } -} - -static void wmi_class_exit(void) -{ - wmi_remove_devs(); - class_unregister(&wmi_class); -} - -static int wmi_class_init(void) -{ - int ret; - - ret = class_register(&wmi_class); - if (ret) - return ret; - - ret = wmi_create_devs(); - if (ret) - wmi_class_exit(); - - return ret; + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) + device_unregister(&wblock->dev); } static bool guid_already_parsed(const char *guid_string) { - struct guid_block *gblock; struct wmi_block *wblock; - struct list_head *p; - - list_for_each(p, &wmi_block_list) { - wblock = list_entry(p, struct wmi_block, list); - gblock = &wblock->gblock; - if (strncmp(gblock->guid, guid_string, 16) == 0) + list_for_each_entry(wblock, &wmi_block_list, list) + if (strncmp(wblock->gblock.guid, guid_string, 16) == 0) return true; - } - return false; -} -static void free_wmi_blocks(void) -{ - struct wmi_block *wblock, *next; - - list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { - list_del(&wblock->list); - kfree(wblock); - } + return false; } /* @@ -814,19 +772,19 @@ static acpi_status parse_wdg(acpi_handle handle) struct wmi_block *wblock; char guid_string[37]; acpi_status status; + int retval; u32 i, total; status = acpi_evaluate_object(handle, "_WDG", NULL, &out); - if (ACPI_FAILURE(status)) - return status; + return -ENXIO; obj = (union acpi_object *) out.pointer; if (!obj) - return AE_ERROR; + return -ENXIO; if (obj->type != ACPI_TYPE_BUFFER) { - status = AE_ERROR; + retval = -ENXIO; goto out_free_pointer; } @@ -846,31 +804,29 @@ static acpi_status parse_wdg(acpi_handle handle) pr_info("Skipping duplicate GUID %s\n", guid_string); continue; } + if (debug_dump_wdg) wmi_dump_wdg(&gblock[i]); - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); - if (!wblock) { - status = AE_NO_MEMORY; - goto out_free_pointer; + wblock = wmi_create_device(&gblock[i], handle); + if (IS_ERR(wblock)) { + retval = PTR_ERR(wblock); + wmi_free_devices(); + break; } - wblock->gblock = gblock[i]; - wblock->handle = handle; if (debug_event) { wblock->handler = wmi_notify_debug; wmi_method_enable(wblock, 1); } - list_add_tail(&wblock->list, &wmi_block_list); } + retval = 0; + out_free_pointer: kfree(out.pointer); - if (ACPI_FAILURE(status)) - free_wmi_blocks(); - - return status; + return retval; } /* @@ -949,6 +905,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) { acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); + wmi_free_devices(); return 0; } @@ -956,7 +913,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) static int acpi_wmi_add(struct acpi_device *device) { acpi_status status; - int result = 0; + int error; status = acpi_install_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, @@ -967,47 +924,44 @@ static int acpi_wmi_add(struct acpi_device *device) return -ENODEV; } - status = parse_wdg(device->handle); - if (ACPI_FAILURE(status)) { + error = parse_wdg(device->handle); + if (error) { acpi_remove_address_space_handler(device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); pr_err("Failed to parse WDG method\n"); - return -ENODEV; + return error; } - return result; + return 0; } static int __init acpi_wmi_init(void) { - int result; + int error; if (acpi_disabled) return -ENODEV; - result = acpi_bus_register_driver(&acpi_wmi_driver); - if (result < 0) { - pr_err("Error loading mapper\n"); - return -ENODEV; - } + error = class_register(&wmi_class); + if (error) + return error; - result = wmi_class_init(); - if (result) { - acpi_bus_unregister_driver(&acpi_wmi_driver); - return result; + error = acpi_bus_register_driver(&acpi_wmi_driver); + if (error) { + pr_err("Error loading mapper\n"); + class_unregister(&wmi_class); + return error; } pr_info("Mapper loaded\n"); - return 0; } static void __exit acpi_wmi_exit(void) { - wmi_class_exit(); acpi_bus_unregister_driver(&acpi_wmi_driver); - free_wmi_blocks(); + class_unregister(&wmi_class); pr_info("Mapper unloaded\n"); } -- cgit v0.10.2 From 037accfa14b28ecf49d9060063929c4b4cde373f Mon Sep 17 00:00:00 2001 From: Keng-Yu Lin Date: Tue, 28 Sep 2010 11:43:31 +0800 Subject: dell-laptop: Add debugfs support Export the status of RF killswitch through debugfs. The killswitch status is obtained by the SMI to BIOS. Exporting this status through debugfs can help identify the issue with the misbehaving firmware. Signed-off-by: Keng-Yu Lin Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 4413975..cf8a89a 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d @@ -325,6 +327,75 @@ static const struct rfkill_ops dell_rfkill_ops = { .query = dell_rfkill_query, }; +static struct dentry *dell_laptop_dir; + +static int dell_debugfs_show(struct seq_file *s, void *data) +{ + int status; + + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + release_buffer(); + + seq_printf(s, "status:\t0x%X\n", status); + seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", + status & BIT(0)); + seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n", + (status & BIT(1)) >> 1); + seq_printf(s, "Bit 2 : Wifi is supported: %lu\n", + (status & BIT(2)) >> 2); + seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n", + (status & BIT(3)) >> 3); + seq_printf(s, "Bit 4 : WWAN is supported: %lu\n", + (status & BIT(4)) >> 4); + seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n", + (status & BIT(5)) >> 5); + seq_printf(s, "Bit 8 : Wifi is installed: %lu\n", + (status & BIT(8)) >> 8); + seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n", + (status & BIT(9)) >> 9); + seq_printf(s, "Bit 10: WWAN is installed: %lu\n", + (status & BIT(10)) >> 10); + seq_printf(s, "Bit 16: Hardware switch is on: %lu\n", + (status & BIT(16)) >> 16); + seq_printf(s, "Bit 17: Wifi is blocked: %lu\n", + (status & BIT(17)) >> 17); + seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n", + (status & BIT(18)) >> 18); + seq_printf(s, "Bit 19: WWAN is blocked: %lu\n", + (status & BIT(19)) >> 19); + + seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state); + seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n", + hwswitch_state & BIT(0)); + seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n", + (hwswitch_state & BIT(1)) >> 1); + seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n", + (hwswitch_state & BIT(2)) >> 2); + seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n", + (hwswitch_state & BIT(7)) >> 7); + seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n", + (hwswitch_state & BIT(8)) >> 8); + seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n", + (hwswitch_state & BIT(15)) >> 15); + + return 0; +} + +static int dell_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, dell_debugfs_show, inode->i_private); +} + +static const struct file_operations dell_debugfs_fops = { + .owner = THIS_MODULE, + .open = dell_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void dell_update_rfkill(struct work_struct *ignored) { if (wifi_rfkill) @@ -556,6 +627,11 @@ static int __init dell_init(void) goto fail_filter; } + dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); + if (dell_laptop_dir != NULL) + debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, + &dell_debugfs_fops); + #ifdef CONFIG_ACPI /* In the event of an ACPI backlight being available, don't * register the platform controller. @@ -615,6 +691,7 @@ fail_platform_driver: static void __exit dell_exit(void) { + debugfs_remove_recursive(dell_laptop_dir); i8042_remove_filter(dell_laptop_i8042_filter); cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); -- cgit v0.10.2 From 6a09f21dd1e205a68c8f8c4f39e4cff8f63801b6 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:38:46 +0800 Subject: ideapad: add ACPI helpers There are two methods under VPC2004 which is used to access VDAT/VCMD of EC register. Add helpers for read and write these two registers. And add read_method_int for reading the return value from ACPI methods which requires no parameter. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c index 7984963..c87693c 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad_acpi.c @@ -49,6 +49,115 @@ static struct { { "ideapad_killsw", RFKILL_TYPE_WLAN } }; +/* + * ACPI Helpers + */ +#define IDEAPAD_EC_TIMEOUT (100) /* in ms */ + +static int read_method_int(acpi_handle handle, const char *method, int *val) +{ + acpi_status status; + unsigned long long result; + + status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); + if (ACPI_FAILURE(status)) { + *val = -1; + return -1; + } else { + *val = result; + return 0; + } +} + +static int method_vpcr(acpi_handle handle, int cmd, int *ret) +{ + acpi_status status; + unsigned long long result; + struct acpi_object_list params; + union acpi_object in_obj; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = cmd; + + status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); + + if (ACPI_FAILURE(status)) { + *ret = -1; + return -1; + } else { + *ret = result; + return 0; + } +} + +static int method_vpcw(acpi_handle handle, int cmd, int data) +{ + struct acpi_object_list params; + union acpi_object in_obj[2]; + acpi_status status; + + params.count = 2; + params.pointer = in_obj; + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = cmd; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = data; + + status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); + if (status != AE_OK) + return -1; + return 0; +} + +static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) +{ + int val; + unsigned long int end_jiffies; + + if (method_vpcw(handle, 1, cmd)) + return -1; + + for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; + time_before(jiffies, end_jiffies);) { + schedule(); + if (method_vpcr(handle, 1, &val)) + return -1; + if (val == 0) { + if (method_vpcr(handle, 0, &val)) + return -1; + *data = val; + return 0; + } + } + pr_err("timeout in read_ec_cmd\n"); + return -1; +} + +static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) +{ + int val; + unsigned long int end_jiffies; + + if (method_vpcw(handle, 0, data)) + return -1; + if (method_vpcw(handle, 1, cmd)) + return -1; + + for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; + time_before(jiffies, end_jiffies);) { + schedule(); + if (method_vpcr(handle, 1, &val)) + return -1; + if (val == 0) + return 0; + } + pr_err("timeout in write_ec_cmd\n"); + return -1; +} +/* the above is ACPI helpers */ + static int ideapad_dev_exists(int device) { acpi_status status; -- cgit v0.10.2 From 8e7d354370f61cbe82a8b4a0f74224aed900b410 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:39:05 +0800 Subject: ideapad: check VPC bit before sync rfkill hw status Check VPC bit to make sure the HW rfkill is touched. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c index c87693c..e07d607 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad_acpi.c @@ -379,7 +379,21 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) { - ideapad_sync_rfk_state(adevice); + acpi_handle handle = adevice->handle; + unsigned long vpc1, vpc2, vpc_bit; + + if (read_ec_data(handle, 0x10, &vpc1)) + return; + if (read_ec_data(handle, 0x1A, &vpc2)) + return; + + vpc1 = (vpc2 << 8) | vpc1; + for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { + if (test_bit(vpc_bit, &vpc1)) { + if (vpc_bit == 9) + ideapad_sync_rfk_state(adevice); + } + } } static struct acpi_driver ideapad_acpi_driver = { -- cgit v0.10.2 From 6f8371c05e64138c305aa1b6a21857cd7a50e147 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:39:14 +0800 Subject: ideapad: make sure we bind on the correct device By reading from method _CFG to make sure we bind on the correct VPC2004 device. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c index e07d607..bf684f4 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad_acpi.c @@ -326,10 +326,13 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static int ideapad_acpi_add(struct acpi_device *adevice) { - int i; + int i, cfg; int devs_present[5]; struct ideapad_private *priv; + if (read_method_int(adevice->handle, "_CFG", &cfg)) + return -ENODEV; + for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { devs_present[i] = ideapad_dev_exists(i); if (devs_present[i] < 0) -- cgit v0.10.2 From dfa7f6fe0ad7697ba43303bf37487987409b1b91 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:39:29 +0800 Subject: ideapad: use return value of _CFG to tell if device exist or not There are several bits of the return value of _CFG shows if RF/Camera devices exist or not. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c index bf684f4..1f736d1 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad_acpi.c @@ -40,13 +40,14 @@ struct ideapad_private { static struct { char *name; + int cfgbit; int type; } ideapad_rfk_data[] = { - /* camera has no rfkill */ - { "ideapad_wlan", RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", RFKILL_TYPE_WWAN }, - { "ideapad_killsw", RFKILL_TYPE_WLAN } + { "ideapad_camera", 19, NUM_RFKILL_TYPES }, + { "ideapad_wlan", 18, RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", 16, RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", 17, RFKILL_TYPE_WWAN }, + { "ideapad_killsw", 0, RFKILL_TYPE_WLAN } }; /* @@ -158,32 +159,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) } /* the above is ACPI helpers */ -static int ideapad_dev_exists(int device) -{ - acpi_status status; - union acpi_object in_param; - struct acpi_object_list input = { 1, &in_param }; - struct acpi_buffer output; - union acpi_object out_obj; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - in_param.type = ACPI_TYPE_INTEGER; - in_param.integer.value = device + 1; - - status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output); - if (ACPI_FAILURE(status)) { - printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status); - return -ENODEV; - } - if (out_obj.type != ACPI_TYPE_INTEGER) { - printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n"); - return -ENODEV; - } - return out_obj.integer.value; -} - static int ideapad_dev_get_state(int device) { acpi_status status; @@ -334,9 +309,10 @@ static int ideapad_acpi_add(struct acpi_device *adevice) return -ENODEV; for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { - devs_present[i] = ideapad_dev_exists(i); - if (devs_present[i] < 0) - return devs_present[i]; + if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) + devs_present[i] = 1; + else + devs_present[i] = 0; } /* The hardware switch is always present */ -- cgit v0.10.2 From 26c81d5c9a88af404a5fef43caa259e8637fec94 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:39:40 +0800 Subject: ideapad: use EC command to control camera Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c index 1f736d1..e45fc50 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad_acpi.c @@ -35,6 +35,7 @@ #define IDEAPAD_DEV_KILLSW 4 struct ideapad_private { + acpi_handle handle; struct rfkill *rfk[5]; }; @@ -207,24 +208,28 @@ static ssize_t show_ideapad_cam(struct device *dev, struct device_attribute *attr, char *buf) { - int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA); - if (state < 0) - return state; + struct ideapad_private *priv = dev_get_drvdata(dev); + acpi_handle handle = priv->handle; + unsigned long result; - return sprintf(buf, "%d\n", state); + if (read_ec_data(handle, 0x1D, &result)) + return sprintf(buf, "-1\n"); + return sprintf(buf, "%lu\n", result); } static ssize_t store_ideapad_cam(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct ideapad_private *priv = dev_get_drvdata(dev); + acpi_handle handle = priv->handle; int ret, state; if (!count) return 0; if (sscanf(buf, "%i", &state) != 1) return -EINVAL; - ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state); + ret = write_ec_cmd(handle, 0x1E, state); if (ret < 0) return ret; return count; @@ -330,6 +335,7 @@ static int ideapad_acpi_add(struct acpi_device *adevice) } } + priv->handle = adevice->handle; dev_set_drvdata(&adevice->dev, priv); for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { if (!devs_present[i]) -- cgit v0.10.2 From 2b7266bd49efc84f6642cf9bb7fb37d286345d15 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:39:49 +0800 Subject: ideapad: rewrite the hw rfkill notify 1. Read hw rfkill status by ec command 2. Not to touch sw status of each rfkill when hw rfkill notify 3. Initial rfkill status when module loaded Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c index e45fc50..09f6ce6 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad_acpi.c @@ -160,32 +160,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) } /* the above is ACPI helpers */ -static int ideapad_dev_get_state(int device) -{ - acpi_status status; - union acpi_object in_param; - struct acpi_object_list input = { 1, &in_param }; - struct acpi_buffer output; - union acpi_object out_obj; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - in_param.type = ACPI_TYPE_INTEGER; - in_param.integer.value = device + 1; - - status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output); - if (ACPI_FAILURE(status)) { - printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status); - return -ENODEV; - } - if (out_obj.type != ACPI_TYPE_INTEGER) { - printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n"); - return -ENODEV; - } - return out_obj.integer.value; -} - static int ideapad_dev_set_state(int device, int state) { acpi_status status; @@ -253,32 +227,39 @@ static struct rfkill_ops ideapad_rfk_ops = { static void ideapad_sync_rfk_state(struct acpi_device *adevice) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW); + acpi_handle handle = priv->handle; + unsigned long hw_blocked; int i; - rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked); - for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) - if (priv->rfk[i]) - rfkill_set_hw_state(priv->rfk[i], hw_blocked); - if (hw_blocked) + if (read_ec_data(handle, 0x23, &hw_blocked)) return; + hw_blocked = !hw_blocked; - for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) + for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) if (priv->rfk[i]) - rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i)); + rfkill_set_hw_state(priv->rfk[i], hw_blocked); } static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int ret; + unsigned long sw_blocked; - priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev, - ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops, + priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, + ideapad_rfk_data[dev].type, &ideapad_rfk_ops, (void *)(long)dev); if (!priv->rfk[dev]) return -ENOMEM; + if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, + &sw_blocked)) { + rfkill_init_sw_state(priv->rfk[dev], 0); + } else { + sw_blocked = !sw_blocked; + rfkill_init_sw_state(priv->rfk[dev], sw_blocked); + } + ret = rfkill_register(priv->rfk[dev]); if (ret) { rfkill_destroy(priv->rfk[dev]); -- cgit v0.10.2 From fa08359ee29bd0dc52a4281d0e482fff08664b96 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:39:59 +0800 Subject: ideapad: rewrite the sw rfkill set Control power of rf modules by ec commands Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c index 09f6ce6..e9f7395 100644 --- a/drivers/platform/x86/ideapad_acpi.c +++ b/drivers/platform/x86/ideapad_acpi.c @@ -37,18 +37,19 @@ struct ideapad_private { acpi_handle handle; struct rfkill *rfk[5]; -}; +} *ideapad_priv; static struct { char *name; int cfgbit; + int opcode; int type; } ideapad_rfk_data[] = { - { "ideapad_camera", 19, NUM_RFKILL_TYPES }, - { "ideapad_wlan", 18, RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", 16, RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", 17, RFKILL_TYPE_WWAN }, - { "ideapad_killsw", 0, RFKILL_TYPE_WLAN } + { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, + { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, + { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } }; /* @@ -160,24 +161,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) } /* the above is ACPI helpers */ -static int ideapad_dev_set_state(int device, int state) -{ - acpi_status status; - union acpi_object in_params[2]; - struct acpi_object_list input = { 2, in_params }; - - in_params[0].type = ACPI_TYPE_INTEGER; - in_params[0].integer.value = device + 1; - in_params[1].type = ACPI_TYPE_INTEGER; - in_params[1].integer.value = state; - - status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL); - if (ACPI_FAILURE(status)) { - printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status); - return -ENODEV; - } - return 0; -} static ssize_t show_ideapad_cam(struct device *dev, struct device_attribute *attr, char *buf) @@ -217,7 +200,10 @@ static int ideapad_rfk_set(void *data, bool blocked) if (device == IDEAPAD_DEV_KILLSW) return -EINVAL; - return ideapad_dev_set_state(device, !blocked); + + return write_ec_cmd(ideapad_priv->handle, + ideapad_rfk_data[device].opcode, + !blocked); } static struct rfkill_ops ideapad_rfk_ops = { @@ -318,6 +304,7 @@ static int ideapad_acpi_add(struct acpi_device *adevice) priv->handle = adevice->handle; dev_set_drvdata(&adevice->dev, priv); + ideapad_priv = priv; for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { if (!devs_present[i]) continue; -- cgit v0.10.2 From 57ac3b051cc09693f2e0f4725c87091ab11c7318 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:40:09 +0800 Subject: ideapad: Change the driver name to ideapad-laptop Since the platform drivers doing more for laptops than just using specific ACPI device. It will be good to change the name from *_acpi to *-laptop. Reference: http://lkml.org/lkml/2010/8/14/154 Signed-off-by: Ike Panhc Acked-by: Len Brown Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 55579f1..b49f9b1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -222,8 +222,8 @@ config SONYPI_COMPAT ---help--- Build the sonypi driver compatibility code into the sony-laptop driver. -config IDEAPAD_ACPI - tristate "Lenovo IdeaPad ACPI Laptop Extras" +config IDEAPAD_LAPTOP + tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI depends on RFKILL help diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 85fb2b84..9846fa2 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o -obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o +obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad_laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c new file mode 100644 index 0000000..e9f7395 --- /dev/null +++ b/drivers/platform/x86/ideapad-laptop.c @@ -0,0 +1,382 @@ +/* + * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras + * + * Copyright © 2010 Intel Corporation + * Copyright © 2010 David Woodhouse + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define IDEAPAD_DEV_CAMERA 0 +#define IDEAPAD_DEV_WLAN 1 +#define IDEAPAD_DEV_BLUETOOTH 2 +#define IDEAPAD_DEV_3G 3 +#define IDEAPAD_DEV_KILLSW 4 + +struct ideapad_private { + acpi_handle handle; + struct rfkill *rfk[5]; +} *ideapad_priv; + +static struct { + char *name; + int cfgbit; + int opcode; + int type; +} ideapad_rfk_data[] = { + { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, + { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, + { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } +}; + +/* + * ACPI Helpers + */ +#define IDEAPAD_EC_TIMEOUT (100) /* in ms */ + +static int read_method_int(acpi_handle handle, const char *method, int *val) +{ + acpi_status status; + unsigned long long result; + + status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); + if (ACPI_FAILURE(status)) { + *val = -1; + return -1; + } else { + *val = result; + return 0; + } +} + +static int method_vpcr(acpi_handle handle, int cmd, int *ret) +{ + acpi_status status; + unsigned long long result; + struct acpi_object_list params; + union acpi_object in_obj; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = cmd; + + status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); + + if (ACPI_FAILURE(status)) { + *ret = -1; + return -1; + } else { + *ret = result; + return 0; + } +} + +static int method_vpcw(acpi_handle handle, int cmd, int data) +{ + struct acpi_object_list params; + union acpi_object in_obj[2]; + acpi_status status; + + params.count = 2; + params.pointer = in_obj; + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = cmd; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = data; + + status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); + if (status != AE_OK) + return -1; + return 0; +} + +static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) +{ + int val; + unsigned long int end_jiffies; + + if (method_vpcw(handle, 1, cmd)) + return -1; + + for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; + time_before(jiffies, end_jiffies);) { + schedule(); + if (method_vpcr(handle, 1, &val)) + return -1; + if (val == 0) { + if (method_vpcr(handle, 0, &val)) + return -1; + *data = val; + return 0; + } + } + pr_err("timeout in read_ec_cmd\n"); + return -1; +} + +static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) +{ + int val; + unsigned long int end_jiffies; + + if (method_vpcw(handle, 0, data)) + return -1; + if (method_vpcw(handle, 1, cmd)) + return -1; + + for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; + time_before(jiffies, end_jiffies);) { + schedule(); + if (method_vpcr(handle, 1, &val)) + return -1; + if (val == 0) + return 0; + } + pr_err("timeout in write_ec_cmd\n"); + return -1; +} +/* the above is ACPI helpers */ + +static ssize_t show_ideapad_cam(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ideapad_private *priv = dev_get_drvdata(dev); + acpi_handle handle = priv->handle; + unsigned long result; + + if (read_ec_data(handle, 0x1D, &result)) + return sprintf(buf, "-1\n"); + return sprintf(buf, "%lu\n", result); +} + +static ssize_t store_ideapad_cam(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ideapad_private *priv = dev_get_drvdata(dev); + acpi_handle handle = priv->handle; + int ret, state; + + if (!count) + return 0; + if (sscanf(buf, "%i", &state) != 1) + return -EINVAL; + ret = write_ec_cmd(handle, 0x1E, state); + if (ret < 0) + return ret; + return count; +} + +static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); + +static int ideapad_rfk_set(void *data, bool blocked) +{ + int device = (unsigned long)data; + + if (device == IDEAPAD_DEV_KILLSW) + return -EINVAL; + + return write_ec_cmd(ideapad_priv->handle, + ideapad_rfk_data[device].opcode, + !blocked); +} + +static struct rfkill_ops ideapad_rfk_ops = { + .set_block = ideapad_rfk_set, +}; + +static void ideapad_sync_rfk_state(struct acpi_device *adevice) +{ + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); + acpi_handle handle = priv->handle; + unsigned long hw_blocked; + int i; + + if (read_ec_data(handle, 0x23, &hw_blocked)) + return; + hw_blocked = !hw_blocked; + + for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) + if (priv->rfk[i]) + rfkill_set_hw_state(priv->rfk[i], hw_blocked); +} + +static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) +{ + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); + int ret; + unsigned long sw_blocked; + + priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, + ideapad_rfk_data[dev].type, &ideapad_rfk_ops, + (void *)(long)dev); + if (!priv->rfk[dev]) + return -ENOMEM; + + if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, + &sw_blocked)) { + rfkill_init_sw_state(priv->rfk[dev], 0); + } else { + sw_blocked = !sw_blocked; + rfkill_init_sw_state(priv->rfk[dev], sw_blocked); + } + + ret = rfkill_register(priv->rfk[dev]); + if (ret) { + rfkill_destroy(priv->rfk[dev]); + return ret; + } + return 0; +} + +static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) +{ + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); + + if (!priv->rfk[dev]) + return; + + rfkill_unregister(priv->rfk[dev]); + rfkill_destroy(priv->rfk[dev]); +} + +static const struct acpi_device_id ideapad_device_ids[] = { + { "VPC2004", 0}, + { "", 0}, +}; +MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); + +static int ideapad_acpi_add(struct acpi_device *adevice) +{ + int i, cfg; + int devs_present[5]; + struct ideapad_private *priv; + + if (read_method_int(adevice->handle, "_CFG", &cfg)) + return -ENODEV; + + for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { + if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) + devs_present[i] = 1; + else + devs_present[i] = 0; + } + + /* The hardware switch is always present */ + devs_present[IDEAPAD_DEV_KILLSW] = 1; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (devs_present[IDEAPAD_DEV_CAMERA]) { + int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); + if (ret) { + kfree(priv); + return ret; + } + } + + priv->handle = adevice->handle; + dev_set_drvdata(&adevice->dev, priv); + ideapad_priv = priv; + for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { + if (!devs_present[i]) + continue; + + ideapad_register_rfkill(adevice, i); + } + ideapad_sync_rfk_state(adevice); + return 0; +} + +static int ideapad_acpi_remove(struct acpi_device *adevice, int type) +{ + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); + int i; + + device_remove_file(&adevice->dev, &dev_attr_camera_power); + + for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) + ideapad_unregister_rfkill(adevice, i); + + dev_set_drvdata(&adevice->dev, NULL); + kfree(priv); + return 0; +} + +static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) +{ + acpi_handle handle = adevice->handle; + unsigned long vpc1, vpc2, vpc_bit; + + if (read_ec_data(handle, 0x10, &vpc1)) + return; + if (read_ec_data(handle, 0x1A, &vpc2)) + return; + + vpc1 = (vpc2 << 8) | vpc1; + for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { + if (test_bit(vpc_bit, &vpc1)) { + if (vpc_bit == 9) + ideapad_sync_rfk_state(adevice); + } + } +} + +static struct acpi_driver ideapad_acpi_driver = { + .name = "ideapad_acpi", + .class = "IdeaPad", + .ids = ideapad_device_ids, + .ops.add = ideapad_acpi_add, + .ops.remove = ideapad_acpi_remove, + .ops.notify = ideapad_acpi_notify, + .owner = THIS_MODULE, +}; + + +static int __init ideapad_acpi_module_init(void) +{ + acpi_bus_register_driver(&ideapad_acpi_driver); + + return 0; +} + + +static void __exit ideapad_acpi_module_exit(void) +{ + acpi_bus_unregister_driver(&ideapad_acpi_driver); + +} + +MODULE_AUTHOR("David Woodhouse "); +MODULE_DESCRIPTION("IdeaPad ACPI Extras"); +MODULE_LICENSE("GPL"); + +module_init(ideapad_acpi_module_init); +module_exit(ideapad_acpi_module_exit); diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c deleted file mode 100644 index e9f7395..0000000 --- a/drivers/platform/x86/ideapad_acpi.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras - * - * Copyright © 2010 Intel Corporation - * Copyright © 2010 David Woodhouse - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define IDEAPAD_DEV_CAMERA 0 -#define IDEAPAD_DEV_WLAN 1 -#define IDEAPAD_DEV_BLUETOOTH 2 -#define IDEAPAD_DEV_3G 3 -#define IDEAPAD_DEV_KILLSW 4 - -struct ideapad_private { - acpi_handle handle; - struct rfkill *rfk[5]; -} *ideapad_priv; - -static struct { - char *name; - int cfgbit; - int opcode; - int type; -} ideapad_rfk_data[] = { - { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, - { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, - { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } -}; - -/* - * ACPI Helpers - */ -#define IDEAPAD_EC_TIMEOUT (100) /* in ms */ - -static int read_method_int(acpi_handle handle, const char *method, int *val) -{ - acpi_status status; - unsigned long long result; - - status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); - if (ACPI_FAILURE(status)) { - *val = -1; - return -1; - } else { - *val = result; - return 0; - } -} - -static int method_vpcr(acpi_handle handle, int cmd, int *ret) -{ - acpi_status status; - unsigned long long result; - struct acpi_object_list params; - union acpi_object in_obj; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = cmd; - - status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); - - if (ACPI_FAILURE(status)) { - *ret = -1; - return -1; - } else { - *ret = result; - return 0; - } -} - -static int method_vpcw(acpi_handle handle, int cmd, int data) -{ - struct acpi_object_list params; - union acpi_object in_obj[2]; - acpi_status status; - - params.count = 2; - params.pointer = in_obj; - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = cmd; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = data; - - status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); - if (status != AE_OK) - return -1; - return 0; -} - -static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) -{ - int val; - unsigned long int end_jiffies; - - if (method_vpcw(handle, 1, cmd)) - return -1; - - for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; - time_before(jiffies, end_jiffies);) { - schedule(); - if (method_vpcr(handle, 1, &val)) - return -1; - if (val == 0) { - if (method_vpcr(handle, 0, &val)) - return -1; - *data = val; - return 0; - } - } - pr_err("timeout in read_ec_cmd\n"); - return -1; -} - -static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) -{ - int val; - unsigned long int end_jiffies; - - if (method_vpcw(handle, 0, data)) - return -1; - if (method_vpcw(handle, 1, cmd)) - return -1; - - for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; - time_before(jiffies, end_jiffies);) { - schedule(); - if (method_vpcr(handle, 1, &val)) - return -1; - if (val == 0) - return 0; - } - pr_err("timeout in write_ec_cmd\n"); - return -1; -} -/* the above is ACPI helpers */ - -static ssize_t show_ideapad_cam(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; - unsigned long result; - - if (read_ec_data(handle, 0x1D, &result)) - return sprintf(buf, "-1\n"); - return sprintf(buf, "%lu\n", result); -} - -static ssize_t store_ideapad_cam(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; - int ret, state; - - if (!count) - return 0; - if (sscanf(buf, "%i", &state) != 1) - return -EINVAL; - ret = write_ec_cmd(handle, 0x1E, state); - if (ret < 0) - return ret; - return count; -} - -static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); - -static int ideapad_rfk_set(void *data, bool blocked) -{ - int device = (unsigned long)data; - - if (device == IDEAPAD_DEV_KILLSW) - return -EINVAL; - - return write_ec_cmd(ideapad_priv->handle, - ideapad_rfk_data[device].opcode, - !blocked); -} - -static struct rfkill_ops ideapad_rfk_ops = { - .set_block = ideapad_rfk_set, -}; - -static void ideapad_sync_rfk_state(struct acpi_device *adevice) -{ - struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - acpi_handle handle = priv->handle; - unsigned long hw_blocked; - int i; - - if (read_ec_data(handle, 0x23, &hw_blocked)) - return; - hw_blocked = !hw_blocked; - - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) - if (priv->rfk[i]) - rfkill_set_hw_state(priv->rfk[i], hw_blocked); -} - -static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) -{ - struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - int ret; - unsigned long sw_blocked; - - priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, - ideapad_rfk_data[dev].type, &ideapad_rfk_ops, - (void *)(long)dev); - if (!priv->rfk[dev]) - return -ENOMEM; - - if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, - &sw_blocked)) { - rfkill_init_sw_state(priv->rfk[dev], 0); - } else { - sw_blocked = !sw_blocked; - rfkill_init_sw_state(priv->rfk[dev], sw_blocked); - } - - ret = rfkill_register(priv->rfk[dev]); - if (ret) { - rfkill_destroy(priv->rfk[dev]); - return ret; - } - return 0; -} - -static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) -{ - struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - - if (!priv->rfk[dev]) - return; - - rfkill_unregister(priv->rfk[dev]); - rfkill_destroy(priv->rfk[dev]); -} - -static const struct acpi_device_id ideapad_device_ids[] = { - { "VPC2004", 0}, - { "", 0}, -}; -MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); - -static int ideapad_acpi_add(struct acpi_device *adevice) -{ - int i, cfg; - int devs_present[5]; - struct ideapad_private *priv; - - if (read_method_int(adevice->handle, "_CFG", &cfg)) - return -ENODEV; - - for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { - if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) - devs_present[i] = 1; - else - devs_present[i] = 0; - } - - /* The hardware switch is always present */ - devs_present[IDEAPAD_DEV_KILLSW] = 1; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - if (devs_present[IDEAPAD_DEV_CAMERA]) { - int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); - if (ret) { - kfree(priv); - return ret; - } - } - - priv->handle = adevice->handle; - dev_set_drvdata(&adevice->dev, priv); - ideapad_priv = priv; - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { - if (!devs_present[i]) - continue; - - ideapad_register_rfkill(adevice, i); - } - ideapad_sync_rfk_state(adevice); - return 0; -} - -static int ideapad_acpi_remove(struct acpi_device *adevice, int type) -{ - struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - int i; - - device_remove_file(&adevice->dev, &dev_attr_camera_power); - - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) - ideapad_unregister_rfkill(adevice, i); - - dev_set_drvdata(&adevice->dev, NULL); - kfree(priv); - return 0; -} - -static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) -{ - acpi_handle handle = adevice->handle; - unsigned long vpc1, vpc2, vpc_bit; - - if (read_ec_data(handle, 0x10, &vpc1)) - return; - if (read_ec_data(handle, 0x1A, &vpc2)) - return; - - vpc1 = (vpc2 << 8) | vpc1; - for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { - if (test_bit(vpc_bit, &vpc1)) { - if (vpc_bit == 9) - ideapad_sync_rfk_state(adevice); - } - } -} - -static struct acpi_driver ideapad_acpi_driver = { - .name = "ideapad_acpi", - .class = "IdeaPad", - .ids = ideapad_device_ids, - .ops.add = ideapad_acpi_add, - .ops.remove = ideapad_acpi_remove, - .ops.notify = ideapad_acpi_notify, - .owner = THIS_MODULE, -}; - - -static int __init ideapad_acpi_module_init(void) -{ - acpi_bus_register_driver(&ideapad_acpi_driver); - - return 0; -} - - -static void __exit ideapad_acpi_module_exit(void) -{ - acpi_bus_unregister_driver(&ideapad_acpi_driver); - -} - -MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("IdeaPad ACPI Extras"); -MODULE_LICENSE("GPL"); - -module_init(ideapad_acpi_module_init); -module_exit(ideapad_acpi_module_exit); -- cgit v0.10.2 From bfa97b7dab708b100040a1335ea0860a8b9ef346 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Fri, 1 Oct 2010 15:40:22 +0800 Subject: ideapad: Add param: no_bt_rfkill Add new module parameter that force module not to register bluetooth rfkill. There is report that saying using this bluetooth rfkill to enable/disable bluetooth will let bluetooth device initial failed when enable on Lenovo ideapad S12. Fortunately there is another rfkill registered by bluetooth driver for S12 and user can shutdown the bluetooth by either bluetooth driver or HW RF switch. For dual OS user, it may have some trouble that using Linux after turning off bluetooth with another OS if we do not register bluetooth rfkill at all. So we will force bluetooth enable when no_bt_rfkill=1. Signed-off-by: Ike Panhc Tested-by: Mario 'BitKoenig' Holbe Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index e9f7395..5ff1220 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -52,6 +52,10 @@ static struct { { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } }; +static bool no_bt_rfkill; +module_param(no_bt_rfkill, bool, 0444); +MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); + /* * ACPI Helpers */ @@ -232,6 +236,14 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) int ret; unsigned long sw_blocked; + if (no_bt_rfkill && + (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { + /* Force to enable bluetooth when no_bt_rfkill=1 */ + write_ec_cmd(ideapad_priv->handle, + ideapad_rfk_data[dev].opcode, 1); + return 0; + } + priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, ideapad_rfk_data[dev].type, &ideapad_rfk_ops, (void *)(long)dev); -- cgit v0.10.2 From ffcfff3a8d6cc94f1fb598e0b021c64ce35b5036 Mon Sep 17 00:00:00 2001 From: Alek Du Date: Mon, 4 Oct 2010 16:40:35 +0100 Subject: intel_pmic_gpio: swap the bits and mask args for intel_scu_ipc_update_register The intel_scu_ipc_update_register 2nd paramter should the bits and 3rd paramter should be the mask. This typo was introduced during IPC function changing... Reported-by: Ryan Zhou Signed-off-by: Alek Du Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 5cdcff6..c6e2a1d 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -142,16 +142,16 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip, if (offset < 8)/* it is GPIO */ rc = intel_scu_ipc_update_register(GPIO0 + offset, - GPIO_DRV | GPIO_DOU | GPIO_DIR, - GPIO_DRV | (value ? GPIO_DOU : 0)); + GPIO_DRV | (value ? GPIO_DOU : 0), + GPIO_DRV | GPIO_DOU | GPIO_DIR); else if (offset < 16)/* it is GPOSW */ rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, - GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, - GPOSW_DRV | (value ? GPOSW_DOU : 0)); + GPOSW_DRV | (value ? GPOSW_DOU : 0), + GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); else if (offset > 15 && offset < 24)/* it is GPO */ rc = intel_scu_ipc_update_register(GPO, - 1 << (offset - 16), - value ? 1 << (offset - 16) : 0); + value ? 1 << (offset - 16) : 0, + 1 << (offset - 16)); else { printk(KERN_ERR "%s: invalid PMIC GPIO pin %d!\n", __func__, offset); @@ -179,16 +179,16 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { if (offset < 8)/* it is GPIO */ intel_scu_ipc_update_register(GPIO0 + offset, - GPIO_DRV | GPIO_DOU, - GPIO_DRV | (value ? GPIO_DOU : 0)); + GPIO_DRV | (value ? GPIO_DOU : 0), + GPIO_DRV | GPIO_DOU); else if (offset < 16)/* it is GPOSW */ intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, - GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, - GPOSW_DRV | (value ? GPOSW_DOU : 0)); + GPOSW_DRV | (value ? GPOSW_DOU : 0), + GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); else if (offset > 15 && offset < 24) /* it is GPO */ intel_scu_ipc_update_register(GPO, - 1 << (offset - 16), - value ? 1 << (offset - 16) : 0); + value ? 1 << (offset - 16) : 0, + 1 << (offset - 16)); } static int pmic_irq_type(unsigned irq, unsigned type) -- cgit v0.10.2 From 75b2d09a295d3232fdacd0a0a07d91c6f118ca18 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 5 Oct 2010 09:18:30 -0400 Subject: ideapad-laptop: Fix Makefile The makefile didn't get updated when the driver changed name, which broke the build. Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9846fa2..b26df2e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o -obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad_laptop.o +obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o -- cgit v0.10.2 From bd9fc3a72345807683a009c1e19dc0d517f0f4e7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 5 Oct 2010 12:08:57 +0200 Subject: Move hdaps driver to platform/x86 The hdaps driver isn't a hardware monitoring driver, so it shouldn't live under driver/hwmon. drivers/platform/x86 seems much more appropriate, as the driver is only useful on x86 laptops. Signed-off-by: Jean Delvare Cc: Guenter Roeck Cc: Matthew Garrett Cc: Frank Seidel Signed-off-by: Matthew Garrett diff --git a/MAINTAINERS b/MAINTAINERS index f2a2b8e..3436e26 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2604,10 +2604,10 @@ F: drivers/net/greth* HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER M: Frank Seidel -L: lm-sensors@lm-sensors.org +L: platform-driver-x86@vger.kernel.org W: http://www.kernel.org/pub/linux/kernel/people/fseidel/hdaps/ S: Maintained -F: drivers/hwmon/hdaps.c +F: drivers/platform/x86/hdaps.c HWPOISON MEMORY FAILURE HANDLING M: Andi Kleen diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 97499d0..e382da3 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1088,26 +1088,6 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. -config SENSORS_HDAPS - tristate "IBM Hard Drive Active Protection System (hdaps)" - depends on INPUT && X86 - select INPUT_POLLDEV - default n - help - This driver provides support for the IBM Hard Drive Active Protection - System (hdaps), which provides an accelerometer and other misc. data. - ThinkPads starting with the R50, T41, and X40 are supported. The - accelerometer data is readable via sysfs. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - If your ThinkPad is not recognized by the driver, please update to latest - BIOS. This is especially the case for some R52 ThinkPads. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of hdaps. - config SENSORS_LIS3_SPI tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" depends on !ACPI && SPI_MASTER && INPUT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index e3c2484..ec9cb73 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o -obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c deleted file mode 100644 index bfd42f1..0000000 --- a/drivers/hwmon/hdaps.c +++ /dev/null @@ -1,637 +0,0 @@ -/* - * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System - * - * Copyright (C) 2005 Robert Love - * Copyright (C) 2005 Jesper Juhl - * - * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads - * starting with the R40, T41, and X40. It provides a basic two-axis - * accelerometer and other data, such as the device's temperature. - * - * This driver is based on the document by Mark A. Smith available at - * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial - * and error. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License v2 as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ -#define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */ - -#define HDAPS_PORT_STATE 0x1611 /* device state */ -#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ -#define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ -#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */ -#define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ -#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ -#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ -#define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ -#define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ - -#define STATE_FRESH 0x50 /* accelerometer data is fresh */ - -#define KEYBD_MASK 0x20 /* set if keyboard activity */ -#define MOUSE_MASK 0x40 /* set if mouse activity */ -#define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */ -#define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */ - -#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ -#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ - -#define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/ -#define HDAPS_INPUT_FUZZ 4 /* input event threshold */ -#define HDAPS_INPUT_FLAT 4 - -#define HDAPS_X_AXIS (1 << 0) -#define HDAPS_Y_AXIS (1 << 1) -#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS) - -static struct platform_device *pdev; -static struct input_polled_dev *hdaps_idev; -static unsigned int hdaps_invert; -static u8 km_activity; -static int rest_x; -static int rest_y; - -static DEFINE_MUTEX(hdaps_mtx); - -/* - * __get_latch - Get the value from a given port. Callers must hold hdaps_mtx. - */ -static inline u8 __get_latch(u16 port) -{ - return inb(port) & 0xff; -} - -/* - * __check_latch - Check a port latch for a given value. Returns zero if the - * port contains the given value. Callers must hold hdaps_mtx. - */ -static inline int __check_latch(u16 port, u8 val) -{ - if (__get_latch(port) == val) - return 0; - return -EINVAL; -} - -/* - * __wait_latch - Wait up to 100us for a port latch to get a certain value, - * returning zero if the value is obtained. Callers must hold hdaps_mtx. - */ -static int __wait_latch(u16 port, u8 val) -{ - unsigned int i; - - for (i = 0; i < 20; i++) { - if (!__check_latch(port, val)) - return 0; - udelay(5); - } - - return -EIO; -} - -/* - * __device_refresh - request a refresh from the accelerometer. Does not wait - * for refresh to complete. Callers must hold hdaps_mtx. - */ -static void __device_refresh(void) -{ - udelay(200); - if (inb(0x1604) != STATE_FRESH) { - outb(0x11, 0x1610); - outb(0x01, 0x161f); - } -} - -/* - * __device_refresh_sync - request a synchronous refresh from the - * accelerometer. We wait for the refresh to complete. Returns zero if - * successful and nonzero on error. Callers must hold hdaps_mtx. - */ -static int __device_refresh_sync(void) -{ - __device_refresh(); - return __wait_latch(0x1604, STATE_FRESH); -} - -/* - * __device_complete - indicate to the accelerometer that we are done reading - * data, and then initiate an async refresh. Callers must hold hdaps_mtx. - */ -static inline void __device_complete(void) -{ - inb(0x161f); - inb(0x1604); - __device_refresh(); -} - -/* - * hdaps_readb_one - reads a byte from a single I/O port, placing the value in - * the given pointer. Returns zero on success or a negative error on failure. - * Can sleep. - */ -static int hdaps_readb_one(unsigned int port, u8 *val) -{ - int ret; - - mutex_lock(&hdaps_mtx); - - /* do a sync refresh -- we need to be sure that we read fresh data */ - ret = __device_refresh_sync(); - if (ret) - goto out; - - *val = inb(port); - __device_complete(); - -out: - mutex_unlock(&hdaps_mtx); - return ret; -} - -/* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */ -static int __hdaps_read_pair(unsigned int port1, unsigned int port2, - int *x, int *y) -{ - /* do a sync refresh -- we need to be sure that we read fresh data */ - if (__device_refresh_sync()) - return -EIO; - - *y = inw(port2); - *x = inw(port1); - km_activity = inb(HDAPS_PORT_KMACT); - __device_complete(); - - /* hdaps_invert is a bitvector to negate the axes */ - if (hdaps_invert & HDAPS_X_AXIS) - *x = -*x; - if (hdaps_invert & HDAPS_Y_AXIS) - *y = -*y; - - return 0; -} - -/* - * hdaps_read_pair - reads the values from a pair of ports, placing the values - * in the given pointers. Returns zero on success. Can sleep. - */ -static int hdaps_read_pair(unsigned int port1, unsigned int port2, - int *val1, int *val2) -{ - int ret; - - mutex_lock(&hdaps_mtx); - ret = __hdaps_read_pair(port1, port2, val1, val2); - mutex_unlock(&hdaps_mtx); - - return ret; -} - -/* - * hdaps_device_init - initialize the accelerometer. Returns zero on success - * and negative error code on failure. Can sleep. - */ -static int hdaps_device_init(void) -{ - int total, ret = -ENXIO; - - mutex_lock(&hdaps_mtx); - - outb(0x13, 0x1610); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - - /* - * Most ThinkPads return 0x01. - * - * Others--namely the R50p, T41p, and T42p--return 0x03. These laptops - * have "inverted" axises. - * - * The 0x02 value occurs when the chip has been previously initialized. - */ - if (__check_latch(0x1611, 0x03) && - __check_latch(0x1611, 0x02) && - __check_latch(0x1611, 0x01)) - goto out; - - printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x).\n", - __get_latch(0x1611)); - - outb(0x17, 0x1610); - outb(0x81, 0x1611); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - if (__wait_latch(0x1611, 0x00)) - goto out; - if (__wait_latch(0x1612, 0x60)) - goto out; - if (__wait_latch(0x1613, 0x00)) - goto out; - outb(0x14, 0x1610); - outb(0x01, 0x1611); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - outb(0x10, 0x1610); - outb(0xc8, 0x1611); - outb(0x00, 0x1612); - outb(0x02, 0x1613); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - if (__device_refresh_sync()) - goto out; - if (__wait_latch(0x1611, 0x00)) - goto out; - - /* we have done our dance, now let's wait for the applause */ - for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { - int x, y; - - /* a read of the device helps push it into action */ - __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); - if (!__wait_latch(0x1611, 0x02)) { - ret = 0; - break; - } - - msleep(INIT_WAIT_MSECS); - } - -out: - mutex_unlock(&hdaps_mtx); - return ret; -} - - -/* Device model stuff */ - -static int hdaps_probe(struct platform_device *dev) -{ - int ret; - - ret = hdaps_device_init(); - if (ret) - return ret; - - printk(KERN_INFO "hdaps: device successfully initialized.\n"); - return 0; -} - -static int hdaps_resume(struct platform_device *dev) -{ - return hdaps_device_init(); -} - -static struct platform_driver hdaps_driver = { - .probe = hdaps_probe, - .resume = hdaps_resume, - .driver = { - .name = "hdaps", - .owner = THIS_MODULE, - }, -}; - -/* - * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_mtx. - */ -static void hdaps_calibrate(void) -{ - __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); -} - -static void hdaps_mousedev_poll(struct input_polled_dev *dev) -{ - struct input_dev *input_dev = dev->input; - int x, y; - - mutex_lock(&hdaps_mtx); - - if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) - goto out; - - input_report_abs(input_dev, ABS_X, x - rest_x); - input_report_abs(input_dev, ABS_Y, y - rest_y); - input_sync(input_dev); - -out: - mutex_unlock(&hdaps_mtx); -} - - -/* Sysfs Files */ - -static ssize_t hdaps_position_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, x, y; - - ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); - if (ret) - return ret; - - return sprintf(buf, "(%d,%d)\n", x, y); -} - -static ssize_t hdaps_variance_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, x, y; - - ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y); - if (ret) - return ret; - - return sprintf(buf, "(%d,%d)\n", x, y); -} - -static ssize_t hdaps_temp1_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 temp; - int ret; - - ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", temp); -} - -static ssize_t hdaps_temp2_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 temp; - int ret; - - ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", temp); -} - -static ssize_t hdaps_keyboard_activity_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity)); -} - -static ssize_t hdaps_mouse_activity_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity)); -} - -static ssize_t hdaps_calibrate_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "(%d,%d)\n", rest_x, rest_y); -} - -static ssize_t hdaps_calibrate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - mutex_lock(&hdaps_mtx); - hdaps_calibrate(); - mutex_unlock(&hdaps_mtx); - - return count; -} - -static ssize_t hdaps_invert_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", hdaps_invert); -} - -static ssize_t hdaps_invert_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int invert; - - if (sscanf(buf, "%d", &invert) != 1 || - invert < 0 || invert > HDAPS_BOTH_AXES) - return -EINVAL; - - hdaps_invert = invert; - hdaps_calibrate(); - - return count; -} - -static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); -static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); -static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); -static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL); -static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL); -static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); -static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); -static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); - -static struct attribute *hdaps_attributes[] = { - &dev_attr_position.attr, - &dev_attr_variance.attr, - &dev_attr_temp1.attr, - &dev_attr_temp2.attr, - &dev_attr_keyboard_activity.attr, - &dev_attr_mouse_activity.attr, - &dev_attr_calibrate.attr, - &dev_attr_invert.attr, - NULL, -}; - -static struct attribute_group hdaps_attribute_group = { - .attrs = hdaps_attributes, -}; - - -/* Module stuff */ - -/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ -static int __init hdaps_dmi_match(const struct dmi_system_id *id) -{ - printk(KERN_INFO "hdaps: %s detected.\n", id->ident); - return 1; -} - -/* hdaps_dmi_match_invert - found an inverted match. */ -static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) -{ - hdaps_invert = (unsigned long)id->driver_data; - printk(KERN_INFO "hdaps: inverting axis (%u) readings.\n", - hdaps_invert); - return hdaps_dmi_match(id); -} - -#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \ - .ident = vendor " " model, \ - .callback = hdaps_dmi_match_invert, \ - .driver_data = (void *)axes, \ - .matches = { \ - DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ - DMI_MATCH(DMI_PRODUCT_VERSION, model) \ - } \ -} - -#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \ - HDAPS_DMI_MATCH_INVERT(vendor, model, 0) - -/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match - "ThinkPad T42p", so the order of the entries matters. - If your ThinkPad is not recognized, please update to latest - BIOS. This is especially the case for some R52 ThinkPads. */ -static struct dmi_system_id __initdata hdaps_whitelist[] = { - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES), - { .ident = NULL } -}; - -static int __init hdaps_init(void) -{ - struct input_dev *idev; - int ret; - - if (!dmi_check_system(hdaps_whitelist)) { - printk(KERN_WARNING "hdaps: supported laptop not found!\n"); - ret = -ENODEV; - goto out; - } - - if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) { - ret = -ENXIO; - goto out; - } - - ret = platform_driver_register(&hdaps_driver); - if (ret) - goto out_region; - - pdev = platform_device_register_simple("hdaps", -1, NULL, 0); - if (IS_ERR(pdev)) { - ret = PTR_ERR(pdev); - goto out_driver; - } - - ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group); - if (ret) - goto out_device; - - hdaps_idev = input_allocate_polled_device(); - if (!hdaps_idev) { - ret = -ENOMEM; - goto out_group; - } - - hdaps_idev->poll = hdaps_mousedev_poll; - hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL; - - /* initial calibrate for the input device */ - hdaps_calibrate(); - - /* initialize the input class */ - idev = hdaps_idev->input; - idev->name = "hdaps"; - idev->phys = "isa1600/input0"; - idev->id.bustype = BUS_ISA; - idev->dev.parent = &pdev->dev; - idev->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(idev, ABS_X, - -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); - input_set_abs_params(idev, ABS_Y, - -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); - - ret = input_register_polled_device(hdaps_idev); - if (ret) - goto out_idev; - - printk(KERN_INFO "hdaps: driver successfully loaded.\n"); - return 0; - -out_idev: - input_free_polled_device(hdaps_idev); -out_group: - sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); -out_device: - platform_device_unregister(pdev); -out_driver: - platform_driver_unregister(&hdaps_driver); -out_region: - release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); -out: - printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret); - return ret; -} - -static void __exit hdaps_exit(void) -{ - input_unregister_polled_device(hdaps_idev); - input_free_polled_device(hdaps_idev); - sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); - platform_device_unregister(pdev); - platform_driver_unregister(&hdaps_driver); - release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); - - printk(KERN_INFO "hdaps: driver unloaded.\n"); -} - -module_init(hdaps_init); -module_exit(hdaps_exit); - -module_param_named(invert, hdaps_invert, int, 0); -MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, " - "2 invert y-axis, 3 invert both axes."); - -MODULE_AUTHOR("Robert Love"); -MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b49f9b1..d2d15108 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -368,6 +368,26 @@ config THINKPAD_ACPI_HOTKEY_POLL If you are not sure, say Y here. The driver enables polling only if it is strictly necessary to do so. +config SENSORS_HDAPS + tristate "Thinkpad Hard Drive Active Protection System (hdaps)" + depends on INPUT && X86 + select INPUT_POLLDEV + default n + help + This driver provides support for the IBM Hard Drive Active Protection + System (hdaps), which provides an accelerometer and other misc. data. + ThinkPads starting with the R50, T41, and X40 are supported. The + accelerometer data is readable via sysfs. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + If your ThinkPad is not recognized by the driver, please update to latest + BIOS. This is especially the case for some R52 ThinkPads. + + Say Y here if you have an applicable laptop and want to experience + the awesome power of hdaps. + config INTEL_MENLOW tristate "Thermal Management driver for Intel menlow platform" depends on ACPI_THERMAL diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index b26df2e..c97ac2a 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o +obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c new file mode 100644 index 0000000..067bf36 --- /dev/null +++ b/drivers/platform/x86/hdaps.c @@ -0,0 +1,637 @@ +/* + * hdaps.c - driver for IBM's Hard Drive Active Protection System + * + * Copyright (C) 2005 Robert Love + * Copyright (C) 2005 Jesper Juhl + * + * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads + * starting with the R40, T41, and X40. It provides a basic two-axis + * accelerometer and other data, such as the device's temperature. + * + * This driver is based on the document by Mark A. Smith available at + * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial + * and error. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License v2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ +#define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */ + +#define HDAPS_PORT_STATE 0x1611 /* device state */ +#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ +#define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ +#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */ +#define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ +#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ +#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ +#define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ +#define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ + +#define STATE_FRESH 0x50 /* accelerometer data is fresh */ + +#define KEYBD_MASK 0x20 /* set if keyboard activity */ +#define MOUSE_MASK 0x40 /* set if mouse activity */ +#define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */ +#define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */ + +#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ +#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ + +#define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/ +#define HDAPS_INPUT_FUZZ 4 /* input event threshold */ +#define HDAPS_INPUT_FLAT 4 + +#define HDAPS_X_AXIS (1 << 0) +#define HDAPS_Y_AXIS (1 << 1) +#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS) + +static struct platform_device *pdev; +static struct input_polled_dev *hdaps_idev; +static unsigned int hdaps_invert; +static u8 km_activity; +static int rest_x; +static int rest_y; + +static DEFINE_MUTEX(hdaps_mtx); + +/* + * __get_latch - Get the value from a given port. Callers must hold hdaps_mtx. + */ +static inline u8 __get_latch(u16 port) +{ + return inb(port) & 0xff; +} + +/* + * __check_latch - Check a port latch for a given value. Returns zero if the + * port contains the given value. Callers must hold hdaps_mtx. + */ +static inline int __check_latch(u16 port, u8 val) +{ + if (__get_latch(port) == val) + return 0; + return -EINVAL; +} + +/* + * __wait_latch - Wait up to 100us for a port latch to get a certain value, + * returning zero if the value is obtained. Callers must hold hdaps_mtx. + */ +static int __wait_latch(u16 port, u8 val) +{ + unsigned int i; + + for (i = 0; i < 20; i++) { + if (!__check_latch(port, val)) + return 0; + udelay(5); + } + + return -EIO; +} + +/* + * __device_refresh - request a refresh from the accelerometer. Does not wait + * for refresh to complete. Callers must hold hdaps_mtx. + */ +static void __device_refresh(void) +{ + udelay(200); + if (inb(0x1604) != STATE_FRESH) { + outb(0x11, 0x1610); + outb(0x01, 0x161f); + } +} + +/* + * __device_refresh_sync - request a synchronous refresh from the + * accelerometer. We wait for the refresh to complete. Returns zero if + * successful and nonzero on error. Callers must hold hdaps_mtx. + */ +static int __device_refresh_sync(void) +{ + __device_refresh(); + return __wait_latch(0x1604, STATE_FRESH); +} + +/* + * __device_complete - indicate to the accelerometer that we are done reading + * data, and then initiate an async refresh. Callers must hold hdaps_mtx. + */ +static inline void __device_complete(void) +{ + inb(0x161f); + inb(0x1604); + __device_refresh(); +} + +/* + * hdaps_readb_one - reads a byte from a single I/O port, placing the value in + * the given pointer. Returns zero on success or a negative error on failure. + * Can sleep. + */ +static int hdaps_readb_one(unsigned int port, u8 *val) +{ + int ret; + + mutex_lock(&hdaps_mtx); + + /* do a sync refresh -- we need to be sure that we read fresh data */ + ret = __device_refresh_sync(); + if (ret) + goto out; + + *val = inb(port); + __device_complete(); + +out: + mutex_unlock(&hdaps_mtx); + return ret; +} + +/* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */ +static int __hdaps_read_pair(unsigned int port1, unsigned int port2, + int *x, int *y) +{ + /* do a sync refresh -- we need to be sure that we read fresh data */ + if (__device_refresh_sync()) + return -EIO; + + *y = inw(port2); + *x = inw(port1); + km_activity = inb(HDAPS_PORT_KMACT); + __device_complete(); + + /* hdaps_invert is a bitvector to negate the axes */ + if (hdaps_invert & HDAPS_X_AXIS) + *x = -*x; + if (hdaps_invert & HDAPS_Y_AXIS) + *y = -*y; + + return 0; +} + +/* + * hdaps_read_pair - reads the values from a pair of ports, placing the values + * in the given pointers. Returns zero on success. Can sleep. + */ +static int hdaps_read_pair(unsigned int port1, unsigned int port2, + int *val1, int *val2) +{ + int ret; + + mutex_lock(&hdaps_mtx); + ret = __hdaps_read_pair(port1, port2, val1, val2); + mutex_unlock(&hdaps_mtx); + + return ret; +} + +/* + * hdaps_device_init - initialize the accelerometer. Returns zero on success + * and negative error code on failure. Can sleep. + */ +static int hdaps_device_init(void) +{ + int total, ret = -ENXIO; + + mutex_lock(&hdaps_mtx); + + outb(0x13, 0x1610); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + + /* + * Most ThinkPads return 0x01. + * + * Others--namely the R50p, T41p, and T42p--return 0x03. These laptops + * have "inverted" axises. + * + * The 0x02 value occurs when the chip has been previously initialized. + */ + if (__check_latch(0x1611, 0x03) && + __check_latch(0x1611, 0x02) && + __check_latch(0x1611, 0x01)) + goto out; + + printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x).\n", + __get_latch(0x1611)); + + outb(0x17, 0x1610); + outb(0x81, 0x1611); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + if (__wait_latch(0x1611, 0x00)) + goto out; + if (__wait_latch(0x1612, 0x60)) + goto out; + if (__wait_latch(0x1613, 0x00)) + goto out; + outb(0x14, 0x1610); + outb(0x01, 0x1611); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + outb(0x10, 0x1610); + outb(0xc8, 0x1611); + outb(0x00, 0x1612); + outb(0x02, 0x1613); + outb(0x01, 0x161f); + if (__wait_latch(0x161f, 0x00)) + goto out; + if (__device_refresh_sync()) + goto out; + if (__wait_latch(0x1611, 0x00)) + goto out; + + /* we have done our dance, now let's wait for the applause */ + for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { + int x, y; + + /* a read of the device helps push it into action */ + __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); + if (!__wait_latch(0x1611, 0x02)) { + ret = 0; + break; + } + + msleep(INIT_WAIT_MSECS); + } + +out: + mutex_unlock(&hdaps_mtx); + return ret; +} + + +/* Device model stuff */ + +static int hdaps_probe(struct platform_device *dev) +{ + int ret; + + ret = hdaps_device_init(); + if (ret) + return ret; + + printk(KERN_INFO "hdaps: device successfully initialized.\n"); + return 0; +} + +static int hdaps_resume(struct platform_device *dev) +{ + return hdaps_device_init(); +} + +static struct platform_driver hdaps_driver = { + .probe = hdaps_probe, + .resume = hdaps_resume, + .driver = { + .name = "hdaps", + .owner = THIS_MODULE, + }, +}; + +/* + * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_mtx. + */ +static void hdaps_calibrate(void) +{ + __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); +} + +static void hdaps_mousedev_poll(struct input_polled_dev *dev) +{ + struct input_dev *input_dev = dev->input; + int x, y; + + mutex_lock(&hdaps_mtx); + + if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) + goto out; + + input_report_abs(input_dev, ABS_X, x - rest_x); + input_report_abs(input_dev, ABS_Y, y - rest_y); + input_sync(input_dev); + +out: + mutex_unlock(&hdaps_mtx); +} + + +/* Sysfs Files */ + +static ssize_t hdaps_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, x, y; + + ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); + if (ret) + return ret; + + return sprintf(buf, "(%d,%d)\n", x, y); +} + +static ssize_t hdaps_variance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, x, y; + + ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y); + if (ret) + return ret; + + return sprintf(buf, "(%d,%d)\n", x, y); +} + +static ssize_t hdaps_temp1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 temp; + int ret; + + ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t hdaps_temp2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 temp; + int ret; + + ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t hdaps_keyboard_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity)); +} + +static ssize_t hdaps_mouse_activity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity)); +} + +static ssize_t hdaps_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "(%d,%d)\n", rest_x, rest_y); +} + +static ssize_t hdaps_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + mutex_lock(&hdaps_mtx); + hdaps_calibrate(); + mutex_unlock(&hdaps_mtx); + + return count; +} + +static ssize_t hdaps_invert_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", hdaps_invert); +} + +static ssize_t hdaps_invert_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int invert; + + if (sscanf(buf, "%d", &invert) != 1 || + invert < 0 || invert > HDAPS_BOTH_AXES) + return -EINVAL; + + hdaps_invert = invert; + hdaps_calibrate(); + + return count; +} + +static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); +static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); +static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); +static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL); +static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL); +static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); +static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); +static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); + +static struct attribute *hdaps_attributes[] = { + &dev_attr_position.attr, + &dev_attr_variance.attr, + &dev_attr_temp1.attr, + &dev_attr_temp2.attr, + &dev_attr_keyboard_activity.attr, + &dev_attr_mouse_activity.attr, + &dev_attr_calibrate.attr, + &dev_attr_invert.attr, + NULL, +}; + +static struct attribute_group hdaps_attribute_group = { + .attrs = hdaps_attributes, +}; + + +/* Module stuff */ + +/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ +static int __init hdaps_dmi_match(const struct dmi_system_id *id) +{ + printk(KERN_INFO "hdaps: %s detected.\n", id->ident); + return 1; +} + +/* hdaps_dmi_match_invert - found an inverted match. */ +static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) +{ + hdaps_invert = (unsigned long)id->driver_data; + printk(KERN_INFO "hdaps: inverting axis (%u) readings.\n", + hdaps_invert); + return hdaps_dmi_match(id); +} + +#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \ + .ident = vendor " " model, \ + .callback = hdaps_dmi_match_invert, \ + .driver_data = (void *)axes, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ + DMI_MATCH(DMI_PRODUCT_VERSION, model) \ + } \ +} + +#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \ + HDAPS_DMI_MATCH_INVERT(vendor, model, 0) + +/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match + "ThinkPad T42p", so the order of the entries matters. + If your ThinkPad is not recognized, please update to latest + BIOS. This is especially the case for some R52 ThinkPads. */ +static struct dmi_system_id __initdata hdaps_whitelist[] = { + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), + HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES), + HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES), + { .ident = NULL } +}; + +static int __init hdaps_init(void) +{ + struct input_dev *idev; + int ret; + + if (!dmi_check_system(hdaps_whitelist)) { + printk(KERN_WARNING "hdaps: supported laptop not found!\n"); + ret = -ENODEV; + goto out; + } + + if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) { + ret = -ENXIO; + goto out; + } + + ret = platform_driver_register(&hdaps_driver); + if (ret) + goto out_region; + + pdev = platform_device_register_simple("hdaps", -1, NULL, 0); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); + goto out_driver; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group); + if (ret) + goto out_device; + + hdaps_idev = input_allocate_polled_device(); + if (!hdaps_idev) { + ret = -ENOMEM; + goto out_group; + } + + hdaps_idev->poll = hdaps_mousedev_poll; + hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL; + + /* initial calibrate for the input device */ + hdaps_calibrate(); + + /* initialize the input class */ + idev = hdaps_idev->input; + idev->name = "hdaps"; + idev->phys = "isa1600/input0"; + idev->id.bustype = BUS_ISA; + idev->dev.parent = &pdev->dev; + idev->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(idev, ABS_X, + -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); + input_set_abs_params(idev, ABS_Y, + -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); + + ret = input_register_polled_device(hdaps_idev); + if (ret) + goto out_idev; + + printk(KERN_INFO "hdaps: driver successfully loaded.\n"); + return 0; + +out_idev: + input_free_polled_device(hdaps_idev); +out_group: + sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); +out_device: + platform_device_unregister(pdev); +out_driver: + platform_driver_unregister(&hdaps_driver); +out_region: + release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); +out: + printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret); + return ret; +} + +static void __exit hdaps_exit(void) +{ + input_unregister_polled_device(hdaps_idev); + input_free_polled_device(hdaps_idev); + sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); + platform_device_unregister(pdev); + platform_driver_unregister(&hdaps_driver); + release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); + + printk(KERN_INFO "hdaps: driver unloaded.\n"); +} + +module_init(hdaps_init); +module_exit(hdaps_exit); + +module_param_named(invert, hdaps_invert, int, 0); +MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, " + "2 invert y-axis, 3 invert both axes."); + +MODULE_AUTHOR("Robert Love"); +MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 260586d2b444909380137de6c6423e5b44edf4db Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 5 Oct 2010 15:55:21 +0100 Subject: Add OLPC XO-1 rfkill driver Add a software rfkill switch for the WLAN interface in the OLPC XO-1 laptop. It uses the OLPC embedded controller to cut/restore power to the Marvell WLAN chip on the motherboard. Signed-off-by: Daniel Drake Signed-off-by: Matthew Garrett diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h index 101229b..42a978c 100644 --- a/arch/x86/include/asm/olpc.h +++ b/arch/x86/include/asm/olpc.h @@ -89,6 +89,8 @@ extern int olpc_ec_mask_unset(uint8_t bits); /* EC commands */ #define EC_FIRMWARE_REV 0x08 +#define EC_WLAN_ENTER_RESET 0x35 +#define EC_WLAN_LEAVE_RESET 0x25 /* SCI source values */ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index d2d15108..91e431b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -615,4 +615,12 @@ config INTEL_IPS functionality. If in doubt, say Y here; it will only load on supported platforms. +config XO1_RFKILL + tristate "OLPC XO-1 software RF kill switch" + depends on OLPC + depends on RFKILL + ---help--- + Support for enabling/disabling the WLAN interface on the OLPC XO-1 + laptop. + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c97ac2a..c15e963 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -31,4 +31,5 @@ obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o +obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c new file mode 100644 index 0000000..e549eee --- /dev/null +++ b/drivers/platform/x86/xo1-rfkill.c @@ -0,0 +1,85 @@ +/* + * Support for rfkill through the OLPC XO-1 laptop embedded controller + * + * Copyright (C) 2010 One Laptop per Child + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include + +static int rfkill_set_block(void *data, bool blocked) +{ + unsigned char cmd; + if (blocked) + cmd = EC_WLAN_ENTER_RESET; + else + cmd = EC_WLAN_LEAVE_RESET; + + return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); +} + +static const struct rfkill_ops rfkill_ops = { + .set_block = rfkill_set_block, +}; + +static int __devinit xo1_rfkill_probe(struct platform_device *pdev) +{ + struct rfkill *rfk; + int r; + + rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN, + &rfkill_ops, NULL); + if (!rfk) + return -ENOMEM; + + r = rfkill_register(rfk); + if (r) { + rfkill_destroy(rfk); + return r; + } + + platform_set_drvdata(pdev, rfk); + return 0; +} + +static int __devexit xo1_rfkill_remove(struct platform_device *pdev) +{ + struct rfkill *rfk = platform_get_drvdata(pdev); + rfkill_unregister(rfk); + rfkill_destroy(rfk); + return 0; +} + +static struct platform_driver xo1_rfkill_driver = { + .driver = { + .name = "xo1-rfkill", + .owner = THIS_MODULE, + }, + .probe = xo1_rfkill_probe, + .remove = __devexit_p(xo1_rfkill_remove), +}; + +static int __init xo1_rfkill_init(void) +{ + return platform_driver_register(&xo1_rfkill_driver); +} + +static void __exit xo1_rfkill_exit(void) +{ + platform_driver_unregister(&xo1_rfkill_driver); +} + +MODULE_AUTHOR("Daniel Drake "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:xo1-rfkill"); + +module_init(xo1_rfkill_init); +module_exit(xo1_rfkill_exit); -- cgit v0.10.2 From 35f0ce032b0f2d6974da516b5a113f49b7b70b09 Mon Sep 17 00:00:00 2001 From: Vernon Mauery Date: Tue, 5 Oct 2010 15:47:18 -0700 Subject: IBM Real-Time "SMI Free" mode driver -v7 After a period of RFC for this driver, I think it is ready for inclusion in the platform-driver-x86 tree, hopefully to be staged in the next merge window into Linus's tree. --Vernon ------------------------------------------------------------ IBM Real-Time "SMI Free" mode driver This driver supports the Real-Time Linux (RTL) BIOS feature. The RTL feature allows non-fatal System Management Interrupts (SMIs) to be disabled on supported IBM platforms and is intended to be coupled with a user-space daemon to monitor the hardware in a way that can be prioritized and scheduled to better suit the requirements for the system. The Device is presented as a special "_RTL_" table to the OS in the Extended BIOS Data Area. There is a simple protocol for entering and exiting the mode at runtime. This driver creates a simple sysfs interface to allow a simple entry and exit from RTL mode in the UFI/BIOS. Since the driver is specific to IBM SystemX hardware (x86- based servers) it only builds on x86 builds. To reduce the risk of loading on the wrong hardware, the module uses DMI information and checks a list of servers that are known to work. Signed-off-by: Vernon Mauery Signed-off-by: Matthew Garrett diff --git a/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl new file mode 100644 index 0000000..b82deea --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-system-ibm-rtl @@ -0,0 +1,22 @@ +What: state +Date: Sep 2010 +KernelVersion: 2.6.37 +Contact: Vernon Mauery +Description: The state file allows a means by which to change in and + out of Premium Real-Time Mode (PRTM), as well as the + ability to query the current state. + 0 => PRTM off + 1 => PRTM enabled +Users: The ibm-prtm userspace daemon uses this interface. + + +What: version +Date: Sep 2010 +KernelVersion: 2.6.37 +Contact: Vernon Mauery +Description: The version file provides a means by which to query + the RTL table version that lives in the Extended + BIOS Data Area (EBDA). +Users: The ibm-prtm userspace daemon uses this interface. + + diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 91e431b..faec777 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -615,6 +615,22 @@ config INTEL_IPS functionality. If in doubt, say Y here; it will only load on supported platforms. +config IBM_RTL + tristate "Device driver to enable PRTL support" + depends on X86 && PCI + ---help--- + Enable support for IBM Premium Real Time Mode (PRTM). + This module will allow you the enter and exit PRTM in the BIOS via + sysfs on platforms that support this feature. System in PRTM will + not receive CPU-generated SMIs for recoverable errors. Use of this + feature without proper support may void your hardware warranty. + + If the proper BIOS support is found the driver will load and create + /sys/devices/system/ibm_rtl/. The "state" variable will indicate + whether or not the BIOS is in PRTM. + state = 0 (BIOS SMIs on) + state = 1 (BIOS SMIs off) + config XO1_RFKILL tristate "OLPC XO-1 software RF kill switch" depends on OLPC diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c15e963..9950ccc 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -32,4 +32,4 @@ obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o - +obj-$(CONFIG_IBM_RTL) += ibm_rtl.o diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c new file mode 100644 index 0000000..3c2c6b9 --- /dev/null +++ b/drivers/platform/x86/ibm_rtl.c @@ -0,0 +1,341 @@ +/* + * IBM Real-Time Linux driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2010 + * + * Author: Keith Mannthey + * Vernon Mauery + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static bool debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Show debug output"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Keith Mannthey "); +MODULE_AUTHOR("Vernon Mauery "); + +#define RTL_ADDR_TYPE_IO 1 +#define RTL_ADDR_TYPE_MMIO 2 + +#define RTL_CMD_ENTER_PRTM 1 +#define RTL_CMD_EXIT_PRTM 2 + +/* The RTL table as presented by the EBDA: */ +struct ibm_rtl_table { + char signature[5]; /* signature should be "_RTL_" */ + u8 version; + u8 rt_status; + u8 command; + u8 command_status; + u8 cmd_address_type; + u8 cmd_granularity; + u8 cmd_offset; + u16 reserve1; + u32 cmd_port_address; /* platform dependent address */ + u32 cmd_port_value; /* platform dependent value */ +} __attribute__((packed)); + +/* to locate "_RTL_" signature do a masked 5-byte integer compare */ +#define RTL_SIGNATURE 0x0000005f4c54525fULL +#define RTL_MASK 0x000000ffffffffffULL + +#define RTL_DEBUG(A, ...) do { \ + if (debug) \ + pr_info("ibm-rtl: " A, ##__VA_ARGS__ ); \ +} while (0) + +static DEFINE_MUTEX(rtl_lock); +static struct ibm_rtl_table __iomem *rtl_table; +static void __iomem *ebda_map; +static void __iomem *rtl_cmd_addr; +static u8 rtl_cmd_type; +static u8 rtl_cmd_width; + +static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) +{ + if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + return ioremap(addr, len); + return ioport_map(addr, len); +} + +static void rtl_port_unmap(void __iomem *addr) +{ + if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO) + iounmap(addr); + else + ioport_unmap(addr); +} + +static int ibm_rtl_write(u8 value) +{ + int ret = 0, count = 0; + static u32 cmd_port_val; + + RTL_DEBUG("%s(%d)\n", __FUNCTION__, value); + + value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM; + + mutex_lock(&rtl_lock); + + if (ioread8(&rtl_table->rt_status) != value) { + iowrite8(value, &rtl_table->command); + + switch (rtl_cmd_width) { + case 8: + cmd_port_val = ioread8(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite8((u8)cmd_port_val, rtl_cmd_addr); + break; + case 16: + cmd_port_val = ioread16(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite16((u16)cmd_port_val, rtl_cmd_addr); + break; + case 32: + cmd_port_val = ioread32(&rtl_table->cmd_port_value); + RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); + iowrite32(cmd_port_val, rtl_cmd_addr); + break; + } + + while (ioread8(&rtl_table->command)) { + msleep(10); + if (count++ > 500) { + pr_err("ibm-rtl: Hardware not responding to " + "mode switch request\n"); + ret = -EIO; + break; + } + + } + + if (ioread8(&rtl_table->command_status)) { + RTL_DEBUG("command_status reports failed command\n"); + ret = -EIO; + } + } + + mutex_unlock(&rtl_lock); + return ret; +} + +static ssize_t rtl_show_version(struct sysdev_class * dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version)); +} + +static ssize_t rtl_show_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status)); +} + +static ssize_t rtl_set_state(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + const char *buf, + size_t count) +{ + ssize_t ret; + + if (count < 1 || count > 2) + return -EINVAL; + + switch (buf[0]) { + case '0': + ret = ibm_rtl_write(0); + break; + case '1': + ret = ibm_rtl_write(1); + break; + default: + ret = -EINVAL; + } + if (ret >= 0) + ret = count; + + return ret; +} + +static struct sysdev_class class_rtl = { + .name = "ibm_rtl", +}; + +static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL); +static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state); + +static struct sysdev_class_attribute *rtl_attributes[] = { + &attr_version, + &attr_state, + NULL +}; + + +static int rtl_setup_sysfs(void) { + int ret, i; + ret = sysdev_class_register(&class_rtl); + + if (!ret) { + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_create_file(&class_rtl, rtl_attributes[i]); + } + return ret; +} + +static void rtl_teardown_sysfs(void) { + int i; + for (i = 0; rtl_attributes[i]; i ++) + sysdev_class_remove_file(&class_rtl, rtl_attributes[i]); + sysdev_class_unregister(&class_rtl); +} + +static int dmi_check_cb(const struct dmi_system_id *id) +{ + RTL_DEBUG("found IBM server '%s'\n", id->ident); + return 0; +} + +#define ibm_dmi_entry(NAME, TYPE) \ +{ \ + .ident = NAME, \ + .matches = { \ + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ + DMI_MATCH(DMI_PRODUCT_NAME, TYPE), \ + }, \ + .callback = dmi_check_cb \ +} + +static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { + ibm_dmi_entry("BladeCenter LS21", "7971"), + ibm_dmi_entry("BladeCenter LS22", "7901"), + ibm_dmi_entry("BladeCenter HS21 XM", "7995"), + ibm_dmi_entry("BladeCenter HS22", "7870"), + ibm_dmi_entry("BladeCenter HS22V", "7871"), + ibm_dmi_entry("System x3550 M2", "7946"), + ibm_dmi_entry("System x3650 M2", "7947"), + ibm_dmi_entry("System x3550 M3", "7944"), + ibm_dmi_entry("System x3650 M3", "7945"), + { } +}; + +static int __init ibm_rtl_init(void) { + unsigned long ebda_addr, ebda_size; + unsigned int ebda_kb; + int ret = -ENODEV, i; + + if (force) + pr_warning("ibm-rtl: module loaded by force\n"); + /* first ensure that we are running on IBM HW */ + else if (!dmi_check_system(ibm_rtl_dmi_table)) + return -ENODEV; + + /* Get the address for the Extended BIOS Data Area */ + ebda_addr = get_bios_ebda(); + if (!ebda_addr) { + RTL_DEBUG("no BIOS EBDA found\n"); + return -ENODEV; + } + + ebda_map = ioremap(ebda_addr, 4); + if (!ebda_map) + return -ENOMEM; + + /* First word in the EDBA is the Size in KB */ + ebda_kb = ioread16(ebda_map); + RTL_DEBUG("EBDA is %d kB\n", ebda_kb); + + if (ebda_kb == 0) + goto out; + + iounmap(ebda_map); + ebda_size = ebda_kb*1024; + + /* Remap the whole table */ + ebda_map = ioremap(ebda_addr, ebda_size); + if (!ebda_map) + return -ENOMEM; + + /* search for the _RTL_ signature at the start of the table */ + for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) { + struct ibm_rtl_table __iomem * tmp; + tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i); + if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) { + phys_addr_t addr; + unsigned int plen; + RTL_DEBUG("found RTL_SIGNATURE at %#llx\n", (u64)tmp); + rtl_table = tmp; + /* The address, value, width and offset are platform + * dependent and found in the ibm_rtl_table */ + rtl_cmd_width = ioread8(&rtl_table->cmd_granularity); + rtl_cmd_type = ioread8(&rtl_table->cmd_address_type); + RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n", + rtl_cmd_width, rtl_cmd_type); + addr = ioread32(&rtl_table->cmd_port_address); + RTL_DEBUG("addr = %#llx\n", addr); + plen = rtl_cmd_width/sizeof(char); + rtl_cmd_addr = rtl_port_map(addr, plen); + RTL_DEBUG("rtl_cmd_addr = %#llx\n", (u64)rtl_cmd_addr); + if (!rtl_cmd_addr) { + ret = -ENOMEM; + break; + } + ret = rtl_setup_sysfs(); + break; + } + } + +out: + if (ret) { + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } + + return ret; +} + +static void __exit ibm_rtl_exit(void) +{ + if (rtl_table) { + RTL_DEBUG("cleaning up"); + /* do not leave the machine in SMI-free mode */ + ibm_rtl_write(0); + /* unmap, unlink and remove all traces */ + rtl_teardown_sysfs(); + iounmap(ebda_map); + rtl_port_unmap(rtl_cmd_addr); + } +} + +module_init(ibm_rtl_init); +module_exit(ibm_rtl_exit); -- cgit v0.10.2 From 4119617919c243755946699808ffd0f4befa62c7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Oct 2010 17:54:31 +0800 Subject: intel_pmic_gpio: fix off-by-one value range checking In pmic_irq_type(), we use gpio as array index for trigger, thus the valid value range for gpio should be 0 .. NUM_GPIO - 1. Signed-off-by: Axel Lin Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index c6e2a1d..f540ff9 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -197,7 +197,7 @@ static int pmic_irq_type(unsigned irq, unsigned type) u32 gpio = irq - pg->irq_base; unsigned long flags; - if (gpio > pg->chip.ngpio) + if (gpio >= pg->chip.ngpio) return -EINVAL; spin_lock_irqsave(&pg->irqtypes.lock, flags); -- cgit v0.10.2 From aa13857f13c3d5535904781e264d8f9115e30438 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 21 Oct 2010 11:48:47 +0200 Subject: panasonic-laptop: Handle errors properly if they happen acpi_pcc_retrieve_biosdata() returns success instead of error if HKEY.SINF is invalid. Fix this. Furthermore, if acpi_pcc_retrieve_biosdata() returns an error during device addition, initialization is properly reverted but value 0 is returned, which means success. This would cause a crash when later using or removing the device, so fix this too. Signed-off-by: Jean Delvare Cc: Harald Welte Cc: Bruno Premont Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index b3b9e04..96dd341 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) hkey = buffer.pointer; if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); + status = AE_ERROR; goto end; } @@ -596,6 +597,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't retrieve BIOS data\n")); + result = -EIO; goto out_input; } /* initialize backlight */ -- cgit v0.10.2 From e253fb944d3335a29bc392eafbe14c43832e806a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 21 Oct 2010 11:50:47 +0200 Subject: panasonic-laptop: Simplify calls to acpi_pcc_retrieve_biosdata Function acpi_pcc_retrieve_biosdata is always called with parameters (pcc, pcc->sinf), so we can drop the second parameter. It was dangerous to pass the sinf array separately anyway, as its length is checked as pcc->num_sifr, which pretty much assumed it was pcc->sinf (or at least had the same size.) This change makes the code slightly more compact and thus marginally faster. Signed-off-by: Jean Delvare Cc: Harald Welte Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 96dd341..cc1e0ba 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device) } } -static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) +static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) { acpi_status status; struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -299,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) for (i = 0; i < hkey->package.count; i++) { union acpi_object *element = &(hkey->package.elements[i]); if (likely(element->type == ACPI_TYPE_INTEGER)) { - sinf[i] = element->integer.value; + pcc->sinf[i] = element->integer.value; } else ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF data\n")); } - sinf[hkey->package.count] = -1; + pcc->sinf[hkey->package.count] = -1; end: kfree(buffer.pointer); @@ -322,7 +322,7 @@ static int bl_get(struct backlight_device *bd) { struct pcc_acpi *pcc = bl_get_data(bd); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return pcc->sinf[SINF_AC_CUR_BRIGHT]; @@ -334,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd) int bright = bd->props.brightness; int rc; - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) @@ -368,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); @@ -380,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); @@ -392,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); @@ -404,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, struct acpi_device *acpi = to_acpi_device(dev); struct pcc_acpi *pcc = acpi_driver_data(acpi); - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) + if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); @@ -594,7 +594,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) goto out_sinf; } - if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { + if (!acpi_pcc_retrieve_biosdata(pcc)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't retrieve BIOS data\n")); result = -EIO; -- cgit v0.10.2 From eda1748418beb1b9a75d0cea3304edf922c66134 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Mon, 11 Oct 2010 18:47:17 -0500 Subject: eeepc-wmi: add additional hotkeys Added 4 hotkeys using same keymap values as eeepc-latop. These are mousepad toggle, resolution change, screen off, and task manager. These were tested on 1005PE and are the Fn-F3, F4, F7, and F9, respectively. Also, added a new hot key for power toggles (Fn-Space on 1005PE) and is meant to drive cpufv interface from userspace. Signed-off-by: Chris Bagwell Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 9dc50fb..21df266 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -69,6 +69,11 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0xe1, { KEY_F14 } }, + { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } }, + { KE_KEY, 0xe0, { KEY_PROG1 } }, + { KE_KEY, 0x5c, { KEY_F15 } }, { KE_END, 0}, }; -- cgit v0.10.2 From 7f80d734b3b5d23b9851cc03cc20733bca2c724e Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Mon, 11 Oct 2010 18:47:18 -0500 Subject: eeepc-wmi: Add cpufv sysfs interface eeepc-laptop provides a sysfs interface to read and control what it calls cpufv. When WMI is enabled, the ACPI interface changes slightly and becames a write-only control with 3 valid values. Expose cpufv again to allow for user space utils that can extended battery life noticably and come a little closer to parity with eeepc-laptop. Write-only is OK for most user space apps because read status was mostly used to prevent unneeded mode changes. Since this same check to ignore changes to same mode also exists in the DSDT then it was wasted ACPI call. acpi_osi="!Windows 2009" can be used for get back eeepc-laptop's read support of cpufv for debugging things such as behaviour during resume. This patch was tested with EEE PC 1005PE by monitoring powertop output while writing values of "0", "1", and "2" and by reviewing the decompiled DSDT of an 1201NL and comparing it to 1005PE's DSDT. Signed-off-by: Chris Bagwell Signed-off-by: Matthew Garrett diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 21df266..462ceab 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -57,6 +57,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_METHODID_DEVS 0x53564544 #define EEEPC_WMI_METHODID_DSTS 0x53544344 +#define EEEPC_WMI_METHODID_CFVS 0x53564643 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 @@ -297,6 +298,49 @@ static void eeepc_wmi_notify(u32 value, void *context) kfree(obj); } +static int store_cpufv(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int value; + struct acpi_buffer input = { (acpi_size)sizeof(value), &value }; + acpi_status status; + + if (!count || sscanf(buf, "%i", &value) != 1) + return -EINVAL; + if (value < 0 || value > 2) + return -EINVAL; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, + 1, EEEPC_WMI_METHODID_CFVS, &input, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; + else + return count; +} + +static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); + +static void eeepc_wmi_sysfs_exit(struct platform_device *device) +{ + device_remove_file(&device->dev, &dev_attr_cpufv); +} + +static int eeepc_wmi_sysfs_init(struct platform_device *device) +{ + int retval = -ENOMEM; + + retval = device_create_file(&device->dev, &dev_attr_cpufv); + if (retval) + goto error_sysfs; + + return 0; + +error_sysfs: + eeepc_wmi_sysfs_exit(platform_device); + return retval; +} + static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) { struct eeepc_wmi *eeepc; @@ -392,8 +436,14 @@ static int __init eeepc_wmi_init(void) goto del_dev; } + err = eeepc_wmi_sysfs_init(platform_device); + if (err) + goto del_sysfs; + return 0; +del_sysfs: + eeepc_wmi_sysfs_exit(platform_device); del_dev: platform_device_del(platform_device); put_dev: @@ -408,6 +458,7 @@ static void __exit eeepc_wmi_exit(void) { struct eeepc_wmi *eeepc; + eeepc_wmi_sysfs_exit(platform_device); eeepc = platform_get_drvdata(platform_device); platform_driver_unregister(&platform_driver); platform_device_unregister(platform_device); -- cgit v0.10.2