]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge branch 'next' into for-linus
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 16 Oct 2008 03:29:12 +0000 (23:29 -0400)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 16 Oct 2008 03:29:12 +0000 (23:29 -0400)
51 files changed:
Documentation/kernel-parameters.txt
MAINTAINERS
drivers/char/keyboard.c
drivers/input/gameport/gameport.c
drivers/input/joystick/a3d.c
drivers/input/joystick/adi.c
drivers/input/joystick/analog.c
drivers/input/joystick/cobra.c
drivers/input/joystick/gf2k.c
drivers/input/joystick/grip.c
drivers/input/joystick/grip_mp.c
drivers/input/joystick/guillemot.c
drivers/input/joystick/interact.c
drivers/input/joystick/joydump.c
drivers/input/joystick/sidewinder.c
drivers/input/joystick/tmdc.c
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/bf54x-keys.c
drivers/input/keyboard/gpio_keys.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ati_remote2.c
drivers/input/misc/cm109.c [new file with mode: 0644]
drivers/input/misc/wistron_btns.c
drivers/input/misc/yealink.c
drivers/input/mouse/Kconfig
drivers/input/mouse/Makefile
drivers/input/mouse/alps.c
drivers/input/mouse/appletouch.c
drivers/input/mouse/hgpk.c [new file with mode: 0644]
drivers/input/mouse/hgpk.h [new file with mode: 0644]
drivers/input/mouse/logips2pp.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/trackpoint.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/serio/serio_raw.c
drivers/input/tablet/aiptek.c
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/atmel_tsadcc.c
drivers/input/touchscreen/mainstone-wm97xx.c
drivers/input/touchscreen/wm9705.c
drivers/input/touchscreen/wm9712.c
drivers/input/touchscreen/wm9713.c
drivers/input/touchscreen/wm97xx-core.c
include/linux/Kbuild
include/linux/gameport.h
include/linux/input.h
include/linux/map_to_7segment.h [moved from drivers/input/misc/map_to_7segment.h with 98% similarity]
include/linux/mod_devicetable.h
include/linux/spi/ads7846.h

index 1150444a21ab64140f13e706797f16664e28244f..c44bf65b8cd9fd6316c1176cd75ecf0a6466b0b0 100644 (file)
@@ -796,6 +796,7 @@ and is between 256 and 4096 characters. It is defined in the file
                        Defaults to the default architecture's huge page size
                        if not specified.
 
+       i8042.debug     [HW] Toggle i8042 debug mode
        i8042.direct    [HW] Put keyboard port into non-translated mode
        i8042.dumbkbd   [HW] Pretend that controller can only read data from
                             keyboard and cannot control its state
index 8dae4555f10e1b91f0c2eafb84f04fbf71812e55..83de0f0c541f9ac00c58b2ec60acfa29a9f5b54a 100644 (file)
@@ -4548,7 +4548,7 @@ WM97XX TOUCHSCREEN DRIVERS
 P:     Mark Brown
 M:     broonie@opensource.wolfsonmicro.com
 P:     Liam Girdwood
-M:     liam.girdwood@wolfsonmicro.com
+M:     lrg@slimlogic.co.uk
 L:     linux-input@vger.kernel.org
 T:     git git://opensource.wolfsonmicro.com/linux-2.6-touch
 W:     http://opensource.wolfsonmicro.com/node/7
index 7b3a212c86b1ab21d5e3d783716ded2787f3729d..de26a978fbdd140aec88179b99c14fae555c467c 100644 (file)
@@ -1249,7 +1249,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
                return;
        }
 
-       if (keycode > NR_KEYS)
+       if (keycode >= NR_KEYS)
                if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
                        keysym = K(KT_BRL, keycode - KEY_BRL_DOT1 + 1);
                else
index 078e4eed0894ba45a495184bb91b6518935663b9..2880eaae157a90896ce7785cea9b4e2c3c32dd4f 100644 (file)
@@ -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);
 }
 
