]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 30 Mar 2012 06:17:44 +0000 (23:17 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 30 Mar 2012 06:17:44 +0000 (23:17 -0700)
Pull 2nd round of input updates from Dmitry Torokhov:
 - update to Wacom driver to support wireless devices
 - update to Sentelci touchpad driver to support newer hardware
 - update to gpio-keys driver to support "interrupt-only" keys
 - fixups to earlier commits

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: wacom - check for allocation failure in probe()
  Input: tegra-kbc - allocate pdata before using it
  Input: amijoy - add missing platform check
  Input: wacom - wireless battery status
  Input: wacom - create inputs when wireless connect
  Input: wacom - wireless monitor framework
  Input: wacom - isolate input registration
  Input: sentelic - improve packet debugging information
  Input: sentelic - minor code cleanup
  Input: sentelic - enabling absolute coordinates output for newer hardware
  Input: sentelic - refactor code for upcoming new hardware support
  Input: gpio_keys - add support for interrupt only keys
  Input: gpio_keys - consolidate key destructor code
  Input: revert "gpio_keys - switch to using threaded IRQs"
  Input: gpio_keys - constify platform data
  Input: spear-keyboard - remove kbd_set_plat_data()

12 files changed:
arch/arm/plat-spear/include/plat/keyboard.h
drivers/input/joystick/amijoy.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/tegra-kbc.c
drivers/input/mouse/sentelic.c
drivers/input/mouse/sentelic.h
drivers/input/tablet/Kconfig
drivers/input/tablet/wacom.h
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h
include/linux/gpio_keys.h

index c16cc31ecbed6fcb1c28e3a1b2e6f51e16df9bca..0562f134621df7c2320987cabdc3529c35e56c71 100644 (file)
@@ -159,11 +159,4 @@ struct kbd_platform_data {
        unsigned int mode;
 };
 
-/* This function is used to set platform data field of pdev->dev */
-static inline void
-kbd_set_plat_data(struct platform_device *pdev, struct kbd_platform_data *data)
-{
-       pdev->dev.platform_data = data;
-}
-
 #endif /* __PLAT_KEYBOARD_H */
index 24044dacbf70086a90c6191dc9e310a0608b84ca..c65b5fa69f1eb9b5d7fa75356d2dd9d91abc8cb4 100644 (file)
@@ -107,6 +107,9 @@ static int __init amijoy_init(void)
        int i, j;
        int err;
 
+       if (!MACH_IS_AMIGA)
+               return -ENODEV;
+
        for (i = 0; i < 2; i++) {
                if (!amijoy[i])
                        continue;
index ed1ed469d0850028118683c4bf66df11ef03effc..62bfce468f9f0a5dd8c5aa79fd1e6872923ffe0d 100644 (file)
 #include <linux/gpio.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
+#include <linux/spinlock.h>
 
 struct gpio_button_data {
-       struct gpio_keys_button *button;
+       const struct gpio_keys_button *button;
        struct input_dev *input;
        struct timer_list timer;
        struct work_struct work;
-       int timer_debounce;     /* in msecs */
+       unsigned int timer_debounce;    /* in msecs */
+       unsigned int irq;
+       spinlock_t lock;
        bool disabled;
+       bool key_pressed;
 };
 
 struct gpio_keys_drvdata {
@@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
                /*
                 * Disable IRQ and possible debouncing timer.
                 */
-               disable_irq(gpio_to_irq(bdata->button->gpio));
+               disable_irq(bdata->irq);
                if (bdata->timer_debounce)
                        del_timer_sync(&bdata->timer);
 
@@ -135,7 +139,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
 static void gpio_keys_enable_button(struct gpio_button_data *bdata)
 {
        if (bdata->disabled) {
-               enable_irq(gpio_to_irq(bdata->button->gpio));
+               enable_irq(bdata->irq);
                bdata->disabled = false;
        }
 }
@@ -195,7 +199,7 @@ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
  * @type: button type (%EV_KEY, %EV_SW)
  *
  * This function parses stringified bitmap from @buf and disables/enables
- * GPIO buttons accordinly. Returns 0 on success and negative error
+ * GPIO buttons accordingly. Returns 0 on success and negative error
  * on failure.
  */
 static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
@@ -320,9 +324,9 @@ static struct attribute_group gpio_keys_attr_group = {
        .attrs = gpio_keys_attrs,
 };
 
-static void gpio_keys_report_event(struct gpio_button_data *bdata)
+static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
 {
-       struct gpio_keys_button *button = bdata->button;
+       const 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_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
@@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
        input_sync(input);
 }
 
-static void gpio_keys_work_func(struct work_struct *work)
+static void gpio_keys_gpio_work_func(struct work_struct *work)
 {
        struct gpio_button_data *bdata =
                container_of(work, struct gpio_button_data, work);
 
-       gpio_keys_report_event(bdata);
+       gpio_keys_gpio_report_event(bdata);
 }
 
-static void gpio_keys_timer(unsigned long _data)
+static void gpio_keys_gpio_timer(unsigned long _data)
 {
-       struct gpio_button_data *data = (struct gpio_button_data *)_data;
+       struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
 
-       schedule_work(&data->work);
+       schedule_work(&bdata->work);
 }
 
-static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
+static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
 {
        struct gpio_button_data *bdata = dev_id;
-       struct gpio_keys_button *button = bdata->button;
 
-       BUG_ON(irq != gpio_to_irq(button->gpio));
+       BUG_ON(irq != bdata->irq);
 
        if (bdata->timer_debounce)
                mod_timer(&bdata->timer,
@@ -367,50 +370,133 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void gpio_keys_irq_timer(unsigned long _data)
+{
+       struct gpio_button_data *bdata = (struct gpio_button_data *)_data;
+       struct input_dev *input = bdata->input;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bdata->lock, flags);
+       if (bdata->key_pressed) {
+               input_event(input, EV_KEY, bdata->button->code, 0);
+               input_sync(input);
+               bdata->key_pressed = false;
+       }
+       spin_unlock_irqrestore(&bdata->lock, flags);
+}
+
+static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
+{
+       struct gpio_button_data *bdata = dev_id;
+       const struct gpio_keys_button *button = bdata->button;
+       struct input_dev *input = bdata->input;
+       unsigned long flags;
+
+       BUG_ON(irq != bdata->irq);
+
+       spin_lock_irqsave(&bdata->lock, flags);
+
+       if (!bdata->key_pressed) {
+               input_event(input, EV_KEY, button->code, 1);
+               input_sync(input);
+
+               if (!bdata->timer_debounce) {
+                       input_event(input, EV_KEY, button->code, 0);
+                       input_sync(input);
+                       goto out;
+               }
+
+               bdata->key_pressed = true;
+       }
+
+       if (bdata->timer_debounce)
+               mod_timer(&bdata->timer,
+                       jiffies + msecs_to_jiffies(bdata->timer_debounce));
+out:
+       spin_unlock_irqrestore(&bdata->lock, flags);
+       return IRQ_HANDLED;
+}
+
 static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
+                                        struct input_dev *input,
                                         struct gpio_button_data *bdata,
-                                        struct gpio_keys_button *button)
+                                        const struct gpio_keys_button *button)
 {
        const char *desc = button->desc ? button->desc : "gpio_keys";
        struct device *dev = &pdev->dev;
+       irq_handler_t isr;
        unsigned long irqflags;
        int irq, error;
 
-       setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
-       INIT_WORK(&bdata->work, gpio_keys_work_func);
+       bdata->input = input;
+       bdata->button = button;
+       spin_lock_init(&bdata->lock);
 
-       error = gpio_request(button->gpio, desc);
-       if (error < 0) {
-               dev_err(dev, "failed to request GPIO %d, error %d\n",
-                       button->gpio, error);
-               goto fail2;
-       }
+       if (gpio_is_valid(button->gpio)) {
 
-       error = gpio_direction_input(button->gpio);
-       if (error < 0) {
-               dev_err(dev, "failed to configure"
-                       " direction for GPIO %d, error %d\n",
-                       button->gpio, error);
-               goto fail3;
-       }
+               error = gpio_request(button->gpio, desc);
+               if (error < 0) {
+                       dev_err(dev, "Failed to request GPIO %d, error %d\n",
+                               button->gpio, error);
+                       return error;
+               }
 
-       if (button->debounce_interval) {
-               error = gpio_set_debounce(button->gpio,
-                                         button->debounce_interval * 1000);
-               /* use timer if gpiolib doesn't provide debounce */
-               if (error < 0)
-                       bdata->timer_debounce = button->debounce_interval;
-       }
+               error = gpio_direction_input(button->gpio);
+               if (error < 0) {
+                       dev_err(dev,
+                               "Failed to configure direction for GPIO %d, error %d\n",
+                               button->gpio, error);
+                       goto fail;
+               }
 
-       irq = gpio_to_irq(button->gpio);
-       if (irq < 0) {
-               error = irq;
-               dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
-                       button->gpio, error);
-               goto fail3;
+               if (button->debounce_interval) {
+                       error = gpio_set_debounce(button->gpio,
+                                       button->debounce_interval * 1000);
+                       /* use timer if gpiolib doesn't provide debounce */
+                       if (error < 0)
+                               bdata->timer_debounce =
+                                               button->debounce_interval;
+               }
+
+               irq = gpio_to_irq(button->gpio);
+               if (irq < 0) {
+                       error = irq;
+                       dev_err(dev,
+                               "Unable to get irq number for GPIO %d, error %d\n",
+                               button->gpio, error);
+                       goto fail;
+               }
+               bdata->irq = irq;
+
+               INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
+               setup_timer(&bdata->timer,
+                           gpio_keys_gpio_timer, (unsigned long)bdata);
+
+               isr = gpio_keys_gpio_isr;
+               irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+       } else {
+               if (!button->irq) {
+                       dev_err(dev, "No IRQ specified\n");
+                       return -EINVAL;
+               }
+               bdata->irq = button->irq;
+
+               if (button->type && button->type != EV_KEY) {
+                       dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
+                       return -EINVAL;
+               }
+
+               bdata->timer_debounce = button->debounce_interval;
+               setup_timer(&bdata->timer,
+                           gpio_keys_irq_timer, (unsigned long)bdata);
+
+               isr = gpio_keys_irq_isr;
+               irqflags = 0;
        }
 
-       irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+       input_set_capability(input, button->type ?: EV_KEY, button->code);
+
        /*
         * If platform has specified that the button can be disabled,
         * we don't want it to share the interrupt line.
@@ -418,18 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
        if (!button->can_disable)
                irqflags |= IRQF_SHARED;
 
-       error = request_threaded_irq(irq, NULL, gpio_keys_isr, irqflags, desc, bdata);
+       error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata);
        if (error < 0) {
                dev_err(dev, "Unable to claim irq %d; error %d\n",
-                       irq, error);
-               goto fail3;
+                       bdata->irq, error);
+               goto fail;
        }
 
        return 0;
 
-fail3:
-       gpio_free(button->gpio);
-fail2:
+fail:
+       if (gpio_is_valid(button->gpio))
+               gpio_free(button->gpio);
+
        return error;
 }
 
@@ -547,9 +634,19 @@ static int gpio_keys_get_devtree_pdata(struct device *dev,
 
 #endif
 
+static void gpio_remove_key(struct gpio_button_data *bdata)
+{
+       free_irq(bdata->irq, bdata);
+       if (bdata->timer_debounce)
+               del_timer_sync(&bdata->timer);
+       cancel_work_sync(&bdata->work);
+       if (gpio_is_valid(bdata->button->gpio))
+               gpio_free(bdata->button->gpio);
+}
+
 static int __devinit gpio_keys_probe(struct platform_device *pdev)
 {
-       struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+       const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
        struct gpio_keys_drvdata *ddata;
        struct device *dev = &pdev->dev;
        struct gpio_keys_platform_data alt_pdata;
@@ -599,21 +696,15 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
                __set_bit(EV_REP, input->evbit);
 
        for (i = 0; i < pdata->nbuttons; i++) {
-               struct gpio_keys_button *button = &pdata->buttons[i];
+               const struct gpio_keys_button *button = &pdata->buttons[i];
                struct gpio_button_data *bdata = &ddata->data[i];
-               unsigned int type = button->type ?: EV_KEY;
-
-               bdata->input = input;
-               bdata->button = button;
 
-               error = gpio_keys_setup_key(pdev, bdata, button);
+               error = gpio_keys_setup_key(pdev, input, bdata, button);
                if (error)
                        goto fail2;
 
                if (button->wakeup)
                        wakeup = 1;
-
-               input_set_capability(input, type, button->code);
        }
 
        error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
@@ -630,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
                goto fail3;
        }
 
-       /* get current state of buttons */
-       for (i = 0; i < pdata->nbuttons; i++)
-               gpio_keys_report_event(&ddata->data[i]);
+       /* get current state of buttons that are connected to GPIOs */
+       for (i = 0; i < pdata->nbuttons; i++) {
+               struct gpio_button_data *bdata = &ddata->data[i];
+               if (gpio_is_valid(bdata->button->gpio))
+                       gpio_keys_gpio_report_event(bdata);
+       }
        input_sync(input);
 
        device_init_wakeup(&pdev->dev, wakeup);
@@ -642,13 +736,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
  fail3:
        sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
  fail2:
-       while (--i >= 0) {
-               free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
-               if (ddata->data[i].timer_debounce)
-                       del_timer_sync(&ddata->data[i].timer);
-               cancel_work_sync(&ddata->data[i].work);
-               gpio_free(pdata->buttons[i].gpio);
-       }
+       while (--i >= 0)
+               gpio_remove_key(&ddata->data[i]);
 
        platform_set_drvdata(pdev, NULL);
  fail1:
@@ -671,14 +760,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
 
        device_init_wakeup(&pdev->dev, 0);
 
-       for (i = 0; i < ddata->n_buttons; i++) {
-               int irq = gpio_to_irq(ddata->data[i].button->gpio);
-               free_irq(irq, &ddata->data[i]);
-               if (ddata->data[i].timer_debounce)
-                       del_timer_sync(&ddata->data[i].timer);
-               cancel_work_sync(&ddata->data[i].work);
-               gpio_free(ddata->data[i].button->gpio);
-       }
+       for (i = 0; i < ddata->n_buttons; i++)
+               gpio_remove_key(&ddata->data[i]);
 
        input_unregister_device(input);
 
@@ -703,11 +786,9 @@ static int gpio_keys_suspend(struct device *dev)
 
        if (device_may_wakeup(dev)) {
                for (i = 0; i < ddata->n_buttons; i++) {
-                       struct gpio_keys_button *button = ddata->data[i].button;
-                       if (button->wakeup) {
-                               int irq = gpio_to_irq(button->gpio);
-                               enable_irq_wake(irq);
-                       }
+                       struct gpio_button_data *bdata = &ddata->data[i];
+                       if (bdata->button->wakeup)
+                               enable_irq_wake(bdata->irq);
                }
        }
 
@@ -720,14 +801,12 @@ static int gpio_keys_resume(struct device *dev)
        int i;
 
        for (i = 0; i < ddata->n_buttons; i++) {
+               struct gpio_button_data *bdata = &ddata->data[i];
+               if (bdata->button->wakeup && device_may_wakeup(dev))
+                       disable_irq_wake(bdata->irq);
 
-               struct gpio_keys_button *button = ddata->data[i].button;
-               if (button->wakeup && device_may_wakeup(dev)) {
-                       int irq = gpio_to_irq(button->gpio);
-                       disable_irq_wake(irq);
-               }
-
-               gpio_keys_report_event(&ddata->data[i]);
+               if (gpio_is_valid(bdata->button->gpio))
+                       gpio_keys_gpio_report_event(bdata);
        }
        input_sync(ddata->input);
 
index 21c42f852343e4fbfe2905d9dc5b75b96b9670db..fe4ac95ca6c8d340b23ac20909c8407c14637c98 100644 (file)
@@ -630,6 +630,7 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev)
        if (!np)
                return NULL;
 
+       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return NULL;
 
index 2a77a52d2e62a53e53f71c4a46c9db3726c1f484..a977bfaa6821faef1c2993c919f77a854b9a03d8 100644 (file)
@@ -2,7 +2,7 @@
  * Finger Sensing Pad PS/2 mouse driver.
  *
  * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
- * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
  *
  *   This program is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU General Public License
@@ -21,6 +21,7 @@
 
 #include <linux/module.h>
 #include <linux/input.h>
+#include <linux/input/mt.h>
 #include <linux/ctype.h>
 #include <linux/libps2.h>
 #include <linux/serio.h>
@@ -36,6 +37,9 @@
 #define        FSP_CMD_TIMEOUT         200
 #define        FSP_CMD_TIMEOUT2        30
 
+#define        GET_ABS_X(packet)       ((packet[1] << 2) | ((packet[3] >> 2) & 0x03))
+#define        GET_ABS_Y(packet)       ((packet[2] << 2) | (packet[3] & 0x03))
+
 /** Driver version. */
 static const char fsp_drv_ver[] = "1.0.0-K";
 
@@ -128,8 +132,9 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
  out:
        ps2_end_command(ps2dev);
        psmouse_activate(psmouse);
-       dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
-               reg_addr, *reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
+                   reg_addr, *reg_val, rc);
        return rc;
 }
 
