summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2018-08-20 18:09:06 +0200
committerJiri Kosina <jkosina@suse.cz>2018-08-20 18:09:06 +0200
commit16501e846dd23f56a23330cd874cfbfaf60daf1a (patch)
tree22513a8f89b046b8feeb84a43ad754ea66441a5d /drivers/hid
parentf5dd80715ae1f34909cb04f29b7092dfc6717ed7 (diff)
parent28a042a3b7ab3e28039d6309f0764a8e4c1b261d (diff)
Merge branch 'for-4.19/multitouch-multiaxis' into for-linus
Multitouch updates: - Dial support - Palm rejection for touchscreens - a few small assorted fixes
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-core.c17
-rw-r--r--drivers/hid/hid-input.c3
-rw-r--r--drivers/hid/hid-microsoft.c49
-rw-r--r--drivers/hid/hid-multitouch.c989
4 files changed, 657 insertions, 401 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 402ad974b31c..3da354af7a0a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -128,9 +128,19 @@ static int open_collection(struct hid_parser *parser, unsigned type)
usage = parser->local.usage[0];
- if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
- hid_err(parser->device, "collection stack overflow\n");
- return -EINVAL;
+ if (parser->collection_stack_ptr == parser->collection_stack_size) {
+ unsigned int *collection_stack;
+ unsigned int new_size = parser->collection_stack_size +
+ HID_COLLECTION_STACK_SIZE;
+
+ collection_stack = krealloc(parser->collection_stack,
+ new_size * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!collection_stack)
+ return -ENOMEM;
+
+ parser->collection_stack = collection_stack;
+ parser->collection_stack_size = new_size;
}
if (parser->device->maxcollection == parser->device->collection_size) {
@@ -840,6 +850,7 @@ static int hid_scan_report(struct hid_device *hid)
break;
}
+ kfree(parser->collection_stack);
vfree(parser);
return 0;
}
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index ab93dd5927c3..4e94ea3e280a 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1550,6 +1550,9 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
case HID_GD_WIRELESS_RADIO_CTLS:
suffix = "Wireless Radio Control";
break;
+ case HID_GD_SYSTEM_MULTIAXIS:
+ suffix = "System Multi Axis";
+ break;
default:
break;
}
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 96e7d3231d2f..72d983626afd 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -22,12 +22,13 @@
#include "hid-ids.h"
-#define MS_HIDINPUT 0x01
-#define MS_ERGONOMY 0x02
-#define MS_PRESENTER 0x04
-#define MS_RDESC 0x08
-#define MS_NOGET 0x10
-#define MS_DUPLICATE_USAGES 0x20
+#define MS_HIDINPUT BIT(0)
+#define MS_ERGONOMY BIT(1)
+#define MS_PRESENTER BIT(2)
+#define MS_RDESC BIT(3)
+#define MS_NOGET BIT(4)
+#define MS_DUPLICATE_USAGES BIT(5)
+#define MS_SURFACE_DIAL BIT(6)
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
@@ -130,6 +131,30 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
return 1;
}
+static int ms_surface_dial_quirk(struct hid_input *hi, struct hid_field *field,
+ struct hid_usage *usage, unsigned long **bit, int *max)
+{
+ switch (usage->hid & HID_USAGE_PAGE) {
+ case 0xff070000:
+ /* fall-through */
+ case HID_UP_DIGITIZER:
+ /* ignore those axis */
+ return -1;
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ /* fall-through */
+ case HID_GD_Y:
+ /* fall-through */
+ case HID_GD_RFKILL_BTN:
+ /* ignore those axis */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
@@ -146,6 +171,13 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
ms_presenter_8k_quirk(hi, usage, bit, max))
return 1;
+ if (quirks & MS_SURFACE_DIAL) {
+ int ret = ms_surface_dial_quirk(hi, field, usage, bit, max);
+
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -229,6 +261,9 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (quirks & MS_NOGET)
hdev->quirks |= HID_QUIRK_NOGET;
+ if (quirks & MS_SURFACE_DIAL)
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
+
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
@@ -281,6 +316,8 @@ static const struct hid_device_id ms_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
+ .driver_data = MS_SURFACE_DIAL },
{ }
};
MODULE_DEVICE_TABLE(hid, ms_devices);
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 45968f7970f8..40fbb7c52723 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -28,14 +28,11 @@
*/
/*
- * This driver is regularly tested thanks to the tool hid-test[1].
- * This tool relies on hid-replay[2] and a database of hid devices[3].
+ * This driver is regularly tested thanks to the test suite in hid-tools[1].
* Please run these regression tests before patching this module so that
* your patch won't break existing known devices.
*
- * [1] https://github.com/bentiss/hid-test
- * [2] https://github.com/bentiss/hid-replay
- * [3] https://github.com/bentiss/hid-devices
+ * [1] https://gitlab.freedesktop.org/libevdev/hid-tools
*/
#include <linux/device.h>
@@ -90,13 +87,54 @@ enum latency_mode {
#define MT_IO_FLAGS_ACTIVE_SLOTS 1
#define MT_IO_FLAGS_PENDING_SLOTS 2
-struct mt_slot {
- __s32 x, y, cx, cy, p, w, h, a;
- __s32 contactid; /* the device ContactID assigned to this slot */
- bool touch_state; /* is the touch valid? */
- bool inrange_state; /* is the finger in proximity of the sensor? */
- bool confidence_state; /* is the touch made by a finger? */
- bool has_azimuth; /* the contact reports azimuth */
+static const bool mtrue = true; /* default for true */
+static const bool mfalse; /* default for false */
+static const __s32 mzero; /* default for 0 */
+
+#define DEFAULT_TRUE ((void *)&mtrue)
+#define DEFAULT_FALSE ((void *)&mfalse)
+#define DEFAULT_ZERO ((void *)&mzero)
+
+struct mt_usages {
+ struct list_head list;
+ __s32 *x, *y, *cx, *cy, *p, *w, *h, *a;
+ __s32 *contactid; /* the device ContactID assigned to this slot */
+ bool *tip_state; /* is the touch valid? */
+ bool *inrange_state; /* is the finger in proximity of the sensor? */
+ bool *confidence_state; /* is the touch made by a finger? */
+};
+
+struct mt_application {
+ struct list_head list;
+ unsigned int application;
+ struct list_head mt_usages; /* mt usages list */
+
+ __s32 quirks;
+
+ __s32 *scantime; /* scantime reported */
+ __s32 scantime_logical_max; /* max value for raw scantime */
+
+ __s32 *raw_cc; /* contact count in the report */
+ int left_button_state; /* left button state */
+ unsigned int mt_flags; /* flags to pass to input-mt */
+
+ unsigned long *pending_palm_slots; /* slots where we reported palm
+ * and need to release */
+
+ __u8 num_received; /* how many contacts we received */
+ __u8 num_expected; /* expected last contact index */
+ __u8 buttons_count; /* number of physical buttons per touchpad */
+ __u8 touches_by_report; /* how many touches are present in one report:
+ * 1 means we should use a serial protocol
+ * > 1 means hybrid (multitouch) protocol
+ */
+
+ __s32 dev_time; /* the scan time provided by the device */
+ unsigned long jiffies; /* the frame's jiffies */
+ int timestamp; /* the timestamp to be sent */
+ int prev_scantime; /* scantime reported previously */
+
+ bool have_contact_count;
};
struct mt_class {
@@ -111,46 +149,30 @@ struct mt_class {
bool export_all_inputs; /* do not ignore mouse, keyboards, etc... */
};
-struct mt_fields {
- unsigned usages[HID_MAX_FIELDS];
- unsigned int length;
+struct mt_report_data {
+ struct list_head list;
+ struct hid_report *report;
+ struct mt_application *application;
+ bool is_mt_collection;
};
struct mt_device {
- struct mt_slot curdata; /* placeholder of incoming data */
struct mt_class mtclass; /* our mt device class */
struct timer_list release_timer; /* to release sticky fingers */
struct hid_device *hdev; /* hid_device we're attached to */
- struct mt_fields *fields; /* temporary placeholder for storing the
- multitouch fields */
unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */
- int cc_index; /* contact count field index in the report */
- int cc_value_index; /* contact count value index in the field */
- int scantime_index; /* scantime field index in the report */
- int scantime_val_index; /* scantime value index in the field */
- int prev_scantime; /* scantime reported in the previous packet */
- int left_button_state; /* left button state */
- unsigned last_slot_field; /* the last field of a slot */
- unsigned mt_report_id; /* the report ID of the multitouch device */
__u8 inputmode_value; /* InputMode HID feature value */
- __u8 num_received; /* how many contacts we received */
- __u8 num_expected; /* expected last contact index */
__u8 maxcontacts;
- __u8 touches_by_report; /* how many touches are present in one report:
- * 1 means we should use a serial protocol
- * > 1 means hybrid (multitouch) protocol */
- __u8 buttons_count; /* number of physical buttons per touchpad */
bool is_buttonpad; /* is this device a button pad? */
bool serial_maybe; /* need to check for serial protocol */
- bool curvalid; /* is the current contact valid? */
- unsigned mt_flags; /* flags to pass to input-mt */
- __s32 dev_time; /* the scan time provided by the device */
- unsigned long jiffies; /* the frame's jiffies */
- int timestamp; /* the timestamp to be sent */
+
+ struct list_head applications;
+ struct list_head reports;
};
-static void mt_post_parse_default_settings(struct mt_device *td);
-static void mt_post_parse(struct mt_device *td);
+static void mt_post_parse_default_settings(struct mt_device *td,
+ struct mt_application *app);
+static void mt_post_parse(struct mt_device *td, struct mt_application *app);
/* classes of device behavior */
#define MT_CLS_DEFAULT 0x0001
@@ -203,15 +225,16 @@ static void mt_post_parse(struct mt_device *td);
* to a valid contact that was just read.
*/
-static int cypress_compute_slot(struct mt_device *td)
+static int cypress_compute_slot(struct mt_application *application,
+ struct mt_usages *slot)
{
- if (td->curdata.contactid != 0 || td->num_received == 0)
- return td->curdata.contactid;
+ if (*slot->contactid != 0 || application->num_received == 0)
+ return *slot->contactid;
else
return -1;
}
-static struct mt_class mt_classes[] = {
+static const struct mt_class mt_classes[] = {
{ .name = MT_CLS_DEFAULT,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE },
@@ -353,6 +376,7 @@ static ssize_t mt_set_quirks(struct device *dev,
{
struct hid_device *hdev = to_hid_device(dev);
struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_application *application;
unsigned long val;
@@ -361,8 +385,11 @@ static ssize_t mt_set_quirks(struct device *dev,
td->mtclass.quirks = val;
- if (td->cc_index < 0)
- td->mtclass.quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
+ list_for_each_entry(application, &td->applications, list) {
+ application->quirks = val;
+ if (!application->have_contact_count)
+ application->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
+ }
return count;
}
@@ -457,41 +484,199 @@ static void set_abs(struct input_dev *input, unsigned int code,
input_abs_set_res(input, code, hidinput_calc_abs_res(field, code));
}
-static void mt_store_field(struct hid_usage *usage, struct mt_device *td,
- struct hid_input *hi)
+static struct mt_usages *mt_allocate_usage(struct hid_device *hdev,
+ struct mt_application *application)
+{
+ struct mt_usages *usage;
+
+ usage = devm_kzalloc(&hdev->dev, sizeof(*usage), GFP_KERNEL);
+ if (!usage)
+ return NULL;
+
+ /* set some defaults so we do not need to check for null pointers */
+ usage->x = DEFAULT_ZERO;
+ usage->y = DEFAULT_ZERO;
+ usage->cx = DEFAULT_ZERO;
+ usage->cy = DEFAULT_ZERO;
+ usage->p = DEFAULT_ZERO;
+ usage->w = DEFAULT_ZERO;
+ usage->h = DEFAULT_ZERO;
+ usage->a = DEFAULT_ZERO;
+ usage->contactid = DEFAULT_ZERO;
+ usage->tip_state = DEFAULT_FALSE;
+ usage->inrange_state = DEFAULT_FALSE;
+ usage->confidence_state = DEFAULT_TRUE;
+
+ list_add_tail(&usage->list, &application->mt_usages);
+
+ return usage;
+}
+
+static struct mt_application *mt_allocate_application(struct mt_device *td,
+ unsigned int application)
+{
+ struct mt_application *mt_application;
+
+ mt_application = devm_kzalloc(&td->hdev->dev, sizeof(*mt_application),
+ GFP_KERNEL);
+ if (!mt_application)
+ return NULL;
+
+ mt_application->application = application;
+ INIT_LIST_HEAD(&mt_application->mt_usages);
+
+ if (application == HID_DG_TOUCHSCREEN)
+ mt_application->mt_flags |= INPUT_MT_DIRECT;
+
+ /*
+ * Model touchscreens providing buttons as touchpads.
+ */
+ if (application == HID_DG_TOUCHPAD) {
+ mt_application->mt_flags |= INPUT_MT_POINTER;
+ td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+ }
+
+ mt_application->scantime = DEFAULT_ZERO;
+ mt_application->raw_cc = DEFAULT_ZERO;
+ mt_application->quirks = td->mtclass.quirks;
+
+ list_add_tail(&mt_application->list, &td->applications);
+
+ return mt_application;
+}
+
+static struct mt_application *mt_find_application(struct mt_device *td,
+ unsigned int application)
+{
+ struct mt_application *tmp, *mt_application = NULL;
+
+ list_for_each_entry(tmp, &td->applications, list) {
+ if (application == tmp->application) {
+ mt_application = tmp;
+ break;
+ }
+ }
+
+ if (!mt_application)
+ mt_application = mt_allocate_application(td, application);
+
+ return mt_application;
+}
+
+static struct mt_report_data *mt_allocate_report_data(struct mt_device *td,
+ struct hid_report *report)
+{
+ struct mt_report_data *rdata;
+ struct hid_field *field;
+ int r, n;
+
+ rdata = devm_kzalloc(&td->hdev->dev, sizeof(*rdata), GFP_KERNEL);
+ if (!rdata)
+ return NULL;
+
+ rdata->report = report;
+ rdata->application = mt_find_application(td, report->application);
+
+ if (!rdata->application) {
+ devm_kfree(&td->hdev->dev, rdata);
+ return NULL;
+ }
+
+ for (r = 0; r < report->maxfield; r++) {
+ field = report->field[r];
+
+ if (!(HID_MAIN_ITEM_VARIABLE & field->flags))
+ continue;
+
+ for (n = 0; n < field->report_count; n++) {
+ if (field->usage[n].hid == HID_DG_CONTACTID)
+ rdata->is_mt_collection = true;
+ }
+ }
+
+ list_add_tail(&rdata->list, &td->reports);
+
+ return rdata;
+}
+
+static struct mt_report_data *mt_find_report_data(struct mt_device *td,
+ struct hid_report *report)
{
- struct mt_fields *f = td->fields;
+ struct mt_report_data *tmp, *rdata = NULL;
- if (f->length >= HID_MAX_FIELDS)
+ list_for_each_entry(tmp, &td->reports, list) {
+ if (report == tmp->report) {
+ rdata = tmp;
+ break;
+ }
+ }
+
+ if (!rdata)
+ rdata = mt_allocate_report_data(td, report);
+
+ return rdata;
+}
+
+static void mt_store_field(struct hid_device *hdev,
+ struct mt_application *application,
+ __s32 *value,
+ size_t offset)
+{
+ struct mt_usages *usage;
+ __s32 **target;
+
+ if (list_empty(&application->mt_usages))
+ usage = mt_allocate_usage(hdev, application);
+ else
+ usage = list_last_entry(&application->mt_usages,
+ struct mt_usages,
+ list);
+
+ if (!usage)
return;
- f->usages[f->length++] = usage->hid;
+ target = (__s32 **)((char *)usage + offset);
+
+ /* the value has already been filled, create a new slot */
+ if (*target != DEFAULT_TRUE &&
+ *target != DEFAULT_FALSE &&
+ *target != DEFAULT_ZERO) {
+ usage = mt_allocate_usage(hdev, application);
+ if (!usage)
+ return;
+
+ target = (__s32 **)((char *)usage + offset);
+ }
+
+ *target = value;
}
+#define MT_STORE_FIELD(__name) \
+ mt_store_field(hdev, app, \
+ &field->value[usage->usage_index], \
+ offsetof(struct mt_usages, __name))
+
static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
- unsigned long **bit, int *max)
+ unsigned long **bit, int *max, struct mt_application *app)
{
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
int code;
struct hid_usage *prev_usage = NULL;
- if (field->application == HID_DG_TOUCHSCREEN)
- td->mt_flags |= INPUT_MT_DIRECT;
-
/*
* Model touchscreens providing buttons as touchpads.
*/
- if (field->application == HID_DG_TOUCHPAD ||
+ if (field->application == HID_DG_TOUCHSCREEN &&
(usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
- td->mt_flags |= INPUT_MT_POINTER;
+ app->mt_flags |= INPUT_MT_POINTER;
td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
}
/* count the buttons on touchpads */
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
- td->buttons_count++;
+ app->buttons_count++;
if (usage->usage_index)
prev_usage = &field->usage[usage->usage_index - 1];
@@ -502,33 +687,40 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
switch (usage->hid) {
case HID_GD_X:
if (prev_usage && (prev_usage->hid == usage->hid)) {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOOL_X);
- set_abs(hi->input, ABS_MT_TOOL_X, field,
- cls->sn_move);
+ code = ABS_MT_TOOL_X;
+ MT_STORE_FIELD(cx);
} else {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_POSITION_X);
- set_abs(hi->input, ABS_MT_POSITION_X, field,
- cls->sn_move);
+ code = ABS_MT_POSITION_X;
+ MT_STORE_FIELD(x);
+ }
+
+ set_abs(hi->input, code, field, cls->sn_move);
+
+ /*
+ * A system multi-axis that exports X and Y has a high
+ * chance of being used directly on a surface
+ */
+ if (field->application == HID_GD_SYSTEM_MULTIAXIS) {
+ __set_bit(INPUT_PROP_DIRECT,
+ hi->input->propbit);
+ input_set_abs_params(hi->input,
+ ABS_MT_TOOL_TYPE,
+ MT_TOOL_DIAL,
+ MT_TOOL_DIAL, 0, 0);
}
- mt_store_field(usage, td, hi);
return 1;
case HID_GD_Y:
if (prev_usage && (prev_usage->hid == usage->hid)) {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOOL_Y);
- set_abs(hi->input, ABS_MT_TOOL_Y, field,
- cls->sn_move);
+ code = ABS_MT_TOOL_Y;
+ MT_STORE_FIELD(cy);
} else {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_POSITION_Y);
- set_abs(hi->input, ABS_MT_POSITION_Y, field,
- cls->sn_move);
+ code = ABS_MT_POSITION_Y;
+ MT_STORE_FIELD(y);
}
- mt_store_field(usage, td, hi);
+ set_abs(hi->input, code, field, cls->sn_move);
+
return 1;
}
return 0;
@@ -536,43 +728,45 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER:
switch (usage->hid) {
case HID_DG_INRANGE:
- if (cls->quirks & MT_QUIRK_HOVERING) {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_DISTANCE);
+ if (app->quirks & MT_QUIRK_HOVERING) {
input_set_abs_params(hi->input,
ABS_MT_DISTANCE, 0, 1, 0, 0);
}
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(inrange_state);
return 1;
case HID_DG_CONFIDENCE:
if ((cls->name == MT_CLS_WIN_8 ||
cls->name == MT_CLS_WIN_8_DUAL) &&
- field->application == HID_DG_TOUCHPAD)
- cls->quirks |= MT_QUIRK_CONFIDENCE;
- mt_store_field(usage, td, hi);
+ (field->application == HID_DG_TOUCHPAD ||
+ field->application == HID_DG_TOUCHSCREEN))
+ app->quirks |= MT_QUIRK_CONFIDENCE;
+
+ if (app->quirks & MT_QUIRK_CONFIDENCE)
+ input_set_abs_params(hi->input,
+ ABS_MT_TOOL_TYPE,
+ MT_TOOL_FINGER,
+ MT_TOOL_PALM, 0, 0);
+
+ MT_STORE_FIELD(confidence_state);
return 1;
case HID_DG_TIPSWITCH:
- hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
- input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
- mt_store_field(usage, td, hi);
+ if (field->application != HID_GD_SYSTEM_MULTIAXIS)
+ input_set_capability(hi->input,
+ EV_KEY, BTN_TOUCH);
+ MT_STORE_FIELD(tip_state);
return 1;
case HID_DG_CONTACTID:
- mt_store_field(usage, td, hi);
- td->touches_by_report++;
- td->mt_report_id = field->report->id;
+ MT_STORE_FIELD(contactid);
+ app->touches_by_report++;
return 1;
case HID_DG_WIDTH:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOUCH_MAJOR);
- if (!(cls->quirks & MT_QUIRK_NO_AREA))
+ if (!(app->quirks & MT_QUIRK_NO_AREA))
set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
cls->sn_width);
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(w);
return 1;
case HID_DG_HEIGHT:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOUCH_MINOR);
- if (!(cls->quirks & MT_QUIRK_NO_AREA)) {
+ if (!(app->quirks & MT_QUIRK_NO_AREA)) {
set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
cls->sn_height);
@@ -585,41 +779,23 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0);
}
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(h);
return 1;
case HID_DG_TIPPRESSURE:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_PRESSURE);
set_abs(hi->input, ABS_MT_PRESSURE, field,
cls->sn_pressure);
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(p);
return 1;
case HID_DG_SCANTIME:
- hid_map_usage(hi, usage, bit, max,
- EV_MSC, MSC_TIMESTAMP);
input_set_capability(hi->input, EV_MSC, MSC_TIMESTAMP);
- /* Ignore if indexes are out of bounds. */
- if (field->index >= field->report->maxfield ||
- usage->usage_index >= field->report_count)
- return 1;
- td->scantime_index = field->index;
- td->scantime_val_index = usage->usage_index;
- /*
- * We don't set td->last_slot_field as scan time is
- * global to the report.
- */
+ app->scantime = &field->value[usage->usage_index];
+ app->scantime_logical_max = field->logical_maximum;
return 1;
case HID_DG_CONTACTCOUNT:
- /* Ignore if indexes are out of bounds. */
- if (field->index >= field->report->maxfield ||
- usage->usage_index >= field->report_count)
- return 1;
- td->cc_index = field->index;
- td->cc_value_index = usage->usage_index;
+ app->have_contact_count = true;
+ app->raw_cc = &field->value[usage->usage_index];
return 1;
case HID_DG_AZIMUTH:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_ORIENTATION);
/*
* Azimuth has the range of [0, MAX) representing a full
* revolution. Set ABS_MT_ORIENTATION to a quarter of
@@ -630,11 +806,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
field->logical_maximum / 4,
cls->sn_move ?
field->logical_maximum / cls->sn_move : 0, 0);
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(a);
return 1;
case HID_DG_CONTACTMAX:
- /* we don't set td->last_slot_field as contactcount and
- * contact max are global to the report */
+ /* contact max are global to the report */
return -1;
case HID_DG_TOUCH:
/* Legacy devices use TIPSWITCH and not TOUCH.
@@ -650,10 +825,14 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* MS PTP spec says that external buttons left and right have
* usages 2 and 3.
*/
- if ((cls->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
+ if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
field->application == HID_DG_TOUCHPAD &&
(usage->hid & HID_USAGE) > 1)
code--;
+
+ if (field->application == HID_GD_SYSTEM_MULTIAXIS)
+ code = BTN_0 + ((usage->hid - 1) & HID_USAGE);
+
hid_map_usage(hi, usage, bit, max, EV_KEY, code);
input_set_capability(hi->input, EV_KEY, code);
return 1;
@@ -666,110 +845,68 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
-static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
+static int mt_compute_slot(struct mt_device *td, struct mt_application *app,
+ struct mt_usages *slot,
+ struct input_dev *input)
{
- __s32 quirks = td->mtclass.quirks;
+ __s32 quirks = app->quirks;
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID)
- return td->curdata.contactid;
+ return *slot->contactid;
if (quirks & MT_QUIRK_CYPRESS)
- return cypress_compute_slot(td);
+ return cypress_compute_slot(app, slot);
if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER)
- return td->num_received;
+ return app->num_received;
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE)
- return td->curdata.contactid - 1;
+ return *slot->contactid - 1;
- return input_mt_get_slot_by_key(input, td->curdata.contactid);
+ return input_mt_get_slot_by_key(input, *slot->contactid);
}
-/*
- * this function is called when a whole contact has been processed,
- * so that it can assign it to a slot and store the data there
- */
-static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
+static void mt_release_pending_palms(struct mt_device *td,
+ struct mt_application *app,
+ struct input_dev *input)
{
- if ((td->mtclass.quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) &&
- td->num_received >= td->num_expected)
- return;
-
- if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) {
- int active;
- int slotnum = mt_compute_slot(td, input);
- struct mt_slot *s = &td->curdata;
- struct input_mt *mt = input->mt;
+ int slotnum;
+ bool need_sync = false;
- if (slotnum < 0 || slotnum >= td->maxcontacts)
- return;
-
- if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) {
- struct input_mt_slot *slot = &mt->slots[slotnum];
- if (input_mt_is_active(slot) &&
- input_mt_is_used(mt, slot))
- return;
- }
-
- if (!(td->mtclass.quirks & MT_QUIRK_CONFIDENCE))
- s->confidence_state = true;
- active = (s->touch_state || s->inrange_state) &&
- s->confidence_state;
+ for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) {
+ clear_bit(slotnum, app->pending_palm_slots);
input_mt_slot(input, slotnum);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
- if (active) {
- /* this finger is in proximity of the sensor */
- int wide = (s->w > s->h);
- int major = max(s->w, s->h);
- int minor = min(s->w, s->h);
- int orientation = wide;
+ input_mt_report_slot_state(input, MT_TOOL_PALM, false);
- if (s->has_azimuth)
- orientation = s->a;
-
- /*
- * divided by two to match visual scale of touch
- * for devices with this quirk
- */
- if (td->mtclass.quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
- major = major >> 1;
- minor = minor >> 1;
- }
-
- input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
- input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
- input_event(input, EV_ABS, ABS_MT_TOOL_X, s->cx);
- input_event(input, EV_ABS, ABS_MT_TOOL_Y, s->cy);
- input_event(input, EV_ABS, ABS_MT_DISTANCE,
- !s->touch_state);
- input_event(input, EV_ABS, ABS_MT_ORIENTATION,
- orientation);
- input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
- input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
- input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
-
- set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags);
- }
+ need_sync = true;
}
- td->num_received++;
+ if (need_sync) {
+ input_mt_sync_frame(input);
+ input_sync(input);
+ }
}
/*
* this function is called when a whole packet has been received and processed,
* so that it can decide what to send to the input layer.
*/
-static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
+static void mt_sync_frame(struct mt_device *td, struct mt_application *app,
+ struct input_dev *input)
{
- if (td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS)
- input_event(input, EV_KEY, BTN_LEFT, td->left_button_state);
+ if (app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS)
+ input_event(input, EV_KEY, BTN_LEFT, app->left_button_state);
input_mt_sync_frame(input);
- input_event(input, EV_MSC, MSC_TIMESTAMP, td->timestamp);
+ input_event(input, EV_MSC, MSC_TIMESTAMP, app->timestamp);
input_sync(input);
- td->num_received = 0;
- td->left_button_state = 0;
+
+ mt_release_pending_palms(td, app, input);
+
+ app->num_received = 0;
+ app->left_button_state = 0;
+
if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags))
set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags);
else
@@ -777,17 +914,15 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags);
}
-static int mt_compute_timestamp(struct mt_device *td, struct hid_field *field,
- __s32 value)
+static int mt_compute_timestamp(struct mt_application *app, __s32 value)
{
- long delta = value - td->dev_time;
- unsigned long jdelta = jiffies_to_usecs(jiffies - td->jiffies);
+ long delta = value - app->prev_scantime;
+ unsigned long jdelta = jiffies_to_usecs(jiffies - app->jiffies);
- td->jiffies = jiffies;
- td->dev_time = value;
+ app->jiffies = jiffies;
if (delta < 0)
- delta += field->logical_maximum;
+ delta += app->scantime_logical_max;
/* HID_DG_SCANTIME is expressed in 100us, we want it in us. */
delta *= 100;
@@ -796,7 +931,7 @@ static int mt_compute_timestamp(struct mt_device *td, struct hid_field *field,
/* No data received for a while, resync the timestamp. */
return 0;
else
- return td->timestamp + delta;
+ return app->timestamp + delta;
}
static int mt_touch_event(struct hid_device *hid, struct hid_field *field,
@@ -809,63 +944,90 @@ static int mt_touch_event(struct hid_device *hid, struct hid_field *field,
return 1;
}
-static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value,
- bool first_packet)
+static int mt_process_slot(struct mt_device *td, struct input_dev *input,
+ struct mt_application *app,
+ struct mt_usages *slot)
{
- struct mt_device *td = hid_get_drvdata(hid);
- __s32 quirks = td->mtclass.quirks;
- struct input_dev *input = field->hidinput->input;
+ struct input_mt *mt = input->mt;
+ __s32 quirks = app->quirks;
+ bool valid = true;
+ bool confidence_state = true;
+ bool inrange_state = false;
+ int active;
+ int slotnum;
+ int tool = MT_TOOL_FINGER;
+
+ if (!slot)
+ return -EINVAL;
- if (hid->claimed & HID_CLAIMED_INPUT) {
- switch (usage->hid) {
- case HID_DG_INRANGE:
- if (quirks & MT_QUIRK_VALID_IS_INRANGE)
- td->curvalid = value;
- if (quirks & MT_QUIRK_HOVERING)
- td->curdata.inrange_state = value;
- break;
- case HID_DG_TIPSWITCH:
- if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
- td->curvalid = value;
- td->curdata.touch_state = value;
- break;
- case HID_DG_CONFIDENCE:
- if (quirks & MT_QUIRK_CONFIDENCE)
- td->curdata.confidence_state = value;
- if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE)
- td->curvalid = value;
- break;
- case HID_DG_CONTACTID:
- td->curdata.contactid = value;
- break;
- case HID_DG_TIPPRESSURE:
- td->curdata.p = value;
- break;
- case HID_GD_X:
- if (usage->code == ABS_MT_TOOL_X)
- td->curdata.cx = value;
- else
- td->curdata.x = value;
- break;
- case HID_GD_Y:
- if (usage->code == ABS_MT_TOOL_Y)
- td->curdata.cy = value;
- else
- td->curdata.y = value;
- break;
- case HID_DG_WIDTH:
- td->curdata.w = value;
- break;
- case HID_DG_HEIGHT:
- td->curdata.h = value;
- break;
- case HID_DG_SCANTIME:
- td->timestamp = mt_compute_timestamp(td, field, value);
- break;
- case HID_DG_CONTACTCOUNT:
- break;
- case HID_DG_AZIMUTH:
+ if ((quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) &&
+ app->num_received >= app->num_expected)
+ return -EAGAIN;
+
+ if (!(quirks & MT_QUIRK_ALWAYS_VALID)) {
+ if (quirks & MT_QUIRK_VALID_IS_INRANGE)
+ valid = *slot->inrange_state;
+ if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
+ valid = *slot->tip_state;
+ if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE)
+ valid = *slot->confidence_state;
+
+ if (!valid)
+ return 0;
+ }
+
+ slotnum = mt_compute_slot(td, app, slot, input);
+ if (slotnum < 0 || slotnum >= td->maxcontacts)
+ return 0;
+
+ if ((quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) {
+ struct input_mt_slot *i_slot = &mt->slots[slotnum];
+
+ if (input_mt_is_active(i_slot) &&
+ input_mt_is_used(mt, i_slot))
+ return -EAGAIN;
+ }
+
+ if (quirks & MT_QUIRK_CONFIDENCE)
+ confidence_state = *slot->confidence_state;
+
+ if (quirks & MT_QUIRK_HOVERING)
+ inrange_state = *slot->inrange_state;
+
+ active = *slot->tip_state || inrange_state;
+
+ if (app->application == HID_GD_SYSTEM_MULTIAXIS)
+ tool = MT_TOOL_DIAL;
+ else if (unlikely(!confidence_state)) {
+ tool = MT_TOOL_PALM;
+ if (!active &&
+ input_mt_is_active(&mt->slots[slotnum])) {
+ /*
+ * The non-confidence was reported for
+ * previously valid contact that is also no
+ * longer valid. We can't simply report
+ * lift-off as userspace will not be aware
+ * of non-confidence, so we need to split
+ * it into 2 events: active MT_TOOL_PALM
+ * and a separate liftoff.
+ */
+ active = true;
+ set_bit(sl