index 92498d470b1f7de7323eed8b87dfcd8dffa72d7e..6489f4010c4f2f6944090d8829b264bbd067901d 100644 (file)
@@ -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)
index d1ca8a14950f62f914e9d4f9f81ae34a7eabadfe..89c4c084d4ad8e86789b123302cd0350f0af7113 100644 (file)
@@ -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)
index 708c5ae13b24f33cb6b8b323996c2332d84520b7..356b3a25efa24c4568a909c59dedaa7ebcbc040c 100644 (file)
@@ -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)
index 639b975a8ed7a6c64ecb06f315dffa45eb2b5a1a..3497b87c3d0591c12446f16bc232337c3f367981 100644 (file)
@@ -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)
index cb6eef1f2d99f6d7b80a9f5e02d7411ececbd153..67c207f5b1a16cd56e88b140c63bbfdefb8f498b 100644 (file)
@@ -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)
index 684e07cfccc89a862d25897b9e0c760e5dd79965..fc55899ba6c50a8d5d4c8336d100ca51677d440f 100644 (file)
@@ -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)
index 8279481b16e7bbf038b70991dfe1d5ec6f16fff3..2d47baf47769843a98e23d9b47665560ef885e7f 100644 (file)
@@ -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)
index 25ec3fad9f2798d5126cec44e6b570a819152270..4058d4b272fe88fed73a2a3f0183b1d2cbeabd9b 100644 (file)
@@ -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)
index 8c3290b68205b933f76223ab139ed5a71aaeb518..2478289aeeea1b1c7c2cfec1ed8cde3544a1ae16 100644 (file)
@@ -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)
index 2a1b82c8b31c155c8c698741317a200b90898dcd..cd894a0564a2f06932954d7dcec2ff6c179a9d92 100644 (file)
@@ -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)
index 7b4865fdee54cdcf48998cff18dc7bdd4316a9c0..ca13a6bec33ef63fd904eaf3bd7b6853b2194dea 100644 (file)
@@ -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)
index 60c37bcb938d932d78bbd7a1065be2c6e335564f..d6c6098071150e02a456d1688c71fbf44a78cd91 100644 (file)
@@ -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)
index b1ce10f50bcfcf7a14eca2c8c4cd9606efaecd90..22016ca153518b8c9535b11f215c59e1d0008880 100644 (file)
@@ -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",
index e348cfccc17a9549105fcab813d644ef6aa322fd..19284016e0f4d1c2ce47e3f86f4a5f59e04445fc 100644 (file)
@@ -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);
 
index ec96b369dd7ac02ed3bb49dec3bcfe8e9c649ae1..05f3f43582c2c1e40658e2d8e6e77f8717210f38 100644 (file)
@@ -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);
index e99b7882f3826e3d9f4563eb48ece92d8085fb03..199055db5082e8020834e8883e3d502d7319bfb8 100644 (file)
@@ -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
index f48009b52226ba94b69965daf1a9eaa968da5764..d7db2aeb8a982e82fe712ed17c3f663c62a2fc35 100644 (file)
@@ -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
index a7fabafbd94c6e9fe3691db3b18caea49a835b88..3c9988dc0e9fcc98e05a35fc25744cfce84cdb4f 100644 (file)
@@ -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 (file)
index 0000000..bce160f
--- /dev/null
@@ -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");
index fe268be3293b666e82b1e567bb645fb5847a3c5f..7c8957dd22c03d42252e410af97e385c78b2c4b7 100644 (file)
@@ -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} },
@@ -616,6 +626,15 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                },
                .driver_data = keymap_fs_amilo_pro_v2000
        },
+       {
+               .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",
index facefd3dba29a1aab6c4428c6bdef9a2a3aa1b55..11b5c7e84ed139ad9af9fe21fccc04927edd2a31 100644 (file)
@@ -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"
index f996546fc443757151f4f7f09f95c996f4c576d9..f488b6852baf78840c31fccf413ca63445965ac2 100644 (file)
@@ -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
index d4d202516090be6075afd735663ded497c7b6b0d..8e6e690978016b0dfae3f89276e05273764665b5 100644 (file)
@@ -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
index 385e32bcf6a6e0ed7328f8c7ffd39badb7e4d45e..cbedf957cc58a380b71dad024b58710f5b2b0912 100644 (file)
@@ -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 */
 };
 
index 1f41ae94f26bdf216a5f667e754b3c559831e7ed..079816e6b23b042bff9a883aa984ffea757ee5a6 100644 (file)
@@ -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 (file)
index 0000000..e82d342
--- /dev/null
@@ -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 (file)
index 0000000..a4b2a96
--- /dev/null
@@ -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
index 0c5660d28caa18bafb5d3ab62a9219f3fc1c96ec..390f1dbb98a482d855154119fa45ff52f3ea6bca 100644 (file)
@@ -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);
index f5a6be1d3c46325b2173ff00e2588a9de9ca5d81..126e977e199e00764d3f2ab32aafbd0e26fbe7e8 100644 (file)
@@ -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;
 