@@ -179,8 +184,9 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
 
  out:
        ps2_end_command(ps2dev);
-       dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
-               reg_addr, reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
+                   reg_addr, reg_val, rc);
        return rc;
 }
 
@@ -237,8 +243,9 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
  out:
        ps2_end_command(ps2dev);
        psmouse_activate(psmouse);
-       dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
-               *reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "READ PAGE REG: 0x%02x (rc = %d)\n",
+                   *reg_val, rc);
        return rc;
 }
 
@@ -274,8 +281,9 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
 
  out:
        ps2_end_command(ps2dev);
-       dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
-               reg_val, rc);
+       psmouse_dbg(psmouse,
+                   "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
+                   reg_val, rc);
        return rc;
 }
 
@@ -319,7 +327,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
        int res = 0;
 
        if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {
-               dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n");
+               psmouse_err(psmouse, "Unable get OPC state.\n");
                return -EIO;
        }
 
@@ -336,8 +344,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
        }
 
        if (res != 0) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to enable OPC tag.\n");
+               psmouse_err(psmouse, "Unable to enable OPC tag.\n");
                res = -EIO;
        }
 
@@ -615,18 +622,40 @@ static struct attribute_group fsp_attribute_group = {
        .attrs = fsp_attributes,
 };
 
-#ifdef FSP_DEBUG
-static void fsp_packet_debug(unsigned char packet[])
+#ifdef FSP_DEBUG
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
 {
        static unsigned int ps2_packet_cnt;
        static unsigned int ps2_last_second;
        unsigned int jiffies_msec;
+       const char *packet_type = "UNKNOWN";
+       unsigned short abs_x = 0, abs_y = 0;
+
+       /* Interpret & dump the packet data. */
+       switch (packet[0] >> FSP_PKT_TYPE_SHIFT) {
+       case FSP_PKT_TYPE_ABS:
+               packet_type = "Absolute";
+               abs_x = GET_ABS_X(packet);
+               abs_y = GET_ABS_Y(packet);
+               break;
+       case FSP_PKT_TYPE_NORMAL:
+               packet_type = "Normal";
+               break;
+       case FSP_PKT_TYPE_NOTIFY:
+               packet_type = "Notify";
+               break;
+       case FSP_PKT_TYPE_NORMAL_OPC:
+               packet_type = "Normal-OPC";
+               break;
+       }
 
        ps2_packet_cnt++;
        jiffies_msec = jiffies_to_msecs(jiffies);
        psmouse_dbg(psmouse,
-                   "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n",
-                   jiffies_msec, packet[0], packet[1], packet[2], packet[3]);
+                   "%08dms %s packets: %02x, %02x, %02x, %02x; "
+                   "abs_x: %d, abs_y: %d\n",
+                   jiffies_msec, packet_type,
+                   packet[0], packet[1], packet[2], packet[3], abs_x, abs_y);
 
        if (jiffies_msec - ps2_last_second > 1000) {
                psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt);
