Skip to content

Commit

Permalink
Input: wacom - add support for new LCD tablets
Browse files Browse the repository at this point in the history
This adds support for the foolowing Wacom devices:

 - 0x9F - a single touch only LCD tablet;
 - 0xE2 - a two finger touch only LCD tablet;
 - 0xE3 -  a two finger touch, penabled LCD tablet.

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 Dec 15, 2009
1 parent ee54500 commit ec67bbe
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 132 deletions.
3 changes: 3 additions & 0 deletions drivers/input/tablet/wacom.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
* v1.51 (pc) - Added support for Intuos4
* v1.52 (pc) - Query Wacom data upon system resume
* - add defines for features->type
* - add new devices (0x9F, 0xE2, and 0XE3)
*/

/*
Expand Down Expand Up @@ -135,6 +136,8 @@ extern void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_w
extern void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern __u16 wacom_le16_to_cpu(unsigned char *data);
Expand Down
226 changes: 155 additions & 71 deletions drivers/input/tablet/wacom_sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) |
BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) |
BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_STYLUS2);
input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
}
Expand Down Expand Up @@ -256,6 +257,7 @@ void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE) |
BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) |
BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) |
BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_TOOL_BRUSH) |
BIT_MASK(BTN_TOOL_PENCIL) | BIT_MASK(BTN_TOOL_AIRBRUSH) |
BIT_MASK(BTN_TOOL_LENS) | BIT_MASK(BTN_STYLUS2);
Expand All @@ -269,20 +271,41 @@ void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)

void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_STYLUS2);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) |
BIT_MASK(BTN_STYLUS) | BIT_MASK(BTN_STYLUS2);
}

void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER);
}

void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
if (wacom_wac->features->device_type == BTN_TOOL_DOUBLETAP ||
wacom_wac->features->device_type == BTN_TOOL_TRIPLETAP) {
input_set_abs_params(input_dev, ABS_RX, 0, wacom_wac->features->x_phy, 0, 0);
input_set_abs_params(input_dev, ABS_RY, 0, wacom_wac->features->y_phy, 0, 0);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_DOUBLETAP);
}
}

void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
if (wacom_wac->features->device_type == BTN_TOOL_TRIPLETAP) {
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_TRIPLETAP);
input_dev->evbit[0] |= BIT_MASK(EV_MSC);
input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL);
}
}

static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
struct wacom_wac *wacom_wac)
struct wacom_features *features)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct wacom_features *features = wacom_wac->features;
char limit = 0, result = 0;
char limit = 0;
/* result has to be defined as int for some devices */
int result = 0;
int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0;
unsigned char *report;

Expand Down Expand Up @@ -328,13 +351,24 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
case HID_USAGE_X:
if (usage == WCM_DESKTOP) {
if (finger) {
features->touch_x_max =
features->touch_y_max =
wacom_le16_to_cpu(&report[i + 3]);
features->device_type = BTN_TOOL_DOUBLETAP;
if (features->type == TABLETPC2FG) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_TPC2FG;
features->device_type = BTN_TOOL_TRIPLETAP;
}
features->x_max =
wacom_le16_to_cpu(&report[i + 3]);
features->x_phy =
wacom_le16_to_cpu(&report[i + 6]);
i += 7;
features->unit = report[i + 9];
features->unitExpo = report[i + 11];
i += 12;
} else if (pen) {
/* penabled only accepts exact bytes of data */
if (features->type == TABLETPC2FG)
features->pktlen = WACOM_PKGLEN_PENABLED;
features->device_type = BTN_TOOL_PEN;
features->x_max =
wacom_le16_to_cpu(&report[i + 3]);
i += 4;
Expand All @@ -350,10 +384,35 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
break;

case HID_USAGE_Y:
if (usage == WCM_DESKTOP)
features->y_max =
wacom_le16_to_cpu(&report[i + 3]);
i += 4;
if (usage == WCM_DESKTOP) {
if (finger) {
features->device_type = BTN_TOOL_DOUBLETAP;
if (features->type == TABLETPC2FG) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_TPC2FG;
features->device_type = BTN_TOOL_TRIPLETAP;
features->y_max =
wacom_le16_to_cpu(&report[i + 3]);
features->y_phy =
wacom_le16_to_cpu(&report[i + 6]);
i += 7;
} else {
features->y_max =
features->x_max;
features->y_phy =
wacom_le16_to_cpu(&report[i + 3]);
i += 4;
}
} else if (pen) {
/* penabled only accepts exact bytes of data */
if (features->type == TABLETPC2FG)
features->pktlen = WACOM_PKGLEN_PENABLED;
features->device_type = BTN_TOOL_PEN;
features->y_max =
wacom_le16_to_cpu(&report[i + 3]);
i += 4;
}
}
break;

