diff options
author | Jiri Kosina <jkosina@suse.cz> | 2018-06-08 11:25:50 +0300 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2018-06-08 11:25:50 +0300 |
commit | 25721aefe1d0b0d604d66fb5912fb383ce56c6ac (patch) | |
tree | 4da443dbf4be259191b8386d90933fa3f0b75a1d /drivers/hid/hid-multitouch.c | |
parent | 72d0beb4d6d4b17f925fc6d1e9ebfb548f2e809c (diff) | |
parent | abb36fe691b28f2a64926b61448d6b9610ed879a (diff) | |
download | linux-25721aefe1d0b0d604d66fb5912fb383ce56c6ac.tar.xz |
Merge branch 'for-4.18/multitouch' into for-linus
- improvement of duplicate usage handling in hid-input from Benjamin Tissoires
- Win 8.1 precisioun touchpad spec implementation from Benjamin Tissoires
Diffstat (limited to 'drivers/hid/hid-multitouch.c')
-rw-r--r-- | drivers/hid/hid-multitouch.c | 232 |
1 files changed, 121 insertions, 111 deletions
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index dad2fbb0e3f8..45968f7970f8 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -81,6 +81,11 @@ MODULE_LICENSE("GPL"); #define MT_BUTTONTYPE_CLICKPAD 0 +enum latency_mode { + HID_LATENCY_NORMAL = 0, + HID_LATENCY_HIGH = 1, +}; + #define MT_IO_FLAGS_RUNNING 0 #define MT_IO_FLAGS_ACTIVE_SLOTS 1 #define MT_IO_FLAGS_PENDING_SLOTS 2 @@ -127,11 +132,7 @@ struct mt_device { 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 */ - __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ - __s16 inputmode_index; /* InputMode HID feature index in the report */ - __s16 maxcontact_report_id; /* Maximum Contact Number HID feature, - -1 if non-existent */ - __u8 inputmode_value; /* InputMode HID feature value */ + __u8 inputmode_value; /* InputMode HID feature value */ __u8 num_received; /* how many contacts we received */ __u8 num_expected; /* expected last contact index */ __u8 maxcontacts; @@ -415,32 +416,9 @@ static void mt_feature_mapping(struct hid_device *hdev, struct mt_device *td = hid_get_drvdata(hdev); switch (usage->hid) { - case HID_DG_INPUTMODE: - /* Ignore if value index is out of bounds. */ - if (usage->usage_index >= field->report_count) { - dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n"); - break; - } - - if (td->inputmode < 0) { - td->inputmode = field->report->id; - td->inputmode_index = usage->usage_index; - } else { - /* - * Some elan panels wrongly declare 2 input mode - * features, and silently ignore when we set the - * value in the second field. Skip the second feature - * and hope for the best. - */ - dev_info(&hdev->dev, - "Ignoring the extra HID_DG_INPUTMODE\n"); - } - - break; case HID_DG_CONTACTMAX: mt_get_feature(hdev, field->report); - td->maxcontact_report_id = field->report->id; td->maxcontacts = field->value[0]; if (!td->maxcontacts && field->logical_maximum <= MT_MAX_MAXCONTACT) @@ -620,13 +598,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, hid_map_usage(hi, usage, bit, max, EV_MSC, MSC_TIMESTAMP); input_set_capability(hi->input, EV_MSC, MSC_TIMESTAMP); - mt_store_field(usage, td, hi); /* 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. + */ return 1; case HID_DG_CONTACTCOUNT: /* Ignore if indexes are out of bounds. */ @@ -1181,61 +1162,100 @@ static void mt_report(struct hid_device *hid, struct hid_report *report) input_sync(field->hidinput->input); } -static void mt_set_input_mode(struct hid_device *hdev) +static bool mt_need_to_apply_feature(struct hid_device *hdev, + struct hid_field *field, + struct hid_usage *usage, + enum latency_mode latency, + bool surface_switch, + bool button_switch) { struct mt_device *td = hid_get_drvdata(hdev); - struct hid_report *r; - struct hid_report_enum *re; struct mt_class *cls = &td->mtclass; + struct hid_report *report = field->report; + unsigned int index = usage->usage_index; char *buf; u32 report_len; + int max; - if (td->inputmode < 0) - return; - - re = &(hdev->report_enum[HID_FEATURE_REPORT]); - r = re->report_id_hash[td->inputmode]; - if (r) { + switch (usage->hid) { + case HID_DG_INPUTMODE: if (cls->quirks & MT_QUIRK_FORCE_GET_FEATURE) { - report_len = hid_report_len(r); - buf = hid_alloc_report_buf(r, GFP_KERNEL); + report_len = hid_report_len(report); + buf = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) { - hid_err(hdev, "failed to allocate buffer for report\n"); - return; + hid_err(hdev, + "failed to allocate buffer for report\n"); + return false; } - hid_hw_raw_request(hdev, r->id, buf, report_len, + hid_hw_raw_request(hdev, report->id, buf, report_len, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); kfree(buf); } - r->field[0]->value[td->inputmode_index] = td->inputmode_value; - hid_hw_request(hdev, r, HID_REQ_SET_REPORT); - } -} -static void mt_set_maxcontacts(struct hid_device *hdev) -{ - struct mt_device *td = hid_get_drvdata(hdev); - struct hid_report *r; - struct hid_report_enum *re; - int fieldmax, max; + field->value[index] = td->inputmode_value; + return true; - if (td->maxcontact_report_id < 0) - return; + case HID_DG_CONTACTMAX: + if (td->mtclass.maxcontacts) { + max = min_t(int, field->logical_maximum, + td->mtclass.maxcontacts); + if (field->value[index] != max) { + field->value[index] = max; + return true; + } + } + break; - if (!td->mtclass.maxcontacts) - return; + case HID_DG_LATENCYMODE: + field->value[index] = latency; + return true; + + case HID_DG_SURFACESWITCH: + field->value[index] = surface_switch; + return true; + + case HID_DG_BUTTONSWITCH: + field->value[index] = button_switch; + return true; + } - re = &hdev->report_enum[HID_FEATURE_REPORT]; - r = re->report_id_hash[td->maxcontact_report_id]; - if (r) { - max = td->mtclass.maxcontacts; - fieldmax = r->field[0]->logical_maximum; - max = min(fieldmax, max); - if (r->field[0]->value[0] != max) { - r->field[0]->value[0] = max; - hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + return false; /* no need to update the report */ +} + +static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency, + bool surface_switch, bool button_switch) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_usage *usage; + int i, j; + bool update_report; + + rep_enum = &hdev->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + update_report = false; + + for (i = 0; i < rep->maxfield; i++) { + /* Ignore if report count is out of bounds. */ + if (rep->field[i]->report_count < 1) + continue; + + for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + + if (mt_need_to_apply_feature(hdev, + rep->field[i], + usage, + latency, + surface_switch, + button_switch)) + update_report = true; + } } + + if (update_report) + hid_hw_request(hdev, rep, HID_REQ_SET_REPORT); } } @@ -1274,54 +1294,48 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) struct mt_device *td = hid_get_drvdata(hdev); char *name; const char *suffix = NULL; - struct hid_field *field = hi->report->field[0]; + unsigned int application = 0; + struct hid_report *report; int ret; - if (hi->report->id == td->mt_report_id) { - ret = mt_touch_input_configured(hdev, hi); - if (ret) - return ret; + list_for_each_entry(report, &hi->reports, hidinput_list) { + application = report->application; + if (report->id == td->mt_report_id) { + ret = mt_touch_input_configured(hdev, hi); + if (ret) + return ret; + } + + /* + * some egalax touchscreens have "application == DG_TOUCHSCREEN" + * for the stylus. Check this first, and then rely on + * the application field. + */ + if (report->field[0]->physical == HID_DG_STYLUS) { + suffix = "Pen"; + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); + } } - /* - * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" - * for the stylus. Check this first, and then rely on the application - * field. - */ - if (hi->report->field[0]->physical == HID_DG_STYLUS) { - suffix = "Pen"; - /* force BTN_STYLUS to allow tablet matching in udev */ - __set_bit(BTN_STYLUS, hi->input->keybit); - } else { - switch (field->application) { + if (!suffix) { + switch (application) { case HID_GD_KEYBOARD: - suffix = "Keyboard"; - break; case HID_GD_KEYPAD: - suffix = "Keypad"; - break; case HID_GD_MOUSE: - suffix = "Mouse"; - break; - case HID_DG_STYLUS: - suffix = "Pen"; - /* force BTN_STYLUS to allow tablet matching in udev */ - __set_bit(BTN_STYLUS, hi->input->keybit); - break; - case HID_DG_TOUCHSCREEN: - /* we do not set suffix = "Touchscreen" */ - break; case HID_DG_TOUCHPAD: - suffix = "Touchpad"; - break; case HID_GD_SYSTEM_CONTROL: - suffix = "System Control"; - break; case HID_CP_CONSUMER_CONTROL: - suffix = "Consumer Control"; - break; case HID_GD_WIRELESS_RADIO_CTLS: - suffix = "Wireless Radio Control"; + /* already handled by hid core */ + break; + case HID_DG_TOUCHSCREEN: + /* we do not set suffix = "Touchscreen" */ + hi->input->name = hdev->name; + break; + case HID_DG_STYLUS: + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); break; case HID_VD_ASUS_CUSTOM_MEDIA_KEYS: suffix = "Custom Media Keys"; @@ -1434,8 +1448,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) } td->hdev = hdev; td->mtclass = *mtclass; - td->inputmode = -1; - td->maxcontact_report_id = -1; td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; td->cc_index = -1; td->scantime_index = -1; @@ -1459,10 +1471,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) /* * This allows the driver to handle different input sensors - * that emits events through different reports on the same HID + * that emits events through different applications on the same HID * device. */ - hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_INPUT_PER_APP; timer_setup(&td->release_timer, mt_expired_timeout, 0); @@ -1482,8 +1494,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) dev_warn(&hdev->dev, "Cannot allocate sysfs group for %s\n", hdev->name); - mt_set_maxcontacts(hdev); - mt_set_input_mode(hdev); + mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true); /* release .fields memory as it is not used anymore */ devm_kfree(&hdev->dev, td->fields); @@ -1496,8 +1507,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) static int mt_reset_resume(struct hid_device *hdev) { mt_release_contacts(hdev); - mt_set_maxcontacts(hdev); - mt_set_input_mode(hdev); + mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true); return 0; } |