@@ -635,17 +664,29 @@ static void fsp_packet_debug(unsigned char packet[])
        }
 }
 #else
-static void fsp_packet_debug(unsigned char packet[])
+static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
 {
 }
 #endif
 
+static void fsp_set_slot(struct input_dev *dev, int slot, bool active,
+                        unsigned int x, unsigned int y)
+{
+       input_mt_slot(dev, slot);
+       input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+       if (active) {
+               input_report_abs(dev, ABS_MT_POSITION_X, x);
+               input_report_abs(dev, ABS_MT_POSITION_Y, y);
+       }
+}
+
 static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
        struct fsp_data *ad = psmouse->private;
        unsigned char *packet = psmouse->packet;
        unsigned char button_status = 0, lscroll = 0, rscroll = 0;
+       unsigned short abs_x, abs_y, fgrs = 0;
        int rel_x, rel_y;
 
        if (psmouse->pktcnt < 4)
@@ -655,16 +696,76 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
         * Full packet accumulated, process it
         */
 
+       fsp_packet_debug(psmouse, packet);
+
        switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
        case FSP_PKT_TYPE_ABS:
-               dev_warn(&psmouse->ps2dev.serio->dev,
-                        "Unexpected absolute mode packet, ignored.\n");
+               abs_x = GET_ABS_X(packet);
+               abs_y = GET_ABS_Y(packet);
+
+               if (packet[0] & FSP_PB0_MFMC) {
+                       /*
+                        * MFMC packet: assume that there are two fingers on
+                        * pad
+                        */
+                       fgrs = 2;
+
+                       /* MFMC packet */
+                       if (packet[0] & FSP_PB0_MFMC_FGR2) {
+                               /* 2nd finger */
+                               if (ad->last_mt_fgr == 2) {
+                                       /*
+                                        * workaround for buggy firmware
+                                        * which doesn't clear MFMC bit if
+                                        * the 1st finger is up
+                                        */
+                                       fgrs = 1;
+                                       fsp_set_slot(dev, 0, false, 0, 0);
+                               }
+                               ad->last_mt_fgr = 2;
+
+                               fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y);
+                       } else {
+                               /* 1st finger */
+                               if (ad->last_mt_fgr == 1) {
+                                       /*
+                                        * workaround for buggy firmware
+                                        * which doesn't clear MFMC bit if
+                                        * the 2nd finger is up
+                                        */
+                                       fgrs = 1;
+                                       fsp_set_slot(dev, 1, false, 0, 0);
+                               }
+                               ad->last_mt_fgr = 1;
+                               fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y);
+                       }
+               } else {
+                       /* SFAC packet */
+
+                       /* no multi-finger information */
+                       ad->last_mt_fgr = 0;
+
+                       if (abs_x != 0 && abs_y != 0)
+                               fgrs = 1;
+
+                       fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y);
+                       fsp_set_slot(dev, 1, false, 0, 0);
+               }
+               if (fgrs > 0) {
+                       input_report_abs(dev, ABS_X, abs_x);
+                       input_report_abs(dev, ABS_Y, abs_y);
+               }
+               input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+               input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+               input_report_key(dev, BTN_TOUCH, fgrs);
+               input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1);
+               input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2);
                break;
 
        case FSP_PKT_TYPE_NORMAL_OPC:
                /* on-pad click, filter it if necessary */
                if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
