|
@@ -81,6 +81,11 @@ MODULE_LICENSE("GPL");
|
|
|
|
|
|
#define MT_BUTTONTYPE_CLICKPAD 0
|
|
#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_RUNNING 0
|
|
#define MT_IO_FLAGS_ACTIVE_SLOTS 1
|
|
#define MT_IO_FLAGS_ACTIVE_SLOTS 1
|
|
#define MT_IO_FLAGS_PENDING_SLOTS 2
|
|
#define MT_IO_FLAGS_PENDING_SLOTS 2
|
|
@@ -127,11 +132,7 @@ struct mt_device {
|
|
int left_button_state; /* left button state */
|
|
int left_button_state; /* left button state */
|
|
unsigned last_slot_field; /* the last field of a slot */
|
|
unsigned last_slot_field; /* the last field of a slot */
|
|
unsigned mt_report_id; /* the report ID of the multitouch device */
|
|
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_received; /* how many contacts we received */
|
|
__u8 num_expected; /* expected last contact index */
|
|
__u8 num_expected; /* expected last contact index */
|
|
__u8 maxcontacts;
|
|
__u8 maxcontacts;
|
|
@@ -415,32 +416,9 @@ static void mt_feature_mapping(struct hid_device *hdev,
|
|
struct mt_device *td = hid_get_drvdata(hdev);
|
|
struct mt_device *td = hid_get_drvdata(hdev);
|
|
|
|
|
|
switch (usage->hid) {
|
|
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:
|
|
case HID_DG_CONTACTMAX:
|
|
mt_get_feature(hdev, field->report);
|
|
mt_get_feature(hdev, field->report);
|
|
|
|
|
|
- td->maxcontact_report_id = field->report->id;
|
|
|
|
td->maxcontacts = field->value[0];
|
|
td->maxcontacts = field->value[0];
|
|
if (!td->maxcontacts &&
|
|
if (!td->maxcontacts &&
|
|
field->logical_maximum <= MT_MAX_MAXCONTACT)
|
|
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,
|
|
hid_map_usage(hi, usage, bit, max,
|
|
EV_MSC, MSC_TIMESTAMP);
|
|
EV_MSC, MSC_TIMESTAMP);
|
|
input_set_capability(hi->input, 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. */
|
|
/* Ignore if indexes are out of bounds. */
|
|
if (field->index >= field->report->maxfield ||
|
|
if (field->index >= field->report->maxfield ||
|
|
usage->usage_index >= field->report_count)
|
|
usage->usage_index >= field->report_count)
|
|
return 1;
|
|
return 1;
|
|
td->scantime_index = field->index;
|
|
td->scantime_index = field->index;
|
|
td->scantime_val_index = usage->usage_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;
|
|
return 1;
|
|
case HID_DG_CONTACTCOUNT:
|
|
case HID_DG_CONTACTCOUNT:
|
|
/* Ignore if indexes are out of bounds. */
|
|
/* 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);
|
|
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 mt_device *td = hid_get_drvdata(hdev);
|
|
- struct hid_report *r;
|
|
|
|
- struct hid_report_enum *re;
|
|
|
|
struct mt_class *cls = &td->mtclass;
|
|
struct mt_class *cls = &td->mtclass;
|
|
|
|
+ struct hid_report *report = field->report;
|
|
|
|
+ unsigned int index = usage->usage_index;
|
|
char *buf;
|
|
char *buf;
|
|
u32 report_len;
|
|
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) {
|
|
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) {
|
|
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_FEATURE_REPORT,
|
|
HID_REQ_GET_REPORT);
|
|
HID_REQ_GET_REPORT);
|
|
kfree(buf);
|
|
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);
|
|
struct mt_device *td = hid_get_drvdata(hdev);
|
|
char *name;
|
|
char *name;
|
|
const char *suffix = NULL;
|
|
const char *suffix = NULL;
|
|
- struct hid_field *field = hi->report->field[0];
|
|
|
|
|
|
+ unsigned int application = 0;
|
|
|
|
+ struct hid_report *report;
|
|
int ret;
|
|
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:
|
|
case HID_GD_KEYBOARD:
|
|
- suffix = "Keyboard";
|
|
|
|
- break;
|
|
|
|
case HID_GD_KEYPAD:
|
|
case HID_GD_KEYPAD:
|
|
- suffix = "Keypad";
|
|
|
|
- break;
|
|
|
|
case HID_GD_MOUSE:
|
|
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:
|
|
case HID_DG_TOUCHPAD:
|
|
- suffix = "Touchpad";
|
|
|
|
- break;
|
|
|
|
case HID_GD_SYSTEM_CONTROL:
|
|
case HID_GD_SYSTEM_CONTROL:
|
|
- suffix = "System Control";
|
|
|
|
- break;
|
|
|
|
case HID_CP_CONSUMER_CONTROL:
|
|
case HID_CP_CONSUMER_CONTROL:
|
|
- suffix = "Consumer Control";
|
|
|
|
- break;
|
|
|
|
case HID_GD_WIRELESS_RADIO_CTLS:
|
|
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;
|
|
break;
|
|
case HID_VD_ASUS_CUSTOM_MEDIA_KEYS:
|
|
case HID_VD_ASUS_CUSTOM_MEDIA_KEYS:
|
|
suffix = "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->hdev = hdev;
|
|
td->mtclass = *mtclass;
|
|
td->mtclass = *mtclass;
|
|
- td->inputmode = -1;
|
|
|
|
- td->maxcontact_report_id = -1;
|
|
|
|
td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
|
|
td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
|
|
td->cc_index = -1;
|
|
td->cc_index = -1;
|
|
td->scantime_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
|
|
* 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.
|
|
* device.
|
|
*/
|
|
*/
|
|
- hdev->quirks |= HID_QUIRK_MULTI_INPUT;
|
|
|
|
|
|
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
|
|
|
|
|
|
timer_setup(&td->release_timer, mt_expired_timeout, 0);
|
|
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",
|
|
dev_warn(&hdev->dev, "Cannot allocate sysfs group for %s\n",
|
|
hdev->name);
|
|
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 */
|
|
/* release .fields memory as it is not used anymore */
|
|
devm_kfree(&hdev->dev, td->fields);
|
|
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)
|
|
static int mt_reset_resume(struct hid_device *hdev)
|
|
{
|
|
{
|
|
mt_release_contacts(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;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|