Skip to content

Commit 9ce12d8

Browse files
Brendan McGrathJiri Kosina
authored andcommitted
HID: asus: Add i2c touchpad support
Update the hid-asus module to add multitouch support for the Asus i2c touchpad. This patch aims to resolve the issue raised here: https://bugzilla.kernel.org/show_bug.cgi?id=120181 The issue is in relation to an Asus touchpad device which currently does not have multitouch support. The device currently falls through to the hid-generic driver which treats the device as a mouse. This patch aims to add the multitouch support. [[email protected]: move most of the 'patch comment' into actual changelog] [[email protected]: drop hunk that changes ->name of the driver] Reviewed-by: Benjamin Tissoires <[email protected]> Signed-off-by: Brendan McGrath <[email protected]> Signed-off-by: Victor Vlasenko <[email protected]> Signed-off-by: Frederik Wenigwieser <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent e2b6535 commit 9ce12d8

File tree

4 files changed

+296
-7
lines changed

4 files changed

+296
-7
lines changed

drivers/hid/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ config HID_ASUS
138138
tristate "Asus"
139139
depends on I2C_HID
140140
---help---
141-
Support for Asus notebook built-in keyboard via i2c.
141+
Support for Asus notebook built-in keyboard and touchpad via i2c.
142142

143143
Supported devices:
144144
- EeeBook X205TA

drivers/hid/hid-asus.c

Lines changed: 293 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
* This module based on hid-ortek by
1212
* Copyright (c) 2010 Johnathon Harris <[email protected]>
1313
* Copyright (c) 2011 Jiri Kosina
14+
*
15+
* This module has been updated to add support for Asus i2c touchpad.
16+
*
17+
* Copyright (c) 2016 Brendan McGrath <[email protected]>
18+
* Copyright (c) 2016 Victor Vlasenko <[email protected]>
19+
* Copyright (c) 2016 Frederik Wenigwieser <[email protected]>
1420
*/
1521

1622
/*
@@ -20,32 +26,313 @@
2026
* any later version.
2127
*/
2228

23-
#include <linux/device.h>
2429
#include <linux/hid.h>
2530
#include <linux/module.h>
31+
#include <linux/input/mt.h>
2632

2733
#include "hid-ids.h"
2834