-                       packet[0] &= ~BIT(0);
+                       packet[0] &= ~FSP_PB0_LBTN;
                /* fall through */
 
        case FSP_PKT_TYPE_NORMAL:
@@ -711,8 +812,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
 
        input_sync(dev);
 
-       fsp_packet_debug(packet);
-
        return PSMOUSE_FULL_PACKET;
 }
 
@@ -736,42 +835,106 @@ static int fsp_activate_protocol(struct psmouse *psmouse)
 
        ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
        if (param[0] != 0x04) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to enable 4 bytes packet format.\n");
+               psmouse_err(psmouse,
+                           "Unable to enable 4 bytes packet format.\n");
                return -EIO;
        }
 
-       if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to read SYSCTL5 register.\n");
-               return -EIO;
-       }
+       if (pad->ver < FSP_VER_STL3888_C0) {
+               /* Preparing relative coordinates output for older hardware */
+               if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
+                       psmouse_err(psmouse,
+                                   "Unable to read SYSCTL5 register.\n");
+                       return -EIO;
+               }
 
-       val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
-       /* Ensure we are not in absolute mode */
-       val &= ~FSP_BIT_EN_PKT_G0;
-       if (pad->buttons == 0x06) {
-               /* Left/Middle/Right & Scroll Up/Down/Right/Left */
-               val |= FSP_BIT_EN_MSID6;
-       }
+               if (fsp_get_buttons(psmouse, &pad->buttons)) {
+                       psmouse_err(psmouse,
+                                   "Unable to retrieve number of buttons.\n");
+                       return -EIO;
+               }
 