@@ -761,6 +780,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .alias          = "touchkit",
                .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,
@@ -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);
index 1317bdd8cc7c42c935fac9899c5bdcbda0a5a51b..8b608a1cdd12171135f3ca2ec0c7a6487728711a 100644 (file)
@@ -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 */
index 26b845fc186ab7557635d6eafe59c7970c3507cc..e68c814c43612f449d4b9976a1cc45fbb7445efd 100644 (file)
@@ -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)
index 5aafe24984c598236efdd54dde31e8db9ec6cc60..a321aea2c7b5f4dad1a9822539d2b3abb7700dfc 100644 (file)
@@ -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"),
+               },
+       },
        { }
 };
 
index c9397c8ee97e8ea44c4f0edc4a6172bd022178ac..470770c09260334fd79c664c2a484ed0384a9f93 100644 (file)
@@ -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 }
 };
 
index 8f037a1d44a63be7586b0ac5f6309522437b290b..e53c838f1866cb69ddc33d2b9def78430777529e 100644 (file)
@@ -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;
 }
 
index ce6f48c695f553a703a9c9af3c739996bc2c971c..b9b7fc6ff1ebcc657370701e9dcc53d3ce20bfdb 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 #include <asm/irq.h>
@@ -68,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];
@@ -85,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];
@@ -116,6 +126,7 @@ struct ads7846 {
        void                    *filter_data;
        void                    (*filter_cleanup)(void *data);
        int                     (*get_pendown_state)(void);
+       int                     gpio_pendown;
 };
 
 /* leave chip selected when we're done, for quicker re-select? */
@@ -461,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)
@@ -491,6 +503,14 @@ static struct attribute_group ads784x_attr_group = {
 
 /*--------------------------------------------------------------------------*/
 
+static int get_pendown_state(struct ads7846 *ts)
+{
+       if (ts->get_pendown_state)
+               return ts->get_pendown_state();
+
+       return !gpio_get_value(ts->gpio_pendown);
+}
+
 /*
  * PENIRQ only kicks the timer.  The timer only reissues the SPI transfer,
  * to retrieve touchscreen status.
@@ -502,16 +522,17 @@ static struct attribute_group ads784x_attr_group = {
 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)
@@ -535,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);
@@ -550,7 +571,7 @@ static void ads7846_rx(void *ads)
         */
        if (ts->penirq_recheck_delay_usecs) {
                udelay(ts->penirq_recheck_delay_usecs);
-               if (!ts->get_pendown_state())
+               if (!get_pendown_state(ts))
                        Rt = 0;
        }
 
@@ -631,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;
@@ -650,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.
                 */
@@ -658,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:
@@ -677,7 +699,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
 
        spin_lock_irq(&ts->lock);
 
-       if (unlikely(!ts->get_pendown_state() ||
+       if (unlikely(!get_pendown_state(ts) ||
                     device_suspended(&ts->spi->dev))) {
                if (ts->pendown) {
                        struct input_dev *input = ts->input;
@@ -716,7 +738,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
        unsigned long flags;
 
        spin_lock_irqsave(&ts->lock, flags);
-       if (likely(ts->get_pendown_state())) {
+       if (likely(get_pendown_state(ts))) {
                if (!ts->irq_disabled) {
                        /* The ARM do_simple_IRQ() dispatcher doesn't act
                         * like the other dispatchers:  it will report IRQs
@@ -763,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 */
@@ -806,9 +827,40 @@ static int ads7846_resume(struct spi_device *spi)
        return 0;
 }
 
+static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
+{
+       struct ads7846_platform_data *pdata = spi->dev.platform_data;
+       int err;
+
+       /* REVISIT when the irq can be triggered active-low, or if for some
+        * reason the touchscreen isn't hooked up, we don't need to access
+        * the pendown state.
+        */
+       if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) {
+               dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
+               return -EINVAL;
+       }
+
+       if (pdata->get_pendown_state) {
+               ts->get_pendown_state = pdata->get_pendown_state;
+               return 0;
+       }
+
+       err = gpio_request(pdata->gpio_pendown, "ads7846_pendown");
+       if (err) {
+               dev_err(&spi->dev, "failed to request pendown GPIO%d\n",
+                               pdata->gpio_pendown);
+               return err;
+       }
+
+       ts->gpio_pendown = pdata->gpio_pendown;
+       return 0;
+}
+
 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;
@@ -833,15 +885,6 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                return -EINVAL;
        }
 
-       /* REVISIT when the irq can be triggered active-low, or if for some
-        * reason the touchscreen isn't hooked up, we don't need to access
-        * the pendown state.
-        */
-       if (pdata->get_pendown_state == NULL) {
-               dev_dbg(&spi->dev, "no get_pendown_state function?\n");
-               return -EINVAL;
-       }
-
        /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except
         * that even if the hardware can do that, the SPI controller driver
         * may not.  So we stick to very-portable 8 bit words, both RX and TX.
@@ -853,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;
@@ -893,7 +938,10 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                ts->filter_data = ts;
        } else
                ts->filter = ads7846_no_filter;
-       ts->get_pendown_state = pdata->get_pendown_state;
+
+       err = setup_pendown(spi, ts);
+       if (err)
+               goto err_cleanup_filter;
 
        if (pdata->penirq_recheck_delay_usecs)
                ts->penirq_recheck_delay_usecs =
@@ -929,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);
 
@@ -947,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);
        }
@@ -965,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);
 
