diff options
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 17 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/input-polldev.c | 176 | ||||
-rw-r--r-- | drivers/input/misc/wistron_btns.c | 359 |
4 files changed, 260 insertions, 293 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 842a7b4d16f..9b26574f146 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -65,9 +65,13 @@ config INPUT_COBALT_BTNS config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86 && !X86_64 + select INPUT_POLLDEV + select NEW_LEDS + select LEDS_CLASS help Say Y here for support of Winstron laptop button interface, used on - laptops of various brands, including Acer and Fujitsu-Siemens. + laptops of various brands, including Acer and Fujitsu-Siemens. If + available, mail and wifi leds will be controlable via /sys/class/leds. To compile this driver as a module, choose M here: the module will be called wistron_btns. @@ -170,17 +174,6 @@ config INPUT_UINPUT To compile this driver as a module, choose M here: the module will be called uinput. -config INPUT_POLLDEV - tristate "Polled input device skeleton" - help - Say Y here if you are using a driver for an input - device that periodically polls hardware state. This - option is only useful for out-of-tree drivers since - in-tree drivers select it automatically. - - To compile this driver as a module, choose M here: the - module will be called input-polldev. - config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on GSC || HP300 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 8b2f7799e25..3585b503841 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -4,7 +4,6 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o diff --git a/drivers/input/misc/input-polldev.c b/drivers/input/misc/input-polldev.c deleted file mode 100644 index b773d4c756a..00000000000 --- a/drivers/input/misc/input-polldev.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Generic implementation of a polled input device - - * Copyright (c) 2007 Dmitry Torokhov - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include <linux/jiffies.h> -#include <linux/mutex.h> -#include <linux/input-polldev.h> - -MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); -MODULE_DESCRIPTION("Generic implementation of a polled input device"); -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.1"); - -static DEFINE_MUTEX(polldev_mutex); -static int polldev_users; -static struct workqueue_struct *polldev_wq; - -static int input_polldev_start_workqueue(void) -{ - int retval; - - retval = mutex_lock_interruptible(&polldev_mutex); - if (retval) - return retval; - - if (!polldev_users) { - polldev_wq = create_singlethread_workqueue("ipolldevd"); - if (!polldev_wq) { - printk(KERN_ERR "input-polldev: failed to create " - "ipolldevd workqueue\n"); - retval = -ENOMEM; - goto out; - } - } - - polldev_users++; - - out: - mutex_unlock(&polldev_mutex); - return retval; -} - -static void input_polldev_stop_workqueue(void) -{ - mutex_lock(&polldev_mutex); - - if (!--polldev_users) - destroy_workqueue(polldev_wq); - - mutex_unlock(&polldev_mutex); -} - -static void input_polled_device_work(struct work_struct *work) -{ - struct input_polled_dev *dev = - container_of(work, struct input_polled_dev, work.work); - - dev->poll(dev); - queue_delayed_work(polldev_wq, &dev->work, - msecs_to_jiffies(dev->poll_interval)); -} - -static int input_open_polled_device(struct input_dev *input) -{ - struct input_polled_dev *dev = input->private; - int error; - - error = input_polldev_start_workqueue(); - if (error) - return error; - - if (dev->flush) - dev->flush(dev); - - queue_delayed_work(polldev_wq, &dev->work, - msecs_to_jiffies(dev->poll_interval)); - - return 0; -} - -static void input_close_polled_device(struct input_dev *input) -{ - struct input_polled_dev *dev = input->private; - - cancel_rearming_delayed_workqueue(polldev_wq, &dev->work); - input_polldev_stop_workqueue(); -} - -/** - * input_allocate_polled_device - allocated memory polled device - * - * The function allocates memory for a polled device and also - * for an input device associated with this polled device. - */ -struct input_polled_dev *input_allocate_polled_device(void) -{ - struct input_polled_dev *dev; - - dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL); - if (!dev) - return NULL; - - dev->input = input_allocate_device(); - if (!dev->input) { - kfree(dev); - return NULL; - } - - return dev; -} -EXPORT_SYMBOL(input_allocate_polled_device); - -/** - * input_free_polled_device - free memory allocated for polled device - * @dev: device to free - * - * The function frees memory allocated for polling device and drops - * reference to the associated input device (if present). - */ -void input_free_polled_device(struct input_polled_dev *dev) -{ - if (dev) { - input_free_device(dev->input); - kfree(dev); - } -} -EXPORT_SYMBOL(input_free_polled_device); - -/** - * input_register_polled_device - register polled device - * @dev: device to register - * - * The function registers previously initialized polled input device - * with input layer. The device should be allocated with call to - * input_allocate_polled_device(). Callers should also set up poll() - * method and set up capabilities (id, name, phys, bits) of the - * corresponing input_dev structure. - */ -int input_register_polled_device(struct input_polled_dev *dev) -{ - struct input_dev *input = dev->input; - - INIT_DELAYED_WORK(&dev->work, input_polled_device_work); - if (!dev->poll_interval) - dev->poll_interval = 500; - input->private = dev; - input->open = input_open_polled_device; - input->close = input_close_polled_device; - - return input_register_device(input); -} -EXPORT_SYMBOL(input_register_polled_device); - -/** - * input_unregister_polled_device - unregister polled device - * @dev: device to unregister - * - * The function unregisters previously registered polled input - * device from input layer. Polling is stopped and device is - * ready to be freed with call to input_free_polled_device(). - * Callers should not attempt to access dev->input pointer - * after calling this function. - */ -void input_unregister_polled_device(struct input_polled_dev *dev) -{ - input_unregister_device(dev->input); - dev->input = NULL; -} -EXPORT_SYMBOL(input_unregister_polled_device); - diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 961aad7a047..60121f10f8d 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -20,37 +20,31 @@ #include <linux/io.h> #include <linux/dmi.h> #include <linux/init.h> -#include <linux/input.h> +#include <linux/input-polldev.h> #include <linux/interrupt.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/mc146818rtc.h> #include <linux/module.h> #include <linux/preempt.h> #include <linux/string.h> -#include <linux/timer.h> #include <linux/types.h> #include <linux/platform_device.h> +#include <linux/leds.h> -/* - * Number of attempts to read data from queue per poll; - * the queue can hold up to 31 entries - */ -#define MAX_POLL_ITERATIONS 64 - -#define POLL_FREQUENCY 10 /* Number of polls per second */ - -#if POLL_FREQUENCY > HZ -#error "POLL_FREQUENCY too high" -#endif +/* How often we poll keys - msecs */ +#define POLL_INTERVAL_DEFAULT 500 /* when idle */ +#define POLL_INTERVAL_BURST 100 /* when a key was recently pressed */ /* BIOS subsystem IDs */ #define WIFI 0x35 #define BLUETOOTH 0x34 +#define MAIL_LED 0x31 MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>"); MODULE_DESCRIPTION("Wistron laptop button driver"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.2"); +MODULE_VERSION("0.3"); static int force; /* = 0; */ module_param(force, bool, 0); @@ -248,9 +242,10 @@ enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH }; #define FE_WIFI_LED 0x02 #define FE_UNTESTED 0x80 -static const struct key_entry *keymap; /* = NULL; Current key map */ +static struct key_entry *keymap; /* = NULL; Current key map */ static int have_wifi; static int have_bluetooth; +static int have_leds; static int __init dmi_matched(struct dmi_system_id *dmi) { @@ -263,6 +258,8 @@ static int __init dmi_matched(struct dmi_system_id *dmi) else if (key->type == KE_BLUETOOTH) have_bluetooth = 1; } + have_leds = key->code & (FE_MAIL_LED | FE_WIFI_LED); + return 1; } @@ -966,118 +963,163 @@ static int __init select_keymap(void) /* Input layer interface */ -static struct input_dev *input_dev; +static struct input_polled_dev *wistron_idev; +static unsigned long jiffies_last_press; +static int wifi_enabled; +static int bluetooth_enabled; -static int __devinit setup_input_dev(void) +static void report_key(struct input_dev *dev, unsigned int keycode) { - const struct key_entry *key; - int error; + input_report_key(dev, keycode, 1); + input_sync(dev); + input_report_key(dev, keycode, 0); + input_sync(dev); +} - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; +static void report_switch(struct input_dev *dev, unsigned int code, int value) +{ + input_report_switch(dev, code, value); + input_sync(dev); +} - input_dev->name = "Wistron laptop buttons"; - input_dev->phys = "wistron/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->cdev.dev = &wistron_device->dev; - for (key = keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, input_dev->evbit); - set_bit(key->keycode, input_dev->keybit); - break; + /* led management */ +static void wistron_mail_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0); +} - case KE_SW: - set_bit(EV_SW, input_dev->evbit); - set_bit(key->sw.code, input_dev->swbit); - break; +/* same as setting up wifi card, but for laptops on which the led is managed */ +static void wistron_wifi_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0); +} - default: - ; - } - } +static struct led_classdev wistron_mail_led = { + .name = "mail:green", + .brightness_set = wistron_mail_led_set, +}; - /* reads information flags on KE_END */ - if (key->code & FE_UNTESTED) - printk(KERN_WARNING "Untested laptop multimedia keys, " - "please report success or failure to eric.piel" - "@tremplin-utc.net\n"); +static struct led_classdev wistron_wifi_led = { + .name = "wifi:red", + .brightness_set = wistron_wifi_led_set, +}; - error = input_register_device(input_dev); - if (error) { - input_free_device(input_dev); - return error; +static void __devinit wistron_led_init(struct device *parent) +{ + if (have_leds & FE_WIFI_LED) { + u16 wifi = bios_get_default_setting(WIFI); + if (wifi & 1) { + wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF; + if (led_classdev_register(parent, &wistron_wifi_led)) + have_leds &= ~FE_WIFI_LED; + else + bios_set_state(WIFI, wistron_wifi_led.brightness); + + } else + have_leds &= ~FE_WIFI_LED; } - return 0; + if (have_leds & FE_MAIL_LED) { + /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */ + wistron_mail_led.brightness = LED_OFF; + if (led_classdev_register(parent, &wistron_mail_led)) + have_leds &= ~FE_MAIL_LED; + else + bios_set_state(MAIL_LED, wistron_mail_led.brightness); + } } -static void report_key(unsigned keycode) +static void __devexit wistron_led_remove(void) { - input_report_key(input_dev, keycode, 1); - input_sync(input_dev); - input_report_key(input_dev, keycode, 0); - input_sync(input_dev); + if (have_leds & FE_MAIL_LED) + led_classdev_unregister(&wistron_mail_led); + + if (have_leds & FE_WIFI_LED) + led_classdev_unregister(&wistron_wifi_led); } -static void report_switch(unsigned code, int value) +static inline void wistron_led_suspend(void) { - input_report_switch(input_dev, code, value); - input_sync(input_dev); + if (have_leds & FE_MAIL_LED) + led_classdev_suspend(&wistron_mail_led); + + if (have_leds & FE_WIFI_LED) + led_classdev_suspend(&wistron_wifi_led); } - /* Driver core */ +static inline void wistron_led_resume(void) +{ + if (have_leds & FE_MAIL_LED) + led_classdev_resume(&wistron_mail_led); -static int wifi_enabled; -static int bluetooth_enabled; + if (have_leds & FE_WIFI_LED) + led_classdev_resume(&wistron_wifi_led); +} + +static struct key_entry *wistron_get_entry_by_scancode(int code) +{ + struct key_entry *key; -static void poll_bios(unsigned long); + for (key = keymap; key->type != KE_END; key++) + if (code == key->code) + return key; -static struct timer_list poll_timer = TIMER_INITIALIZER(poll_bios, 0, 0); + return NULL; +} -static void handle_key(u8 code) +static struct key_entry *wistron_get_entry_by_keycode(int keycode) { - const struct key_entry *key; + struct key_entry *key; - for (key = keymap; key->type != KE_END; key++) { - if (code == key->code) { - switch (key->type) { - case KE_KEY: - report_key(key->keycode); - break; + for (key = keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; - case KE_SW: - report_switch(key->sw.code, key->sw.value); - break; + return NULL; +} - case KE_WIFI: - if (have_wifi) { - wifi_enabled = !wifi_enabled; - bios_set_state(WIFI, wifi_enabled); - } - break; +static void handle_key(u8 code) +{ + const struct key_entry *key = wistron_get_entry_by_scancode(code); - case KE_BLUETOOTH: - if (have_bluetooth) { - bluetooth_enabled = !bluetooth_enabled; - bios_set_state(BLUETOOTH, bluetooth_enabled); - } - break; + if (key) { + switch (key->type) { + case KE_KEY: + report_key(wistron_idev->input, key->keycode); + break; - case KE_END: - break; - default: - BUG(); + case KE_SW: + report_switch(wistron_idev->input, + key->sw.code, key->sw.value); + break; + + case KE_WIFI: + if (have_wifi) { + wifi_enabled = !wifi_enabled; + bios_set_state(WIFI, wifi_enabled); + } + break; + + case KE_BLUETOOTH: + if (have_bluetooth) { + bluetooth_enabled = !bluetooth_enabled; + bios_set_state(BLUETOOTH, bluetooth_enabled); } - return; + break; + + default: + BUG(); } - } - printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); + jiffies_last_press = jiffies; + } else + printk(KERN_NOTICE + "wistron_btns: Unknown key code %02X\n", code); } -static void poll_bios(unsigned long discard) +static void poll_bios(bool discard) { u8 qlen; u16 val; @@ -1090,15 +1132,118 @@ static void poll_bios(unsigned long discard) if (val != 0 && !discard) handle_key((u8)val); } +} + +static void wistron_flush(struct input_polled_dev *dev) +{ + /* Flush stale event queue */ + poll_bios(true); +} + +static void wistron_poll(struct input_polled_dev *dev) +{ + poll_bios(false); + + /* Increase poll frequency if user is currently pressing keys (< 2s ago) */ + if (time_before(jiffies, jiffies_last_press + 2 * HZ)) + dev->poll_interval = POLL_INTERVAL_BURST; + else + dev->poll_interval = POLL_INTERVAL_DEFAULT; +} + +static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + const struct key_entry *key = wistron_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = wistron_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!wistron_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } - mod_timer(&poll_timer, jiffies + HZ / POLL_FREQUENCY); + return -EINVAL; } +static int __devinit setup_input_dev(void) +{ + const struct key_entry *key; + struct input_dev *input_dev; + int error; + + wistron_idev = input_allocate_polled_device(); + if (!wistron_idev) + return -ENOMEM; + + wistron_idev->flush = wistron_flush; + wistron_idev->poll = wistron_poll; + wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT; + + input_dev = wistron_idev->input; + input_dev->name = "Wistron laptop buttons"; + input_dev->phys = "wistron/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &wistron_device->dev; + + input_dev->getkeycode = wistron_getkeycode; + input_dev->setkeycode = wistron_setkeycode; + + for (key = keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, input_dev->evbit); + set_bit(key->keycode, input_dev->keybit); + break; + + case KE_SW: + set_bit(EV_SW, input_dev->evbit); + set_bit(key->sw.code, input_dev->swbit); + break; + + default: + break; + } + } + + /* reads information flags on KE_END */ + if (key->code & FE_UNTESTED) + printk(KERN_WARNING "Untested laptop multimedia keys, " + "please report success or failure to eric.piel" + "@tremplin-utc.net\n"); + + error = input_register_polled_device(wistron_idev); + if (error) { + input_free_polled_device(wistron_idev); + return error; + } + + return 0; +} + +/* Driver core */ + static int __devinit wistron_probe(struct platform_device *dev) { - int err = setup_input_dev(); - if (err) - return err; + int err; bios_attach(); cmos_address = bios_get_cmos_address(); @@ -1125,15 +1270,21 @@ static int __devinit wistron_probe(struct platform_device *dev) bios_set_state(BLUETOOTH, bluetooth_enabled); } - poll_bios(1); /* Flush stale event queue and arm timer */ + wistron_led_init(&dev->dev); + err = setup_input_dev(); + if (err) { + bios_detach(); + return err; + } return 0; } static int __devexit wistron_remove(struct platform_device *dev) { - del_timer_sync(&poll_timer); - input_unregister_device(input_dev); + wistron_led_remove(); + input_unregister_polled_device(wistron_idev); + input_free_polled_device(wistron_idev); bios_detach(); return 0; @@ -1142,14 +1293,13 @@ static int __devexit wistron_remove(struct platform_device *dev) #ifdef CONFIG_PM static int wistron_suspend(struct platform_device *dev, pm_message_t state) { - del_timer_sync(&poll_timer); - if (have_wifi) bios_set_state(WIFI, 0); if (have_bluetooth) bios_set_state(BLUETOOTH, 0); + wistron_led_suspend(); return 0; } @@ -1161,7 +1311,8 @@ static int wistron_resume(struct platform_device *dev) if (have_bluetooth) bios_set_state(BLUETOOTH, bluetooth_enabled); - poll_bios(1); + wistron_led_resume(); + poll_bios(true); return 0; } |