-       if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Unable to set up required mode bits.\n");
-               return -EIO;
+               val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
+               /* Ensure we are not in absolute mode */
+               val &= ~FSP_BIT_EN_PKT_G0;
+               if (pad->buttons == 0x06) {
+                       /* Left/Middle/Right & Scroll Up/Down/Right/Left */
+                       val |= FSP_BIT_EN_MSID6;
+               }
+
+               if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
+                       psmouse_err(psmouse,
+                                   "Unable to set up required mode bits.\n");
+                       return -EIO;
+               }
+
+               /*
+                * Enable OPC tags such that driver can tell the difference
+                * between on-pad and real button click
+                */
+               if (fsp_opc_tag_enable(psmouse, true))
+                       psmouse_warn(psmouse,
+                                    "Failed to enable OPC tag mode.\n");
+               /* enable on-pad click by default */
+               pad->flags |= FSPDRV_FLAG_EN_OPC;
+
+               /* Enable on-pad vertical and horizontal scrolling */
+               fsp_onpad_vscr(psmouse, true);
+               fsp_onpad_hscr(psmouse, true);
+       } else {
+               /* Enable absolute coordinates output for Cx/Dx hardware */
+               if (fsp_reg_write(psmouse, FSP_REG_SWC1,
+                                 FSP_BIT_SWC1_EN_ABS_1F |
+                                 FSP_BIT_SWC1_EN_ABS_2F |
+                                 FSP_BIT_SWC1_EN_FUP_OUT |
+                                 FSP_BIT_SWC1_EN_ABS_CON)) {
+                       psmouse_err(psmouse,
+                                   "Unable to enable absolute coordinates output.\n");
+                       return -EIO;
+               }
        }
 
-       /*
-        * Enable OPC tags such that driver can tell the difference between
-        * on-pad and real button click
-        */
-       if (fsp_opc_tag_enable(psmouse, true))
-               dev_warn(&psmouse->ps2dev.serio->dev,
-                        "Failed to enable OPC tag mode.\n");
+       return 0;
+}
 
-       /* Enable on-pad vertical and horizontal scrolling */
-       fsp_onpad_vscr(psmouse, true);
-       fsp_onpad_hscr(psmouse, true);
+static int fsp_set_input_params(struct psmouse *psmouse)
+{
+       struct input_dev *dev = psmouse->dev;
+       struct fsp_data *pad = psmouse->private;
+
+       if (pad->ver < FSP_VER_STL3888_C0) {
+               __set_bit(BTN_MIDDLE, dev->keybit);
+               __set_bit(BTN_BACK, dev->keybit);
+               __set_bit(BTN_FORWARD, dev->keybit);
+               __set_bit(REL_WHEEL, dev->relbit);
+               __set_bit(REL_HWHEEL, dev->relbit);
+       } else {
+               /*
+                * Hardware prior to Cx performs much better in relative mode;
+                * hence, only enable absolute coordinates output as well as
+                * multi-touch output for the newer hardware.
+                *
+                * Maximum coordinates can be computed as:
+                *
+                *      number of scanlines * 64 - 57
+                *
+                * where number of X/Y scanline lines are 16/12.
+                */
+               int abs_x = 967, abs_y = 711;
+
+               __set_bit(EV_ABS, dev->evbit);
+               __clear_bit(EV_REL, dev->evbit);
+               __set_bit(BTN_TOUCH, dev->keybit);
+               __set_bit(BTN_TOOL_FINGER, dev->keybit);
+               __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+               __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+
+               input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
+               input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
+               input_mt_init_slots(dev, 2);
+               input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
+               input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
+       }
 
        return 0;
 }
@@ -829,18 +992,16 @@ static int fsp_reconnect(struct psmouse *psmouse)
 int fsp_init(struct psmouse *psmouse)
 {
        struct fsp_data *priv;
-       int ver, rev, buttons;
+       int ver, rev;
        int error;
 
        if (fsp_get_version(psmouse, &ver) ||
-           fsp_get_revision(psmouse, &rev) ||
-           fsp_get_buttons(psmouse, &buttons)) {
+           fsp_get_revision(psmouse, &rev)) {
                return -ENODEV;
        }
 
-       psmouse_info(psmouse,
-                    "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n",
-                    ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7);
+       psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n",
+                    ver >> 4, ver & 0x0F, rev, fsp_drv_ver);
 
        psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
        if (!priv)
@@ -848,17 +1009,6 @@ int fsp_init(struct psmouse *psmouse)
 
        priv->ver = ver;
        priv->rev = rev;
-       priv->buttons = buttons;
-
-       /* enable on-pad click by default */
-       priv->flags |= FSPDRV_FLAG_EN_OPC;
-
-       /* Set up various supported input event bits */
-       __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
-       __set_bit(BTN_BACK, psmouse->dev->keybit);
-       __set_bit(BTN_FORWARD, psmouse->dev->keybit);
-       __set_bit(REL_WHEEL, psmouse->dev->relbit);
-       __set_bit(REL_HWHEEL, psmouse->dev->relbit);
 
        psmouse->protocol_handler = fsp_process_byte;
        psmouse->disconnect = fsp_disconnect;
@@ -866,16 +1016,20 @@ int fsp_init(struct psmouse *psmouse)
        psmouse->cleanup = fsp_reset;
        psmouse->pktsize = 4;
 
-       /* set default packet output based on number of buttons we found */
        error = fsp_activate_protocol(psmouse);
        if (error)
                goto err_out;
 
+       /* Set up various supported input event bits */
+       error = fsp_set_input_params(psmouse);
+       if (error)
+               goto err_out;
+
        error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
                                   &fsp_attribute_group);
        if (error) {
-               dev_err(&psmouse->ps2dev.serio->dev,
-                       "Failed to create sysfs attributes (%d)", error);
+               psmouse_err(psmouse,
+                           "Failed to create sysfs attributes (%d)", error);
                goto err_out;
        }
 
index 2e4af24f8c1586b6ecfb3c7f8325cbeb34eb4ae5..334de19e5ddb9c4ac0387f3e4fe3f90cb612cbcf 100644 (file)
@@ -2,7 +2,7 @@
  * Finger Sensing Pad PS/2 mouse driver.
  *
  * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
- * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation.
+ * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
  *
  *   This program is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU General Public License
 #define        FSP_BIT_FIX_HSCR        BIT(5)
 #define        FSP_BIT_DRAG_LOCK       BIT(6)
 
+#define        FSP_REG_SWC1            (0x90)
+#define        FSP_BIT_SWC1_EN_ABS_1F  BIT(0)
+#define        FSP_BIT_SWC1_EN_GID     BIT(1)
+#define        FSP_BIT_SWC1_EN_ABS_2F  BIT(2)
+#define        FSP_BIT_SWC1_EN_FUP_OUT BIT(3)
+#define        FSP_BIT_SWC1_EN_ABS_CON BIT(4)
+#define        FSP_BIT_SWC1_GST_GRP0   BIT(5)
+#define        FSP_BIT_SWC1_GST_GRP1   BIT(6)
+#define        FSP_BIT_SWC1_BX_COMPAT  BIT(7)
+
 /* Finger-sensing Pad packet formating related definitions */
 
 /* absolute packet type */
 #define        FSP_PKT_TYPE_NORMAL_OPC (0x03)
 #define        FSP_PKT_TYPE_SHIFT      (6)
 
