Skip to content

Commit

Permalink
Input: wacom_w8001 - add single-touch support
Browse files Browse the repository at this point in the history
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: Ping Cheng <pingc@wacom.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
  • Loading branch information
Ping Cheng authored and Dmitry Torokhov committed Jan 11, 2011
1 parent 9d084a3 commit 5fca6ca
Showing 1 changed file with 149 additions and 33 deletions.
182 changes: 149 additions & 33 deletions drivers/input/touchscreen/wacom_w8001.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* Copyright (c) 2008 Jaya Kumar
* 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
* License. See the file COPYING in the main directory of this archive for
Expand Down Expand Up @@ -64,11 +65,11 @@ struct w8001_coord {

/* touch query reply packet */
struct w8001_touch_query {
u16 x;
u16 y;
u8 panel_res;
u8 capacity_res;
u8 sensor_id;
u16 x;
u16 y;
};

/*
Expand All @@ -87,9 +88,14 @@ struct w8001 {
char phys[32];
int type;
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));

Expand All @@ -113,27 +119,60 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
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;
unsigned char *data = w8001->data;
unsigned int x, y;
int i;
int count = 0;

for (i = 0; i < 2; i++) {
bool touch = data[0] & (1 << i);

input_mt_slot(dev, i);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
if (touch) {
int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
x = (data[6 * i + 1] << 7) | data[6 * i + 2];
y = (data[6 * i + 3] << 7) | data[6 * i + 4];
/* 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_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);
}

Expand All @@ -152,6 +191,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
query->y = data[5] << 9;
query->y |= data[6] << 2;
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)
Expand All @@ -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,
* 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 comes into proximity with f2 set → eraser
* If f2 disappears after assuming eraser, fake proximity out for
* eraser and in for pen.
*/

if (!w8001->type) {
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
} else if (w8001->type == BTN_TOOL_RUBBER) {
switch (w8001->type) {
case BTN_TOOL_RUBBER:
if (!coord->f2) {
input_report_abs(dev, ABS_PRESSURE, 0);
input_report_key(dev, BTN_TOUCH, 0);
Expand All @@ -180,8 +227,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
input_sync(dev);
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);
break;
}

input_report_abs(dev, ABS_X, coord->x);
Expand All @@ -193,7 +253,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
input_sync(dev);

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,
Expand All @@ -214,9 +293,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,

case W8001_PKTLEN_TOUCH93 - 1:
case W8001_PKTLEN_TOUCH9A - 1:
/* ignore one-finger touch packet. */
if (w8001->pktlen == w8001->idx)
tmp = w8001->data[0] & W8001_TOUCH_BYTE;
if (tmp != W8001_TOUCH_BYTE)
break;

if (w8001->pktlen == w8001->idx) {
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;

/* Pen coordinates packet */
Expand All @@ -225,18 +313,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
if (unlikely(tmp == W8001_TAB_BYTE))
break;

tmp = (w8001->data[0] & W8001_TOUCH_BYTE);
tmp = w8001->data[0] & W8001_TOUCH_BYTE;
if (tmp == W8001_TOUCH_BYTE)
break;

w8001->idx = 0;
parse_data(w8001->data, &coord);
parse_pen_data(w8001->data, &coord);
report_pen_events(w8001, &coord);
break;

/* control packet */
case W8001_PKTLEN_TPCCTL - 1:
tmp = (w8001->data[0] & W8001_TOUCH_MASK);
tmp = w8001->data[0] & W8001_TOUCH_MASK;
if (tmp == W8001_TOUCH_BYTE)
break;

Expand All @@ -249,7 +337,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
/* 2 finger touch packet */
case W8001_PKTLEN_TOUCH2FG - 1:
w8001->idx = 0;
parse_touch(w8001);
parse_multi_touch(w8001);
break;
}

Expand Down Expand Up @@ -279,6 +367,7 @@ static int w8001_setup(struct w8001 *w8001)
{
struct input_dev *dev = w8001->dev;
struct w8001_coord coord;
struct w8001_touch_query touch;
int error;

error = w8001_command(w8001, W8001_CMD_STOP, false);
Expand All @@ -287,14 +376,21 @@ static int w8001_setup(struct w8001 *w8001)

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? */
error = w8001_command(w8001, W8001_CMD_QUERY, true);
if (!error) {
__set_bit(BTN_TOUCH, dev->keybit);
__set_bit(BTN_TOOL_PEN, dev->keybit);
__set_bit(BTN_TOOL_RUBBER, dev->keybit);
__set_bit(BTN_STYLUS, 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_Y, 0, coord.y, 0, 0);
Expand All @@ -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_Y, 0, coord.tilt_y, 0, 0);
}
w8001->id = 0x90;
strlcat(w8001->name, " Penabled", sizeof(w8001->name));
}

/* Touch enabled? */
Expand All @@ -313,24 +411,38 @@ static int w8001_setup(struct w8001 *w8001)
* second byte is empty, which indicates touch is not supported.
*/
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);
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_Y, 0, touch.y, 0, 0);
__set_bit(BTN_TOOL_FINGER, dev->keybit);

switch (touch.sensor_id) {
case 0:
case 2:
w8001->pktlen = W8001_PKTLEN_TOUCH93;
w8001->id = 0x93;
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
break;

case 1:
case 3:
case 4:
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
w8001->id = 0x9a;
break;

case 5:
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;

Expand All @@ -341,10 +453,18 @@ static int w8001_setup(struct w8001 *w8001)
0, touch.y, 0, 0);
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
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;
}
}

strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));

return w8001_command(w8001, W8001_CMD_START, false);
}

Expand Down Expand Up @@ -384,22 +504,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
}

w8001->serio = serio;
w8001->id = serio->id.id;
w8001->dev = input_dev;
init_completion(&w8001->cmd_done);
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);
err = serio_open(serio, drv);
if (err)
Expand All @@ -409,6 +517,14 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
if (err)
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);
if (err)
goto fail3;
Expand Down

0 comments on commit 5fca6ca

Please sign in to comment.