case HID_USAGE_FINGER:
Expand All @@ -376,7 +435,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
break;

case HID_COLLECTION:
/* reset UsagePage ans Finger */
/* reset UsagePage and Finger */
finger = usage = 0;
break;
}
Expand All @@ -388,51 +447,100 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
return result;
}

static int wacom_query_tablet_data(struct usb_interface *intf)
static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features)
{
unsigned char *rep_data;
int limit = 0;
int error;
int limit = 0, report_id = 2;
int error = -ENOMEM;

rep_data = kmalloc(2, GFP_KERNEL);
if (!rep_data)
return -ENOMEM;

do {
rep_data[0] = 2;
rep_data[1] = 2;
error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
2, rep_data, 2);
if (error >= 0)
error = usb_get_report(intf,
WAC_HID_FEATURE_REPORT, 2,
rep_data, 2);
} while ((error < 0 || rep_data[1] != 2) && limit++ < 5);
return error;

/* ask to report tablet data if it is 2FGT or not a Tablet PC */
if (features->device_type == BTN_TOOL_TRIPLETAP) {
do {
rep_data[0] = 3;
rep_data[1] = 4;
report_id = 3;
error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
report_id, rep_data, 2);
if (error >= 0)
error = usb_get_report(intf,
WAC_HID_FEATURE_REPORT, report_id,
rep_data, 3);
} while ((error < 0 || rep_data[1] != 4) && limit++ < 5);
} else if (features->type != TABLETPC && features->type != TABLETPC2FG) {
do {
rep_data[0] = 2;
rep_data[1] = 2;
error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
report_id, rep_data, 2);
if (error >= 0)
error = usb_get_report(intf,
WAC_HID_FEATURE_REPORT, report_id,
rep_data, 2);
} while ((error < 0 || rep_data[1] != 2) && limit++ < 5);
}

kfree(rep_data);

return error < 0 ? error : 0;
}

static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
struct wacom_features *features)
{
int error = 0;
struct usb_host_interface *interface = intf->cur_altsetting;
struct hid_descriptor *hid_desc;

/* default device to penabled */
features->device_type = BTN_TOOL_PEN;

/* only Tablet PCs need to retrieve the info */
if ((features->type != TABLETPC) && (features->type != TABLETPC2FG))
goto out;

if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
if (usb_get_extra_descriptor(&interface->endpoint[0],
HID_DEVICET_REPORT, &hid_desc)) {
printk("wacom: can not retrieve extra class descriptor\n");
error = 1;
goto out;
}
}
error = wacom_parse_hid(intf, hid_desc, features);
if (error)
goto out;

/* touch device found but size is not defined. use default */
if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
features->x_max = 1023;
features->y_max = 1023;
}

out:
return error;
}

static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface = intf->cur_altsetting;
struct usb_endpoint_descriptor *endpoint;
struct wacom *wacom;
struct wacom_wac *wacom_wac;
struct wacom_features *features;
struct input_dev *input_dev;
int error = -ENOMEM;
struct hid_descriptor *hid_desc;

wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
input_dev = input_allocate_device();
if (!wacom || !input_dev || !wacom_wac)
goto fail1;

wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
wacom_wac->data = usb_buffer_alloc(dev, WACOM_PKGLEN_MAX, GFP_KERNEL, &wacom->data_dma);
if (!wacom_wac->data)
goto fail1;