+/* bit definitions for the first byte of report packet */
+#define        FSP_PB0_LBTN            BIT(0)
+#define        FSP_PB0_RBTN            BIT(1)
+#define        FSP_PB0_MBTN            BIT(2)
+#define        FSP_PB0_MFMC_FGR2       FSP_PB0_MBTN
+#define        FSP_PB0_MUST_SET        BIT(3)
+#define        FSP_PB0_PHY_BTN         BIT(4)
+#define        FSP_PB0_MFMC            BIT(5)
+
+/* hardware revisions */
+#define        FSP_VER_STL3888_A4      (0xC1)
+#define        FSP_VER_STL3888_B0      (0xD0)
+#define        FSP_VER_STL3888_B1      (0xD1)
+#define        FSP_VER_STL3888_B2      (0xD2)
+#define        FSP_VER_STL3888_C0      (0xE0)
+#define        FSP_VER_STL3888_C1      (0xE1)
+#define        FSP_VER_STL3888_D0      (0xE2)
+#define        FSP_VER_STL3888_D1      (0xE3)
+#define        FSP_VER_STL3888_E0      (0xE4)
+
 #ifdef __KERNEL__
 
 struct fsp_data {
        unsigned char   ver;            /* hardware version */
        unsigned char   rev;            /* hardware revison */
-       unsigned char   buttons;        /* Number of buttons */
+       unsigned int    buttons;        /* Number of buttons */
        unsigned int    flags;
 #define        FSPDRV_FLAG_EN_OPC      (0x001) /* enable on-pad clicking */
 
@@ -78,6 +108,7 @@ struct fsp_data {
 
        unsigned char   last_reg;       /* Last register we requested read from */
        unsigned char   last_val;
+       unsigned int    last_mt_fgr;    /* Last seen finger(multitouch) */
 };
 
 #ifdef CONFIG_MOUSE_PS2_SENTELIC
index e53f4081a586956974bea8731d366366515ff6f8..bed7cbf84cfd513a2a05b7553c3318698e3d5d7e 100644 (file)
@@ -76,6 +76,7 @@ config TABLET_USB_KBTAB
 config TABLET_USB_WACOM
        tristate "Wacom Intuos/Graphire tablet support (USB)"
        depends on USB_ARCH_HAS_HCD
+       select POWER_SUPPLY
        select USB
        select NEW_LEDS
        select LEDS_CLASS
index 0783864a7dc2e1c77ad1aada99841ceab27106b3..b4842d0e61dd4bdd0e0c4aaa5b4305c61e82e63d 100644 (file)
@@ -88,6 +88,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/init.h>
 #include <linux/usb/input.h>
+#include <linux/power_supply.h>
 #include <asm/unaligned.h>
 
 /*
@@ -112,6 +113,7 @@ struct wacom {
        struct urb *irq;
        struct wacom_wac wacom_wac;
        struct mutex lock;
+       struct work_struct work;
        bool open;
        char phys[32];
        struct wacom_led {
@@ -120,8 +122,15 @@ struct wacom {
                u8 hlv;       /* status led brightness button pressed (1..127) */
                u8 img_lum;   /* OLED matrix display brightness */
        } led;
+       struct power_supply battery;
 };
 
+static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
+{
+       struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+       schedule_work(&wacom->work);
+}
+
 extern const struct usb_device_id wacom_ids[];
 
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
index ca28066dc81e335bb85483617a69f46056dbd63e..0d269212931e86be95d4c77c26b667ac781200c2 100644 (file)
@@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev)
                usb_autopm_put_interface(wacom->intf);
 }
 
+/*
+ * Static values for max X/Y and resolution of Pen interface is stored in
+ * features. This mean physical size of active area can be computed.
+ * This is useful to do when Pen and Touch have same active area of tablet.
+ * This means for Touch device, we only need to find max X/Y value and we
+ * have enough information to compute resolution of touch.
+ */
+static void wacom_set_phy_from_res(struct wacom_features *features)
+{
+       features->x_phy = (features->x_max * 100) / features->x_resolution;
+       features->y_phy = (features->y_max * 100) / features->y_resolution;
+}
+
 static int wacom_parse_logical_collection(unsigned char *report,
                                          struct wacom_features *features)
 {
@@ -178,15 +191,7 @@ static int wacom_parse_logical_collection(unsigned char *report,
                features->pktlen = WACOM_PKGLEN_BBTOUCH3;
                features->device_type = BTN_TOOL_FINGER;
 
-               /*
-                * Stylus and Touch have same active area
-                * so compute physical size based on stylus
-                * data before its overwritten.
-                */
-               features->x_phy =
-                       (features->x_max * 100) / features->x_resolution;
-               features->y_phy =
-                       (features->y_max * 100) / features->y_resolution;
+               wacom_set_phy_from_res(features);
 
                features->x_max = features->y_max =
                        get_unaligned_le16(&report[10]);
@@ -422,6 +427,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
                                                report_id, rep_data, 4, 1);
                } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
        } else if (features->type != TABLETPC &&
+                  features->type != WIRELESS &&
                   features->device_type == BTN_TOOL_PEN) {
                do {
                        rep_data[0] = 2;
@@ -454,6 +460,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
        features->pressure_fuzz = 0;
        features->distance_fuzz = 0;
 
+       /*
+        * The wireless device HID is basic and layout conflicts with
+        * other tablets (monitor and touch interface can look like pen).
+        * Skip the query for this type and modify defaults based on
+        * interface number.
+        */
+       if (features->type == WIRELESS) {
+               if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+                       features->device_type = 0;
+               } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
+                       features->device_type = BTN_TOOL_DOUBLETAP;
+                       features->pktlen = WACOM_PKGLEN_BBTOUCH3;
+               }
+       }
+
        /* only Tablet PCs and Bamboo P&T need to retrieve the info */
        if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
            (features->type != BAMBOO_PT))
@@ -822,6 +843,152 @@ static void wacom_destroy_leds(struct wacom *wacom)
        }
 }
 
