aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/tablet/wacom_sys.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/tablet/wacom_sys.c')
-rw-r--r--drivers/input/tablet/wacom_sys.c196
1 files changed, 155 insertions, 41 deletions
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 0d3219f29744..858ad446de91 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -172,6 +172,76 @@ static void wacom_close(struct input_dev *dev)
}
/*
+ * Calculate the resolution of the X or Y axis, given appropriate HID data.
+ * This function is little more than hidinput_calc_abs_res stripped down.
+ */
+static int wacom_calc_hid_res(int logical_extents, int physical_extents,
+ unsigned char unit, unsigned char exponent)
+{
+ int prev, unit_exponent;
+
+ /* Check if the extents are sane */
+ if (logical_extents <= 0 || physical_extents <= 0)
+ return 0;
+
+ /* Get signed value of nybble-sized twos-compliment exponent */
+ unit_exponent = exponent;
+ if (unit_exponent > 7)
+ unit_exponent -= 16;
+
+ /* Convert physical_extents to millimeters */
+ if (unit == 0x11) { /* If centimeters */
+ unit_exponent += 1;
+ } else if (unit == 0x13) { /* If inches */
+ prev = physical_extents;
+ physical_extents *= 254;
+ if (physical_extents < prev)
+ return 0;
+ unit_exponent -= 1;
+ } else {
+ return 0;
+ }
+
+ /* Apply negative unit exponent */
+ for (; unit_exponent < 0; unit_exponent++) {
+ prev = logical_extents;
+ logical_extents *= 10;
+ if (logical_extents < prev)
+ return 0;
+ }
+ /* Apply positive unit exponent */
+ for (; unit_exponent > 0; unit_exponent--) {
+ prev = physical_extents;
+ physical_extents *= 10;
+ if (physical_extents < prev)
+ return 0;
+ }
+
+ /* Calculate resolution */
+ return logical_extents / physical_extents;
+}
+
+/*
+ * The physical dimension specified by the HID descriptor is likely not in
+ * the "100th of a mm" units expected by wacom_calculate_touch_res. This
+ * function adjusts the value of [xy]_phy based on the unit and exponent
+ * provided by the HID descriptor. If an error occurs durring conversion
+ * (e.g. from the unit being left unspecified) [xy]_phy is not modified.
+ */
+static void wacom_fix_phy_from_hid(struct wacom_features *features)
+{
+ int xres = wacom_calc_hid_res(features->x_max, features->x_phy,
+ features->unit, features->unitExpo);
+ int yres = wacom_calc_hid_res(features->y_max, features->y_phy,
+ features->unit, features->unitExpo);
+
+ if (xres > 0 && yres > 0) {
+ features->x_phy = (100 * features->x_max) / xres;
+ features->y_phy = (100 * features->y_max) / yres;
+ }
+}
+
+/*
* Static values for max X/Y and resolution of Pen interface is stored in
* features. This mean physical size of active area can be computed.
* This is useful to do when Pen and Touch have same active area of tablet.
@@ -321,7 +391,7 @@ static int wacom_parse_hid(struct usb_interface *intf,
features->pktlen = WACOM_PKGLEN_TPC2FG;
}
- if (features->type == MTSCREEN)
+ if (features->type == MTSCREEN || features->type == WACOM_24HDT)
features->pktlen = WACOM_PKGLEN_MTOUCH;
if (features->type == BAMBOO_PT) {
@@ -332,6 +402,14 @@ static int wacom_parse_hid(struct usb_interface *intf,
features->x_max =
get_unaligned_le16(&report[i + 8]);
i += 15;
+ } else if (features->type == WACOM_24HDT) {
+ features->x_max =
+ get_unaligned_le16(&report[i + 3]);
+ features->x_phy =
+ get_unaligned_le16(&report[i + 8]);
+ features->unit = report[i - 1];
+ features->unitExpo = report[i - 3];
+ i += 12;
} else {
features->x_max =
get_unaligned_le16(&report[i + 3]);
@@ -364,6 +442,12 @@ static int wacom_parse_hid(struct usb_interface *intf,
features->y_phy =
get_unaligned_le16(&report[i + 6]);
i += 7;
+ } else if (type == WACOM_24HDT) {
+ features->y_max =
+ get_unaligned_le16(&report[i + 3]);
+ features->y_phy =
+ get_unaligned_le16(&report[i - 2]);
+ i += 7;
} else if (type == BAMBOO_PT) {
features->y_phy =
get_unaligned_le16(&report[i + 3]);
@@ -432,56 +516,55 @@ static int wacom_parse_hid(struct usb_interface *intf,
return result;
}
-static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features)
+static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int length, int mode)
{
unsigned char *rep_data;
- int limit = 0, report_id = 2;
- int error = -ENOMEM;
+ int error = -ENOMEM, limit = 0;
- rep_data = kmalloc(4, GFP_KERNEL);
+ rep_data = kzalloc(length, GFP_KERNEL);
if (!rep_data)
return error;
- /* ask to report Wacom data */
+ rep_data[0] = report_id;
+ rep_data[1] = mode;
+
+ do {
+ error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
+ report_id, rep_data, length, 1);
+ if (error >= 0)
+ error = wacom_get_report(intf, WAC_HID_FEATURE_REPORT,
+ report_id, rep_data, length, 1);
+ } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES);
+
+ kfree(rep_data);
+
+ return error < 0 ? error : 0;
+}
+
+/*
+ * Switch the tablet into its most-capable mode. Wacom tablets are
+ * typically configured to power-up in a mode which sends mouse-like
+ * reports to the OS. To get absolute position, pressure data, etc.
+ * from the tablet, it is necessary to switch the tablet out of this
+ * mode and into one which sends the full range of tablet data.
+ */
+static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features)
+{
if (features->device_type == BTN_TOOL_FINGER) {
- /* if it is an MT Tablet PC touch */
if (features->type > TABLETPC) {
- do {
- rep_data[0] = 3;
- rep_data[1] = 4;
- rep_data[2] = 0;
- rep_data[3] = 0;
- report_id = 3;
- error = wacom_set_report(intf,
- WAC_HID_FEATURE_REPORT,
- report_id,
- rep_data, 4, 1);
- if (error >= 0)
- error = wacom_get_report(intf,
- WAC_HID_FEATURE_REPORT,
- report_id,
- rep_data, 4, 1);
- } while ((error < 0 || rep_data[1] != 4) &&
- limit++ < WAC_MSG_RETRIES);
+ /* MT Tablet PC touch */
+ return wacom_set_device_mode(intf, 3, 4, 4);
+ }
+ else if (features->type == WACOM_24HDT) {
+ return wacom_set_device_mode(intf, 18, 3, 2);
+ }
+ } else if (features->device_type == BTN_TOOL_PEN) {
+ if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
+ return wacom_set_device_mode(intf, 2, 2, 2);
}
- } else if (features->type <= BAMBOO_PT &&
- features->type != WIRELESS &&
- features->device_type == BTN_TOOL_PEN) {
- do {
- rep_data[0] = 2;
- rep_data[1] = 2;
- error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
- report_id, rep_data, 2, 1);
- if (error >= 0)
- error = wacom_get_report(intf,
- WAC_HID_FEATURE_REPORT,
- report_id, rep_data, 2, 1);
- } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES);
}
- kfree(rep_data);
-
- return error < 0 ? error : 0;
+ return 0;
}
static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
@@ -531,6 +614,7 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
error = wacom_parse_hid(intf, hid_desc, features);
if (error)
goto out;
+ wacom_fix_phy_from_hid(features);
out:
return error;
@@ -546,6 +630,30 @@ struct wacom_usbdev_data {
static LIST_HEAD(wacom_udev_list);
static DEFINE_MUTEX(wacom_udev_list_lock);
+static struct usb_device *wacom_get_sibling(struct usb_device *dev, int vendor, int product)
+{
+ int port1;
+ struct usb_device *sibling;
+
+ if (vendor == 0 && product == 0)
+ return dev;
+
+ if (dev->parent == NULL)
+ return NULL;
+
+ usb_hub_for_each_child(dev->parent, port1, sibling) {
+ struct usb_device_descriptor *d;
+ if (sibling == NULL)
+ continue;
+
+ d = &sibling->descriptor;
+ if (d->idVendor == vendor && d->idProduct == product)
+ return sibling;
+ }
+
+ return NULL;
+}
+
static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev)
{
struct wacom_usbdev_data *data;
@@ -1190,13 +1298,19 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
+ struct usb_device *other_dev;
+
/* Append the device type to the name */
strlcat(wacom_wac->name,
features->device_type == BTN_TOOL_PEN ?
" Pen" : " Finger",
sizeof(wacom_wac->name));
- error = wacom_add_shared_data(wacom_wac, dev);
+
+ other_dev = wacom_get_sibling(dev, features->oVid, features->oPid);
+ if (other_dev == NULL || wacom_get_usbdev_data(other_dev) == NULL)
+ other_dev = dev;
+ error = wacom_add_shared_data(wacom_wac, other_dev);
if (error)
goto fail3;
}