Commit 5fca6cac authored by Ping Cheng's avatar Ping Cheng Committed by Dmitry Torokhov

Input: wacom_w8001 - add single-touch support

Emulate single-touch compatible events for the 2-finger panels
so that they can be used with single-touch legacy clients.

Assign device ids as Wacom USB vendor ID and product ID.
Name the device to reflect its specific features.

Scale touch coordinates to pen maximum if pen supported.
Signed-off-by: default avatarPing Cheng <pingc@wacom.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 9d084a3d
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (c) 2008 Jaya Kumar * Copyright (c) 2008 Jaya Kumar
* Copyright (c) 2010 Red Hat, Inc. * Copyright (c) 2010 Red Hat, Inc.
* Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>
* *
* This file is subject to the terms and conditions of the GNU General Public * This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for * License. See the file COPYING in the main directory of this archive for
...@@ -64,11 +65,11 @@ struct w8001_coord { ...@@ -64,11 +65,11 @@ struct w8001_coord {
/* touch query reply packet */ /* touch query reply packet */
struct w8001_touch_query { struct w8001_touch_query {
u16 x;
u16 y;
u8 panel_res; u8 panel_res;
u8 capacity_res; u8 capacity_res;
u8 sensor_id; u8 sensor_id;
u16 x;
u16 y;
}; };
/* /*
...@@ -87,9 +88,14 @@ struct w8001 { ...@@ -87,9 +88,14 @@ struct w8001 {
char phys[32]; char phys[32];
int type; int type;
unsigned int pktlen; unsigned int pktlen;
u16 max_touch_x;
u16 max_touch_y;
u16 max_pen_x;
u16 max_pen_y;
char name[64];
}; };
static void parse_data(u8 *data, struct w8001_coord *coord) static void parse_pen_data(u8 *data, struct w8001_coord *coord)
{ {
memset(coord, 0, sizeof(*coord)); memset(coord, 0, sizeof(*coord));
...@@ -113,11 +119,30 @@ static void parse_data(u8 *data, struct w8001_coord *coord) ...@@ -113,11 +119,30 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
coord->tilt_y = data[8] & 0x7F; coord->tilt_y = data[8] & 0x7F;
} }
static void parse_touch(struct w8001 *w8001) static void parse_single_touch(u8 *data, struct w8001_coord *coord)
{
coord->x = (data[1] << 7) | data[2];
coord->y = (data[3] << 7) | data[4];
coord->tsw = data[0] & 0x01;
}
static void scale_touch_coordinates(struct w8001 *w8001,
unsigned int *x, unsigned int *y)
{
if (w8001->max_pen_x && w8001->max_touch_x)
*x = *x * w8001->max_pen_x / w8001->max_touch_x;
if (w8001->max_pen_y && w8001->max_touch_y)
*y = *y * w8001->max_pen_y / w8001->max_touch_y;
}
static void parse_multi_touch(struct w8001 *w8001)
{ {
struct input_dev *dev = w8001->dev; struct input_dev *dev = w8001->dev;
unsigned char *data = w8001->data; unsigned char *data = w8001->data;
unsigned int x, y;
int i; int i;
int count = 0;
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
bool touch = data[0] & (1 << i); bool touch = data[0] & (1 << i);
...@@ -125,15 +150,29 @@ static void parse_touch(struct w8001 *w8001) ...@@ -125,15 +150,29 @@ static void parse_touch(struct w8001 *w8001)
input_mt_slot(dev, i); input_mt_slot(dev, i);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
if (touch) { if (touch) {
int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); x = (data[6 * i + 1] << 7) | data[6 * i + 2];
int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); y = (data[6 * i + 3] << 7) | data[6 * i + 4];
/* data[5,6] and [11,12] is finger capacity */ /* data[5,6] and [11,12] is finger capacity */
/* scale to pen maximum */
scale_touch_coordinates(w8001, &x, &y);
input_report_abs(dev, ABS_MT_POSITION_X, x); input_report_abs(dev, ABS_MT_POSITION_X, x);
input_report_abs(dev, ABS_MT_POSITION_Y, y); input_report_abs(dev, ABS_MT_POSITION_Y, y);
count++;
} }
} }
/* emulate single touch events when stylus is out of proximity.
* This is to make single touch backward support consistent
* across all Wacom single touch devices.
*/
if (w8001->type != BTN_TOOL_PEN &&
w8001->type != BTN_TOOL_RUBBER) {
w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED;
input_mt_report_pointer_emulation(dev, true);
}
input_sync(dev); input_sync(dev);
} }
...@@ -152,6 +191,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query) ...@@ -152,6 +191,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
query->y = data[5] << 9; query->y = data[5] << 9;
query->y |= data[6] << 2; query->y |= data[6] << 2;
query->y |= (data[2] >> 3) & 0x3; query->y |= (data[2] >> 3) & 0x3;
/* Early days' single-finger touch models need the following defaults */
if (!query->x && !query->y) {
query->x = 1024;
query->y = 1024;
if (query->panel_res)
query->x = query->y = (1 << query->panel_res);
query->panel_res = 10;
}
} }
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
...@@ -161,16 +209,15 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) ...@@ -161,16 +209,15 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
/* /*
* We have 1 bit for proximity (rdy) and 3 bits for tip, side, * We have 1 bit for proximity (rdy) and 3 bits for tip, side,
* side2/eraser. If rdy && f2 are set, this can be either pen + side2, * side2/eraser. If rdy && f2 are set, this can be either pen + side2,
* or eraser. assume * or eraser. Assume:
* - if dev is already in proximity and f2 is toggled → pen + side2 * - if dev is already in proximity and f2 is toggled → pen + side2
* - if dev comes into proximity with f2 set → eraser * - if dev comes into proximity with f2 set → eraser
* If f2 disappears after assuming eraser, fake proximity out for * If f2 disappears after assuming eraser, fake proximity out for
* eraser and in for pen. * eraser and in for pen.
*/ */
if (!w8001->type) { switch (w8001->type) {
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; case BTN_TOOL_RUBBER:
} else if (w8001->type == BTN_TOOL_RUBBER) {
if (!coord->f2) { if (!coord->f2) {
input_report_abs(dev, ABS_PRESSURE, 0); input_report_abs(dev, ABS_PRESSURE, 0);
input_report_key(dev, BTN_TOUCH, 0); input_report_key(dev, BTN_TOUCH, 0);
...@@ -180,8 +227,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) ...@@ -180,8 +227,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
input_sync(dev); input_sync(dev);
w8001->type = BTN_TOOL_PEN; w8001->type = BTN_TOOL_PEN;
} }
} else { break;
case BTN_TOOL_FINGER:
input_report_key(dev, BTN_TOUCH, 0);
input_report_key(dev, BTN_TOOL_FINGER, 0);
input_sync(dev);
/* fall through */
case KEY_RESERVED:
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
break;
default:
input_report_key(dev, BTN_STYLUS2, coord->f2); input_report_key(dev, BTN_STYLUS2, coord->f2);
break;
} }
input_report_abs(dev, ABS_X, coord->x); input_report_abs(dev, ABS_X, coord->x);
...@@ -193,7 +253,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) ...@@ -193,7 +253,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
input_sync(dev); input_sync(dev);
if (!coord->rdy) if (!coord->rdy)
w8001->type = 0; w8001->type = KEY_RESERVED;
}
static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
{
struct input_dev *dev = w8001->dev;
unsigned int x = coord->x;
unsigned int y = coord->y;
/* scale to pen maximum */
scale_touch_coordinates(w8001, &x, &y);
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
input_report_key(dev, BTN_TOUCH, coord->tsw);
input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
input_sync(dev);
w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;
} }
static irqreturn_t w8001_interrupt(struct serio *serio, static irqreturn_t w8001_interrupt(struct serio *serio,
...@@ -214,9 +293,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio, ...@@ -214,9 +293,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
case W8001_PKTLEN_TOUCH93 - 1: case W8001_PKTLEN_TOUCH93 - 1:
case W8001_PKTLEN_TOUCH9A - 1: case W8001_PKTLEN_TOUCH9A - 1:
/* ignore one-finger touch packet. */ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
if (w8001->pktlen == w8001->idx) if (tmp != W8001_TOUCH_BYTE)
break;
if (w8001->pktlen == w8001->idx) {
w8001->idx = 0; w8001->idx = 0;
if (w8001->type != BTN_TOOL_PEN &&
w8001->type != BTN_TOOL_RUBBER) {
parse_single_touch(w8001->data, &coord);
report_single_touch(w8001, &coord);
}
}
break; break;
/* Pen coordinates packet */ /* Pen coordinates packet */
...@@ -225,18 +313,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio, ...@@ -225,18 +313,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
if (unlikely(tmp == W8001_TAB_BYTE)) if (unlikely(tmp == W8001_TAB_BYTE))
break; break;
tmp = (w8001->data[0] & W8001_TOUCH_BYTE); tmp = w8001->data[0] & W8001_TOUCH_BYTE;
if (tmp == W8001_TOUCH_BYTE) if (tmp == W8001_TOUCH_BYTE)
break; break;
w8001->idx = 0; w8001->idx = 0;
parse_data(w8001->data, &coord); parse_pen_data(w8001->data, &coord);
report_pen_events(w8001, &coord); report_pen_events(w8001, &coord);
break; break;
/* control packet */ /* control packet */
case W8001_PKTLEN_TPCCTL - 1: case W8001_PKTLEN_TPCCTL - 1:
tmp = (w8001->data[0] & W8001_TOUCH_MASK); tmp = w8001->data[0] & W8001_TOUCH_MASK;
if (tmp == W8001_TOUCH_BYTE) if (tmp == W8001_TOUCH_BYTE)
break; break;
...@@ -249,7 +337,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio, ...@@ -249,7 +337,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
/* 2 finger touch packet */ /* 2 finger touch packet */
case W8001_PKTLEN_TOUCH2FG - 1: case W8001_PKTLEN_TOUCH2FG - 1:
w8001->idx = 0; w8001->idx = 0;
parse_touch(w8001); parse_multi_touch(w8001);
break; break;
} }
...@@ -279,6 +367,7 @@ static int w8001_setup(struct w8001 *w8001) ...@@ -279,6 +367,7 @@ static int w8001_setup(struct w8001 *w8001)
{ {
struct input_dev *dev = w8001->dev; struct input_dev *dev = w8001->dev;
struct w8001_coord coord; struct w8001_coord coord;
struct w8001_touch_query touch;
int error; int error;
error = w8001_command(w8001, W8001_CMD_STOP, false); error = w8001_command(w8001, W8001_CMD_STOP, false);
...@@ -287,14 +376,21 @@ static int w8001_setup(struct w8001 *w8001) ...@@ -287,14 +376,21 @@ static int w8001_setup(struct w8001 *w8001)
msleep(250); /* wait 250ms before querying the device */ msleep(250); /* wait 250ms before querying the device */
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
/* penabled? */ /* penabled? */
error = w8001_command(w8001, W8001_CMD_QUERY, true); error = w8001_command(w8001, W8001_CMD_QUERY, true);
if (!error) { if (!error) {
__set_bit(BTN_TOUCH, dev->keybit);
__set_bit(BTN_TOOL_PEN, dev->keybit); __set_bit(BTN_TOOL_PEN, dev->keybit);
__set_bit(BTN_TOOL_RUBBER, dev->keybit); __set_bit(BTN_TOOL_RUBBER, dev->keybit);
__set_bit(BTN_STYLUS, dev->keybit); __set_bit(BTN_STYLUS, dev->keybit);
__set_bit(BTN_STYLUS2, dev->keybit); __set_bit(BTN_STYLUS2, dev->keybit);
parse_data(w8001->response, &coord);
parse_pen_data(w8001->response, &coord);
w8001->max_pen_x = coord.x;
w8001->max_pen_y = coord.y;
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
...@@ -303,6 +399,8 @@ static int w8001_setup(struct w8001 *w8001) ...@@ -303,6 +399,8 @@ static int w8001_setup(struct w8001 *w8001)
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
} }
w8001->id = 0x90;
strlcat(w8001->name, " Penabled", sizeof(w8001->name));
} }
/* Touch enabled? */ /* Touch enabled? */
...@@ -313,24 +411,38 @@ static int w8001_setup(struct w8001 *w8001) ...@@ -313,24 +411,38 @@ static int w8001_setup(struct w8001 *w8001)
* second byte is empty, which indicates touch is not supported. * second byte is empty, which indicates touch is not supported.
*/ */
if (!error && w8001->response[1]) { if (!error && w8001->response[1]) {
struct w8001_touch_query touch; __set_bit(BTN_TOUCH, dev->keybit);
__set_bit(BTN_TOOL_FINGER, dev->keybit);
parse_touchquery(w8001->response, &touch); parse_touchquery(w8001->response, &touch);
w8001->max_touch_x = touch.x;
w8001->max_touch_y = touch.y;
/* scale to pen maximum */
if (w8001->max_pen_x && w8001->max_pen_y) {
touch.x = w8001->max_pen_x;
touch.y = w8001->max_pen_y;
}
input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
__set_bit(BTN_TOOL_FINGER, dev->keybit);
switch (touch.sensor_id) { switch (touch.sensor_id) {
case 0: case 0:
case 2: case 2:
w8001->pktlen = W8001_PKTLEN_TOUCH93; w8001->pktlen = W8001_PKTLEN_TOUCH93;
w8001->id = 0x93;
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
break; break;
case 1: case 1:
case 3: case 3:
case 4: case 4:
w8001->pktlen = W8001_PKTLEN_TOUCH9A; w8001->pktlen = W8001_PKTLEN_TOUCH9A;
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
w8001->id = 0x9a;
break; break;
case 5: case 5:
w8001->pktlen = W8001_PKTLEN_TOUCH2FG; w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
...@@ -341,10 +453,18 @@ static int w8001_setup(struct w8001 *w8001) ...@@ -341,10 +453,18 @@ static int w8001_setup(struct w8001 *w8001)
0, touch.y, 0, 0); 0, touch.y, 0, 0);
input_set_abs_params(dev, ABS_MT_TOOL_TYPE, input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
0, MT_TOOL_MAX, 0, 0); 0, MT_TOOL_MAX, 0, 0);
strlcat(w8001->name, " 2FG", sizeof(w8001->name));
if (w8001->max_pen_x && w8001->max_pen_y)
w8001->id = 0xE3;
else
w8001->id = 0xE2;
break; break;
} }
} }
strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
return w8001_command(w8001, W8001_CMD_START, false); return w8001_command(w8001, W8001_CMD_START, false);
} }
...@@ -384,22 +504,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) ...@@ -384,22 +504,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
} }
w8001->serio = serio; w8001->serio = serio;
w8001->id = serio->id.id;
w8001->dev = input_dev; w8001->dev = input_dev;
init_completion(&w8001->cmd_done); init_completion(&w8001->cmd_done);
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
input_dev->name = "Wacom W8001 Penabled Serial TouchScreen";
input_dev->phys = w8001->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = SERIO_W8001;
input_dev->id.product = w8001->id;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
__set_bit(BTN_TOUCH, input_dev->keybit);
serio_set_drvdata(serio, w8001); serio_set_drvdata(serio, w8001);
err = serio_open(serio, drv); err = serio_open(serio, drv);
if (err) if (err)
...@@ -409,6 +517,14 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) ...@@ -409,6 +517,14 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
if (err) if (err)
goto fail3; goto fail3;
input_dev->name = w8001->name;
input_dev->phys = w8001->phys;
input_dev->id.product = w8001->id;
input_dev->id.bustype = BUS_RS232;
input_dev->id.vendor = 0x056a;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &serio->dev;
err = input_register_device(w8001->dev); err = input_register_device(w8001->dev);
if (err) if (err)
goto fail3; goto fail3;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment