Input: wacom_w8001 - add multitouch slot support
authorPeter Hutterer <peter.hutterer@who-t.net>
Sun, 29 Aug 2010 05:00:47 +0000 (22:00 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sun, 29 Aug 2010 05:46:14 +0000 (22:46 -0700)
Some serial wacom devices support two-finger touch. Test for this during
init and parse the touch packets accordingly. Touch packets are
processed using Protocol B (MT Slots).

Note: there are several wacom versions that do touch but not two-finger
touch. These are not catered for here, touch events for these are simply
discarded.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Acked-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/touchscreen/wacom_w8001.c

index 89abda0..9ae4c7b 100644 (file)
@@ -48,6 +48,8 @@ MODULE_LICENSE("GPL");
 #define W8001_PKTLEN_TPCCTL    11      /* control packet */
 #define W8001_PKTLEN_TOUCH2FG  13
 
+#define MAX_TRACKING_ID                0xFF    /* arbitrarily chosen */
+
 struct w8001_coord {
        u8 rdy;
        u8 tsw;
@@ -85,6 +87,7 @@ struct w8001 {
        char phys[32];
        int type;
        unsigned int pktlen;
+       int trkid[2];
 };
 
 static void parse_data(u8 *data, struct w8001_coord *coord)
@@ -111,6 +114,35 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
        coord->tilt_y = data[8] & 0x7F;
 }
 
+static void parse_touch(struct w8001 *w8001)
+{
+       static int trkid;
+       struct input_dev *dev = w8001->dev;
+       unsigned char *data = w8001->data;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               input_mt_slot(dev, i);
+
+               if (data[0] & (1 << i)) {
+                       int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
+                       int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
+                       /* data[5,6] and [11,12] is finger capacity */
+
+                       input_report_abs(dev, ABS_MT_POSITION_X, x);
+                       input_report_abs(dev, ABS_MT_POSITION_Y, y);
+                       input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
+                       if (w8001->trkid[i] < 0)
+                               w8001->trkid[i] = trkid++ & MAX_TRACKING_ID;
+               } else {
+                       w8001->trkid[i] = -1;
+               }
+               input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]);
+       }
+
+       input_sync(dev);
+}
+
 static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
 {
        memset(query, 0, sizeof(*query));
@@ -220,10 +252,10 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
                complete(&w8001->cmd_done);
                break;
 
+       /* 2 finger touch packet */
        case W8001_PKTLEN_TOUCH2FG - 1:
-               /* ignore two-finger touch packet. */
-               if (w8001->pktlen == w8001->idx)
-                       w8001->idx = 0;
+               w8001->idx = 0;
+               parse_touch(w8001);
                break;
        }
 
@@ -285,6 +317,16 @@ static int w8001_setup(struct w8001 *w8001)
                        break;
                case 5:
                        w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+                       input_mt_create_slots(dev, 2);
+                       input_set_abs_params(dev, ABS_MT_TRACKING_ID,
+                                               0, MAX_TRACKING_ID, 0, 0);
+                       input_set_abs_params(dev, ABS_MT_POSITION_X,
+                                               0, touch.x, 0, 0);
+                       input_set_abs_params(dev, ABS_MT_POSITION_Y,
+                                               0, touch.y, 0, 0);
+                       input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
+                                               0, 0, 0, 0);
                        break;
                }
        }
@@ -330,6 +372,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
        w8001->serio = serio;
        w8001->id = serio->id.id;
        w8001->dev = input_dev;
+       w8001->trkid[0] = w8001->trkid[1] = -1;
        init_completion(&w8001->cmd_done);
        snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);