Expand All @@ -448,7 +556,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
strlcat(wacom->phys, "/input0", sizeof(wacom->phys));

wacom_wac->features = features = get_wacom_feature(id);
BUG_ON(features->pktlen > 10);
BUG_ON(features->pktlen > WACOM_PKGLEN_MAX);

input_dev->name = wacom_wac->features->name;
wacom->wacom_wac = wacom_wac;
Expand All @@ -463,47 +571,24 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i

endpoint = &intf->cur_altsetting->endpoint[0].desc;

/* Initialize touch_x_max and touch_y_max in case it is not defined */
if (wacom_wac->features->type == TABLETPC) {
features->touch_x_max = 1023;
features->touch_y_max = 1023;
} else {
features->touch_x_max = 0;
features->touch_y_max = 0;
}

/* TabletPC need to retrieve the physical and logical maximum from report descriptor */
if (wacom_wac->features->type == TABLETPC) {
if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
if (usb_get_extra_descriptor(&interface->endpoint[0],
HID_DEVICET_REPORT, &hid_desc)) {
printk("wacom: can not retrive extra class descriptor\n");
goto fail2;
}
}
error = wacom_parse_hid(intf, hid_desc, wacom_wac);
if (error)
goto fail2;
}
/* Retrieve the physical and logical size for OEM devices */
error = wacom_retrieve_hid_descriptor(intf, features);
if (error)
goto fail2;

input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) |
BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS);
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOUCH);

input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0);
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
if (features->type == TABLETPC) {
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_DOUBLETAP);
input_set_abs_params(input_dev, ABS_RX, 0, features->touch_x_max, 4, 0);
input_set_abs_params(input_dev, ABS_RY, 0, features->touch_y_max, 4, 0);
}
input_dev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);

wacom_init_input_dev(input_dev, wacom_wac);

usb_fill_int_urb(wacom->irq, dev,
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
wacom_wac->data, wacom_wac->features->pktlen,
wacom_wac->data, features->pktlen,
wacom_sys_irq, wacom, endpoint->bInterval);
wacom->irq->transfer_dma = wacom->data_dma;
wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
Expand All @@ -512,18 +597,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
if (error)
goto fail3;

/*
* Ask the tablet to report tablet data if it is not a Tablet PC.
* Note that if query fails it is not a hard failure.
*/
if (wacom_wac->features->type != TABLETPC)
wacom_query_tablet_data(intf);
/* Note that if query fails it is not a hard failure */
wacom_query_tablet_data(intf, features);

usb_set_intfdata(intf, wacom);
return 0;

fail3: usb_free_urb(wacom->irq);
fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
fail2: usb_buffer_free(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
fail1: input_free_device(input_dev);
kfree(wacom);
kfree(wacom_wac);
Expand All @@ -539,7 +620,7 @@ static void wacom_disconnect(struct usb_interface *intf)
usb_kill_urb(wacom->irq);
input_unregister_device(wacom->dev);
usb_free_urb(wacom->irq);
usb_buffer_free(interface_to_usbdev(intf), 10,
usb_buffer_free(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
wacom->wacom_wac->data, wacom->data_dma);
kfree(wacom->wacom_wac);
kfree(wacom);
Expand All @@ -559,12 +640,15 @@ static int wacom_suspend(struct usb_interface *intf, pm_message_t message)
static int wacom_resume(struct usb_interface *intf)
{
struct wacom *wacom = usb_get_intfdata(intf);
struct wacom_features *features = wacom->wacom_wac->features;
int rv;

mutex_lock(&wacom->lock);
if (wacom->open) {
rv = usb_submit_urb(wacom->irq, GFP_NOIO);
wacom_query_tablet_data(intf);
/* switch to wacom mode if needed */
if (!wacom_retrieve_hid_descriptor(intf, features))
wacom_query_tablet_data(intf, features);
} else
rv = 0;
mutex_unlock(&wacom->lock);
Expand Down
Loading

0 comments on commit ec67bbe

Please sign in to comment.