diff options
Diffstat (limited to 'drivers/input')
43 files changed, 2228 insertions, 517 deletions
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 078e4eed089..2880eaae157 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -231,6 +231,7 @@ static void gameport_find_driver(struct gameport *gameport) enum gameport_event_type { GAMEPORT_REGISTER_PORT, GAMEPORT_REGISTER_DRIVER, + GAMEPORT_ATTACH_DRIVER, }; struct gameport_event { @@ -245,11 +246,12 @@ static LIST_HEAD(gameport_event_list); static DECLARE_WAIT_QUEUE_HEAD(gameport_wait); static struct task_struct *gameport_task; -static void gameport_queue_event(void *object, struct module *owner, - enum gameport_event_type event_type) +static int gameport_queue_event(void *object, struct module *owner, + enum gameport_event_type event_type) { unsigned long flags; struct gameport_event *event; + int retval = 0; spin_lock_irqsave(&gameport_event_lock, flags); @@ -268,24 +270,34 @@ static void gameport_queue_event(void *object, struct module *owner, } } - if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) { - if (!try_module_get(owner)) { - printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type); - kfree(event); - goto out; - } - - event->type = event_type; - event->object = object; - event->owner = owner; + event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); + if (!event) { + printk(KERN_ERR + "gameport: Not enough memory to queue event %d\n", + event_type); + retval = -ENOMEM; + goto out; + } - list_add_tail(&event->node, &gameport_event_list); - wake_up(&gameport_wait); - } else { - printk(KERN_ERR "gameport: Not enough memory to queue event %d\n", event_type); + if (!try_module_get(owner)) { + printk(KERN_WARNING + "gameport: Can't get module reference, dropping event %d\n", + event_type); + kfree(event); + retval = -EINVAL; + goto out; } + + event->type = event_type; + event->object = object; + event->owner = owner; + + list_add_tail(&event->node, &gameport_event_list); + wake_up(&gameport_wait); + out: spin_unlock_irqrestore(&gameport_event_lock, flags); + return retval; } static void gameport_free_event(struct gameport_event *event) @@ -378,9 +390,10 @@ static void gameport_handle_event(void) } /* - * Remove all events that have been submitted for a given gameport port. + * Remove all events that have been submitted for a given object, + * be it a gameport port or a driver. */ -static void gameport_remove_pending_events(struct gameport *gameport) +static void gameport_remove_pending_events(void *object) { struct list_head *node, *next; struct gameport_event *event; @@ -390,7 +403,7 @@ static void gameport_remove_pending_events(struct gameport *gameport) list_for_each_safe(node, next, &gameport_event_list) { event = list_entry(node, struct gameport_event, node); - if (event->object == gameport) { + if (event->object == object) { list_del_init(node); gameport_free_event(event); } @@ -705,10 +718,40 @@ static void gameport_add_driver(struct gameport_driver *drv) drv->driver.name, error); } -void __gameport_register_driver(struct gameport_driver *drv, struct module *owner) +int __gameport_register_driver(struct gameport_driver *drv, struct module *owner, + const char *mod_name) { + int error; + drv->driver.bus = &gameport_bus; - gameport_queue_event(drv, owner, GAMEPORT_REGISTER_DRIVER); + drv->driver.owner = owner; + drv->driver.mod_name = mod_name; + + /* + * Temporarily disable automatic binding because probing + * takes long time and we are better off doing it in kgameportd + */ + drv->ignore = 1; + + error = driver_register(&drv->driver); + if (error) { + printk(KERN_ERR + "gameport: driver_register() failed for %s, error: %d\n", + drv->driver.name, error); + return error; + } + + /* + * Reset ignore flag and let kgameportd bind the driver to free ports + */ + drv->ignore = 0; + error = gameport_queue_event(drv, NULL, GAMEPORT_ATTACH_DRIVER); + if (error) { + driver_unregister(&drv->driver); + return error; + } + + return 0; } void gameport_unregister_driver(struct gameport_driver *drv) @@ -716,7 +759,9 @@ void gameport_unregister_driver(struct gameport_driver *drv) struct gameport *gameport; mutex_lock(&gameport_mutex); + drv->ignore = 1; /* so gameport_find_driver ignores it */ + gameport_remove_pending_events(drv); start_over: list_for_each_entry(gameport, &gameport_list, node) { @@ -729,6 +774,7 @@ start_over: } driver_unregister(&drv->driver); + mutex_unlock(&gameport_mutex); } diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index 92498d470b1..6489f4010c4 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -414,8 +414,7 @@ static struct gameport_driver a3d_drv = { static int __init a3d_init(void) { - gameport_register_driver(&a3d_drv); - return 0; + return gameport_register_driver(&a3d_drv); } static void __exit a3d_exit(void) diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c index d1ca8a14950..89c4c084d4a 100644 --- a/drivers/input/joystick/adi.c +++ b/drivers/input/joystick/adi.c @@ -572,8 +572,7 @@ static struct gameport_driver adi_drv = { static int __init adi_init(void) { - gameport_register_driver(&adi_drv); - return 0; + return gameport_register_driver(&adi_drv); } static void __exit adi_exit(void) diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 708c5ae13b2..356b3a25efa 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -761,9 +761,7 @@ static struct gameport_driver analog_drv = { static int __init analog_init(void) { analog_parse_options(); - gameport_register_driver(&analog_drv); - - return 0; + return gameport_register_driver(&analog_drv); } static void __exit analog_exit(void) diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c index 639b975a8ed..3497b87c3d0 100644 --- a/drivers/input/joystick/cobra.c +++ b/drivers/input/joystick/cobra.c @@ -263,8 +263,7 @@ static struct gameport_driver cobra_drv = { static int __init cobra_init(void) { - gameport_register_driver(&cobra_drv); - return 0; + return gameport_register_driver(&cobra_drv); } static void __exit cobra_exit(void) diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index cb6eef1f2d9..67c207f5b1a 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -375,8 +375,7 @@ static struct gameport_driver gf2k_drv = { static int __init gf2k_init(void) { - gameport_register_driver(&gf2k_drv); - return 0; + return gameport_register_driver(&gf2k_drv); } static void __exit gf2k_exit(void) diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c index 684e07cfccc..fc55899ba6c 100644 --- a/drivers/input/joystick/grip.c +++ b/drivers/input/joystick/grip.c @@ -426,8 +426,7 @@ static struct gameport_driver grip_drv = { static int __init grip_init(void) { - gameport_register_driver(&grip_drv); - return 0; + return gameport_register_driver(&grip_drv); } static void __exit grip_exit(void) diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index 8279481b16e..2d47baf4776 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -689,8 +689,7 @@ static struct gameport_driver grip_drv = { static int __init grip_init(void) { - gameport_register_driver(&grip_drv); - return 0; + return gameport_register_driver(&grip_drv); } static void __exit grip_exit(void) diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c index 25ec3fad9f2..4058d4b272f 100644 --- a/drivers/input/joystick/guillemot.c +++ b/drivers/input/joystick/guillemot.c @@ -283,8 +283,7 @@ static struct gameport_driver guillemot_drv = { static int __init guillemot_init(void) { - gameport_register_driver(&guillemot_drv); - return 0; + return gameport_register_driver(&guillemot_drv); } static void __exit guillemot_exit(void) diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c index 8c3290b6820..2478289aeee 100644 --- a/drivers/input/joystick/interact.c +++ b/drivers/input/joystick/interact.c @@ -317,8 +317,7 @@ static struct gameport_driver interact_drv = { static int __init interact_init(void) { - gameport_register_driver(&interact_drv); - return 0; + return gameport_register_driver(&interact_drv); } static void __exit interact_exit(void) diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c index 2a1b82c8b31..cd894a0564a 100644 --- a/drivers/input/joystick/joydump.c +++ b/drivers/input/joystick/joydump.c @@ -161,8 +161,7 @@ static struct gameport_driver joydump_drv = { static int __init joydump_init(void) { - gameport_register_driver(&joydump_drv); - return 0; + return gameport_register_driver(&joydump_drv); } static void __exit joydump_exit(void) diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c index 7b4865fdee5..ca13a6bec33 100644 --- a/drivers/input/joystick/sidewinder.c +++ b/drivers/input/joystick/sidewinder.c @@ -818,8 +818,7 @@ static struct gameport_driver sw_drv = { static int __init sw_init(void) { - gameport_register_driver(&sw_drv); - return 0; + return gameport_register_driver(&sw_drv); } static void __exit sw_exit(void) diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index 60c37bcb938..d6c60980711 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -438,8 +438,7 @@ static struct gameport_driver tmdc_drv = { static int __init tmdc_init(void) { - gameport_register_driver(&tmdc_drv); - return 0; + return gameport_register_driver(&tmdc_drv); } static void __exit tmdc_exit(void) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index b1ce10f50bc..22016ca1535 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -834,10 +834,10 @@ static void atkbd_disconnect(struct serio *serio) } /* - * Most special keys (Fn+F?) on Dell Latitudes do not generate release + * Most special keys (Fn+F?) on Dell laptops do not generate release * events so we have to do it ourselves. */ -static void atkbd_latitude_keymap_fixup(struct atkbd *atkbd) +static void atkbd_dell_laptop_keymap_fixup(struct atkbd *atkbd) { const unsigned int forced_release_keys[] = { 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, @@ -1207,15 +1207,13 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun { struct input_dev *old_dev, *new_dev; unsigned long value; - char *rest; int err; unsigned char old_extra, old_set; if (!atkbd->write) return -EIO; - value = simple_strtoul(buf, &rest, 10); - if (*rest || value > 1) + if (strict_strtoul(buf, 10, &value) || value > 1) return -EINVAL; if (atkbd->extra != value) { @@ -1264,12 +1262,10 @@ static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t cou { struct input_dev *old_dev, *new_dev; unsigned long value; - char *rest; int err; unsigned char old_scroll; - value = simple_strtoul(buf, &rest, 10); - if (*rest || value > 1) + if (strict_strtoul(buf, 10, &value) || value > 1) return -EINVAL; if (atkbd->scroll != value) { @@ -1310,15 +1306,13 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) { struct input_dev *old_dev, *new_dev; unsigned long value; - char *rest; int err; unsigned char old_set, old_extra; if (!atkbd->write) return -EIO; - value = simple_strtoul(buf, &rest, 10); - if (*rest || (value != 2 && value != 3)) + if (strict_strtoul(buf, 10, &value) || (value != 2 && value != 3)) return -EINVAL; if (atkbd->set != value) { @@ -1361,15 +1355,13 @@ static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t { struct input_dev *old_dev, *new_dev; unsigned long value; - char *rest; int err; unsigned char old_softrepeat, old_softraw; if (!atkbd->write) return -EIO; - value = simple_strtoul(buf, &rest, 10); - if (*rest || value > 1) + if (strict_strtoul(buf, 10, &value) || value > 1) return -EINVAL; if (atkbd->softrepeat != value) { @@ -1413,12 +1405,10 @@ static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t co { struct input_dev *old_dev, *new_dev; unsigned long value; - char *rest; int err; unsigned char old_softraw; - value = simple_strtoul(buf, &rest, 10); - if (*rest || value > 1) + if (strict_strtoul(buf, 10, &value) || value > 1) return -EINVAL; if (atkbd->softraw != value) { @@ -1461,13 +1451,13 @@ static int __init atkbd_setup_fixup(const struct dmi_system_id *id) static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { { - .ident = "Dell Latitude series", + .ident = "Dell Laptop", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, .callback = atkbd_setup_fixup, - .driver_data = atkbd_latitude_keymap_fixup, + .driver_data = atkbd_dell_laptop_keymap_fixup, }, { .ident = "HP 2133", diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index e348cfccc17..19284016e0f 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -8,7 +8,7 @@ * * * Modified: - * Copyright 2007 Analog Devices Inc. + * Copyright 2007-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -81,6 +81,9 @@ struct bf54x_kpad { unsigned short *keycode; struct timer_list timer; unsigned int keyup_test_jiffies; + unsigned short kpad_msel; + unsigned short kpad_prescale; + unsigned short kpad_ctl; }; static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad, @@ -360,6 +363,10 @@ static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state) { struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL(); + bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE(); + bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL(); + if (device_may_wakeup(&pdev->dev)) enable_irq_wake(bf54x_kpad->irq); @@ -370,6 +377,10 @@ static int bfin_kpad_resume(struct platform_device *pdev) { struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel); + bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale); + bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl); + if (device_may_wakeup(&pdev->dev)) disable_irq_wake(bf54x_kpad->irq); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ec96b369dd7..05f3f43582c 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -36,9 +36,10 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; -static void gpio_keys_report_event(struct gpio_keys_button *button, - struct input_dev *input) +static void gpio_keys_report_event(struct gpio_button_data *bdata) { + struct gpio_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; @@ -50,34 +51,23 @@ static void gpio_check_button(unsigned long _data) { struct gpio_button_data *data = (struct gpio_button_data *)_data; - gpio_keys_report_event(data->button, data->input); + gpio_keys_report_event(data); } static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { - struct platform_device *pdev = dev_id; - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); - int i; + struct gpio_button_data *bdata = dev_id; + struct gpio_keys_button *button = bdata->button; - for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; + BUG_ON(irq != gpio_to_irq(button->gpio)); - if (irq == gpio_to_irq(button->gpio)) { - struct gpio_button_data *bdata = &ddata->data[i]; - - if (button->debounce_interval) - mod_timer(&bdata->timer, - jiffies + - msecs_to_jiffies(button->debounce_interval)); - else - gpio_keys_report_event(button, bdata->input); - - return IRQ_HANDLED; - } - } + if (button->debounce_interval) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(button->debounce_interval)); + else + gpio_keys_report_event(bdata); - return IRQ_NONE; + return IRQ_HANDLED; } static int __devinit gpio_keys_probe(struct platform_device *pdev) @@ -151,7 +141,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, button->desc ? button->desc : "gpio_keys", - pdev); + bdata); if (error) { pr_err("gpio-keys: Unable to claim irq %d; error %d\n", irq, error); @@ -178,7 +168,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) fail2: while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev); + free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); if (pdata->buttons[i].debounce_interval) del_timer_sync(&ddata->data[i].timer); gpio_free(pdata->buttons[i].gpio); @@ -203,7 +193,7 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { int irq = gpio_to_irq(pdata->buttons[i].gpio); - free_irq(irq, pdev); + free_irq(irq, &ddata->data[i]); if (pdata->buttons[i].debounce_interval) del_timer_sync(&ddata->data[i].timer); gpio_free(pdata->buttons[i].gpio); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index e99b7882f38..199055db508 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -180,6 +180,19 @@ config INPUT_YEALINK To compile this driver as a module, choose M here: the module will be called yealink. +config INPUT_CM109 + tristate "C-Media CM109 USB I/O Controller" + depends on EXPERIMENTAL + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to enable keyboard and buzzer functions of the + C-Media CM109 usb phones. The audio part is enabled by the generic + usb sound driver, so you might want to enable that as well. + + To compile this driver as a module, choose M here: the module will be + called cm109. + config INPUT_UINPUT tristate "User level driver support" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index f48009b5222..d7db2aeb8a9 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_APANEL) += apanel.o diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index a7fabafbd94..3c9988dc0e9 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -1,8 +1,8 @@ /* * ati_remote2 - ATI/Philips USB RF remote driver * - * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi> - * Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk> + * Copyright (C) 2005-2008 Ville Syrjala <syrjala@sci.fi> + * Copyright (C) 2007-2008 Peter Stokes <linux@dadeos.co.uk> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -12,7 +12,7 @@ #include <linux/usb/input.h> #define DRIVER_DESC "ATI/Philips USB RF remote driver" -#define DRIVER_VERSION "0.2" +#define DRIVER_VERSION "0.3" MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); @@ -27,7 +27,7 @@ MODULE_LICENSE("GPL"); * A remote's "channel" may be altered by pressing and holding the "PC" button for * approximately 3 seconds, after which the button will slowly flash the count of the * currently configured "channel", using the numeric keypad enter a number between 1 and - * 16 and then the "PC" button again, the button will slowly flash the count of the + * 16 and then press the "PC" button again, the button will slowly flash the count of the * newly configured "channel". */ @@ -45,9 +45,25 @@ static struct usb_device_id ati_remote2_id_table[] = { }; MODULE_DEVICE_TABLE(usb, ati_remote2_id_table); -static struct { - int hw_code; - int key_code; +static DEFINE_MUTEX(ati_remote2_mutex); + +enum { + ATI_REMOTE2_OPENED = 0x1, + ATI_REMOTE2_SUSPENDED = 0x2, +}; + +enum { + ATI_REMOTE2_AUX1, + ATI_REMOTE2_AUX2, + ATI_REMOTE2_AUX3, + ATI_REMOTE2_AUX4, + ATI_REMOTE2_PC, + ATI_REMOTE2_MODES, +}; + +static const struct { + u8 hw_code; + u16 keycode; } ati_remote2_key_table[] = { { 0x00, KEY_0 }, { 0x01, KEY_1 }, @@ -73,6 +89,7 @@ static struct { { 0x37, KEY_RECORD }, { 0x38, KEY_DVD }, { 0x39, KEY_TV }, + { 0x3f, KEY_PROG1 }, /* AUX1-AUX4 and PC */ { 0x54, KEY_MENU }, { 0x58, KEY_UP }, { 0x59, KEY_DOWN }, @@ -91,15 +108,9 @@ static struct { { 0xa9, BTN_LEFT }, { 0xaa, BTN_RIGHT }, { 0xbe, KEY_QUESTION }, - { 0xd5, KEY_FRONT }, { 0xd0, KEY_EDIT }, + { 0xd5, KEY_FRONT }, { 0xf9, KEY_INFO }, - { (0x00 << 8) | 0x3f, KEY_PROG1 }, - { (0x01 << 8) | 0x3f, KEY_PROG2 }, - { (0x02 << 8) | 0x3f, KEY_PROG3 }, - { (0x03 << 8) | 0x3f, KEY_PROG4 }, - { (0x04 << 8) | 0x3f, KEY_PC }, - { 0, KEY_RESERVED } }; struct ati_remote2 { @@ -117,46 +128,106 @@ struct ati_remote2 { char name[64]; char phys[64]; + + /* Each mode (AUX1-AUX4 and PC) can have an independent keymap. */ + u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)]; + + unsigned int flags; }; static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); static void ati_remote2_disconnect(struct usb_interface *interface); +static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message); +static int ati_remote2_resume(struct usb_interface *interface); static struct usb_driver ati_remote2_driver = { .name = "ati_remote2", .probe = ati_remote2_probe, .disconnect = ati_remote2_disconnect, .id_table = ati_remote2_id_table, + .suspend = ati_remote2_suspend, + .resume = ati_remote2_resume, + .supports_autosuspend = 1, }; -static int ati_remote2_open(struct input_dev *idev) +static int ati_remote2_submit_urbs(struct ati_remote2 *ar2) { - struct ati_remote2 *ar2 = input_get_drvdata(idev); int r; r = usb_submit_urb(ar2->urb[0], GFP_KERNEL); if (r) { dev_err(&ar2->intf[0]->dev, - "%s: usb_submit_urb() = %d\n", __func__, r); + "%s(): usb_submit_urb() = %d\n", __func__, r); return r; } r = usb_submit_urb(ar2->urb[1], GFP_KERNEL); if (r) { usb_kill_urb(ar2->urb[0]); dev_err(&ar2->intf[1]->dev, - "%s: usb_submit_urb() = %d\n", __func__, r); + "%s(): usb_submit_urb() = %d\n", __func__, r); return r; } return 0; } +static void ati_remote2_kill_urbs(struct ati_remote2 *ar2) +{ + usb_kill_urb(ar2->urb[1]); + usb_kill_urb(ar2->urb[0]); +} + +static int ati_remote2_open(struct input_dev *idev) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + int r; + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + r = usb_autopm_get_interface(ar2->intf[0]); + if (r) { + dev_err(&ar2->intf[0]->dev, + "%s(): usb_autopm_get_interface() = %d\n", __func__, r); + goto fail1; + } + + mutex_lock(&ati_remote2_mutex); + + if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) { + r = ati_remote2_submit_urbs(ar2); + if (r) + goto fail2; + } + + ar2->flags |= ATI_REMOTE2_OPENED; + + mutex_unlock(&ati_remote2_mutex); + + usb_autopm_put_interface(ar2->intf[0]); + + return 0; + + fail2: + mutex_unlock(&ati_remote2_mutex); + usb_autopm_put_interface(ar2->intf[0]); + fail1: + return r; +} + static void ati_remote2_close(struct input_dev *idev) { struct ati_remote2 *ar2 = input_get_drvdata(idev); - usb_kill_urb(ar2->urb[0]); - usb_kill_urb(ar2->urb[1]); + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) + ati_remote2_kill_urbs(ar2); + + ar2->flags &= ~ATI_REMOTE2_OPENED; + + mutex_unlock(&ati_remote2_mutex); } static void ati_remote2_input_mouse(struct ati_remote2 *ar2) @@ -172,7 +243,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2) mode = data[0] & 0x0F; - if (mode > 4) { + if (mode > ATI_REMOTE2_PC) { dev_err(&ar2->intf[0]->dev, "Unknown mode byte (%02x %02x %02x %02x)\n", data[3], data[2], data[1], data[0]); @@ -191,7 +262,7 @@ static int ati_remote2_lookup(unsigned int hw_code) { int i; - for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) + for (i = 0; i < ARRAY_SIZE(ati_remote2_key_table); i++) if (ati_remote2_key_table[i].hw_code == hw_code) return i; @@ -211,7 +282,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2) mode = data[0] & 0x0F; - if (mode > 4) { + if (mode > ATI_REMOTE2_PC) { dev_err(&ar2->intf[1]->dev, "Unknown mode byte (%02x %02x %02x %02x)\n", data[3], data[2], data[1], data[0]); @@ -219,10 +290,6 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2) } hw_code = data[2]; - /* - * Mode keys (AUX1-AUX4, PC) all generate the same code byte. - * Use the mode byte to figure out which one was pressed. - */ if (hw_code == 0x3f) { /* * For some incomprehensible reason the mouse pad generates @@ -236,8 +303,6 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2) if (data[1] == 0) ar2->mode = mode; - - hw_code |= mode << 8; } if (!((1 << mode) & mode_mask)) @@ -260,8 +325,8 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2) case 2: /* repeat */ /* No repeat for mouse buttons. */ - if (ati_remote2_key_table[index].key_code == BTN_LEFT || - ati_remote2_key_table[index].key_code == BTN_RIGHT) + if (ar2->keycode[mode][index] == BTN_LEFT || + ar2->keycode[mode][index] == BTN_RIGHT) return; if (!time_after_eq(jiffies, ar2->jiffies)) @@ -276,7 +341,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2) return; } - input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]); + input_event(idev, EV_KEY, ar2->keycode[mode][index], data[1]); input_sync(idev); } @@ -287,6 +352,7 @@ static void ati_remote2_complete_mouse(struct urb *urb) switch (urb->status) { case 0: + usb_mark_last_busy(ar2->udev); ati_remote2_input_mouse(ar2); break; case -ENOENT: @@ -297,6 +363,7 @@ static void ati_remote2_complete_mouse(struct urb *urb) "%s(): urb status = %d\n", __func__, urb->status); return; default: + usb_mark_last_busy(ar2->udev); dev_err(&ar2->intf[0]->dev, "%s(): urb status = %d\n", __func__, urb->status); } @@ -314,6 +381,7 @@ static void ati_remote2_complete_key(struct urb *urb) switch (urb->status) { case 0: + usb_mark_last_busy(ar2->udev); ati_remote2_input_key(ar2); break; case -ENOENT: @@ -324,6 +392,7 @@ static void ati_remote2_complete_key(struct urb *urb) "%s(): urb status = %d\n", __func__, urb->status); return; default: + usb_mark_last_busy(ar2->udev); dev_err(&ar2->intf[1]->dev, "%s(): urb status = %d\n", __func__, urb->status); } @@ -334,10 +403,60 @@ static void ati_remote2_complete_key(struct urb *urb) "%s(): usb_submit_urb() = %d\n", __func__, r); } +static int ati_remote2_getkeycode(struct input_dev *idev, + int scancode, int *keycode) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + int index, mode; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask)) + return -EINVAL; + + index = ati_remote2_lookup(scancode & 0xFF); + if (index < 0) + return -EINVAL; + + *keycode = ar2->keycode[mode][index]; + return 0; +} + +static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keycode) +{ + struct ati_remote2 *ar2 = input_get_drvdata(idev); + int index, mode, old_keycode; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask)) + return -EINVAL; + + index = ati_remote2_lookup(scancode & 0xFF); + if (index < 0) + return -EINVAL; + + if (keycode < KEY_RESERVED || keycode > KEY_MAX) + return -EINVAL; + + old_keycode = ar2->keycode[mode][index]; + ar2->keycode[mode][index] = keycode; + set_bit(keycode, idev->keybit); + + for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { + for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { + if (ar2->keycode[mode][index] == old_keycode) + return 0; + } + } + + clear_bit(old_keycode, idev->keybit); + + return 0; +} + static int ati_remote2_input_init(struct ati_remote2 *ar2) { struct input_dev *idev; - int i, retval; + int index, mode, retval; idev = input_allocate_device(); if (!idev) @@ -350,8 +469,26 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) - set_bit(ati_remote2_key_table[i].key_code, idev->keybit); + + for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { + for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { + ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode; + set_bit(ar2->keycode[mode][index], idev->keybit); + } + } + + /* AUX1-AUX4 and PC generate the same scancode. */ + index = ati_remote2_lookup(0x3f); + ar2->keycode[ATI_REMOTE2_AUX1][index] = KEY_PROG1; + ar2->keycode[ATI_REMOTE2_AUX2][index] = KEY_PROG2; + ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3; + ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4; + ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC; + set_bit(KEY_PROG1, idev->keybit); + set_bit(KEY_PROG2, idev->keybit); + set_bit(KEY_PROG3, idev->keybit); + set_bit(KEY_PROG4, idev->keybit); + set_bit(KEY_PC, idev->keybit); idev->rep[REP_DELAY] = 250; idev->rep[REP_PERIOD] = 33; @@ -359,6 +496,9 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) idev->open = ati_remote2_open; idev->close = ati_remote2_close; + idev->getkeycode = ati_remote2_getkeycode; + idev->setkeycode = ati_remote2_setkeycode; + idev->name = ar2->name; idev->phys = ar2->phys; @@ -490,6 +630,8 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d usb_set_intfdata(interface, ar2); + interface->needs_remote_wakeup = 1; + return 0; fail2: @@ -522,6 +664,57 @@ static void ati_remote2_disconnect(struct usb_interface *interface) kfree(ar2); } +static int ati_remote2_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (ar2->flags & ATI_REMOTE2_OPENED) + ati_remote2_kill_urbs(ar2); + + ar2->flags |= ATI_REMOTE2_SUSPENDED; + + mutex_unlock(&ati_remote2_mutex); + + return 0; +} + +static int ati_remote2_resume(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + int r = 0; + + if (alt->desc.bInterfaceNumber) + return 0; + + ar2 = usb_get_intfdata(interface); + + dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__); + + mutex_lock(&ati_remote2_mutex); + + if (ar2->flags & ATI_REMOTE2_OPENED) + r = ati_remote2_submit_urbs(ar2); + + if (!r) + ar2->flags &= ~ATI_REMOTE2_SUSPENDED; + + mutex_unlock(&ati_remote2_mutex); + + return r; +} + static int __init ati_remote2_init(void) { int r; diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c new file mode 100644 index 00000000000..bce160f4349 --- /dev/null +++ b/drivers/input/misc/cm109.c @@ -0,0 +1,882 @@ +/* + * Driver for the VoIP USB phones with CM109 chipsets. + * + * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org> + * + * 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, version 2. + */ + +/* + * Tested devices: + * - Komunikate KIP1000 + * - Genius G-talk + * - Allied-Telesis Corega USBPH01 + * - ... + * + * This driver is based on the yealink.c driver + * + * Thanks to: + * - Authors of yealink.c + * - Thomas Reitmayr + * - Oliver Neukum for good review comments and code + * - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap + * - Dmitry Torokhov for valuable input and review + * + * Todo: + * - Read/write EEPROM + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/rwsem.h> +#include <linux/usb/input.h> + +#define DRIVER_VERSION "20080805" +#define DRIVER_AUTHOR "Alfred E. Heggestad" +#define DRIVER_DESC "CM109 phone driver" + +static char *phone = "kip1000"; +module_param(phone, charp, S_IRUSR); +MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01}"); + +enum { + /* HID Registers */ + HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */ + HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */ + HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */ + HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */ + HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */ + HID_OR1 = 0x01, /* GPO - General Purpose Output */ + HID_OR2 = 0x02, /* Set GPIO to input/output mode */ + HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */ + + /* HID_IR0 */ + RECORD_MUTE = 1 << 3, + PLAYBACK_MUTE = 1 << 2, + VOLUME_DOWN = 1 << 1, + VOLUME_UP = 1 << 0, + + /* HID_OR0 */ + /* bits 7-6 + 0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer + and SPDIF + 1: HID_OR0-3 are used as generic HID registers + 2: Values written to HID_OR0-3 are also mapped to MCU_CTRL, + EEPROM_DATA0-1, EEPROM_CTRL (see Note) + 3: Reserved + */ + HID_OR_GPO_BUZ_SPDIF = 0 << 6, + HID_OR_GENERIC_HID_REG = 1 << 6, + HID_OR_MAP_MCU_EEPROM = 2 << 6, + + BUZZER_ON = 1 << 5, + + /* up to 256 normal keys, up to 16 special keys */ + KEYMAP_SIZE = 256 + 16, +}; + +/* CM109 protocol packet */ +struct cm109_ctl_packet { + u8 byte[4]; +} __attribute__ ((packed)); + +enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) }; + +/* CM109 device structure */ +struct cm109_dev { + struct input_dev *idev; /* input device */ + struct usb_device *udev; /* usb device */ + struct usb_interface *intf; + + /* irq input channel */ + struct cm109_ctl_packet *irq_data; + dma_addr_t irq_dma; + struct urb *urb_irq; + + /* control output channel */ + struct cm109_ctl_packet *ctl_data; + dma_addr_t ctl_dma; + struct usb_ctrlrequest *ctl_req; + dma_addr_t ctl_req_dma; + struct urb *urb_ctl; + /* + * The 3 bitfields below are protected by ctl_submit_lock. + * They have to be separate since they are accessed from IRQ + * context. + */ + unsigned irq_urb_pending:1; /* irq_urb is in flight */ + unsigned ctl_urb_pending:1; /* ctl_urb is in flight */ + unsigned buzzer_pending:1; /* need to issue buzz command */ + spinlock_t ctl_submit_lock; + + unsigned char buzzer_state; /* on/off */ + + /* flags */ + unsigned open:1; + unsigned resetting:1; + unsigned shutdown:1; + + /* This mutex protects writes to the above flags */ + struct mutex pm_mutex; + + unsigned short keymap[KEYMAP_SIZE]; + + char phys[64]; /* physical device path */ + int key_code; /* last reported key */ + int keybit; /* 0=new scan 1,2,4,8=scan columns */ + u8 gpi; /* Cached value of GPI (high nibble) */ +}; + +/****************************************************************************** + * CM109 key interface + *****************************************************************************/ + +static unsigned short special_keymap(int code) +{ + if (code > 0xff) { + switch (code - 0xff) { + case RECORD_MUTE: return KEY_MUTE; + case PLAYBACK_MUTE: return KEY_MUTE; + case VOLUME_DOWN: return KEY_VOLUMEDOWN; + case VOLUME_UP: return KEY_VOLUMEUP; + } + } + return KEY_RESERVED; +} + +/* Map device buttons to internal key events. + * + * The "up" and "down" keys, are symbolised by arrows on the button. + * The "pickup" and "hangup" keys are symbolised by a green and red phone + * on the button. + + Komunikate KIP1000 Keyboard Matrix + + -> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10) + | | | | + <- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20) + | | | | + END - 7 -- 8 -- 9 --> GPI pin 6 (0x40) + | | | | + OK -- * -- 0 -- # --> GPI pin 7 (0x80) + | | | | + + /|\ /|\ /|\ /|\ + | | | | +GPO +pin: 3 2 1 0 + 0x8 0x4 0x2 0x1 + + */ +static unsigned short keymap_kip1000(int scancode) +{ + switch (scancode) { /* phone key: */ + case 0x82: return KEY_NUMERIC_0; /* 0 */ + case 0x14: return KEY_NUMERIC_1; /* 1 */ + case 0x12: return KEY_NUMERIC_2; /* 2 */ + case 0x11: return KEY_NUMERIC_3; /* 3 */ + case 0x24: return KEY_NUMERIC_4; /* 4 */ + case 0x22: return KEY_NUMERIC_5; /* 5 */ + case 0x21: return KEY_NUMERIC_6; /* 6 */ + case 0x44: return KEY_NUMERIC_7; /* 7 */ + case 0x42: return KEY_NUMERIC_8; /* 8 */ + case 0x41: return KEY_NUMERIC_9; /* 9 */ + case 0x81: return KEY_NUMERIC_POUND; /* # */ + case 0x84: return KEY_NUMERIC_STAR; /* * */ + case 0x88: return KEY_ENTER; /* pickup */ + case 0x48: return KEY_ESC; /* hangup */ + case 0x28: return KEY_LEFT; /* IN */ + case 0x18: return KEY_RIGHT; /* OUT */ + default: return special_keymap(scancode); + } +} + +/* + Contributed by Shaun Jackman <sjackman@gmail.com> + + Genius G-Talk keyboard matrix + 0 1 2 3 + 4: 0 4 8 Talk + 5: 1 5 9 End + 6: 2 6 # Up + 7: 3 7 * Down +*/ +static unsigned short keymap_gtalk(int scancode) +{ + switch (scancode) { + case 0x11: return KEY_NUMERIC_0; + case 0x21: return KEY_NUMERIC_1; + case 0x41: return KEY_NUMERIC_2; + case 0x81: return KEY_NUMERIC_3; + case 0x12: return KEY_NUMERIC_4; + case 0x22: return KEY_NUMERIC_5; + case 0x42: return KEY_NUMERIC_6; + case 0x82: return KEY_NUMERIC_7; + case 0x14: return KEY_NUMERIC_8; + case 0x24: return KEY_NUMERIC_9; + case 0x44: return KEY_NUMERIC_POUND; /* # */ + case 0x84: return KEY_NUMERIC_STAR; /* * */ + case 0x18: return KEY_ENTER; /* Talk (green handset) */ + case 0x28: return KEY_ESC; /* End (red handset) */ + case 0x48: return KEY_UP; /* Menu up (rocker switch) */ + case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */ + default: return special_keymap(scancode); + } +} + +/* + * Keymap for Allied-Telesis Corega USBPH01 + * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html + * + * Contributed by july@nat.bg + */ +static unsigned short keymap_usbph01(int scancode) +{ + switch (scancode) { + case 0x11: return KEY_NUMERIC_0; /* 0 */ + case 0x21: return KEY_NUMERIC_1; /* 1 */ + case 0x41: return KEY_NUMERIC_2; /* 2 */ + case 0x81: return KEY_NUMERIC_3; /* 3 */ + case 0x12: return KEY_NUMERIC_4; /* 4 */ + case 0x22: return KEY_NUMERIC_5; /* 5 */ + case 0x42: return KEY_NUMERIC_6; /* 6 */ + case 0x82: return KEY_NUMERIC_7; /* 7 */ + case 0x14: return KEY_NUMERIC_8; /* 8 */ + case 0x24: return KEY_NUMERIC_9; /* 9 */ + case 0x44: return KEY_NUMERIC_POUND; /* # */ + case 0x84: return KEY_NUMERIC_STAR; /* * */ + case 0x18: return KEY_ENTER; /* pickup */ + case 0x28: return KEY_ESC; /* hangup */ + case 0x48: return KEY_LEFT; /* IN */ + case 0x88: return KEY_RIGHT; /* OUT */ + default: return special_keymap(scancode); + } +} + +static unsigned short (*keymap)(int) = keymap_kip1000; + +/* + * Completes a request by converting the data into events for the + * input subsystem. + */ +static void report_key(struct cm109_dev *dev, int key) +{ + struct input_dev *idev = dev->idev; + + if (dev->key_code >= 0) { + /* old key up */ + input_report_key(idev, dev->key_code, 0); + } + + dev->key_code = key; + if (key >= 0) { + /* new valid key */ + input_report_key(idev, key, 1); + } + + input_sync(idev); +} + +/****************************************************************************** + * CM109 usb communication interface + *****************************************************************************/ + +static void cm109_submit_buzz_toggle(struct cm109_dev *dev) +{ + int error; + + if (dev->buzzer_state) + dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; + else + dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; + + error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); + if (error) + err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error); +} + +/* + * IRQ handler + */ +static void cm109_urb_irq_callback(struct urb *urb) +{ + struct cm109_dev *dev = urb->context; + const int status = urb->status; + int error; + + dev_dbg(&urb->dev->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n", + dev->irq_data->byte[0], + dev->irq_data->byte[1], + dev->irq_data->byte[2], + dev->irq_data->byte[3], + dev->keybit); + + if (status) { + if (status == -ESHUTDOWN) + return; + err("%s: urb status %d", __func__, status); + } + + /* Special keys */ + if (dev->irq_data->byte[HID_IR0] & 0x0f) { + const int code = (dev->irq_data->byte[HID_IR0] & 0x0f); + report_key(dev, dev->keymap[0xff + code]); + } + + /* Scan key column */ + if (dev->keybit == 0xf) { + + /* Any changes ? */ + if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0)) + goto out; + + dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0; + dev->keybit = 0x1; + } else { + report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]); + + dev->keybit <<= 1; + if (dev->keybit > 0x8) + dev->keybit = 0xf; + } + + out: + + spin_lock(&dev->ctl_submit_lock); + + dev->irq_urb_pending = 0; + + if (likely(!dev->shutdown)) { + + if (dev->buzzer_state) + dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; + else + dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; + + dev->ctl_data->byte[HID_OR1] = dev->keybit; + dev->ctl_data->byte[HID_OR2] = dev->keybit; + + dev->buzzer_pending = 0; + dev->ctl_urb_pending = 1; + + error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC); + if (error) + err("%s: usb_submit_urb (urb_ctl) failed %d", + __func__, error); + } + + spin_unlock(&dev->ctl_submit_lock); +} + +static void cm109_urb_ctl_callback(struct urb *urb) +{ + struct cm109_dev *dev = urb->context; + const int status = urb->status; + int error; + + dev_dbg(&urb->dev->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n", + dev->ctl_data->byte[0], + dev->ctl_data->byte[1], + dev->ctl_data->byte[2], + dev->ctl_data->byte[3]); + + if (status) + err("%s: urb status %d", __func__, status); + + spin_lock(&dev->ctl_submit_lock); + + dev->ctl_urb_pending = 0; + + if (likely(!dev->shutdown)) { + + if (dev->buzzer_pending) { + dev->buzzer_pending = 0; + dev->ctl_urb_pending = 1; + cm109_submit_buzz_toggle(dev); + } else if (likely(!dev->irq_urb_pending)) { + /* ask for key data */ + dev->irq_urb_pending = 1; + error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC); + if (error) + err("%s: usb_submit_urb (urb_irq) failed %d", + __func__, error); + } + } + + spin_unlock(&dev->ctl_submit_lock); +} + +static void cm109_toggle_buzzer_async(struct cm109_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->ctl_submit_lock, flags); + + if (dev->ctl_urb_pending) { + /* URB completion will resubmit */ + dev->buzzer_pending = 1; + } else { + dev->ctl_urb_pending = 1; + cm109_submit_buzz_toggle(dev); + } + + spin_unlock_irqrestore(&dev->ctl_submit_lock, flags); +} + +static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on) +{ + int error; + + if (on) + dev->ctl_data->byte[HID_OR0] |= BUZZER_ON; + else + dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON; + + error = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + dev->ctl_req->bRequest, + dev->ctl_req->bRequestType, + le16_to_cpu(dev->ctl_req->wValue), + le16_to_cpu(dev->ctl_req->wIndex), + dev->ctl_data, + USB_PKT_LEN, USB_CTRL_SET_TIMEOUT); + if (error && error != EINTR) + err("%s: usb_control_msg() failed %d", __func__, error); +} + +static void cm109_stop_traffic(struct cm109_dev *dev) +{ + dev->shutdown = 1; + /* + * Make sure other CPUs see this + */ + smp_wmb(); + + usb_kill_urb(dev->urb_ctl); + usb_kill_urb(dev->urb_irq); + + cm109_toggle_buzzer_sync(dev, 0); + + dev->shutdown = 0; + smp_wmb(); +} + +static void cm109_restore_state(struct cm109_dev *dev) +{ + if (dev->open) { + /* + * Restore buzzer state. + * This will also kick regular URB submission + */ + cm109_toggle_buzzer_async(dev); + } +} + +/****************************************************************************** + * input event interface + *****************************************************************************/ + +static int cm109_input_open(struct input_dev *idev) +{ + struct cm109_dev *dev = input_get_drvdata(idev); + int error; + + error = usb_autopm_get_interface(dev->intf); + if (error < 0) { + err("%s - cannot autoresume, result %d", + __func__, error); + return error; + } + + mutex_lock(&dev->pm_mutex); + + dev->buzzer_state = 0; + dev->key_code = -1; /* no keys pressed */ + dev->keybit = 0xf; + + /* issue INIT */ + dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF; + dev->ctl_data->byte[HID_OR1] = dev->keybit; + dev->ctl_data->byte[HID_OR2] = dev->keybit; + dev->ctl_data->byte[HID_OR3] = 0x00; + + error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL); + if (error) + err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error); + else + dev->open = 1; + + mutex_unlock(&dev->pm_mutex); + + if (error) + usb_autopm_put_interface(dev->intf); + + return error; +} + +static void cm109_input_close(struct input_dev *idev) +{ + struct cm109_dev *dev = input_get_drvdata(idev); + + mutex_lock(&dev->pm_mutex); + + /* + * Once we are here event delivery is stopped so we + * don't need to worry about someone starting buzzer + * again + */ + cm109_stop_traffic(dev); + dev->open = 0; + + mutex_unlock(&dev->pm_mutex); + + usb_autopm_put_interface(dev->intf); +} + +static int cm109_input_ev(struct input_dev *idev, unsigned int type, + unsigned int code, int value) +{ + struct cm109_dev *dev = input_get_drvdata(idev); + + dev_dbg(&dev->udev->dev, + "input_ev: type=%u code=%u value=%d\n", type, code, value); + + if (type != EV_SND) + return -EINVAL; + + switch (code) { + case SND_TONE: + case SND_BELL: + dev->buzzer_state = !!value; + if (!dev->resetting) + cm109_toggle_buzzer_async(dev); + return 0; + + default: + return -EINVAL; + } +} + + +/****************************************************************************** + * Linux interface and usb initialisation + *****************************************************************************/ + +struct driver_info { + char *name; +}; + +static const struct driver_info info_cm109 = { + .name = "CM109 USB driver", +}; + +enum { + VENDOR_ID = 0x0d8c, /* C-Media Electronics */ + PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */ +}; + +/* table of devices that work with this driver */ +static const struct usb_device_id cm109_usb_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = VENDOR_ID, + .idProduct = PRODUCT_ID_CM109, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .driver_info = (kernel_ulong_t) &info_cm109 + }, + /* you can add more devices here with product ID 0x0008 - 0x000f */ + { } +}; + +static void cm109_usb_cleanup(struct cm109_dev *dev) +{ + if (dev->ctl_req) + usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)), + dev->ctl_req, dev->ctl_req_dma); + if (dev->ctl_data) + usb_buffer_free(dev->udev, USB_PKT_LEN, + dev->ctl_data, dev->ctl_dma); + if (dev->irq_data) + usb_buffer_free(dev->udev, USB_PKT_LEN, + dev->irq_data, dev->irq_dma); + + usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */ + usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */ + kfree(dev); +} + +static void cm109_usb_disconnect(struct usb_interface *interface) +{ + struct cm109_dev *dev = usb_get_intfdata(interface); + + usb_set_intfdata(interface, NULL); + input_unregister_device(dev->idev); + cm109_usb_cleanup(dev); +} + +static int cm109_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct driver_info *nfo = (struct driver_info *)id->driver_info; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct cm109_dev *dev; + struct input_dev *input_dev = NULL; + int ret, pipe, i; + int error = -ENOMEM; + + interface = intf->cur_altsetting; + endpoint = &interface->endpoint[0].desc; + + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->ctl_submit_lock); + mutex_init(&dev->pm_mutex); + + dev->udev = udev; + dev->intf = intf; + + dev->idev = input_dev = input_allocate_device(); + if (!input_dev) + goto err_out; + + /* allocate usb buffers */ + dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN, + GFP_KERNEL, &dev->irq_dma); + if (!dev->irq_data) + goto err_out; + + dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN, + GFP_KERNEL, &dev->ctl_dma); + if (!dev->ctl_data) + goto err_out; + + dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)), + GFP_KERNEL, &dev->ctl_req_dma); + if (!dev->ctl_req) + goto err_out; + + /* allocate urb structures */ + dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb_irq) + goto err_out; + + dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urb_ctl) + goto err_out; + + /* get a handle to the interrupt data pipe */ + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + if (ret != USB_PKT_LEN) + err("invalid payload size %d, expected %d", ret, USB_PKT_LEN); + + /* initialise irq urb */ + usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data, + USB_PKT_LEN, + cm109_urb_irq_callback, dev, endpoint->bInterval); + dev->urb_irq->transfer_dma = dev->irq_dma; + dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + dev->urb_irq->dev = udev; + + /* initialise ctl urb */ + dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_OUT; + dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION; + dev->ctl_req->wValue = cpu_to_le16(0x200); + dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber); + dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN); + + usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0), + (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN, + cm109_urb_ctl_callback, dev); + dev->urb_ctl->setup_dma = dev->ctl_req_dma; + dev->urb_ctl->transfer_dma = dev->ctl_dma; + dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP | + URB_NO_TRANSFER_DMA_MAP; + dev->urb_ctl->dev = udev; + + /* find out the physical bus location */ + usb_make_path(udev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + /* register settings for the input device */ + input_dev->name = nfo->name; + input_dev->phys = dev->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, dev); + input_dev->open = cm109_input_open; + input_dev->close = cm109_input_close; + input_dev->event = cm109_input_ev; + + input_dev->keycode = dev->keymap; + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = ARRAY_SIZE(dev->keymap); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + + /* register available key events */ + for (i = 0; i < KEYMAP_SIZE; i++) { + unsigned short k = keymap(i); + dev->keymap[i] = k; + __set_bit(k, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); + + error = input_register_device(dev->idev); + if (error) + goto err_out; + + usb_set_intfdata(intf, dev); + + return 0; + + err_out: + input_free_device(input_dev); + cm109_usb_cleanup(dev); + return error; +} + +static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event); + + mutex_lock(&dev->pm_mutex); + cm109_stop_traffic(dev); + mutex_unlock(&dev->pm_mutex); + + return 0; +} + +static int cm109_usb_resume(struct usb_interface *intf) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + dev_info(&intf->dev, "cm109: usb_resume\n"); + + mutex_lock(&dev->pm_mutex); + cm109_restore_state(dev); + mutex_unlock(&dev->pm_mutex); + + return 0; +} + +static int cm109_usb_pre_reset(struct usb_interface *intf) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + mutex_lock(&dev->pm_mutex); + + /* + * Make sure input events don't try to toggle buzzer + * while we are resetting + */ + dev->resetting = 1; + smp_wmb(); + + cm109_stop_traffic(dev); + + return 0; +} + +static int cm109_usb_post_reset(struct usb_interface *intf) +{ + struct cm109_dev *dev = usb_get_intfdata(intf); + + dev->resetting = 0; + smp_wmb(); + + cm109_restore_state(dev); + + mutex_unlock(&dev->pm_mutex); + + return 0; +} + +static struct usb_driver cm109_driver = { + .name = "cm109", + .probe = cm109_usb_probe, + .disconnect = cm109_usb_disconnect, + .suspend = cm109_usb_suspend, + .resume = cm109_usb_resume, + .reset_resume = cm109_usb_resume, + .pre_reset = cm109_usb_pre_reset, + .post_reset = cm109_usb_post_reset, + .id_table = cm109_usb_table, + .supports_autosuspend = 1, +}; + +static int __init cm109_select_keymap(void) +{ + /* Load the phone keymap */ + if (!strcasecmp(phone, "kip1000")) { + keymap = keymap_kip1000; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for Komunikate KIP1000 phone loaded\n"); + } else if (!strcasecmp(phone, "gtalk")) { + keymap = keymap_gtalk; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for Genius G-talk phone loaded\n"); + } else if (!strcasecmp(phone, "usbph01")) { + keymap = keymap_usbph01; + printk(KERN_INFO KBUILD_MODNAME ": " + "Keymap for Allied-Telesis Corega USBPH01 phone loaded\n"); + } else { + printk(KERN_ERR KBUILD_MODNAME ": " + "Unsupported phone: %s\n", phone); + return -EINVAL; + } + + return 0; +} + +static int __init cm109_init(void) +{ + int err; + + err = cm109_select_keymap(); + if (err) + return err; + + err = usb_register(&cm109_driver); + if (err) + return err; + + printk(KERN_INFO KBUILD_MODNAME ": " + DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n"); + + return 0; +} + +static void __exit cm109_exit(void) +{ + usb_deregister(&cm109_driver); +} + +module_init(cm109_init); +module_exit(cm109_exit); + +MODULE_DEVICE_TABLE(usb, cm109_usb_table); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/map_to_7segment.h b/drivers/input/misc/map_to_7segment.h deleted file mode 100644 index a424094d9fe..00000000000 --- a/drivers/input/misc/map_to_7segment.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * drivers/usb/input/map_to_7segment.h - * - * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com> - * - * 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 - */ - -#ifndef MAP_TO_7SEGMENT_H -#define MAP_TO_7SEGMENT_H - -/* This file provides translation primitives and tables for the conversion - * of (ASCII) characters to a 7-segments notation. - * - * The 7 segment's wikipedia notation below is used as standard. - * See: http://en.wikipedia.org/wiki/Seven_segment_display - * - * Notation: +-a-+ - * f b - * +-g-+ - * e c - * +-d-+ - * - * Usage: - * - * Register a map variable, and fill it with a character set: - * static SEG7_DEFAULT_MAP(map_seg7); - * - * - * Then use for conversion: - * seg7 = map_to_seg7(&map_seg7, some_char); - * ... - * - * In device drivers it is recommended, if required, to make the char map - * accessible via the sysfs interface using the following scheme: - * - * static ssize_t show_map(struct device *dev, char *buf) { - * memcpy(buf, &map_seg7, sizeof(map_seg7)); - * return sizeof(map_seg7); - * } - * static ssize_t store_map(struct device *dev, const char *buf, size_t cnt) { - * if(cnt != sizeof(map_seg7)) - * return -EINVAL; - * memcpy(&map_seg7, buf, cnt); - * return cnt; - * } - * static DEVICE_ATTR(map_seg7, PERMS_RW, show_map, store_map); - * - * History: - * 2005-05-31 RFC linux-kernel@vger.kernel.org - */ -#include <linux/errno.h> - - -#define BIT_SEG7_A 0 -#define BIT_SEG7_B 1 -#define BIT_SEG7_C 2 -#define BIT_SEG7_D 3 -#define BIT_SEG7_E 4 -#define BIT_SEG7_F 5 -#define BIT_SEG7_G 6 -#define BIT_SEG7_RESERVED 7 - -struct seg7_conversion_map { - unsigned char table[128]; -}; - -static inline int map_to_seg7(struct seg7_conversion_map *map, int c) -{ - return c >= 0 && c < sizeof(map->table) ? map->table[c] : -EINVAL; -} - -#define SEG7_CONVERSION_MAP(_name, _map) \ - struct seg7_conversion_map _name = { .table = { _map } } - -/* - * It is recommended to use a facility that allows user space to redefine - * custom character sets for LCD devices. Please use a sysfs interface - * as described above. - */ -#define MAP_TO_SEG7_SYSFS_FILE "map_seg7" - -/******************************************************************************* - * ASCII conversion table - ******************************************************************************/ - -#define _SEG7(l,a,b,c,d,e,f,g) \ - ( a<<BIT_SEG7_A | b<<BIT_SEG7_B | c<<BIT_SEG7_C | d<<BIT_SEG7_D | \ - e<<BIT_SEG7_E | f<<BIT_SEG7_F | g<<BIT_SEG7_G ) - -#define _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - -#define _MAP_33_47_ASCII_SEG7_SYMBOL \ - _SEG7('!',0,0,0,0,1,1,0), _SEG7('"',0,1,0,0,0,1,0), _SEG7('#',0,1,1,0,1,1,0),\ - _SEG7('$',1,0,1,1,0,1,1), _SEG7('%',0,0,1,0,0,1,0), _SEG7('&',1,0,1,1,1,1,1),\ - _SEG7('\'',0,0,0,0,0,1,0),_SEG7('(',1,0,0,1,1,1,0), _SEG7(')',1,1,1,1,0,0,0),\ - _SEG7('*',0,1,1,0,1,1,1), _SEG7('+',0,1,1,0,0,0,1), _SEG7(',',0,0,0,0,1,0,0),\ - _SEG7('-',0,0,0,0,0,0,1), _SEG7('.',0,0,0,0,1,0,0), _SEG7('/',0,1,0,0,1,0,1), - -#define _MAP_48_57_ASCII_SEG7_NUMERIC \ - _SEG7('0',1,1,1,1,1,1,0), _SEG7('1',0,1,1,0,0,0,0), _SEG7('2',1,1,0,1,1,0,1),\ - _SEG7('3',1,1,1,1,0,0,1), _SEG7('4',0,1,1,0,0,1,1), _SEG7('5',1,0,1,1,0,1,1),\ - _SEG7('6',1,0,1,1,1,1,1), _SEG7('7',1,1,1,0,0,0,0), _SEG7('8',1,1,1,1,1,1,1),\ - _SEG7('9',1,1,1,1,0,1,1), - -#define _MAP_58_64_ASCII_SEG7_SYMBOL \ - _SEG7(':',0,0,0,1,0,0,1), _SEG7(';',0,0,0,1,0,0,1), _SEG7('<',1,0,0,0,0,1,1),\ - _SEG7('=',0,0,0,1,0,0,1), _SEG7('>',1,1,0,0,0,0,1), _SEG7('?',1,1,1,0,0,1,0),\ - _SEG7('@',1,1,0,1,1,1,1), - -#define _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \ - _SEG7('A',1,1,1,0,1,1,1), _SEG7('B',1,1,1,1,1,1,1), _SEG7('C',1,0,0,1,1,1,0),\ - _SEG7('D',1,1,1,1,1,1,0), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\ - _SEG7('G',1,1,1,1,0,1,1), _SEG7('H',0,1,1,0,1,1,1), _SEG7('I',0,1,1,0,0,0,0),\ - _SEG7('J',0,1,1,1,0,0,0), _SEG7('K',0,1,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\ - _SEG7('M',1,1,1,0,1,1,0), _SEG7('N',1,1,1,0,1,1,0), _SEG7('O',1,1,1,1,1,1,0),\ - _SEG7('P',1,1,0,0,1,1,1), _SEG7('Q',1,1,1,1,1,1,0), _SEG7('R',1,1,1,0,1,1,1),\ - _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('U',0,1,1,1,1,1,0),\ - _SEG7('V',0,1,1,1,1,1,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\ - _SEG7('Y',0,1,1,0,0,1,1), _SEG7('Z',1,1,0,1,1,0,1), - -#define _MAP_91_96_ASCII_SEG7_SYMBOL \ - _SEG7('[',1,0,0,1,1,1,0), _SEG7('\\',0,0,1,0,0,1,1),_SEG7(']',1,1,1,1,0,0,0),\ - _SEG7('^',1,1,0,0,0,1,0), _SEG7('_',0,0,0,1,0,0,0), _SEG7('`',0,1,0,0,0,0,0), - -#define _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ - _SEG7('A',1,1,1,0,1,1,1), _SEG7('b',0,0,1,1,1,1,1), _SEG7('c',0,0,0,1,1,0,1),\ - _SEG7('d',0,1,1,1,1,0,1), _SEG7('E',1,0,0,1,1,1,1), _SEG7('F',1,0,0,0,1,1,1),\ - _SEG7('G',1,1,1,1,0,1,1), _SEG7('h',0,0,1,0,1,1,1), _SEG7('i',0,0,1,0,0,0,0),\ - _SEG7('j',0,0,1,1,0,0,0), _SEG7('k',0,0,1,0,1,1,1), _SEG7('L',0,0,0,1,1,1,0),\ - _SEG7('M',1,1,1,0,1,1,0), _SEG7('n',0,0,1,0,1,0,1), _SEG7('o',0,0,1,1,1,0,1),\ - _SEG7('P',1,1,0,0,1,1,1), _SEG7('q',1,1,1,0,0,1,1), _SEG7('r',0,0,0,0,1,0,1),\ - _SEG7('S',1,0,1,1,0,1,1), _SEG7('T',0,0,0,1,1,1,1), _SEG7('u',0,0,1,1,1,0,0),\ - _SEG7('v',0,0,1,1,1,0,0), _SEG7('W',0,1,1,1,1,1,1), _SEG7('X',0,1,1,0,1,1,1),\ - _SEG7('y',0,1,1,1,0,1,1), _SEG7('Z',1,1,0,1,1,0,1), - -#define _MAP_123_126_ASCII_SEG7_SYMBOL \ - _SEG7('{',1,0,0,1,1,1,0), _SEG7('|',0,0,0,0,1,1,0), _SEG7('}',1,1,1,1,0,0,0),\ - _SEG7('~',1,0,0,0,0,0,0), - -/* Maps */ - -/* This set tries to map as close as possible to the visible characteristics - * of the ASCII symbol, lowercase and uppercase letters may differ in - * presentation on the display. - */ -#define MAP_ASCII7SEG_ALPHANUM \ - _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \ - _MAP_33_47_ASCII_SEG7_SYMBOL \ - _MAP_48_57_ASCII_SEG7_NUMERIC \ - _MAP_58_64_ASCII_SEG7_SYMBOL \ - _MAP_65_90_ASCII_SEG7_ALPHA_UPPR \ - _MAP_91_96_ASCII_SEG7_SYMBOL \ - _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ - _MAP_123_126_ASCII_SEG7_SYMBOL - -/* This set tries to map as close as possible to the symbolic characteristics - * of the ASCII character for maximum discrimination. - * For now this means all alpha chars are in lower case representations. - * (This for example facilitates the use of hex numbers with uppercase input.) - */ -#define MAP_ASCII7SEG_ALPHANUM_LC \ - _MAP_0_32_ASCII_SEG7_NON_PRINTABLE \ - _MAP_33_47_ASCII_SEG7_SYMBOL \ - _MAP_48_57_ASCII_SEG7_NUMERIC \ - _MAP_58_64_ASCII_SEG7_SYMBOL \ - _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ - _MAP_91_96_ASCII_SEG7_SYMBOL \ - _MAP_97_122_ASCII_SEG7_ALPHA_LOWER \ - _MAP_123_126_ASCII_SEG7_SYMBOL - -#define SEG7_DEFAULT_MAP(_name) \ - SEG7_CONVERSION_MAP(_name,MAP_ASCII7SEG_ALPHANUM) - -#endif /* MAP_TO_7SEGMENT_H */ - diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index fe268be3293..7c8957dd22c 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -277,6 +277,16 @@ static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = { { KE_END, 0 } }; +static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */ + { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */ + { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */ + { KE_KEY, 0x36, {KEY_WWW} }, /* www button */ + { KE_WIFI, 0x78 }, /* satelite dish button */ + { KE_END, 0 } +}; + static struct key_entry keymap_fujitsu_n3510[] __initdata = { { KE_KEY, 0x11, {KEY_PROG1} }, { KE_KEY, 0x12, {KEY_PROG2} }, @@ -618,6 +628,15 @@ static struct dmi_system_id dmi_ids[] __initdata = { }, { .callback = dmi_matched, + .ident = "Fujitsu-Siemens Amilo Pro Edition V3505", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"), + }, + .driver_data = keymap_fs_amilo_pro_v3505 + }, + { + .callback = dmi_matched, .ident = "Fujitsu-Siemens Amilo M7400", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index facefd3dba2..11b5c7e84ed 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c @@ -52,8 +52,8 @@ #include <linux/module.h> #include <linux/rwsem.h> #include <linux/usb/input.h> +#include <linux/map_to_7segment.h> -#include "map_to_7segment.h" #include "yealink.h" #define DRIVER_VERSION "yld-20051230" diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index f996546fc44..f488b6852ba 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -96,6 +96,16 @@ config MOUSE_PS2_TOUCHKIT If unsure, say N. +config MOUSE_PS2_OLPC + bool "OLPC PS/2 mouse protocol extension" + depends on MOUSE_PS2 && OLPC + help + Say Y here if you have an OLPC XO-1 laptop (with built-in + PS/2 touchpad/tablet device). The manufacturer calls the + touchpad an HGPK. + + If unsure, say N. + config MOUSE_SERIAL tristate "Serial mouse" select SERIO diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index d4d20251609..8e6e6909780 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o psmouse-objs := psmouse-base.o synaptics.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o +psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 385e32bcf6a..cbedf957cc5 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -54,6 +54,7 @@ static const struct alps_model_info alps_model_data[] = { { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ + { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */ { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 } /* Dell Vostro 1400 */ }; diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 1f41ae94f26..079816e6b23 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -136,12 +136,28 @@ MODULE_DEVICE_TABLE(usb, atp_table); #define ATP_GEYSER_MODE_REQUEST_INDEX 0 #define ATP_GEYSER_MODE_VENDOR_VALUE 0x04 +/** + * enum atp_status_bits - status bit meanings + * + * These constants represent the meaning of the status bits. + * (only Geyser 3/4) + * + * @ATP_STATUS_BUTTON: The button was pressed + * @ATP_STATUS_BASE_UPDATE: Update of the base values (untouched pad) + * @ATP_STATUS_FROM_RESET: Reset previously performed + */ +enum atp_status_bits { + ATP_STATUS_BUTTON = BIT(0), + ATP_STATUS_BASE_UPDATE = BIT(2), + ATP_STATUS_FROM_RESET = BIT(4), +}; + /* Structure to hold all of our device specific stuff */ struct atp { char phys[64]; struct usb_device *udev; /* usb device */ struct urb *urb; /* usb request block */ - signed char *data; /* transferred data */ + u8 *data; /* transferred data */ struct input_dev *input; /* input dev */ enum atp_touchpad_type type; /* type of touchpad */ bool open; @@ -251,8 +267,6 @@ static void atp_reinit(struct work_struct *work) int retval; dprintk("appletouch: putting appletouch to sleep (reinit)\n"); - dev->idlecount = 0; - atp_geyser_init(udev); retval = usb_submit_urb(dev->urb, GFP_ATOMIC); @@ -327,11 +341,14 @@ static inline void atp_report_fingers(struct input_dev *input, int fingers) input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2); } -static void atp_complete(struct urb *urb) +/* Check URB status and for correct length of data package */ + +#define ATP_URB_STATUS_SUCCESS 0 +#define ATP_URB_STATUS_ERROR 1 +#define ATP_URB_STATUS_ERROR_FATAL 2 + +static int atp_status_check(struct urb *urb) { - int x, y, x_z, y_z, x_f, y_f; - int retval, i, j; - int key; struct atp *dev = urb->context; switch (urb->status) { @@ -351,11 +368,12 @@ static void atp_complete(struct urb *urb) /* This urb is terminated, clean up */ dbg("atp_complete: urb shutting down with status: %d", urb->status); - return; + return ATP_URB_STATUS_ERROR_FATAL; + default: dbg("atp_complete: nonzero urb status received: %d", urb->status); - goto exit; + return ATP_URB_STATUS_ERROR; } /* drop incomplete datasets */ @@ -363,30 +381,33 @@ static void atp_complete(struct urb *urb) dprintk("appletouch: incomplete data package" " (first byte: %d, length: %d).\n", dev->data[0], dev->urb->actual_length); - goto exit; + return ATP_URB_STATUS_ERROR; } - /* reorder the sensors values */ - if (dev->type == ATP_GEYSER3 || dev->type == ATP_GEYSER4) { - memset(dev->xy_cur, 0, sizeof(dev->xy_cur)); + return ATP_URB_STATUS_SUCCESS; +} - /* - * The values are laid out like this: - * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ... - * '-' is an unused value. - */ +/* + * USB interrupt callback functions + */ - /* read X values */ - for (i = 0, j = 19; i < 20; i += 2, j += 3) { - dev->xy_cur[i] = dev->data[j + 1]; - dev->xy_cur[i + 1] = dev->data[j + 2]; - } - /* read Y values */ - for (i = 0, j = 1; i < 9; i += 2, j += 3) { - dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1]; - dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2]; - } - } else if (dev->type == ATP_GEYSER2) { +/* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */ + +static void atp_complete_geyser_1_2(struct urb *urb) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i, j; + int key; + struct atp *dev = urb->context; + int status = atp_status_check(urb); + + if (status == ATP_URB_STATUS_ERROR_FATAL) + return; + else if (status == ATP_URB_STATUS_ERROR) + goto exit; + + /* reorder the sensors values */ + if (dev->type == ATP_GEYSER2) { memset(dev->xy_cur, 0, sizeof(dev->xy_cur)); /* @@ -427,34 +448,40 @@ static void atp_complete(struct urb *urb) /* first sample */ dev->valid = true; dev->x_old = dev->y_old = -1; + + /* Store first sample */ memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); - if (dev->size_detect_done || - dev->type == ATP_GEYSER3) /* No 17" Macbooks (yet) */ - goto exit; + /* Perform size detection, if not done already */ + if (!dev->size_detect_done) { + + /* 17" Powerbooks have extra X sensors */ + for (i = (dev->type == ATP_GEYSER2 ? 15 : 16); + i < ATP_XSENSORS; i++) { + if (!dev->xy_cur[i]) + continue; + + printk(KERN_INFO + "appletouch: 17\" model detected.\n"); + + if (dev->type == ATP_GEYSER2) + input_set_abs_params(dev->input, ABS_X, + 0, + (20 - 1) * + ATP_XFACT - 1, + ATP_FUZZ, 0); + else + input_set_abs_params(dev->input, ABS_X, + 0, + (26 - 1) * + ATP_XFACT - 1, + ATP_FUZZ, 0); + break; + } - /* 17" Powerbooks have extra X sensors */ - for (i = (dev->type == ATP_GEYSER2 ? 15 : 16); - i < ATP_XSENSORS; i++) { - if (!dev->xy_cur[i]) - continue; - - printk(KERN_INFO "appletouch: 17\" model detected.\n"); - if (dev->type == ATP_GEYSER2) - input_set_abs_params(dev->input, ABS_X, 0, - (20 - 1) * - ATP_XFACT - 1, - ATP_FUZZ, 0); - else - input_set_abs_params(dev->input, ABS_X, 0, - (ATP_XSENSORS - 1) * - ATP_XFACT - 1, - ATP_FUZZ, 0); - break; + dev->size_detect_done = 1; + goto exit; } - - dev->size_detect_done = 1; - goto exit; } for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) { @@ -475,7 +502,118 @@ static void atp_complete(struct urb *urb) ATP_XFACT, &x_z, &x_f); y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, ATP_YFACT, &y_z, &y_f); - key = dev->data[dev->datalen - 1] & 1; + key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON; + + if (x && y) { + if (dev->x_old != -1) { + x = (dev->x_old * 3 + x) >> 2; + y = (dev->y_old * 3 + y) >> 2; + dev->x_old = x; + dev->y_old = y; + + if (debug > 1) + printk(KERN_DEBUG "appletouch: " + "X: %3d Y: %3d Xz: %3d Yz: %3d\n", + x, y, x_z, y_z); + + input_report_key(dev->input, BTN_TOUCH, 1); + input_report_abs(dev->input, ABS_X, x); + input_report_abs(dev->input, ABS_Y, y); + input_report_abs(dev->input, ABS_PRESSURE, + min(ATP_PRESSURE, x_z + y_z)); + atp_report_fingers(dev->input, max(x_f, y_f)); + } + dev->x_old = x; + dev->y_old = y; + + } else if (!x && !y) { + + dev->x_old = dev->y_old = -1; + input_report_key(dev->input, BTN_TOUCH, 0); + input_report_abs(dev->input, ABS_PRESSURE, 0); + atp_report_fingers(dev->input, 0); + + /* reset the accumulator on release */ + memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); + } + + input_report_key(dev->input, BTN_LEFT, key); + input_sync(dev->input); + + exit: + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) + err("atp_complete: usb_submit_urb failed with result %d", + retval); +} + +/* Interrupt function for older touchpads: GEYSER3/GEYSER4 */ + +static void atp_complete_geyser_3_4(struct urb *urb) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i, j; + int key; + struct atp *dev = urb->context; + int status = atp_status_check(urb); + + if (status == ATP_URB_STATUS_ERROR_FATAL) + return; + else if (status == ATP_URB_STATUS_ERROR) + goto exit; + + /* Reorder the sensors values: + * + * The values are laid out like this: + * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ... + * '-' is an unused value. + */ + + /* read X values */ + for (i = 0, j = 19; i < 20; i += 2, j += 3) { + dev->xy_cur[i] = dev->data[j + 1]; + dev->xy_cur[i + 1] = dev->data[j + 2]; + } + /* read Y values */ + for (i = 0, j = 1; i < 9; i += 2, j += 3) { + dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1]; + dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2]; + } + + dbg_dump("sample", dev->xy_cur); + + /* Just update the base values (i.e. touchpad in untouched state) */ + if (dev->data[dev->datalen - 1] & ATP_STATUS_BASE_UPDATE) { + + dprintk(KERN_DEBUG "appletouch: updated base values\n"); + + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); + goto exit; + } + + for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) { + /* calculate the change */ + dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i]; + + /* this is a round-robin value, so couple with that */ + if (dev->xy_acc[i] > 127) + dev->xy_acc[i] -= 256; + + if (dev->xy_acc[i] < -127) + dev->xy_acc[i] += 256; + + /* prevent down drifting */ + if (dev->xy_acc[i] < 0) + dev->xy_acc[i] = 0; + } + + dbg_dump("accumulator", dev->xy_acc); + + x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + ATP_XFACT, &x_z, &x_f); + y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + ATP_YFACT, &y_z, &y_f); + key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON; if (x && y) { if (dev->x_old != -1) { @@ -514,28 +652,27 @@ static void atp_complete(struct urb *urb) input_sync(dev->input); /* - * Many Geysers will continue to send packets continually after + * Geysers 3/4 will continue to send packets continually after * the first touch unless reinitialised. Do so if it's been * idle for a while in order to avoid waking the kernel up - * several hundred times a second. Re-initialization does not - * work on Fountain touchpads. + * several hundred times a second. */ - if (dev->type != ATP_FOUNTAIN) { - /* - * Button must not be pressed when entering suspend, - * otherwise we will never release the button. - */ - if (!x && !y && !key) { - dev->idlecount++; - if (dev->idlecount == 10) { - dev->valid = false; - schedule_work(&dev->work); - /* Don't resubmit urb here, wait for reinit */ - return; - } - } else + + /* + * Button must not be pressed when entering suspend, + * otherwise we will never release the button. + */ + if (!x && !y && !key) { + dev->idlecount++; + if (dev->idlecount == 10) { + dev->x_old = dev->y_old = -1; dev->idlecount = 0; - } + schedule_work(&dev->work); + /* Don't resubmit urb here, wait for reinit */ + return; + } + } else + dev->idlecount = 0; exit: retval = usb_submit_urb(dev->urb, GFP_ATOMIC); @@ -632,9 +769,19 @@ static int atp_probe(struct usb_interface *iface, if (!dev->data) goto err_free_urb; - usb_fill_int_urb(dev->urb, udev, - usb_rcvintpipe(udev, int_in_endpointAddr), - dev->data, dev->datalen, atp_complete, dev, 1); + /* Select the USB complete (callback) function */ + if (dev->type == ATP_FOUNTAIN || + dev->type == ATP_GEYSER1 || + dev->type == ATP_GEYSER2) + usb_fill_int_urb(dev->urb, udev, + usb_rcvintpipe(udev, int_in_endpointAddr), + dev->data, dev->datalen, + atp_complete_geyser_1_2, dev, 1); + else + usb_fill_int_urb(dev->urb, udev, + usb_rcvintpipe(udev, int_in_endpointAddr), + dev->data, dev->datalen, + atp_complete_geyser_3_4, dev, 1); error = atp_handle_geyser(dev); if (error) @@ -751,8 +898,6 @@ static int atp_suspend(struct usb_interface *iface, pm_message_t message) struct atp *dev = usb_get_intfdata(iface); usb_kill_urb(dev->urb); - dev->valid = false; - return 0; } diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c new file mode 100644 index 00000000000..e82d34201e9 --- /dev/null +++ b/drivers/input/mouse/hgpk.c @@ -0,0 +1,477 @@ +/* + * OLPC HGPK (XO-1) touchpad PS/2 mouse driver + * + * Copyright (c) 2006-2008 One Laptop Per Child + * Authors: + * Zephaniah E. Hull + * Andres Salomon <dilinger@debian.org> + * + * This driver is partly based on the ALPS driver, which is: + * + * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com> + * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru> + * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> + * + * 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. + */ + +/* + * The spec from ALPS is available from + * <http://wiki.laptop.org/go/Touch_Pad/Tablet>. It refers to this + * device as HGPK (Hybrid GS, PT, and Keymatrix). + * + * The earliest versions of the device had simultaneous reporting; that + * was removed. After that, the device used the Advanced Mode GS/PT streaming + * stuff. That turned out to be too buggy to support, so we've finally + * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad). + */ + +#define DEBUG +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/libps2.h> +#include <linux/delay.h> +#include <asm/olpc.h> + +#include "psmouse.h" +#include "hgpk.h" + +static int tpdebug; +module_param(tpdebug, int, 0644); +MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); + +static int recalib_delta = 100; +module_param(recalib_delta, int, 0644); +MODULE_PARM_DESC(recalib_delta, + "packets containing a delta this large will cause a recalibration."); + +/* + * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" + * above the pad and still have it send packets. This causes a jump cursor + * when one places their finger on the pad. We can probably detect the + * jump as we see a large deltas (>= 100px). In mouse mode, I've been + * unable to even come close to 100px deltas during normal usage, so I think + * this threshold is safe. If a large delta occurs, trigger a recalibration. + */ +static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) +{ + struct hgpk_data *priv = psmouse->private; + + if (abs(x) > recalib_delta || abs(y) > recalib_delta) { + hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n", + recalib_delta, x, y); + /* My car gets forty rods to the hogshead and that's the + * way I likes it! */ + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(1000)); + } +} + +/* + * We have no idea why this particular hardware bug occurs. The touchpad + * will randomly start spewing packets without anything touching the + * pad. This wouldn't necessarily be bad, but it's indicative of a + * severely miscalibrated pad; attempting to use the touchpad while it's + * spewing means the cursor will jump all over the place, and act "drunk". + * + * The packets that are spewed tend to all have deltas between -2 and 2, and + * the cursor will move around without really going very far. It will + * tend to end up in the same location; if we tally up the changes over + * 100 packets, we end up w/ a final delta of close to 0. This happens + * pretty regularly when the touchpad is spewing, and is pretty hard to + * manually trigger (at least for *my* fingers). So, it makes a perfect + * scheme for detecting spews. + */ +static void hgpk_spewing_hack(struct psmouse *psmouse, + int l, int r, int x, int y) +{ + struct hgpk_data *priv = psmouse->private; + + /* ignore button press packets; many in a row could trigger + * a false-positive! */ + if (l || r) + return; + + priv->x_tally += x; + priv->y_tally += y; + + if (++priv->count > 100) { + if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { + hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n", + priv->x_tally, priv->y_tally); + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(1000)); + } + /* reset every 100 packets */ + priv->count = 0; + priv->x_tally = 0; + priv->y_tally = 0; + } +} + +/* + * HGPK Mouse Mode format (standard mouse format, sans middle button) + * + * byte 0: y-over x-over y-neg x-neg 1 0 swr swl + * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 + * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 + * + * swr/swl are the left/right buttons. + * x-neg/y-neg are the x and y delta negative bits + * x-over/y-over are the x and y overflow bits + */ +static int hgpk_validate_byte(unsigned char *packet) +{ + return (packet[0] & 0x0C) == 0x08; +} + +static void hgpk_process_packet(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + int x, y, left, right; + + left = packet[0] & 1; + right = (packet[0] >> 1) & 1; + + x = packet[1] - ((packet[0] << 4) & 0x100); + y = ((packet[0] << 3) & 0x100) - packet[2]; + + hgpk_jumpy_hack(psmouse, x, y); + hgpk_spewing_hack(psmouse, left, right, x, y); + + if (tpdebug) + hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); + + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + + input_report_rel(dev, REL_X, x); + input_report_rel(dev, REL_Y, y); + + input_sync(dev); +} + +static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + if (hgpk_validate_byte(psmouse->packet)) { + hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n", + __func__, psmouse->pktcnt, psmouse->packet[0], + psmouse->packet[1], psmouse->packet[2]); + return PSMOUSE_BAD_DATA; + } + + if (psmouse->pktcnt >= psmouse->pktsize) { + hgpk_process_packet(psmouse); + return PSMOUSE_FULL_PACKET; + } + + if (priv->recalib_window) { + if (time_before(jiffies, priv->recalib_window)) { + /* + * ugh, got a packet inside our recalibration + * window, schedule another recalibration. + */ + hgpk_dbg(psmouse, + "packet inside calibration window, " + "queueing another recalibration\n"); + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(1000)); + } + priv->recalib_window = 0; + } + + return PSMOUSE_GOOD_DATA; +} + +static int hgpk_force_recalibrate(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct hgpk_data *priv = psmouse->private; + + /* C-series touchpads added the recalibrate command */ + if (psmouse->model < HGPK_MODEL_C) + return 0; + + /* we don't want to race with the irq handler, nor with resyncs */ + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + /* start by resetting the device */ + psmouse_reset(psmouse); + + /* send the recalibrate request */ + if (ps2_command(ps2dev, NULL, 0xf5) || + ps2_command(ps2dev, NULL, 0xf5) || + ps2_command(ps2dev, NULL, 0xe6) || + ps2_command(ps2dev, NULL, 0xf5)) { + return -1; + } + + /* according to ALPS, 150mS is required for recalibration */ + msleep(150); + + /* XXX: If a finger is down during this delay, recalibration will + * detect capacitance incorrectly. This is a hardware bug, and + * we don't have a good way to deal with it. The 2s window stuff + * (below) is our best option for now. + */ + + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) + return -1; + + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + + /* After we recalibrate, we shouldn't get any packets for 2s. If + * we do, it's likely that someone's finger was on the touchpad. + * If someone's finger *was* on the touchpad, it's probably + * miscalibrated. So, we should schedule another recalibration + */ + priv->recalib_window = jiffies + msecs_to_jiffies(2000); + + return 0; +} + +/* + * This kills power to the touchpad; according to ALPS, current consumption + * goes down to 50uA after running this. To turn power back on, we drive + * MS-DAT low. + */ +static int hgpk_toggle_power(struct psmouse *psmouse, int enable) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int timeo; + + /* Added on D-series touchpads */ + if (psmouse->model < HGPK_MODEL_D) + return 0; + + if (enable) { + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + /* + * Sending a byte will drive MS-DAT low; this will wake up + * the controller. Once we get an ACK back from it, it + * means we can continue with the touchpad re-init. ALPS + * tells us that 1s should be long enough, so set that as + * the upper bound. + */ + for (timeo = 20; timeo > 0; timeo--) { + if (!ps2_sendbyte(&psmouse->ps2dev, + PSMOUSE_CMD_DISABLE, 20)) + break; + msleep(50); + } + + psmouse_reset(psmouse); + + /* should be all set, enable the touchpad */ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + + } else { + hgpk_dbg(psmouse, "Powering off touchpad.\n"); + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + + if (ps2_command(ps2dev, NULL, 0xec) || + ps2_command(ps2dev, NULL, 0xec) || + ps2_command(ps2dev, NULL, 0xea)) { + return -1; + } + + /* probably won't see an ACK, the touchpad will be off */ + ps2_sendbyte(&psmouse->ps2dev, 0xec, 20); + } + + return 0; +} + +static int hgpk_poll(struct psmouse *psmouse) +{ + /* We can't poll, so always return failure. */ + return -1; +} + +static int hgpk_reconnect(struct psmouse *psmouse) +{ + /* During suspend/resume the ps2 rails remain powered. We don't want + * to do a reset because it's flush data out of buffers; however, + * earlier prototypes (B1) had some brokenness that required a reset. */ + if (olpc_board_at_least(olpc_board(0xb2))) + if (psmouse->ps2dev.serio->dev.power.power_state.event != + PM_EVENT_ON) + return 0; + + psmouse_reset(psmouse); + + return 0; +} + +static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) +{ + struct hgpk_data *priv = psmouse->private; + + return sprintf(buf, "%d\n", priv->powered); +} + +static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct hgpk_data *priv = psmouse->private; + unsigned long value; + int err; + + err = strict_strtoul(buf, 10, &value); + if (err || value > 1) + return -EINVAL; + + if (value != priv->powered) { + /* + * hgpk_toggle_power will deal w/ state so + * we're not racing w/ irq + */ + err = hgpk_toggle_power(psmouse, value); + if (!err) + priv->powered = value; + } + + return err ? err : count; +} + +__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, + hgpk_show_powered, hgpk_set_powered, 0); + +static void hgpk_disconnect(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_powered.dattr); + psmouse_reset(psmouse); + kfree(priv); +} + +static void hgpk_recalib_work(struct work_struct *work) +{ + struct delayed_work *w = container_of(work, struct delayed_work, work); + struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); + struct psmouse *psmouse = priv->psmouse; + + hgpk_dbg(psmouse, "recalibrating touchpad..\n"); + + if (hgpk_force_recalibrate(psmouse)) + hgpk_err(psmouse, "recalibration failed!\n"); +} + +static int hgpk_register(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + int err; + + /* unset the things that psmouse-base sets which we don't have */ + __clear_bit(BTN_MIDDLE, dev->keybit); + + /* set the things we do have */ + __set_bit(EV_KEY, dev->evbit); + __set_bit(EV_REL, dev->evbit); + + __set_bit(REL_X, dev->relbit); + __set_bit(REL_Y, dev->relbit); + + __set_bit(BTN_LEFT, dev->keybit); + __set_bit(BTN_RIGHT, dev->keybit); + + /* register handlers */ + psmouse->protocol_handler = hgpk_process_byte; + psmouse->poll = hgpk_poll; + psmouse->disconnect = hgpk_disconnect; + psmouse->reconnect = hgpk_reconnect; + psmouse->pktsize = 3; + + /* Disable the idle resync. */ + psmouse->resync_time = 0; + /* Reset after a lot of bad bytes. */ + psmouse->resetafter = 1024; + + err = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_powered.dattr); + if (err) + hgpk_err(psmouse, "Failed to create sysfs attribute\n"); + + return err; +} + +int hgpk_init(struct psmouse *psmouse) +{ + struct hgpk_data *priv; + int err = -ENOMEM; + + priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); + if (!priv) + goto alloc_fail; + + psmouse->private = priv; + priv->psmouse = psmouse; + priv->powered = 1; + INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); + + err = psmouse_reset(psmouse); + if (err) + goto init_fail; + + err = hgpk_register(psmouse); + if (err) + goto init_fail; + + return 0; + +init_fail: + kfree(priv); +alloc_fail: + return err; +} + +static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + + /* E7, E7, E7, E9 gets us a 3 byte identifier */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + return -EIO; + } + + hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]); + + /* HGPK signature: 0x67, 0x00, 0x<model> */ + if (param[0] != 0x67 || param[1] != 0x00) + return -ENODEV; + + hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); + + return param[2]; +} + +int hgpk_detect(struct psmouse *psmouse, int set_properties) +{ + int version; + + version = hgpk_get_model(psmouse); + if (version < 0) + return version; + + if (set_properties) { + psmouse->vendor = "ALPS"; + psmouse->name = "HGPK"; + psmouse->model = version; + } + + return 0; +} diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h new file mode 100644 index 00000000000..a4b2a96f5f5 --- /dev/null +++ b/drivers/input/mouse/hgpk.h @@ -0,0 +1,49 @@ +/* + * OLPC HGPK (XO-1) touchpad PS/2 mouse driver + */ + +#ifndef _HGPK_H +#define _HGPK_H + +enum hgpk_model_t { + HGPK_MODEL_PREA = 0x0a, /* pre-B1s */ + HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ + HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */ + HGPK_MODEL_C = 0x3c, + HGPK_MODEL_D = 0x50, /* C1, mass production */ +}; + +struct hgpk_data { + struct psmouse *psmouse; + int powered; + int count, x_tally, y_tally; /* hardware workaround stuff */ + unsigned long recalib_window; + struct delayed_work recalib_wq; +}; + +#define hgpk_dbg(psmouse, format, arg...) \ + dev_dbg(&(psmouse)->ps2dev.serio->dev, format, ## arg) +#define hgpk_err(psmouse, format, arg...) \ + dev_err(&(psmouse)->ps2dev.serio->dev, format, ## arg) +#define hgpk_info(psmouse, format, arg...) \ + dev_info(&(psmouse)->ps2dev.serio->dev, format, ## arg) +#define hgpk_warn(psmouse, format, arg...) \ + dev_warn(&(psmouse)->ps2dev.serio->dev, format, ## arg) +#define hgpk_notice(psmouse, format, arg...) \ + dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg) + +#ifdef CONFIG_MOUSE_PS2_OLPC +int hgpk_detect(struct psmouse *psmouse, int set_properties); +int hgpk_init(struct psmouse *psmouse); +#else +static inline int hgpk_detect(struct psmouse *psmouse, int set_properties) +{ + return -ENODEV; +} +static inline int hgpk_init(struct psmouse *psmouse) +{ + return -ENODEV; +} +#endif + +#endif diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 0c5660d28ca..390f1dbb98a 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -157,10 +157,8 @@ static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, void *data, static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count) { unsigned long value; - char *rest; - value = simple_strtoul(buf, &rest, 10); - if (*rest || value > 1) + if (strict_strtoul(buf, 10, &value) || value > 1) return -EINVAL; ps2pp_set_smartscroll(psmouse, value); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f5a6be1d3c4..126e977e199 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -25,6 +25,7 @@ #include "synaptics.h" #include "logips2pp.h" #include "alps.h" +#include "hgpk.h" #include "lifebook.h" #include "trackpoint.h" #include "touchkit_ps2.h" @@ -201,6 +202,12 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } +void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, + unsigned long delay) +{ + queue_delayed_work(kpsmoused_wq, work, delay); +} + /* * __psmouse_set_state() sets new psmouse state and resets all flags. */ @@ -220,7 +227,7 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta * is not a concern. */ -static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { serio_pause_rx(psmouse->ps2dev.serio); __psmouse_set_state(psmouse, new_state); @@ -305,7 +312,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->badbyte = psmouse->packet[0]; __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); - queue_work(kpsmoused_wq, &psmouse->resync_work); + psmouse_queue_work(psmouse, &psmouse->resync_work, 0); goto out; } @@ -342,7 +349,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { psmouse->badbyte = psmouse->packet[0]; __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); - queue_work(kpsmoused_wq, &psmouse->resync_work); + psmouse_queue_work(psmouse, &psmouse->resync_work, 0); goto out; } @@ -630,8 +637,20 @@ static int psmouse_extensions(struct psmouse *psmouse, } } - if (max_proto > PSMOUSE_IMEX) { +/* + * Try OLPC HGPK touchpad. + */ + if (max_proto > PSMOUSE_IMEX && + hgpk_detect(psmouse, set_properties) == 0) { + if (!set_properties || hgpk_init(psmouse) == 0) + return PSMOUSE_HGPK; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + if (max_proto > PSMOUSE_IMEX) { if (genius_detect(psmouse, set_properties) == 0) return PSMOUSE_GENPS; @@ -762,6 +781,14 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = touchkit_ps2_detect, }, #endif +#ifdef CONFIG_MOUSE_PS2_OLPC + { + .type = PSMOUSE_HGPK, + .name = "OLPC HGPK", + .alias = "hgpk", + .detect = hgpk_detect, + }, +#endif { .type = PSMOUSE_CORTRON, .name = "CortronPS/2", @@ -935,7 +962,7 @@ static int psmouse_poll(struct psmouse *psmouse) static void psmouse_resync(struct work_struct *work) { struct psmouse *parent = NULL, *psmouse = - container_of(work, struct psmouse, resync_work); + container_of(work, struct psmouse, resync_work.work); struct serio *serio = psmouse->ps2dev.serio; psmouse_ret_t rc = PSMOUSE_GOOD_DATA; int failed = 0, enabled = 0; @@ -1194,7 +1221,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto err_free; ps2_init(&psmouse->ps2dev, serio); - INIT_WORK(&psmouse->resync_work, psmouse_resync); + INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync); psmouse->dev = input_dev; snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys); @@ -1395,25 +1422,29 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev psmouse = serio_get_drvdata(serio); - if (psmouse->state == PSMOUSE_IGNORE) { - retval = -ENODEV; - goto out_unlock; - } + if (attr->protect) { + if (psmouse->state == PSMOUSE_IGNORE) { + retval = -ENODEV; + goto out_unlock; + } - if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { - parent = serio_get_drvdata(serio->parent); - psmouse_deactivate(parent); - } + if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { + parent = serio_get_drvdata(serio->parent); + psmouse_deactivate(parent); + } - psmouse_deactivate(psmouse); + psmouse_deactivate(psmouse); + } retval = attr->set(psmouse, attr->data, buf, count); - if (retval != -ENODEV) - psmouse_activate(psmouse); + if (attr->protect) { + if (retval != -ENODEV) + psmouse_activate(psmouse); - if (parent) - psmouse_activate(parent); + if (parent) + psmouse_activate(parent); + } out_unlock: mutex_unlock(&psmouse_mutex); @@ -1433,10 +1464,8 @@ static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const { unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); unsigned long value; - char *rest; - value = simple_strtoul(buf, &rest, 10); - if (*rest) + if (strict_strtoul(buf, 10, &value)) return -EINVAL; if ((unsigned int)value != value) @@ -1549,10 +1578,8 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count) { unsigned long value; - char *rest; - value = simple_strtoul(buf, &rest, 10); - if (*rest) + if (strict_strtoul(buf, 10, &value)) return -EINVAL; psmouse->set_rate(psmouse, value); @@ -1562,10 +1589,8 @@ static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count) { unsigned long value; - char *rest; - value = simple_strtoul(buf, &rest, 10); - if (*rest) + if (strict_strtoul(buf, 10, &value)) return -EINVAL; psmouse->set_resolution(psmouse, value); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 1317bdd8cc7..8b608a1cdd1 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -39,7 +39,7 @@ struct psmouse { void *private; struct input_dev *dev; struct ps2dev ps2dev; - struct work_struct resync_work; + struct delayed_work resync_work; char *vendor; char *name; unsigned char packet[8]; @@ -89,20 +89,24 @@ enum psmouse_type { PSMOUSE_TRACKPOINT, PSMOUSE_TOUCHKIT_PS2, PSMOUSE_CORTRON, + PSMOUSE_HGPK, PSMOUSE_AUTO /* This one should always be last */ }; +void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, + unsigned long delay); int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); int psmouse_reset(struct psmouse *psmouse); +void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); - struct psmouse_attribute { struct device_attribute dattr; void *data; ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf); ssize_t (*set)(struct psmouse *psmouse, void *data, const char *buf, size_t count); + int protect; }; #define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr) @@ -111,7 +115,7 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *at ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \ +#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \ static ssize_t _show(struct psmouse *, void *data, char *); \ static ssize_t _set(struct psmouse *, void *data, const char *, size_t); \ static struct psmouse_attribute psmouse_attr_##_name = { \ @@ -126,6 +130,10 @@ static struct psmouse_attribute psmouse_attr_##_name = { \ .data = _data, \ .show = _show, \ .set = _set, \ + .protect = _protect, \ } +#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \ + __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, 1) + #endif /* _PSMOUSE_H */ diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 26b845fc186..e68c814c436 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -89,10 +89,8 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, struct trackpoint_attr_data *attr = data; unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); unsigned long value; - char *rest; - value = simple_strtoul(buf, &rest, 10); - if (*rest || value > 255) + if (strict_strtoul(buf, 10, &value) || value > 255) return -EINVAL; *field = value; @@ -117,10 +115,8 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, struct trackpoint_attr_data *attr = data; unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset); unsigned long value; - char *rest; - value = simple_strtoul(buf, &rest, 10); - if (*rest || value > 1) + if (strict_strtoul(buf, 10, &value) || value > 1) return -EINVAL; if (attr->inverted) diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 5aafe24984c..a321aea2c7b 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -322,6 +322,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), }, }, + { + .ident = "IBM 2656", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IBM"), + DMI_MATCH(DMI_PRODUCT_NAME, "2656"), + }, + }, { } }; diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index c9397c8ee97..470770c0926 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -373,6 +373,12 @@ static struct serio_device_id serio_raw_serio_ids[] = { .id = SERIO_ANY, .extra = SERIO_ANY, }, + { + .type = SERIO_8042_XL, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, { 0 } }; diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 8f037a1d44a..e53c838f186 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -1202,16 +1202,22 @@ static ssize_t store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); - int x; + long x; + + if (strict_strtol(buf, 10, &x)) { + size_t len = buf[count - 1] == '\n' ? count - 1 : count; + + if (strncmp(buf, "disable", len)) + return -EINVAL; - if (strcmp(buf, "disable") == 0) { aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE; } else { - x = (int)simple_strtol(buf, NULL, 10); - if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX) { - aiptek->newSetting.xTilt = x; - } + if (x < AIPTEK_TILT_MIN || x > AIPTEK_TILT_MAX) + return -EINVAL; + + aiptek->newSetting.xTilt = x; } + return count; } @@ -1238,16 +1244,22 @@ static ssize_t store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); - int y; + long y; + + if (strict_strtol(buf, 10, &y)) { + size_t len = buf[count - 1] == '\n' ? count - 1 : count; + + if (strncmp(buf, "disable", len)) + return -EINVAL; - if (strcmp(buf, "disable") == 0) { aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE; } else { - y = (int)simple_strtol(buf, NULL, 10); - if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX) { - aiptek->newSetting.yTilt = y; - } + if (y < AIPTEK_TILT_MIN || y > AIPTEK_TILT_MAX) + return -EINVAL; + + aiptek->newSetting.yTilt = y; } + return count; } @@ -1269,8 +1281,12 @@ static ssize_t store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + long j; + + if (strict_strtol(buf, 10, &j)) + return -EINVAL; - aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10); + aiptek->newSetting.jitterDelay = (int)j; return count; } @@ -1294,8 +1310,12 @@ static ssize_t store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + long d; - aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10); + if (strict_strtol(buf, 10, &d)) + return -EINVAL; + + aiptek->newSetting.programmableDelay = (int)d; return count; } @@ -1541,8 +1561,11 @@ static ssize_t store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct aiptek *aiptek = dev_get_drvdata(dev); + long w; + + if (strict_strtol(buf, 10, &w)) return -EINVAL; - aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10); + aiptek->newSetting.wheel = (int)w; return count; } diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 8583c766d56..b9b7fc6ff1e 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -69,6 +69,17 @@ struct ts_event { int ignore; }; +/* + * We allocate this separately to avoid cache line sharing issues when + * driver is used with DMA-based SPI controllers (like atmel_spi) on + * systems where main memory is not DMA-coherent (most non-x86 boards). + */ +struct ads7846_packet { + u8 read_x, read_y, read_z1, read_z2, pwrdown; + u16 dummy; /* for the pwrdown read */ + struct ts_event tc; +}; + struct ads7846 { struct input_dev *input; char phys[32]; @@ -86,9 +97,7 @@ struct ads7846 { u16 x_plate_ohms; u16 pressure_max; - u8 read_x, read_y, read_z1, read_z2, pwrdown; - u16 dummy; /* for the pwrdown read */ - struct ts_event tc; + struct ads7846_packet *packet; struct spi_transfer xfer[18]; struct spi_message msg[5]; @@ -463,10 +472,11 @@ static ssize_t ads7846_disable_store(struct device *dev, const char *buf, size_t count) { struct ads7846 *ts = dev_get_drvdata(dev); - char *endp; - int i; + long i; + + if (strict_strtoul(buf, 10, &i)) + return -EINVAL; - i = simple_strtoul(buf, &endp, 10); spin_lock_irq(&ts->lock); if (i) @@ -512,16 +522,17 @@ static int get_pendown_state(struct ads7846 *ts) static void ads7846_rx(void *ads) { struct ads7846 *ts = ads; + struct ads7846_packet *packet = ts->packet; unsigned Rt; u16 x, y, z1, z2; /* ads7846_rx_val() did in-place conversion (including byteswap) from * on-the-wire format as part of debouncing to get stable readings. */ - x = ts->tc.x; - y = ts->tc.y; - z1 = ts->tc.z1; - z2 = ts->tc.z2; + x = packet->tc.x; + y = packet->tc.y; + z1 = packet->tc.z1; + z2 = packet->tc.z2; /* range filtering */ if (x == MAX_12BIT) @@ -545,10 +556,10 @@ static void ads7846_rx(void *ads) * the maximum. Don't report it to user space, repeat at least * once more the measurement */ - if (ts->tc.ignore || Rt > ts->pressure_max) { + if (packet->tc.ignore || Rt > ts->pressure_max) { #ifdef VERBOSE pr_debug("%s: ignored %d pressure %d\n", - ts->spi->dev.bus_id, ts->tc.ignore, Rt); + ts->spi->dev.bus_id, packet->tc.ignore, Rt); #endif hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_MODE_REL); @@ -641,6 +652,7 @@ static int ads7846_no_filter(void *ads, int data_idx, int *val) static void ads7846_rx_val(void *ads) { struct ads7846 *ts = ads; + struct ads7846_packet *packet = ts->packet; struct spi_message *m; struct spi_transfer *t; int val; @@ -660,7 +672,7 @@ static void ads7846_rx_val(void *ads) case ADS7846_FILTER_REPEAT: break; case ADS7846_FILTER_IGNORE: - ts->tc.ignore = 1; + packet->tc.ignore = 1; /* Last message will contain ads7846_rx() as the * completion function. */ @@ -668,7 +680,7 @@ static void ads7846_rx_val(void *ads) break; case ADS7846_FILTER_OK: *(u16 *)t->rx_buf = val; - ts->tc.ignore = 0; + packet->tc.ignore = 0; m = &ts->msg[++ts->msg_idx]; break; default: @@ -773,7 +785,6 @@ static void ads7846_disable(struct ads7846 *ts) /* we know the chip's in lowpower mode since we always * leave it that way after every request */ - } /* Must be called with ts->lock held */ @@ -849,6 +860,7 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) static int __devinit ads7846_probe(struct spi_device *spi) { struct ads7846 *ts; + struct ads7846_packet *packet; struct input_dev *input_dev; struct ads7846_platform_data *pdata = spi->dev.platform_data; struct spi_message *m; @@ -884,14 +896,16 @@ static int __devinit ads7846_probe(struct spi_device *spi) return err; ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); + packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); input_dev = input_allocate_device(); - if (!ts || !input_dev) { + if (!ts || !packet || !input_dev) { err = -ENOMEM; goto err_free_mem; } dev_set_drvdata(&spi->dev, ts); + ts->packet = packet; ts->spi = spi; ts->input = input_dev; ts->vref_mv = pdata->vref_mv; @@ -963,13 +977,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); /* y- still on; turn on only y+ (and ADC) */ - ts->read_y = READ_Y(vref); - x->tx_buf = &ts->read_y; + packet->read_y = READ_Y(vref); + x->tx_buf = &packet->read_y; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.y; + x->rx_buf = &packet->tc.y; x->len = 2; spi_message_add_tail(x, m); @@ -981,12 +995,12 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->delay_usecs = pdata->settle_delay_usecs; x++; - x->tx_buf = &ts->read_y; + x->tx_buf = &packet->read_y; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.y; + x->rx_buf = &packet->tc.y; x->len = 2; spi_message_add_tail(x, m); } @@ -999,13 +1013,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* turn y- off, x+ on, then leave in lowpower */ x++; - ts->read_x = READ_X(vref); - x->tx_buf = &ts->read_x; + packet->read_x = READ_X(vref); + x->tx_buf = &packet->read_x; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.x; + x->rx_buf = &packet->tc.x; x->len = 2; spi_message_add_tail(x, m); @@ -1014,12 +1028,12 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->delay_usecs = pdata->settle_delay_usecs; x++; - x->tx_buf = &ts->read_x; + x->tx_buf = &packet->read_x; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.x; + x->rx_buf = &packet->tc.x; x->len = 2; spi_message_add_tail(x, m); } @@ -1033,13 +1047,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); x++; - ts->read_z1 = READ_Z1(vref); - x->tx_buf = &ts->read_z1; + packet->read_z1 = READ_Z1(vref); + x->tx_buf = &packet->read_z1; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.z1; + x->rx_buf = &packet->tc.z1; x->len = 2; spi_message_add_tail(x, m); @@ -1048,12 +1062,12 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->delay_usecs = pdata->settle_delay_usecs; x++; - x->tx_buf = &ts->read_z1; + x->tx_buf = &packet->read_z1; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.z1; + x->rx_buf = &packet->tc.z1; x->len = 2; spi_message_add_tail(x, m); } @@ -1065,13 +1079,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); x++; - ts->read_z2 = READ_Z2(vref); - x->tx_buf = &ts->read_z2; + packet->read_z2 = READ_Z2(vref); + x->tx_buf = &packet->read_z2; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.z2; + x->rx_buf = &packet->tc.z2; x->len = 2; spi_message_add_tail(x, m); @@ -1080,12 +1094,12 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->delay_usecs = pdata->settle_delay_usecs; x++; - x->tx_buf = &ts->read_z2; + x->tx_buf = &packet->read_z2; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.z2; + x->rx_buf = &packet->tc.z2; x->len = 2; spi_message_add_tail(x, m); } @@ -1099,13 +1113,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); x++; - ts->pwrdown = PWRDOWN; - x->tx_buf = &ts->pwrdown; + packet->pwrdown = PWRDOWN; + x->tx_buf = &packet->pwrdown; x->len = 1; spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->dummy; + x->rx_buf = &packet->dummy; x->len = 2; CS_CHANGE(*x); spi_message_add_tail(x, m); @@ -1158,6 +1172,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->filter_cleanup(ts->filter_data); err_free_mem: input_free_device(input_dev); + kfree(packet); kfree(ts); return err; } @@ -1183,6 +1198,7 @@ static int __devexit ads7846_remove(struct spi_device *spi) if (ts->filter_cleanup) ts->filter_cleanup(ts->filter_data); + kfree(ts->packet); kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index eee126b19e8..a89a6a8f05e 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -91,6 +91,9 @@ struct atmel_tsadcc { char phys[32]; struct clk *clk; int irq; + unsigned int prev_absx; + unsigned int prev_absy; + unsigned char bufferedmeasure; }; static void __iomem *tsc_base; @@ -100,10 +103,9 @@ static void __iomem *tsc_base; static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev) { - struct input_dev *input_dev = ((struct atmel_tsadcc *)dev)->input; + struct atmel_tsadcc *ts_dev = (struct atmel_tsadcc *)dev; + struct input_dev *input_dev = ts_dev->input; - unsigned int absx; - unsigned int absy; unsigned int status; unsigned int reg; @@ -121,6 +123,7 @@ static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev) atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT); input_report_key(input_dev, BTN_TOUCH, 0); + ts_dev->bufferedmeasure = 0; input_sync(input_dev); } else if (status & ATMEL_TSADCC_PENCNT) { @@ -138,16 +141,23 @@ static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev) } else if (status & ATMEL_TSADCC_EOC(3)) { /* Conversion finished */ - absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10; - absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2); - - absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10; - absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0); - - input_report_abs(input_dev, ABS_X, absx); - input_report_abs(input_dev, ABS_Y, absy); - input_report_key(input_dev, BTN_TOUCH, 1); - input_sync(input_dev); + if (ts_dev->bufferedmeasure) { + /* Last measurement is always discarded, since it can + * be erroneous. + * Always report previous measurement */ + input_report_abs(input_dev, ABS_X, ts_dev->prev_absx); + input_report_abs(input_dev, ABS_Y, ts_dev->prev_absy); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } else + ts_dev->bufferedmeasure = 1; + + /* Now make new measurement */ + ts_dev->prev_absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10; + ts_dev->prev_absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2); + + ts_dev->prev_absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10; + ts_dev->prev_absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0); } return IRQ_HANDLED; @@ -223,6 +233,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) } ts_dev->input = input_dev; + ts_dev->bufferedmeasure = 0; snprintf(ts_dev->phys, sizeof(ts_dev->phys), "%s/input0", pdev->dev.bus_id); diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index 37a555f3730..ba648750a8d 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -3,8 +3,7 @@ * Wolfson WM97xx AC97 Codecs. * * Copyright 2004, 2007 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * Parts Copyright : Ian Molton <spyro@f2s.com> * Andrew Zabolotny <zap@homelink.ru> * @@ -296,6 +295,6 @@ module_init(mainstone_wm97xx_init); module_exit(mainstone_wm97xx_exit); /* Module information */ -MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); +MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c index 372efbc694f..6b5be742c27 100644 --- a/drivers/input/touchscreen/wm9705.c +++ b/drivers/input/touchscreen/wm9705.c @@ -2,8 +2,7 @@ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. * * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * Parts Copyright : Ian Molton <spyro@f2s.com> * Andrew Zabolotny <zap@homelink.ru> * Russell King <rmk@arm.linux.org.uk> @@ -347,6 +346,6 @@ struct wm97xx_codec_drv wm9705_codec = { EXPORT_SYMBOL_GPL(wm9705_codec); /* Module information */ -MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); +MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c index c8bb1e7335f..7490b05c356 100644 --- a/drivers/input/touchscreen/wm9712.c +++ b/drivers/input/touchscreen/wm9712.c @@ -2,8 +2,7 @@ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. * * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * Parts Copyright : Ian Molton <spyro@f2s.com> * Andrew Zabolotny <zap@homelink.ru> * Russell King <rmk@arm.linux.org.uk> @@ -462,6 +461,6 @@ struct wm97xx_codec_drv wm9712_codec = { EXPORT_SYMBOL_GPL(wm9712_codec); /* Module information */ -MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); +MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c index 781ee83547e..238b5132712 100644 --- a/drivers/input/touchscreen/wm9713.c +++ b/drivers/input/touchscreen/wm9713.c @@ -2,8 +2,7 @@ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. * * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * Parts Copyright : Ian Molton <spyro@f2s.com> * Andrew Zabolotny <zap@homelink.ru> * Russell King <rmk@arm.linux.org.uk> @@ -476,6 +475,6 @@ struct wm97xx_codec_drv wm9713_codec = { EXPORT_SYMBOL_GPL(wm9713_codec); /* Module information */ -MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); +MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index d589ab0e3ad..d15aa11d705 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -3,8 +3,7 @@ * and WM9713 AC97 Codecs. * * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * Parts Copyright : Ian Molton <spyro@f2s.com> * Andrew Zabolotny <zap@homelink.ru> * Russell King <rmk@arm.linux.org.uk> @@ -824,6 +823,6 @@ module_init(wm97xx_init); module_exit(wm97xx_exit); /* Module information */ -MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); +MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>"); MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); MODULE_LICENSE("GPL"); |