35+
MODULE_AUTHOR("Yusuke Fujimaki <[email protected]>");
36+
MODULE_AUTHOR("Brendan McGrath <[email protected]>");
37+
MODULE_AUTHOR("Victor Vlasenko <[email protected]>");
38+
MODULE_AUTHOR("Frederik Wenigwieser <[email protected]>");
39+
MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
40+
41+
#define FEATURE_REPORT_ID 0x0d
42+
#define INPUT_REPORT_ID 0x5d
43+
44+
#define INPUT_REPORT_SIZE 28
45+
46+
#define MAX_CONTACTS 5
47+
48+
#define MAX_X 2794
49+
#define MAX_Y 1758
50+
#define MAX_TOUCH_MAJOR 8
51+
#define MAX_PRESSURE 128
52+
53+
#define CONTACT_DATA_SIZE 5
54+
55+
#define BTN_LEFT_MASK 0x01
56+
#define CONTACT_TOOL_TYPE_MASK 0x80
57+
#define CONTACT_X_MSB_MASK 0xf0
58+
#define CONTACT_Y_MSB_MASK 0x0f
59+
#define CONTACT_TOUCH_MAJOR_MASK 0x07
60+
#define CONTACT_PRESSURE_MASK 0x7f
61+
62+
#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0)
63+
#define QUIRK_NO_INIT_REPORTS BIT(1)
64+
#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
65+
#define QUIRK_IS_MULTITOUCH BIT(3)
66+
67+
#define NOTEBOOK_QUIRKS QUIRK_FIX_NOTEBOOK_REPORT
68+
#define TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
69+
QUIRK_SKIP_INPUT_MAPPING | \
70+
QUIRK_IS_MULTITOUCH)
71+
72+
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
73+
74+
struct asus_drvdata {
75+
unsigned long quirks;
76+
struct input_dev *input;
77+
};
78+
79+
static void asus_report_contact_down(struct input_dev *input,
80+
int toolType, u8 *data)
81+
{
82+
int touch_major, pressure;
83+
int x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1];
84+
int y = MAX_Y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]);
85+
86+
if (toolType == MT_TOOL_PALM) {
87+
touch_major = MAX_TOUCH_MAJOR;
88+
pressure = MAX_PRESSURE;
89+
} else {
90+
touch_major = (data[3] >> 4) & CONTACT_TOUCH_MAJOR_MASK;
91+
pressure = data[4] & CONTACT_PRESSURE_MASK;
92+
}
93+
94+
input_report_abs(input, ABS_MT_POSITION_X, x);
95+
input_report_abs(input, ABS_MT_POSITION_Y, y);
96+
input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
97+
input_report_abs(input, ABS_MT_PRESSURE, pressure);
98+
}
99+
100+
/* Required for Synaptics Palm Detection */
101+
static void asus_report_tool_width(struct input_dev *input)
102+
{
103+
struct input_mt *mt = input->mt;
104+
struct input_mt_slot *oldest;
105+
int oldid, count, i;
106+
107+
oldest = NULL;
108+
oldid = mt->trkid;
109+
count = 0;
110+
111+
for (i = 0; i < mt->num_slots; ++i) {
112+
struct input_mt_slot *ps = &mt->slots[i];
113+
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
114+
115+
if (id < 0)
116+
continue;
117+
if ((id - oldid) & TRKID_SGN) {
118+
oldest = ps;
119+
oldid = id;
120+
}
121+
count++;
122+
}
123+
124+
if (oldest) {
125+
input_report_abs(input, ABS_TOOL_WIDTH,
126+
input_mt_get_value(oldest, ABS_MT_TOUCH_MAJOR));
127+
}
128+
}
129+
130+
static void asus_report_input(struct input_dev *input, u8 *data)
131+
{
132+
int i;
133+
u8 *contactData = data + 2;
134+
135+
for (i = 0; i < MAX_CONTACTS; i++) {
136+
bool down = !!(data[1] & BIT(i+3));
137+
int toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ?
138+
MT_TOOL_PALM : MT_TOOL_FINGER;
139+
140+
input_mt_slot(input, i);
141+
input_mt_report_slot_state(input, toolType, down);
142+
143+
if (down) {
144+
asus_report_contact_down(input, toolType, contactData);
145+
contactData += CONTACT_DATA_SIZE;
146+
}
147+
}
148+
149+
input_report_key(input, BTN_LEFT, data[1] & BTN_LEFT_MASK);
150+
asus_report_tool_width(input);
151+
152+
input_mt_sync_frame(input);
153+
input_sync(input);
154+
}
155+
156+
static int asus_raw_event(struct hid_device *hdev,
157+
struct hid_report *report, u8 *data, int size)
158+
{
159+
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
160+
161+
if (drvdata->quirks & QUIRK_IS_MULTITOUCH &&
162+
data[0] == INPUT_REPORT_ID &&
163+
size == INPUT_REPORT_SIZE) {
164+
asus_report_input(drvdata->input, data);
165+
return 1;
166+
}
167+
168+
return 0;
169+
}
170+
171+
static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
172+
{
173+
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
174+
175+
if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
176+
int ret;
177+
struct input_dev *input = hi->input;
178+
179+
input_set_abs_params(input, ABS_MT_POSITION_X, 0, MAX_X, 0, 0);
180+
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0);
181+
input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MAX_TOUCH_MAJOR, 0, 0);
182+
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0);
183+
input_set_abs_params(input, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0);
184+
185+
__set_bit(BTN_LEFT, input->keybit);
186+
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
187+
188+
ret = input_mt_init_slots(input, MAX_CONTACTS, INPUT_MT_POINTER);
189+
190+
if (ret) {
191+
hid_err(hdev, "Asus input mt init slots failed: %d\n", ret);
192+
return ret;
193+
}
194+
195+
drvdata->input = input;
196+
}
197+
198+
return 0;
199+
}
200+
201+
static int asus_input_mapping(struct hid_device *hdev,
202+
struct hid_input *hi, struct hid_field *field,
203+
struct hid_usage *usage, unsigned long **bit,
204+
int *max)
205+
{
206+
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
207+
208+
if (drvdata->quirks & QUIRK_SKIP_INPUT_MAPPING) {
209+
/* Don't map anything from the HID report.
210+
* We do it all manually in asus_input_configured
211+
*/
212+
return -1;
213+
}
214+
215+
return 0;
216+
}
217+
218+
static int asus_start_multitouch(struct hid_device *hdev)
219+
{
220+
int ret;
221+
const unsigned char buf[] = { FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00 };
222+
unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
223+
224+
if (!dmabuf) {
225+
ret = -ENOMEM;
226+
hid_err(hdev, "Asus failed to alloc dma buf: %d\n", ret);
227+
return ret;
228+
}
229+
230+
ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf),
231+
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
232+
233+
kfree(dmabuf);
234+
235+
if (ret != sizeof(buf)) {
236+
hid_err(hdev, "Asus failed to start multitouch: %d\n", ret);
237+
return ret;
238+
}
239+
240+
return 0;
241+
}
242+
243+
static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
244+
{
245+
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
246+
247+
if (drvdata->quirks & QUIRK_IS_MULTITOUCH)
248+
return asus_start_multitouch(hdev);
249+
250+
return 0;
251+
}
252+
253+
static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
254+
{
255+
int ret;
256+
struct asus_drvdata *drvdata;
257+
258+
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
259+
if (drvdata == NULL) {
260+
hid_err(hdev, "Can't alloc Asus descriptor\n");
261+
return -ENOMEM;
262+
}
263+
264+
hid_set_drvdata(hdev, drvdata);
265+
266+
drvdata->quirks = id->driver_data;
267+
268+
if (drvdata->quirks & QUIRK_NO_INIT_REPORTS)
269+
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
270+
271+
ret = hid_parse(hdev);
272+
if (ret) {
273+
hid_err(hdev, "Asus hid parse failed: %d\n", ret);
274+
return ret;
275+
}
276+
277+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
278+
if (ret) {
279+
hid_err(hdev, "Asus hw start failed: %d\n", ret);
280+
return ret;
281+
}
282+
283+
if (!drvdata->input) {
284+
hid_err(hdev, "Asus input not registered\n");
285+
ret = -ENOMEM;
286+
goto err_stop_hw;
287+
}
288+
289+
drvdata->input->name = "Asus TouchPad";
290+
291+
if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
292+
ret = asus_start_multitouch(hdev);
293+
if (ret)
294+
goto err_stop_hw;
295+
}
296+
297+
return 0;
298+
err_stop_hw:
299+
hid_hw_stop(hdev);
300+
return ret;
301+
}
302+
29303
static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
30304
unsigned int *rsize)
31305
{
32-
if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x65) {
306+
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
307+
308+
if (drvdata->quirks & QUIRK_FIX_NOTEBOOK_REPORT &&
309+
*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x65) {
33310
hid_info(hdev, "Fixing up Asus notebook report descriptor\n");
34311
rdesc[55] = 0xdd;
35312
}
36313
return rdesc;
37314
}
38315