@@ -980,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);
        }
@@ -999,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);
 
@@ -1014,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);
                }
@@ -1031,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);
 
@@ -1046,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);
                }
@@ -1065,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);
@@ -1085,7 +1133,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                        spi->dev.driver->name, ts)) {
                dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
                err = -EBUSY;
-               goto err_cleanup_filter;
+               goto err_free_gpio;
        }
 
        err = ads784x_hwmon_register(spi, ts);
@@ -1116,11 +1164,15 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        ads784x_hwmon_unregister(spi, ts);
  err_free_irq:
        free_irq(spi->irq, ts);
+ err_free_gpio:
+       if (ts->gpio_pendown != -1)
+               gpio_free(ts->gpio_pendown);
  err_cleanup_filter:
        if (ts->filter_cleanup)
                ts->filter_cleanup(ts->filter_data);
  err_free_mem:
        input_free_device(input_dev);
+       kfree(packet);
        kfree(ts);
        return err;
 }
@@ -1140,9 +1192,13 @@ static int __devexit ads7846_remove(struct spi_device *spi)
        /* suspend left the IRQ disabled */
        enable_irq(ts->spi->irq);
 
+       if (ts->gpio_pendown != -1)
+               gpio_free(ts->gpio_pendown);
+
        if (ts->filter_cleanup)
                ts->filter_cleanup(ts->filter_data);
 
+       kfree(ts->packet);
        kfree(ts);
 
        dev_dbg(&spi->dev, "unregistered touchscreen\n");
index eee126b19e8b53cc3a8949a3798df9cd13c6d402..a89a6a8f05e6c9631f7368fb2488e080490b6fe0 100644 (file)
@@ -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);
index 37a555f37306226ff0dae5495bfa45c733d297a2..ba648750a8d927db4a4cade7e2df61cca5348059 100644 (file)
@@ -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");
index 372efbc694ffbf7ebe71e96decefd71060e6d7c8..6b5be742c27dac15fd5b11734b2759638b8046f0 100644 (file)
@@ -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");
index c8bb1e7335fcf8f9450c0be194cafc9f014d03fb..7490b05c356651cf8aa6e2d0e1ee1b1621096cd6 100644 (file)
@@ -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");
index 781ee83547e68374eba1f253a016a26435135e27..238b5132712eef7b4986636b16ce78c070df15b0 100644 (file)
@@ -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");
index d589ab0e3adc41c0b75eb55c645937afdcdef843..d15aa11d705686adb89203e396577752d44c9cf3 100644 (file)
@@ -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");
index b68ec09399be562110b92b0903a26ca058875e0e..6e1755f2ae03a07d4a170ec0c49c7ed8ae9bc4ab 100644 (file)
@@ -107,6 +107,7 @@ header-y += keyctl.h
 header-y += limits.h
 header-y += magic.h
 header-y += major.h