+static enum power_supply_property wacom_battery_props[] = {
+       POWER_SUPPLY_PROP_CAPACITY
+};
+
+static int wacom_battery_get_property(struct power_supply *psy,
+                                     enum power_supply_property psp,
+                                     union power_supply_propval *val)
+{
+       struct wacom *wacom = container_of(psy, struct wacom, battery);
+       int ret = 0;
+
+       switch (psp) {
+               case POWER_SUPPLY_PROP_CAPACITY:
+                       val->intval =
+                               wacom->wacom_wac.battery_capacity * 100 / 31;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+       }
+
+       return ret;
+}
+
+static int wacom_initialize_battery(struct wacom *wacom)
+{
+       int error = 0;
+
+       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) {
+               wacom->battery.properties = wacom_battery_props;
+               wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
+               wacom->battery.get_property = wacom_battery_get_property;
+               wacom->battery.name = "wacom_battery";
+               wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+               wacom->battery.use_for_apm = 0;
+
+               error = power_supply_register(&wacom->usbdev->dev,
+                                             &wacom->battery);
+       }
+
+       return error;
+}
+
+static void wacom_destroy_battery(struct wacom *wacom)
+{
+       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR)
+               power_supply_unregister(&wacom->battery);
+}
+
+static int wacom_register_input(struct wacom *wacom)
+{
+       struct input_dev *input_dev;
+       struct usb_interface *intf = wacom->intf;
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+       int error;
+
+       input_dev = input_allocate_device();
+       if (!input_dev)
+               return -ENOMEM;
+
+       input_dev->name = wacom_wac->name;
+       input_dev->dev.parent = &intf->dev;
+       input_dev->open = wacom_open;
+       input_dev->close = wacom_close;
+       usb_to_input_id(dev, &input_dev->id);
+       input_set_drvdata(input_dev, wacom);
+
+       wacom_wac->input = input_dev;
+       wacom_setup_input_capabilities(input_dev, wacom_wac);
+
+       error = input_register_device(input_dev);
+       if (error) {
+               input_free_device(input_dev);
+               wacom_wac->input = NULL;
+       }
+
+       return error;
+}
+
+static void wacom_wireless_work(struct work_struct *work)
+{
+       struct wacom *wacom = container_of(work, struct wacom, work);
+       struct usb_device *usbdev = wacom->usbdev;
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+       /*
+        * Regardless if this is a disconnect or a new tablet,
+        * remove any existing input devices.
+        */
+
+       /* Stylus interface */
+       wacom = usb_get_intfdata(usbdev->config->interface[1]);
+       if (wacom->wacom_wac.input)
+               input_unregister_device(wacom->wacom_wac.input);
+       wacom->wacom_wac.input = 0;
+
+       /* Touch interface */
+       wacom = usb_get_intfdata(usbdev->config->interface[2]);
+       if (wacom->wacom_wac.input)
+               input_unregister_device(wacom->wacom_wac.input);
+       wacom->wacom_wac.input = 0;
+
+       if (wacom_wac->pid == 0) {
+               printk(KERN_INFO "wacom: wireless tablet disconnected\n");
+       } else {
+               const struct usb_device_id *id = wacom_ids;
+
+               printk(KERN_INFO
+                      "wacom: wireless tablet connected with PID %x\n",
+                      wacom_wac->pid);
+
+               while (id->match_flags) {
+                       if (id->idVendor == USB_VENDOR_ID_WACOM &&
+                           id->idProduct == wacom_wac->pid)
+                               break;
+                       id++;
+               }
+
+               if (!id->match_flags) {
+                       printk(KERN_INFO
+                              "wacom: ignorning unknown PID.\n");
+                       return;
+               }
+
+               /* Stylus interface */
+               wacom = usb_get_intfdata(usbdev->config->interface[1]);
+               wacom_wac = &wacom->wacom_wac;
+               wacom_wac->features =
+                       *((struct wacom_features *)id->driver_info);
+               wacom_wac->features.device_type = BTN_TOOL_PEN;
+               wacom_register_input(wacom);
+
+               /* Touch interface */
+               wacom = usb_get_intfdata(usbdev->config->interface[2]);
+               wacom_wac = &wacom->wacom_wac;
+               wacom_wac->features =
+                       *((struct wacom_features *)id->driver_info);
+               wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
+               wacom_wac->features.device_type = BTN_TOOL_FINGER;
+               wacom_set_phy_from_res(&wacom_wac->features);
+               wacom_wac->features.x_max = wacom_wac->features.y_max = 4096;
+               wacom_register_input(wacom);
+       }
+}
+
 static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
@@ -829,18 +996,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        struct wacom *wacom;
        struct wacom_wac *wacom_wac;
        struct wacom_features *features;
-       struct input_dev *input_dev;
        int error;
 
        if (!id->driver_info)
                return -EINVAL;
 
        wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!wacom || !input_dev) {
-               error = -ENOMEM;
-               goto fail1;
-       }
+       if (!wacom)
+               return -ENOMEM;
 
        wacom_wac = &wacom->wacom_wac;
        wacom_wac->features = *((struct wacom_features *)id->driver_info);
@@ -866,11 +1029,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        wacom->usbdev = dev;
        wacom->intf = intf;
        mutex_init(&wacom->lock);
+       INIT_WORK(&wacom->work, wacom_wireless_work);
        usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
        strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
 
-       wacom_wac->input = input_dev;
-
        endpoint = &intf->cur_altsetting->endpoint[0].desc;
 
        /* Retrieve the physical and logical size for OEM devices */
@@ -894,15 +1056,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
                        goto fail3;
        }
 
-       input_dev->name = wacom_wac->name;
-       input_dev->dev.parent = &intf->dev;
-       input_dev->open = wacom_open;
-       input_dev->close = wacom_close;
-       usb_to_input_id(dev, &input_dev->id);
-       input_set_drvdata(input_dev, wacom);
-
-       wacom_setup_input_capabilities(input_dev, wacom_wac);
-
        usb_fill_int_urb(wacom->irq, dev,
                         usb_rcvintpipe(dev, endpoint->bEndpointAddress),
                         wacom_wac->data, features->pktlen,
@@ -914,22 +1067,34 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        if (error)
                goto fail4;
 
-       error = input_register_device(input_dev);
+       error = wacom_initialize_battery(wacom);
        if (error)
                goto fail5;
 
+       if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
+               error = wacom_register_input(wacom);
+               if (error)
+                       goto fail6;
+       }
+
        /* Note that if query fails it is not a hard failure */
        wacom_query_tablet_data(intf, features);
 
        usb_set_intfdata(intf, wacom);
+
+       if (features->quirks & WACOM_QUIRK_MONITOR) {
+               if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+                       goto fail5;
+       }
+
        return 0;
 
+ fail6: wacom_destroy_battery(wacom);
  fail5: wacom_destroy_leds(wacom);
  fail4:        wacom_remove_shared_data(wacom_wac);
  fail3:        usb_free_urb(wacom->irq);
  fail2:        usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