39316
static const struct hid_device_id asus_devices[] = {
40-
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
317+
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
318+
USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD), NOTEBOOK_QUIRKS},
319+
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
320+
USB_DEVICE_ID_ASUSTEK_TOUCHPAD), TOUCHPAD_QUIRKS },
41321
{ }
42322
};
43323
MODULE_DEVICE_TABLE(hid, asus_devices);
44324

45325
static struct hid_driver asus_driver = {
46-
.name = "asus",
47-
.id_table = asus_devices,
48-
.report_fixup = asus_report_fixup
326+
.name = "asus",
327+
.id_table = asus_devices,
328+
.report_fixup = asus_report_fixup,
329+
.probe = asus_probe,
330+
.input_mapping = asus_input_mapping,
331+
.input_configured = asus_input_configured,
332+
#ifdef CONFIG_PM
333+
.reset_resume = asus_reset_resume,
334+
#endif
335+
.raw_event = asus_raw_event
49336
};
50337
module_hid_driver(asus_driver);
51338

drivers/hid/hid-core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,6 +1857,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
18571857
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
18581858
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
18591859
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) },
1860+
{ HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_TOUCHPAD) },
18601861
{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
18611862
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
18621863
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) },

drivers/hid/hid-ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
172172
#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
173173
#define USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD 0x8585
174+
#define USB_DEVICE_ID_ASUSTEK_TOUCHPAD 0x0101
174175

175176
#define USB_VENDOR_ID_ATEN 0x0557
176177
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004

0 commit comments

Comments
 (0)