+header-y += map_to_7segment.h
 header-y += matroxfb.h
 header-y += meye.h
 header-y += minix_fs.h
index f64e29c0ef3f57be1b03aeb0703fe30f58ffbbeb..0cd825f7363a7aa61005b53eb658b7e3dfa66b3d 100644 (file)
@@ -146,10 +146,11 @@ static inline void gameport_unpin_driver(struct gameport *gameport)
        mutex_unlock(&gameport->drv_mutex);
 }
 
-void __gameport_register_driver(struct gameport_driver *drv, struct module *owner);
-static inline void gameport_register_driver(struct gameport_driver *drv)
+int __gameport_register_driver(struct gameport_driver *drv,
+                               struct module *owner, const char *mod_name);
+static inline int __must_check gameport_register_driver(struct gameport_driver *drv)
 {
-       __gameport_register_driver(drv, THIS_MODULE);
+       return __gameport_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);
 }
 
 void gameport_unregister_driver(struct gameport_driver *drv);
index a5802c9c81a47831996a93cffc00fcb7190d4089..b86fb5581ce602f5978ff2a06ddc05eb67f19a07 100644 (file)
@@ -577,9 +577,22 @@ struct input_absinfo {
 #define KEY_BRL_DOT9           0x1f9
 #define KEY_BRL_DOT10          0x1fa
 
+#define KEY_NUMERIC_0          0x200   /* used by phones, remote controls, */
+#define KEY_NUMERIC_1          0x201   /* and other keypads */
+#define KEY_NUMERIC_2          0x202
+#define KEY_NUMERIC_3          0x203
+#define KEY_NUMERIC_4          0x204
+#define KEY_NUMERIC_5          0x205
+#define KEY_NUMERIC_6          0x206
+#define KEY_NUMERIC_7          0x207
+#define KEY_NUMERIC_8          0x208
+#define KEY_NUMERIC_9          0x209
+#define KEY_NUMERIC_STAR       0x20a
+#define KEY_NUMERIC_POUND      0x20b
+
 /* We avoid low common keys in module aliases so they don't get huge. */
 #define KEY_MIN_INTERESTING    KEY_MUTE
-#define KEY_MAX                        0x1ff
+#define KEY_MAX                        0x2ff
 #define KEY_CNT                        (KEY_MAX+1)
 
 /*
similarity index 98%
rename from drivers/input/misc/map_to_7segment.h
rename to include/linux/map_to_7segment.h
index a424094d9fe21b836b5b13216147ac09fb690811..7df8432c4402b576b9f1d5b116aea47a6182daef 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * 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
@@ -36,7 +34,7 @@
  * Usage:
  *
  *   Register a map variable, and fill it with a character set:
- *     static SEG7_DEFAULT_MAP(map_seg7);
+ *     static SEG7_DEFAULT_MAP(map_seg7);
  *
  *
  *   Then use for conversion:
index c4db5827963d98a5624ec1c8fb637ad959903a75..0dddfa44ec19e5e06f1b3c38ed3e19a85661e17e 100644 (file)
@@ -274,7 +274,7 @@ struct pcmcia_device_id {
 /* Input */
 #define INPUT_DEVICE_ID_EV_MAX         0x1f
 #define INPUT_DEVICE_ID_KEY_MIN_INTERESTING    0x71
-#define INPUT_DEVICE_ID_KEY_MAX                0x1ff
+#define INPUT_DEVICE_ID_KEY_MAX                0x2ff
 #define INPUT_DEVICE_ID_REL_MAX                0x0f
 #define INPUT_DEVICE_ID_ABS_MAX                0x3f
 #define INPUT_DEVICE_ID_MSC_MAX                0x07
index daf744017a31c86bfaacf0a98ad1ebfdd27a4595..05eab2f11e6323b9817e24a53b9f2c7180184106 100644 (file)
@@ -43,6 +43,9 @@ struct ads7846_platform_data {
        u16     debounce_tol;           /* tolerance used for filtering */
        u16     debounce_rep;           /* additional consecutive good readings
                                         * required after the first two */
+       int     gpio_pendown;           /* the GPIO used to decide the pendown
+                                        * state if get_pendown_state == NULL
+                                        */
        int     (*get_pendown_state)(void);
        int     (*filter_init)  (struct ads7846_platform_data *pdata,
                                 void **filter_data);