linux/drivers/hid/hid-uclogic.c
Nikolai Kondrashov f8a489cc5f HID: Add support for UC-Logic WP????U tablets
Add support for UC-Logic WP4030U, WP5540U and WP8060U tablets.
These tablets are usually sold by Genius, Trust and possibly others under
different names and in different cases, but with the original USB
vendor/product IDs.

Currently, these tablets are supported by standalone X.org driver WizardPen.
This patch aims to fix them in the kernel and make them supported by the
generic evdev X.org driver. Still, some minor fixes in the X.org driver are
to be made for the full stack support.

Signed-off-by: Nikolai Kondrashov <spbnick@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-08-09 19:52:43 +02:00

405 lines
18 KiB
C

/*
* HID driver for UC-Logic devices not fully compliant with HID standard
*
* Copyright (c) 2010 Nikolai Kondrashov
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
/*
* The original descriptors of WPXXXXU tablets have three report IDs, of
* which only two are used (8 and 9), and the remaining (7) seems to have
* the originally intended pen description which was abandoned for some
* reason. From this unused description it is possible to extract the
* actual physical extents and resolution. All the models use the same
* descriptor with different extents for the unused report ID.
*
* Here it is:
*
* Usage Page (Digitizer), ; Digitizer (0Dh)
* Usage (Pen), ; Pen (02h, application collection)
* Collection (Application),
* Report ID (7),
* Usage (Stylus), ; Stylus (20h, logical collection)
* Collection (Physical),
* Usage (Tip Switch), ; Tip switch (42h, momentary control)
* Usage (Barrel Switch), ; Barrel switch (44h, momentary control)
* Usage (Eraser), ; Eraser (45h, momentary control)
* Logical Minimum (0),
* Logical Maximum (1),
* Report Size (1),
* Report Count (3),
* Input (Variable),
* Report Count (3),
* Input (Constant, Variable),
* Usage (In Range), ; In range (32h, momentary control)
* Report Count (1),
* Input (Variable),
* Report Count (1),
* Input (Constant, Variable),
* Usage Page (Desktop), ; Generic desktop controls (01h)
* Usage (X), ; X (30h, dynamic value)
* Report Size (16),
* Report Count (1),
* Push,
* Unit Exponent (13),
* Unit (Inch^3),
* Physical Minimum (0),
* Physical Maximum (Xpm),
* Logical Maximum (Xlm),
* Input (Variable),
* Usage (Y), ; Y (31h, dynamic value)
* Physical Maximum (Ypm),
* Logical Maximum (Ylm),
* Input (Variable),
* Pop,
* Usage Page (Digitizer), ; Digitizer (0Dh)
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
* Logical Maximum (1023),
* Input (Variable),
* Report Size (16),
* End Collection,
* End Collection,
* Usage Page (Desktop), ; Generic desktop controls (01h)
* Usage (Mouse), ; Mouse (02h, application collection)
* Collection (Application),
* Report ID (8),
* Usage (Pointer), ; Pointer (01h, physical collection)
* Collection (Physical),
* Usage Page (Button), ; Button (09h)
* Usage Minimum (01h),
* Usage Maximum (03h),
* Logical Minimum (0),
* Logical Maximum (1),
* Report Count (3),
* Report Size (1),
* Input (Variable),
* Report Count (5),
* Input (Constant),
* Usage Page (Desktop), ; Generic desktop controls (01h)
* Usage (X), ; X (30h, dynamic value)
* Usage (Y), ; Y (31h, dynamic value)
* Usage (Wheel), ; Wheel (38h, dynamic value)
* Usage (00h),
* Logical Minimum (-127),
* Logical Maximum (127),
* Report Size (8),
* Report Count (4),
* Input (Variable, Relative),
* End Collection,
* End Collection,
* Usage Page (Desktop), ; Generic desktop controls (01h)
* Usage (Mouse), ; Mouse (02h, application collection)
* Collection (Application),
* Report ID (9),
* Usage (Pointer), ; Pointer (01h, physical collection)
* Collection (Physical),
* Usage Page (Button), ; Button (09h)
* Usage Minimum (01h),
* Usage Maximum (03h),
* Logical Minimum (0),
* Logical Maximum (1),
* Report Count (3),
* Report Size (1),
* Input (Variable),
* Report Count (5),
* Input (Constant),
* Usage Page (Desktop), ; Generic desktop controls (01h)
* Usage (X), ; X (30h, dynamic value)
* Usage (Y), ; Y (31h, dynamic value)
* Logical Minimum (0),
* Logical Maximum (32767),
* Physical Minimum (0),
* Physical Maximum (32767),
* Report Count (2),
* Report Size (16),
* Input (Variable),
* Usage Page (Digitizer), ; Digitizer (0Dh)
* Usage (Tip Pressure), ; Tip pressure (30h, dynamic value)
* Logical Maximum (1023),
* Report Count (1),
* Report Size (16),
* Input (Variable),
* End Collection,
* End Collection
*
* Here are the extents values for the WPXXXXU models:
*
* Xpm Xlm Ypm Ylm
* WP4030U 4000 8000 3000 6000
* WP5540U 5500 11000 4000 8000
* WP8060U 8000 16000 6000 12000
*
* This suggests that all of them have 2000 LPI resolution, as advertised.
*/
/* Size of the original descriptor of WPXXXXU tablets */
#define WPXXXXU_RDESC_ORIG_SIZE 212
/*
* Fixed WP4030U report descriptor.
* Although the hardware might actually support it, the mouse description
* has been removed, since there seems to be no devices having one and it
* wouldn't make much sense because of the working area size.
*/
static __u8 wp4030u_rdesc_fixed[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0x14, /* Logical Minimum (0), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0xB8, 0x0B, /* Physical Maximum (3000), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Fixed WP5540U report descriptor */
static __u8 wp5540u_rdesc_fixed[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0x14, /* Logical Minimum (0), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x7C, 0x15, /* Physical Maximum (5500), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x02, /* Usage (Mouse), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x08, /* Report ID (8), */
0x09, 0x01, /* Usage (Pointer), */
0xA0, /* Collection (Physical), */
0x75, 0x01, /* Report Size (1), */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0x05, 0x01, /* Usage Page (Desktop), */
0x75, 0x08, /* Report Size (8), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x15, 0x81, /* Logical Minimum (-127), */
0x25, 0x7F, /* Logical Maximum (127), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x06, /* Input (Variable, Relative), */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x06, /* Input (Variable, Relative), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Fixed WP8060U report descriptor */
static __u8 wp8060u_rdesc_fixed[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0x14, /* Logical Minimum (0), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection, */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x02, /* Usage (Mouse), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x08, /* Report ID (8), */
0x09, 0x01, /* Usage (Pointer), */
0xA0, /* Collection (Physical), */
0x75, 0x01, /* Report Size (1), */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x01, /* Input (Constant), */
0x05, 0x01, /* Usage Page (Desktop), */
0x75, 0x08, /* Report Size (8), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x15, 0x81, /* Logical Minimum (-127), */
0x25, 0x7F, /* Logical Maximum (127), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x06, /* Input (Variable, Relative), */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x06, /* Input (Variable, Relative), */
0x81, 0x01, /* Input (Constant), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
switch (hdev->product) {
case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U:
if (*rsize == WPXXXXU_RDESC_ORIG_SIZE)
{
rdesc = wp4030u_rdesc_fixed;
*rsize = sizeof(wp4030u_rdesc_fixed);
}
break;
case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U:
if (*rsize == WPXXXXU_RDESC_ORIG_SIZE)
{
rdesc = wp5540u_rdesc_fixed;
*rsize = sizeof(wp5540u_rdesc_fixed);
}
break;
case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U:
if (*rsize == WPXXXXU_RDESC_ORIG_SIZE)
{
rdesc = wp8060u_rdesc_fixed;
*rsize = sizeof(wp8060u_rdesc_fixed);
}
break;
}
return rdesc;
}
static const struct hid_device_id uclogic_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
static struct hid_driver uclogic_driver = {
.name = "uclogic",
.id_table = uclogic_devices,
.report_fixup = uclogic_report_fixup,
};
static int __init uclogic_init(void)
{
return hid_register_driver(&uclogic_driver);
}
static void __exit uclogic_exit(void)
{
hid_unregister_driver(&uclogic_driver);
}
module_init(uclogic_init);
module_exit(uclogic_exit);
MODULE_LICENSE("GPL");