- fail1:        input_free_device(input_dev);
-       kfree(wacom);
+ fail1:        kfree(wacom);
        return error;
 }
 
@@ -940,7 +1105,10 @@ static void wacom_disconnect(struct usb_interface *intf)
        usb_set_intfdata(intf, NULL);
 
        usb_kill_urb(wacom->irq);
-       input_unregister_device(wacom->wacom_wac.input);
+       cancel_work_sync(&wacom->work);
+       if (wacom->wacom_wac.input)
+               input_unregister_device(wacom->wacom_wac.input);
+       wacom_destroy_battery(wacom);
        wacom_destroy_leds(wacom);
        usb_free_urb(wacom->irq);
        usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
@@ -972,7 +1140,8 @@ static int wacom_resume(struct usb_interface *intf)
        wacom_query_tablet_data(intf, features);
        wacom_led_control(wacom);
 
-       if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
+       if ((wacom->open || features->quirks & WACOM_QUIRK_MONITOR)
+            && usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
                rv = -EIO;
 
        mutex_unlock(&wacom->lock);
index 89a96427faa0285ea6dd16180e9eda41ea2e3ece..cecd35c8f0b3ce46cb82bd51514f7b468ab282cd 100644 (file)
@@ -1044,6 +1044,35 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
        return 0;
 }
 
+static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
+{
+       unsigned char *data = wacom->data;
+       int connected;
+
+       if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80)
+               return 0;
+
+       connected = data[1] & 0x01;
+       if (connected) {
+               int pid, battery;
+
+               pid = get_unaligned_be16(&data[6]);
+               battery = data[5] & 0x3f;
+               if (wacom->pid != pid) {
+                       wacom->pid = pid;
+                       wacom_schedule_work(wacom);
+               }
+               wacom->battery_capacity = battery;
+       } else if (wacom->pid != 0) {
+               /* disconnected while previously connected */
+               wacom->pid = 0;
+               wacom_schedule_work(wacom);
+               wacom->battery_capacity = 0;
+       }
+
+       return 0;
+}
+
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
 {
        bool sync;
@@ -1094,6 +1123,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
                sync = wacom_bpt_irq(wacom_wac, len);
                break;
 
+       case WIRELESS:
+               sync = wacom_wireless_irq(wacom_wac, len);
+               break;
+
        default:
                sync = false;
                break;
@@ -1155,7 +1188,7 @@ void wacom_setup_device_quirks(struct wacom_features *features)
 
        /* these device have multiple inputs */
        if (features->type == TABLETPC || features->type == TABLETPC2FG ||
-           features->type == BAMBOO_PT)
+           features->type == BAMBOO_PT || features->type == WIRELESS)
                features->quirks |= WACOM_QUIRK_MULTI_INPUT;
 
        /* quirk for bamboo touch with 2 low res touches */
@@ -1167,6 +1200,16 @@ void wacom_setup_device_quirks(struct wacom_features *features)
                features->y_fuzz <<= 5;
                features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
        }
+
+       if (features->type == WIRELESS) {
+
+               /* monitor never has input and pen/touch have delayed create */
+               features->quirks |= WACOM_QUIRK_NO_INPUT;
+
+               /* must be monitor interface if no device_type set */
+               if (!features->device_type)
+                       features->quirks |= WACOM_QUIRK_MONITOR;
+       }
 }
 
 static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
@@ -1640,6 +1683,9 @@ static const struct wacom_features wacom_features_0xEC =
 static const struct wacom_features wacom_features_0x47 =
        { "Wacom Intuos2 6x8",    WACOM_PKGLEN_INTUOS,    20320, 16240, 1023,
          31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x84 =
+       { "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0,
+         0, WIRELESS, 0, 0 };
 static const struct wacom_features wacom_features_0xD0 =
        { "Wacom Bamboo 2FG",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1766,6 +1812,7 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID,
                              USB_INTERFACE_SUBCLASS_BOOT,
                              USB_INTERFACE_PROTOCOL_MOUSE) },
+       { USB_DEVICE_WACOM(0x84) },
        { USB_DEVICE_WACOM(0xD0) },
        { USB_DEVICE_WACOM(0xD1) },
        { USB_DEVICE_WACOM(0xD2) },
index 4f0ba21b01962c4b2556535468c96f7355e17d1c..ba5a334e54d6b525995965cad49acba64a41e89b 100644 (file)
@@ -24,6 +24,7 @@
 #define WACOM_PKGLEN_BBTOUCH   20
 #define WACOM_PKGLEN_BBTOUCH3  64
 #define WACOM_PKGLEN_BBPEN     10
+#define WACOM_PKGLEN_WIRELESS  32
 
 /* device IDs */
 #define STYLUS_DEVICE_ID       0x02
@@ -45,6 +46,8 @@
 /* device quirks */
 #define WACOM_QUIRK_MULTI_INPUT                0x0001
 #define WACOM_QUIRK_BBTOUCH_LOWRES     0x0002
+#define WACOM_QUIRK_NO_INPUT           0x0004
+#define WACOM_QUIRK_MONITOR            0x0008
 
 enum {
        PENPARTNER = 0,
@@ -54,6 +57,7 @@ enum {
        PL,
        DTU,
        BAMBOO_PT,
+       WIRELESS,
        INTUOS,
        INTUOS3S,
        INTUOS3,
@@ -107,6 +111,8 @@ struct wacom_wac {
        struct wacom_features features;
        struct wacom_shared *shared;
        struct input_dev *input;
+       int pid;
+       int battery_capacity;
 };
 
 #endif
index 004ff33ab38e4dc3e2135f01ebe6b0d84fbb1529..a7e977ff4abf060190118884b8a107c826c78feb 100644 (file)
@@ -6,7 +6,7 @@ struct device;
 struct gpio_keys_button {
        /* Configuration parameters */
        unsigned int code;      /* input event code (KEY_*, SW_*) */
-       int gpio;
+       int gpio;               /* -1 if this key does not support gpio */
        int active_low;
        const char *desc;
        unsigned int type;      /* input event type (EV_KEY, EV_SW, EV_ABS) */
@@ -14,6 +14,7 @@ struct gpio_keys_button {
        int debounce_interval;  /* debounce ticks interval in msecs */
        bool can_disable;
        int value;              /* axis value for EV_ABS */
+       unsigned int irq;       /* Irq number in case of interrupt keys */
 };
 
 struct gpio_keys_platform_data {