]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Merge branch 'next' into for-linus
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 9 Jan 2012 07:38:23 +0000 (23:38 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 9 Jan 2012 07:38:23 +0000 (23:38 -0800)
1  2 
drivers/input/misc/cma3000_d0x.c
drivers/input/mouse/sentelic.c
drivers/input/mouse/synaptics.c
drivers/input/tablet/wacom_wac.c

index 09f8f20255803cb3b673539a420ed07217a72d64,80793f1608eb0d1fd2eaff655cba48ffcc037709..06517e60e50c1e64742083eb1371fb7a328bd929
@@@ -23,6 -23,7 +23,7 @@@
  #include <linux/slab.h>
  #include <linux/input.h>
  #include <linux/input/cma3000.h>
+ #include <linux/module.h>
  
  #include "cma3000_d0x.h"
  
@@@ -114,8 -115,8 +115,8 @@@ static void decode_mg(struct cma3000_ac
  static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)
  {
        struct cma3000_accl_data *data = dev_id;
 -      int datax, datay, dataz;
 -      u8 ctrl, mode, range, intr_status;
 +      int datax, datay, dataz, intr_status;
 +      u8 ctrl, mode, range;
  
        intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");
        if (intr_status < 0)
index 86d6f39178b0d556364df951ca33575598bfa416,5babc47b39aaf87ba837e36fe79bf9f1b2a97605..e36847de7617cfdd7cc75af410afad11c46e56f8
@@@ -2,7 -2,7 +2,7 @@@
   * Finger Sensing Pad PS/2 mouse driver.
   *
   * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
 - * Copyright (C) 2005-2010 Tai-hwa Liang, Sentelic Corporation.
 + * Copyright (C) 2005-2011 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
@@@ -162,7 -162,7 +162,7 @@@ static int fsp_reg_write(struct psmous
        ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
  
        if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
 -              return -1;
 +              goto out;
  
        if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
                /* inversion is required */
@@@ -261,7 -261,7 +261,7 @@@ static int fsp_page_reg_write(struct ps
        ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
  
        if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
 -              return -1;
 +              goto out;
  
        if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
                ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
@@@ -309,7 -309,7 +309,7 @@@ static int fsp_get_buttons(struct psmou
        };
        int val;
  
 -      if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1)
 +      if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS, &val) == -1)
                return -EIO;
  
        *btn = buttons[(val & 0x30) >> 4];
@@@ -408,7 -408,7 +408,7 @@@ static int fsp_onpad_hscr(struct psmous
  static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
                                   const char *buf, size_t count)
  {
-       unsigned long reg, val;
+       int reg, val;
        char *rest;
        ssize_t retval;
  
        if (rest == buf || *rest != ' ' || reg > 0xff)
                return -EINVAL;
  
-       if (strict_strtoul(rest + 1, 16, &val) || val > 0xff)
+       retval = kstrtoint(rest + 1, 16, &val);
+       if (retval)
+               return retval;
+       if (val > 0xff)
                return -EINVAL;
  
        if (fsp_reg_write_enable(psmouse, true))
@@@ -448,10 -452,13 +452,13 @@@ static ssize_t fsp_attr_set_getreg(stru
                                        const char *buf, size_t count)
  {
        struct fsp_data *pad = psmouse->private;
-       unsigned long reg;
-       int val;
+       int reg, val, err;
+       err = kstrtoint(buf, 16, &reg);
+       if (err)
+               return err;
  
-       if (strict_strtoul(buf, 16, &reg) || reg > 0xff)
+       if (reg > 0xff)
                return -EINVAL;
  
        if (fsp_reg_read(psmouse, reg, &val))
@@@ -480,9 -487,13 +487,13 @@@ static ssize_t fsp_attr_show_pagereg(st
  static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,
                                        const char *buf, size_t count)
  {
-       unsigned long val;
+       int val, err;
  
-       if (strict_strtoul(buf, 16, &val) || val > 0xff)
+       err = kstrtoint(buf, 16, &val);
+       if (err)
+               return err;
+       if (val > 0xff)
                return -EINVAL;
  
        if (fsp_page_reg_write(psmouse, val))
@@@ -505,9 -516,14 +516,14 @@@ static ssize_t fsp_attr_show_vscroll(st
  static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data,
                                        const char *buf, size_t count)
  {
-       unsigned long val;
+       unsigned int val;
+       int err;
+       err = kstrtouint(buf, 10, &val);
+       if (err)
+               return err;
  
-       if (strict_strtoul(buf, 10, &val) || val > 1)
+       if (val > 1)
                return -EINVAL;
  
        fsp_onpad_vscr(psmouse, val);
@@@ -529,9 -545,14 +545,14 @@@ static ssize_t fsp_attr_show_hscroll(st
  static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data,
                                        const char *buf, size_t count)
  {
-       unsigned long val;
+       unsigned int val;
+       int err;
+       err = kstrtouint(buf, 10, &val);
+       if (err)
+               return err;
  
-       if (strict_strtoul(buf, 10, &val) || val > 1)
+       if (val > 1)
                return -EINVAL;
  
        fsp_onpad_hscr(psmouse, val);
index a6dcd18e9adf93b5b97cd00e04b6419a6788e1e5,06c9ee57951e1d4b121431ff3fa77d2d8a789336..8081a0a5d602c0b9f04557b5323882c4bcf89694
@@@ -24,7 -24,6 +24,7 @@@
   */
  
  #include <linux/module.h>
 +#include <linux/delay.h>
  #include <linux/dmi.h>
  #include <linux/input/mt.h>
  #include <linux/serio.h>
@@@ -269,19 -268,49 +269,49 @@@ static int synaptics_query_hardware(str
        return 0;
  }
  
- static int synaptics_set_absolute_mode(struct psmouse *psmouse)
+ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
+ {
+       static unsigned char param = 0xc8;
+       struct synaptics_data *priv = psmouse->private;
+       if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+               return 0;
+       if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+               return -1;
+       if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
+               return -1;
+       /* Advanced gesture mode also sends multi finger data */
+       priv->capabilities |= BIT(1);
+       return 0;
+ }
+ static int synaptics_set_mode(struct psmouse *psmouse)
  {
        struct synaptics_data *priv = psmouse->private;
  
-       priv->mode = SYN_BIT_ABSOLUTE_MODE;
-       if (SYN_ID_MAJOR(priv->identity) >= 4)
+       priv->mode = 0;
+       if (priv->absolute_mode)
+               priv->mode |= SYN_BIT_ABSOLUTE_MODE;
+       if (priv->disable_gesture)
                priv->mode |= SYN_BIT_DISABLE_GESTURE;
+       if (psmouse->rate >= 80)
+               priv->mode |= SYN_BIT_HIGH_RATE;
        if (SYN_CAP_EXTENDED(priv->capabilities))
                priv->mode |= SYN_BIT_W_MODE;
  
        if (synaptics_mode_cmd(psmouse, priv->mode))
                return -1;
  
+       if (priv->absolute_mode &&
+           synaptics_set_advanced_gesture_mode(psmouse)) {
+               psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+               return -1;
+       }
        return 0;
  }
  
@@@ -300,26 -329,6 +330,6 @@@ static void synaptics_set_rate(struct p
        synaptics_mode_cmd(psmouse, priv->mode);
  }
  
- static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
- {
-       static unsigned char param = 0xc8;
-       struct synaptics_data *priv = psmouse->private;
-       if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
-                       SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
-               return 0;
-       if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
-               return -1;
-       if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
-               return -1;
-       /* Advanced gesture mode also sends multi finger data */
-       priv->capabilities |= BIT(1);
-       return 0;
- }
  /*****************************************************************************
   *    Synaptics pass-through PS/2 port support
   ****************************************************************************/
@@@ -1143,8 -1152,24 +1153,24 @@@ static void set_input_params(struct inp
  {
        int i;
  
+       /* Things that apply to both modes */
        __set_bit(INPUT_PROP_POINTER, dev->propbit);
+       __set_bit(EV_KEY, dev->evbit);
+       __set_bit(BTN_LEFT, dev->keybit);
+       __set_bit(BTN_RIGHT, dev->keybit);
  
+       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+               __set_bit(BTN_MIDDLE, dev->keybit);
+       if (!priv->absolute_mode) {
+               /* Relative mode */
+               __set_bit(EV_REL, dev->evbit);
+               __set_bit(REL_X, dev->relbit);
+               __set_bit(REL_Y, dev->relbit);
+               return;
+       }
+       /* Absolute mode */
        __set_bit(EV_ABS, dev->evbit);
        set_abs_position_params(dev, priv, ABS_X, ABS_Y);
        input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
        if (SYN_CAP_PALMDETECT(priv->capabilities))
                input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
  
-       __set_bit(EV_KEY, dev->evbit);
        __set_bit(BTN_TOUCH, dev->keybit);
        __set_bit(BTN_TOOL_FINGER, dev->keybit);
-       __set_bit(BTN_LEFT, dev->keybit);
-       __set_bit(BTN_RIGHT, dev->keybit);
  
        if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
                __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
                __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
        }
  
-       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
-               __set_bit(BTN_MIDDLE, dev->keybit);
        if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
            SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
                __set_bit(BTN_FORWARD, dev->keybit);
        }
  }
  
+ static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse,
+                                             void *data, char *buf)
+ {
+       struct synaptics_data *priv = psmouse->private;
+       return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0');
+ }
+ static ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse,
+                                            void *data, const char *buf,
+                                            size_t len)
+ {
+       struct synaptics_data *priv = psmouse->private;
+       unsigned int value;
+       int err;
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+       if (value > 1)
+               return -EINVAL;
+       if (value == priv->disable_gesture)
+               return len;
+       priv->disable_gesture = value;
+       if (value)
+               priv->mode |= SYN_BIT_DISABLE_GESTURE;
+       else
+               priv->mode &= ~SYN_BIT_DISABLE_GESTURE;
+       if (synaptics_mode_cmd(psmouse, priv->mode))
+               return -EIO;
+       return len;
+ }
+ PSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL,
+                   synaptics_show_disable_gesture,
+                   synaptics_set_disable_gesture);
  static void synaptics_disconnect(struct psmouse *psmouse)
  {
+       struct synaptics_data *priv = psmouse->private;
+       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
+               device_remove_file(&psmouse->ps2dev.serio->dev,
+                                  &psmouse_attr_disable_gesture.dattr);
        synaptics_reset(psmouse);
-       kfree(psmouse->private);
+       kfree(priv);
        psmouse->private = NULL;
  }
  
@@@ -1221,16 -1288,6 +1289,16 @@@ static int synaptics_reconnect(struct p
  
        do {
                psmouse_reset(psmouse);
 +              if (retry) {
 +                      /*
 +                       * On some boxes, right after resuming, the touchpad
 +                       * needs some time to finish initializing (I assume
 +                       * it needs time to calibrate) and start responding
 +                       * to Synaptics-specific queries, so let's wait a
 +                       * bit.
 +                       */
 +                      ssleep(1);
 +              }
                error = synaptics_detect(psmouse, 0);
        } while (error && ++retry < 3);
  
                return -1;
        }
  
-       if (synaptics_set_absolute_mode(psmouse)) {
+       if (synaptics_set_mode(psmouse)) {
                psmouse_err(psmouse, "Unable to initialize device.\n");
                return -1;
        }
  
-       if (synaptics_set_advanced_gesture_mode(psmouse)) {
-               psmouse_err(psmouse,
-                           "Advanced gesture mode reconnect failed.\n");
-               return -1;
-       }
        if (old_priv.identity != priv->identity ||
            old_priv.model_id != priv->model_id ||
            old_priv.capabilities != priv->capabilities ||
@@@ -1332,20 -1383,18 +1394,18 @@@ void __init synaptics_module_init(void
        broken_olpc_ec = dmi_check_system(olpc_dmi_table);
  }
  
int synaptics_init(struct psmouse *psmouse)
static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
  {
        struct synaptics_data *priv;
+       int err = -1;
  
        /*
-        * The OLPC XO has issues with Synaptics' absolute mode; similarly to
-        * the HGPK, it quickly degrades and the hardware becomes jumpy and
-        * overly sensitive.  Not only that, but the constant packet spew
-        * (even at a lowered 40pps rate) overloads the EC such that key
-        * presses on the keyboard are missed.  Given all of that, don't
-        * even attempt to use Synaptics mode.  Relative mode seems to work
-        * just fine.
+        * The OLPC XO has issues with Synaptics' absolute mode; the constant
+        * packet spew overloads the EC such that key presses on the keyboard
+        * are missed.  Given that, don't even attempt to use Absolute mode.
+        * Relative mode seems to work just fine.
         */
-       if (broken_olpc_ec) {
+       if (absolute_mode && broken_olpc_ec) {
                psmouse_info(psmouse,
                             "OLPC XO detected, not enabling Synaptics protocol.\n");
                return -ENODEV;
                goto init_fail;
        }
  
-       if (synaptics_set_absolute_mode(psmouse)) {
-               psmouse_err(psmouse, "Unable to initialize device.\n");
-               goto init_fail;
-       }
+       priv->absolute_mode = absolute_mode;
+       if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+               priv->disable_gesture = true;
  
-       if (synaptics_set_advanced_gesture_mode(psmouse)) {
-               psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+       if (synaptics_set_mode(psmouse)) {
+               psmouse_err(psmouse, "Unable to initialize device.\n");
                goto init_fail;
        }
  
        psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
                          (priv->model_id & 0x000000ff);
  
-       psmouse->protocol_handler = synaptics_process_byte;
+       if (absolute_mode) {
+               psmouse->protocol_handler = synaptics_process_byte;
+               psmouse->pktsize = 6;
+       } else {
+               /* Relative mode follows standard PS/2 mouse protocol */
+               psmouse->protocol_handler = psmouse_process_byte;
+               psmouse->pktsize = 3;
+       }
        psmouse->set_rate = synaptics_set_rate;
        psmouse->disconnect = synaptics_disconnect;
        psmouse->reconnect = synaptics_reconnect;
        psmouse->cleanup = synaptics_reset;
-       psmouse->pktsize = 6;
        /* Synaptics can usually stay in sync without extra help */
        psmouse->resync_time = 0;
  
                psmouse->rate = 40;
        }
  
+       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
+               err = device_create_file(&psmouse->ps2dev.serio->dev,
+                                        &psmouse_attr_disable_gesture.dattr);
+               if (err) {
+                       psmouse_err(psmouse,
+                                   "Failed to create disable_gesture attribute (%d)",
+                                   err);
+                       goto init_fail;
+               }
+       }
        return 0;
  
   init_fail:
        kfree(priv);
-       return -1;
+       return err;
+ }
+ int synaptics_init(struct psmouse *psmouse)
+ {
+       return __synaptics_init(psmouse, true);
+ }
+ int synaptics_init_relative(struct psmouse *psmouse)
+ {
+       return __synaptics_init(psmouse, false);
  }
  
  bool synaptics_supported(void)
index 2ee47d01a3b4ecde112b07ac95ccd798f053e4f4,a22e7789d91bc05962866c4f94cc7b9972403d8c..88672ec296c116e10d7340c7f07397e10e09afe1
@@@ -452,7 -452,7 +452,7 @@@ static void wacom_intuos_general(struc
        if ((data[1] & 0xb8) == 0xa0) {
                t = (data[6] << 2) | ((data[7] >> 6) & 3);
                if ((features->type >= INTUOS4S && features->type <= INTUOS4L) ||
-                   features->type == WACOM_21UX2) {
+                   features->type == WACOM_21UX2 || features->type == WACOM_24HD) {
                        t = (t << 1) | (data[1] & 1);
                }
                input_report_abs(input, ABS_PRESSURE, t);
@@@ -519,6 -519,56 +519,56 @@@ static int wacom_intuos_irq(struct waco
                                input_report_key(input, wacom->tool[1], 0);
                                input_report_abs(input, ABS_MISC, 0);
                        }
+               } else if (features->type == WACOM_24HD) {
+                       input_report_key(input, BTN_0, (data[6] & 0x01));
+                       input_report_key(input, BTN_1, (data[6] & 0x02));
+                       input_report_key(input, BTN_2, (data[6] & 0x04));
+                       input_report_key(input, BTN_3, (data[6] & 0x08));
+                       input_report_key(input, BTN_4, (data[6] & 0x10));
+                       input_report_key(input, BTN_5, (data[6] & 0x20));
+                       input_report_key(input, BTN_6, (data[6] & 0x40));
+                       input_report_key(input, BTN_7, (data[6] & 0x80));
+                       input_report_key(input, BTN_8, (data[8] & 0x01));
+                       input_report_key(input, BTN_9, (data[8] & 0x02));
+                       input_report_key(input, BTN_A, (data[8] & 0x04));
+                       input_report_key(input, BTN_B, (data[8] & 0x08));
+                       input_report_key(input, BTN_C, (data[8] & 0x10));
+                       input_report_key(input, BTN_X, (data[8] & 0x20));
+                       input_report_key(input, BTN_Y, (data[8] & 0x40));
+                       input_report_key(input, BTN_Z, (data[8] & 0x80));
+                       /*
+                        * Three "buttons" are available on the 24HD which are
+                        * physically implemented as a touchstrip. Each button
+                        * is approximately 3 bits wide with a 2 bit spacing.
+                        * The raw touchstrip bits are stored at:
+                        *    ((data[3] & 0x1f) << 8) | data[4])
+                        */
+                       input_report_key(input, KEY_PROG1, data[4] & 0x07);
+                       input_report_key(input, KEY_PROG2, data[4] & 0xE0);
+                       input_report_key(input, KEY_PROG3, data[3] & 0x1C);
+                       if (data[1] & 0x80) {
+                               input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
+                       } else {
+                               /* Out of proximity, clear wheel value. */
+                               input_report_abs(input, ABS_WHEEL, 0);
+                       }
+                       if (data[2] & 0x80) {
+                               input_report_abs(input, ABS_THROTTLE, (data[2] & 0x7f));
+                       } else {
+                               /* Out of proximity, clear second wheel value. */
+                               input_report_abs(input, ABS_THROTTLE, 0);
+                       }
+                       if (data[1] | data[2] | (data[3] & 0x1f) | data[4] | data[6] | data[8]) {
+                               input_report_key(input, wacom->tool[1], 1);
+                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+                       } else {
+                               input_report_key(input, wacom->tool[1], 0);
+                               input_report_abs(input, ABS_MISC, 0);
+                       }
                } else {
                        if (features->type == WACOM_21UX2) {
                                input_report_key(input, BTN_0, (data[5] & 0x01));
@@@ -799,6 -849,9 +849,9 @@@ static int wacom_bpt_touch(struct wacom
        unsigned char *data = wacom->data;
        int i;
  
+       if (data[0] != 0x02)
+           return 0;
        for (i = 0; i < 2; i++) {
                int offset = (data[1] & 0x80) ? (8 * i) : (9 * i);
                bool touch = data[offset + 3] & 0x80;
        return 0;
  }
  
+ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
+ {
+       struct input_dev *input = wacom->input;
+       int slot_id = data[0] - 2;  /* data[0] is between 2 and 17 */
+       bool touch = data[1] & 0x80;
+       touch = touch && !wacom->shared->stylus_in_proximity;
+       input_mt_slot(input, slot_id);
+       input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+       if (touch) {
+               int x = (data[2] << 4) | (data[4] >> 4);
+               int y = (data[3] << 4) | (data[4] & 0x0f);
+               int w = data[6];
+               input_report_abs(input, ABS_MT_POSITION_X, x);
+               input_report_abs(input, ABS_MT_POSITION_Y, y);
+               input_report_abs(input, ABS_MT_TOUCH_MAJOR, w);
+       }
+ }
+ static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
+ {
+       struct input_dev *input = wacom->input;
+       input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
+       input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
+       input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
+       input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
+ }
+ static int wacom_bpt3_touch(struct wacom_wac *wacom)
+ {
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int count = data[1] & 0x03;
+       int i;
+       if (data[0] != 0x02)
+           return 0;
+       /* data has up to 7 fixed sized 8-byte messages starting at data[2] */
+       for (i = 0; i < count; i++) {
+               int offset = (8 * i) + 2;
+               int msg_id = data[offset];
+               if (msg_id >= 2 && msg_id <= 17)
+                       wacom_bpt3_touch_msg(wacom, data + offset);
+               else if (msg_id == 128)
+                       wacom_bpt3_button_msg(wacom, data + offset);
+       }
+       input_mt_report_pointer_emulation(input, true);
+       input_sync(input);
+       return 0;
+ }
  static int wacom_bpt_pen(struct wacom_wac *wacom)
  {
        struct input_dev *input = wacom->input;
        unsigned char *data = wacom->data;
        int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
  
-       /*
-        * Similar to Graphire protocol, data[1] & 0x20 is proximity and
-        * data[1] & 0x18 is tool ID.  0x30 is safety check to ignore
-        * 2 unused tool ID's.
-        */
-       prox = (data[1] & 0x30) == 0x30;
+       if (data[0] != 0x02)
+           return 0;
+       prox = (data[1] & 0x20) == 0x20;
  
        /*
         * All reports shared between PEN and RUBBER tool must be
@@@ -912,7 -1024,9 +1024,9 @@@ static int wacom_bpt_irq(struct wacom_w
  {
        if (len == WACOM_PKGLEN_BBTOUCH)
                return wacom_bpt_touch(wacom);
-       else if (len == WACOM_PKGLEN_BBFUN)
+       else if (len == WACOM_PKGLEN_BBTOUCH3)
+               return wacom_bpt3_touch(wacom);
+       else if (len == WACOM_PKGLEN_BBFUN || len == WACOM_PKGLEN_BBPEN)
                return wacom_bpt_pen(wacom);
  
        return 0;
@@@ -955,6 -1069,7 +1069,7 @@@ void wacom_wac_irq(struct wacom_wac *wa
        case CINTIQ:
        case WACOM_BEE:
        case WACOM_21UX2:
+       case WACOM_24HD:
                sync = wacom_intuos_irq(wacom_wac);
                break;
  
@@@ -1031,9 -1146,9 +1146,9 @@@ void wacom_setup_device_quirks(struct w
            features->type == BAMBOO_PT)
                features->quirks |= WACOM_QUIRK_MULTI_INPUT;
  
-       /* quirks for bamboo touch */
+       /* quirk for bamboo touch with 2 low res touches */
        if (features->type == BAMBOO_PT &&
-           features->device_type == BTN_TOOL_DOUBLETAP) {
+           features->pktlen == WACOM_PKGLEN_BBTOUCH) {
                features->x_max <<= 5;
                features->y_max <<= 5;
                features->x_fuzz <<= 5;
@@@ -1110,6 -1225,26 +1225,26 @@@ void wacom_setup_input_capabilities(str
                __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
                break;
  
+       case WACOM_24HD:
+               __set_bit(BTN_A, input_dev->keybit);
+               __set_bit(BTN_B, input_dev->keybit);
+               __set_bit(BTN_C, input_dev->keybit);
+               __set_bit(BTN_X, input_dev->keybit);
+               __set_bit(BTN_Y, input_dev->keybit);
+               __set_bit(BTN_Z, input_dev->keybit);
+               for (i = 0; i < 10; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+               __set_bit(KEY_PROG1, input_dev->keybit);
+               __set_bit(KEY_PROG2, input_dev->keybit);
+               __set_bit(KEY_PROG3, input_dev->keybit);
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
+               wacom_setup_cintiq(wacom_wac);
+               break;
        case WACOM_21UX2:
                __set_bit(BTN_A, input_dev->keybit);
                __set_bit(BTN_B, input_dev->keybit);
                        __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
                        __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
  
-                       input_mt_init_slots(input_dev, 2);
+                       if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+                               __set_bit(BTN_TOOL_TRIPLETAP,
+                                         input_dev->keybit);
+                               __set_bit(BTN_TOOL_QUADTAP,
+                                         input_dev->keybit);
+                               input_mt_init_slots(input_dev, 16);
+                               input_set_abs_params(input_dev,
+                                                    ABS_MT_TOUCH_MAJOR,
+                                                    0, 255, 0, 0);
+                       } else {
+                               input_mt_init_slots(input_dev, 2);
+                       }
                        input_set_abs_params(input_dev, ABS_MT_POSITION_X,
                                             0, features->x_max,
                                             features->x_fuzz, 0);
@@@ -1425,6 -1574,9 +1574,9 @@@ static const struct wacom_features waco
  static const struct wacom_features wacom_features_0xBC =
        { "Wacom Intuos4 WL",     WACOM_PKGLEN_INTUOS,    40840, 25400, 2047,
          63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ static const struct wacom_features wacom_features_0xF4 =
+       { "Wacom Cintiq 24HD",    WACOM_PKGLEN_INTUOS,   104480, 65600, 2047,
+         63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
  static const struct wacom_features wacom_features_0x3F =
        { "Wacom Cintiq 21UX",    WACOM_PKGLEN_INTUOS,    87200, 65600, 1023,
          63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
@@@ -1470,9 -1622,6 +1622,9 @@@ static const struct wacom_features waco
  static const struct wacom_features wacom_features_0xE6 =
        { "Wacom ISDv4 E6",       WACOM_PKGLEN_TPC2FG,    27760, 15694,  255,
          0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 +static const struct wacom_features wacom_features_0xEC =
 +      { "Wacom ISDv4 EC",       WACOM_PKGLEN_GRAPHIRE,  25710, 14500,  255,
 +        0, TABLETPC,    WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  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 };
@@@ -1509,6 -1658,15 +1661,15 @@@ static const struct wacom_features waco
  static struct wacom_features wacom_features_0xDB =
        { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN,  21648, 13700, 1023,
          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ static const struct wacom_features wacom_features_0xDD =
+         { "Wacom Bamboo Connect", WACOM_PKGLEN_BBPEN,     14720,  9200, 1023,
+           31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ static const struct wacom_features wacom_features_0xDE =
+         { "Wacom Bamboo 16FG 4x5", WACOM_PKGLEN_BBPEN,    14720,  9200, 1023,
+           31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+ static const struct wacom_features wacom_features_0xDF =
+         { "Wacom Bamboo 16FG 6x8", WACOM_PKGLEN_BBPEN,    21648, 13700, 1023,
+           31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
  static const struct wacom_features wacom_features_0x6004 =
        { "ISD-V4",               WACOM_PKGLEN_GRAPHIRE,  12800,  8000,  255,
          0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@@ -1604,6 -1762,9 +1765,9 @@@ const struct usb_device_id wacom_ids[] 
        { USB_DEVICE_WACOM(0xD8) },
        { USB_DEVICE_WACOM(0xDA) },
        { USB_DEVICE_WACOM(0xDB) },
+       { USB_DEVICE_WACOM(0xDD) },
+       { USB_DEVICE_WACOM(0xDE) },
+       { USB_DEVICE_WACOM(0xDF) },
        { USB_DEVICE_WACOM(0xF0) },
        { USB_DEVICE_WACOM(0xCC) },
        { USB_DEVICE_WACOM(0x90) },
        { USB_DEVICE_WACOM(0xE2) },
        { USB_DEVICE_WACOM(0xE3) },
        { USB_DEVICE_WACOM(0xE6) },
 +      { USB_DEVICE_WACOM(0xEC) },
        { USB_DEVICE_WACOM(0x47) },
+       { USB_DEVICE_WACOM(0xF4) },
        { USB_DEVICE_LENOVO(0x6004) },
        { }
  };