summaryrefslogtreecommitdiff
path: root/drivers/input/tablet
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-13 10:56:03 +0900
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-13 10:56:03 +0900
commit8bbbfa70549bd84f29ff331d0ac051897ccbbd72 (patch)
tree306640629d368960428326201098a881d7fd724f /drivers/input/tablet
parentbd81ccea8558daab570d70d2c23746413f26cecf (diff)
parent0cc8d6a9d23d6662da91eeb6bb8e7d1c559850f0 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input layer updates from Dmitry Torokhov: "2nd round of updates for the input subsystem. With it input core no longer limits number of character devices per event handler (such as evdev) to 32, but switches to dynamic minors once legacy range is exhausted. This should get multi-seat installations that currently run our of event devices very quickly. You will also get an update for Wacom driver and a couple of driver fixes." * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: Input: extend the number of event (and other) devices Input: mousedev - mark mousedev interfaces as non-seekable Input: mousedev - rename mixdev_open to opened_by_mixdev Input: mousedev - reformat structure initializers Input: mousedev - factor out psaux code to reduce #ifdefery Input: samsung-keypad - add clk_prepare and clk_unprepare Input: atmel_mxt_ts - simplify mxt_dump_message Input: wacom - clean up wacom_query_tablet_data Input: wacom - introduce wacom_fix_phy_from_hid Input: wacom - allow any multi-input Intuos device to set prox Input: wacom - report correct touch contact size for I5/Bamboo
Diffstat (limited to 'drivers/input/tablet')
-rw-r--r--drivers/input/tablet/wacom_sys.c145
-rw-r--r--drivers/input/tablet/wacom_wac.c30
2 files changed, 130 insertions, 45 deletions
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 0d3219f29744..9edf9806cff9 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.
@@ -432,56 +502,52 @@ 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->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 +597,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;
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 08b462b6c0d8..c3468c8dbd89 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -25,6 +25,11 @@
#define WACOM_INTUOS_RES 100
#define WACOM_INTUOS3_RES 200
+/* Scale factor relating reported contact size to logical contact area.
+ * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo
+ */
+#define WACOM_CONTACT_AREA_SCALE 2607
+
static int wacom_penpartner_irq(struct wacom_wac *wacom)
{
unsigned char *data = wacom->data;
@@ -326,7 +331,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
/* Enter report */
if ((data[1] & 0xfc) == 0xc0) {
- if (features->type >= INTUOS5S && features->type <= INTUOS5L)
+ if (features->quirks == WACOM_QUIRK_MULTI_INPUT)
wacom->shared->stylus_in_proximity = true;
/* serial number of the tool */
@@ -414,7 +419,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
/* Exit report */
if ((data[1] & 0xfe) == 0x80) {
- if (features->type >= INTUOS5S && features->type <= INTUOS5L)
+ if (features->quirks == WACOM_QUIRK_MULTI_INPUT)
wacom->shared->stylus_in_proximity = false;
/*
@@ -1043,11 +1048,19 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
if (touch) {
int x = (data[2] << 4) | (data[4] >> 4);
int y = (data[3] << 4) | (data[4] & 0x0f);
- int w = data[6];
+ int a = data[5];
+
+ // "a" is a scaled-down area which we assume is roughly
+ // circular and which can be described as: a=(pi*r^2)/C.
+ int x_res = input_abs_get_res(input, ABS_X);
+ int y_res = input_abs_get_res(input, ABS_Y);
+ int width = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE);
+ int height = width * y_res / x_res;
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
- input_report_abs(input, ABS_MT_TOUCH_MAJOR, w);
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, width);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, height);
}
}
@@ -1533,7 +1546,9 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
input_mt_init_slots(input_dev, features->touch_max, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, 255, 0, 0);
+ 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
+ 0, features->y_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, features->x_max,
@@ -1641,7 +1656,10 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
input_set_abs_params(input_dev,
ABS_MT_TOUCH_MAJOR,
- 0, 255, 0, 0);
+ 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev,
+ ABS_MT_TOUCH_MINOR,
+ 0, features->y_max, 0, 0);
}
input_set_abs_params(input_dev, ABS_MT_POSITION_X,