Hi Vitaly,
Yes, we made a quirk for separate non-multitouch processing of the
HID_DG_PEN application for Goodix gt7385p (I2C_DEVICE_ID_GOODIX_0113 ==
0x0113). It works well, BTN_TOOL_PEN and BTN_TOUCH events have right order.
I attach patches for kernel 5.4
Buttons support was very tricky, because the panel tries to imitate
eraser usage with combination of tip's and eraser's bits off and on,
seems that Windows driver needs this. In second patch we add
state-machines for pen buttons to simulate right and middle click. It
works also well with this panel/pen, but I'm not sure if it works with
other devices.
Kind regards,
Dmitry Mastykin
On 4/14/21 6:35 PM, Vitaly Minko wrote:
Hello Dmitry,
I have faced with exactly the same problem. Have you managed to solve
the issue?
If yes, could you please share the patch?
Thanks in advance!
Best regards,
Vitaly
From b7ee3d77291460b21437280b9838aaf680046073 Mon Sep 17 00:00:00 2001
From: Dmitry Mastykin <dmastykin@xxxxxxxxxxxxx>
Date: Tue, 25 Aug 2020 10:29:03 +0300
Subject: pen support
no buttons
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 128d8f4319b9..280d85e0eac5 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -48,6 +48,9 @@ MODULE_LICENSE("GPL");
#include "hid-ids.h"
+// #define MY(fmt,arg...) printk(KERN_INFO "%s: " fmt "\n", __func__, ##arg)
+#define MY(fmt,arg...)
+
/* quirks to control the device */
#define MT_QUIRK_NOT_SEEN_MEANS_UP BIT(0)
#define MT_QUIRK_SLOT_IS_CONTACTID BIT(1)
@@ -70,6 +73,7 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_WIN8_PTP_BUTTONS BIT(18)
#define MT_QUIRK_SEPARATE_APP_REPORT BIT(19)
#define MT_QUIRK_FORCE_MULTI_INPUT BIT(20)
+#define MT_QUIRK_NON_MT_PEN BIT(21)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
@@ -100,6 +104,9 @@ struct mt_usages {
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? */
+ bool *barrel_state;
+ bool *invert_state;
+ bool *eraser_state;
};
struct mt_application {
@@ -153,6 +160,7 @@ struct mt_report_data {
struct hid_report *report;
struct mt_application *application;
bool is_mt_collection;
+ bool non_mt_pen;
};
struct mt_device {
@@ -208,6 +216,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
#define MT_CLS_GOOGLE 0x0111
#define MT_CLS_RAZER_BLADE_STEALTH 0x0112
#define MT_CLS_SMART_TECH 0x0113
+#define MT_CLS_NON_MT_PEN 0x0114
#define MT_DEFAULT_MAXCONTACT 10
#define MT_MAX_MAXCONTACT 250
@@ -374,6 +383,9 @@ static const struct mt_class mt_classes[] = {
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_SEPARATE_APP_REPORT,
},
+ { .name = MT_CLS_NON_MT_PEN,
+ .quirks = MT_QUIRK_NON_MT_PEN,
+ },
{ }
};
@@ -523,6 +535,9 @@ static struct mt_usages *mt_allocate_usage(struct hid_device *hdev,
usage->tip_state = DEFAULT_FALSE;
usage->inrange_state = DEFAULT_FALSE;
usage->confidence_state = DEFAULT_TRUE;
+ usage->barrel_state = DEFAULT_FALSE;
+ usage->invert_state = DEFAULT_FALSE;
+ usage->eraser_state = DEFAULT_FALSE;
list_add_tail(&usage->list, &application->mt_usages);
@@ -877,6 +892,67 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
+static int mt_pen_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max, struct mt_application *app)
+{
+ struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_class *cls = &td->mtclass;
+
+ MY("application:%s:%x:%x", hdev->name, field->application, usage->hid);
+
+ switch (usage->hid & HID_USAGE_PAGE) {
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ __set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
+ set_abs(hi->input, ABS_X, field, cls->sn_move);
+ MT_STORE_FIELD(x);
+ return 1;
+ case HID_GD_Y:
+ set_abs(hi->input, ABS_Y, field, cls->sn_move);
+ MT_STORE_FIELD(y);
+ return 1;
+ }
+ return -1;
+
+ case HID_UP_DIGITIZER:
+ switch (usage->hid) {
+ case HID_DG_INRANGE:
+ input_set_capability(hi->input,
+ EV_KEY, BTN_TOOL_PEN);
+ input_set_abs_params(hi->input,
+ ABS_DISTANCE, 0, 1, 0, 0);
+ MT_STORE_FIELD(inrange_state);
+ return 1;
+ case HID_DG_TIPSWITCH:
+ input_set_capability(hi->input,
+ EV_KEY, BTN_TOUCH);
+ MT_STORE_FIELD(tip_state);
+ return 1;
+ case HID_DG_BARRELSWITCH:
+ input_set_capability(hi->input,
+ EV_KEY, BTN_STYLUS2);
+ MT_STORE_FIELD(barrel_state);
+ return 1;
+ case HID_DG_INVERT:
+ MT_STORE_FIELD(invert_state);
+ return 1;
+ case HID_DG_ERASER:
+ MT_STORE_FIELD(eraser_state);
+ return 1;
+ case HID_DG_TIPPRESSURE:
+ set_abs(hi->input, ABS_PRESSURE, field,
+ cls->sn_pressure);
+ MT_STORE_FIELD(p);
+ return 1;
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
static int mt_compute_slot(struct mt_device *td, struct mt_application *app,
struct mt_usages *slot,
struct input_dev *input)
@@ -1242,6 +1318,34 @@ static void mt_touch_report(struct hid_device *hid,
clear_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags);
}
+static void mt_pen_report(struct hid_device *hid,
+ struct mt_report_data *rdata)
+{
+ struct mt_application *app = rdata->application;
+ struct mt_usages *usage;
+ struct input_dev *input = rdata->report->field[0]->hidinput->input;
+
+ if (!(usage = list_first_entry_or_null(&app->mt_usages,
+ struct mt_usages, list)))
+ return;
+
+
+ MY("inr:tip:bar:inv:era %d:%d:%d:%d:%d",
+ *usage->inrange_state,
+ *usage->tip_state,
+ *usage->barrel_state,
+ *usage->invert_state,
+ *usage->eraser_state
+ );
+ input_report_key(input, BTN_TOOL_PEN, *usage->inrange_state);
+ input_report_key(input, BTN_TOUCH, *usage->tip_state);
+ // input_report_key(input, BTN_STYLUS2, *usage->barrel_state);
+ input_event(input, EV_ABS, ABS_X, *usage->x);
+ input_event(input, EV_ABS, ABS_Y, *usage->y);
+ input_event(input, EV_ABS, ABS_PRESSURE, *usage->p);
+ input_event(input, EV_ABS, ABS_DISTANCE, !*usage->tip_state);
+}
+
static int mt_touch_input_configured(struct hid_device *hdev,
struct hid_input *hi,
struct mt_application *app)
@@ -1347,6 +1451,14 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 1;
}
+ if (field->application == HID_DG_PEN &&
+ application->quirks & MT_QUIRK_NON_MT_PEN) {
+ rdata->is_mt_collection = false;
+ rdata->non_mt_pen = true;
+ return mt_pen_input_mapping(hdev, hi, field, usage, bit, max,
+ application);
+ }
+
if (rdata->is_mt_collection)
return mt_touch_input_mapping(hdev, hi, field, usage, bit, max,
application);
@@ -1370,7 +1482,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct mt_report_data *rdata;
rdata = mt_find_report_data(td, field->report);
- if (rdata && rdata->is_mt_collection) {
+ if (rdata && (rdata->is_mt_collection || rdata->non_mt_pen)) {
/* We own these mappings, tell hid-input to ignore them */
return -1;
}
@@ -1404,6 +1516,8 @@ static void mt_report(struct hid_device *hid, struct hid_report *report)
rdata = mt_find_report_data(td, report);
if (rdata && rdata->is_mt_collection)
return mt_touch_report(hid, rdata);
+ if (rdata && rdata->non_mt_pen)
+ mt_pen_report(hid, rdata);
if (field && field->hidinput && field->hidinput->input)
input_sync(field->hidinput->input);
@@ -2151,6 +2265,11 @@ static const struct hid_device_id mt_devices[] = {
HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE,
USB_DEVICE_ID_GOOGLE_TOUCH_ROSE) },
+ { .driver_data = MT_CLS_NON_MT_PEN,
+ HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
+ I2C_VENDOR_ID_GOODIX,
+ I2C_DEVICE_ID_GOODIX_0113) },
+
/* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },
From 530201a53fc0bb9b0db53c41d8e2fe7f324a4d8f Mon Sep 17 00:00:00 2001
From: Dmitry Mastykin <dmastykin@xxxxxxxxxxxxx>
Date: Fri, 20 Nov 2020 18:12:39 +0300
Subject: buttons support
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 280d85e0eac5..5d4301fa5b75 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -107,6 +107,8 @@ struct mt_usages {
bool *barrel_state;
bool *invert_state;
bool *eraser_state;
+ int rclick;
+ int mclick;
};
struct mt_application {
@@ -538,6 +540,8 @@ static struct mt_usages *mt_allocate_usage(struct hid_device *hdev,
usage->barrel_state = DEFAULT_FALSE;
usage->invert_state = DEFAULT_FALSE;
usage->eraser_state = DEFAULT_FALSE;
+ usage->rclick = 0;
+ usage->mclick = 0;
list_add_tail(&usage->list, &application->mt_usages);
@@ -939,6 +943,8 @@ static int mt_pen_input_mapping(struct hid_device *hdev, struct hid_input *hi,
MT_STORE_FIELD(invert_state);
return 1;
case HID_DG_ERASER:
+ input_set_capability(hi->input,
+ EV_KEY, BTN_STYLUS);
MT_STORE_FIELD(eraser_state);
return 1;
case HID_DG_TIPPRESSURE:
@@ -1329,17 +1335,58 @@ static void mt_pen_report(struct hid_device *hid,
struct mt_usages, list)))
return;
+ switch (usage->rclick) {
+ case 0:
+ if (*usage->barrel_state)
+ usage->rclick++;
+ break;
+ case 1:
+ case 7:
+ if (*usage->tip_state)
+ usage->rclick = 2;
+ else if (!*usage->barrel_state)
+ usage->rclick = 0;
+ break;
+ case 2:
+ if (!*usage->tip_state)
+ usage->rclick++;
+ break;
+ default:
+ usage->rclick++;
+ }
+
+ switch (usage->mclick) {
+ case 0:
+ if (*usage->eraser_state)
+ usage->mclick++;
+ break;
+ case 1:
+ if (!*usage->eraser_state)
+ usage->mclick++;
+ break;
+ case 3:
+ if (!*usage->tip_state)
+ usage->mclick = 0;
+ break;
+ default:
+ usage->mclick++;
+ }
- MY("inr:tip:bar:inv:era %d:%d:%d:%d:%d",
+ MY("inr:tip:bar:rck::inv:era:mck %d:%d:%d:%d::%d:%d:%d",
*usage->inrange_state,
*usage->tip_state,
*usage->barrel_state,
+ usage->rclick,
*usage->invert_state,
- *usage->eraser_state
+ *usage->eraser_state,
+ usage->mclick
);
- input_report_key(input, BTN_TOOL_PEN, *usage->inrange_state);
- input_report_key(input, BTN_TOUCH, *usage->tip_state);
- // input_report_key(input, BTN_STYLUS2, *usage->barrel_state);
+ input_report_key(input, BTN_TOOL_PEN, *usage->inrange_state ||
+ *usage->tip_state);
+ input_report_key(input, BTN_TOUCH, *usage->tip_state &&
+ !(usage->rclick || usage->mclick));
+ input_report_key(input, BTN_STYLUS2, usage->rclick == 2);
+ input_report_key(input, BTN_STYLUS, usage->mclick);
input_event(input, EV_ABS, ABS_X, *usage->x);
input_event(input, EV_ABS, ABS_Y, *usage->y);
input_event(input, EV_ABS, ABS_PRESSURE, *usage->p);