aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandrov Stansilav <neko@nya.ai>2018-01-13 18:33:49 -0500
committerJiri Kosina <jkosina@suse.cz>2018-02-16 07:25:04 -0500
commit9a6a4193d65b853020ef0e66cecdf9e64a863883 (patch)
treef293ccb4fd1c15e2c9ad27000618c04fc1680dad
parent183b6366cf473ff0e706a6751adc082faa44843d (diff)
HID: Add driver for USB ELAN Touchpad
This is driver for usb touchpad found on HP Pavilion x2 10-p0xx laptop. On this device keyboard and touchpad connected as a single usb device with two interfaces: keyboard, which exposes ordinary keys and second interface is touchpad which also contains FlightMode button and audio mute led (which physically placed on keyboard for some reason). Initially, this touchpad works in mouse emulation mode, this driver will switch it to touchpad mode, which can track 5 fingers and can report coordinates for two of them. Signed-off-by: Alexandrov Stansilav <neko@nya.ai> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/Kconfig8
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-elan.c421
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-quirks.c3
5 files changed, 434 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 19c499f5623d..276c50e3bf45 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -274,6 +274,14 @@ config HID_EMS_FF
274 Currently the following devices are known to be supported: 274 Currently the following devices are known to be supported:
275 - Trio Linker Plus II 275 - Trio Linker Plus II
276 276
277config HID_ELAN
278 tristate "ELAN USB Touchpad Support"
279 depends on LEDS_CLASS && USB_HID
280 ---help---
281 Say Y to enable support for the USB ELAN touchpad
282 Currently the following devices are known to be supported:
283 - HP Pavilion X2 10-p0XX.
284
277config HID_ELECOM 285config HID_ELECOM
278 tristate "ELECOM HID devices" 286 tristate "ELECOM HID devices"
279 depends on HID 287 depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index eb13b9e92d85..713601c7bfa6 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
39obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o 39obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
40obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o 40obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
41obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o 41obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
42obj-$(CONFIG_HID_ELAN) += hid-elan.o
42obj-$(CONFIG_HID_ELECOM) += hid-elecom.o 43obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
43obj-$(CONFIG_HID_ELO) += hid-elo.o 44obj-$(CONFIG_HID_ELO) += hid-elo.o
44obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o 45obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c
new file mode 100644
index 000000000000..803a725785cf
--- /dev/null
+++ b/drivers/hid/hid-elan.c
@@ -0,0 +1,421 @@
1/*
2 * HID Driver for ELAN Touchpad
3 *
4 * Currently only supports touchpad found on HP Pavilion X2 10
5 *
6 * Copyright (c) 2016 Alexandrov Stanislav <neko@nya.ai>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 */
13
14#include <linux/hid.h>
15#include <linux/input/mt.h>
16#include <linux/leds.h>
17#include <linux/module.h>
18#include <linux/usb.h>
19
20#include "hid-ids.h"
21
22#define ELAN_SINGLE_FINGER 0x81
23#define ELAN_MT_FIRST_FINGER 0x82
24#define ELAN_MT_SECOND_FINGER 0x83
25#define ELAN_INPUT_REPORT_SIZE 8
26
27#define ELAN_MUTE_LED_REPORT 0xBC
28#define ELAN_LED_REPORT_SIZE 8
29
30struct elan_touchpad_settings {
31 u8 max_fingers;
32 u16 max_x;
33 u16 max_y;
34 u8 max_area_x;
35 u8 max_area_y;
36 u8 max_w;
37 int usb_bInterfaceNumber;
38};
39
40struct elan_drvdata {
41 struct input_dev *input;
42 u8 prev_report[ELAN_INPUT_REPORT_SIZE];
43 struct led_classdev mute_led;
44 u8 mute_led_state;
45 struct elan_touchpad_settings *settings;
46};
47
48static int is_not_elan_touchpad(struct hid_device *hdev)
49{
50 struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
51 struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
52
53 return (intf->altsetting->desc.bInterfaceNumber != drvdata->settings->usb_bInterfaceNumber);
54}
55
56static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
57 struct hid_field *field, struct hid_usage *usage,
58 unsigned long **bit, int *max)
59{
60 if (is_not_elan_touchpad(hdev))
61 return 0;
62
63 if (field->report->id == ELAN_SINGLE_FINGER ||
64 field->report->id == ELAN_MT_FIRST_FINGER ||
65 field->report->id == ELAN_MT_SECOND_FINGER)
66 return -1;
67
68 return 0;
69}
70
71static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
72{
73 int ret;
74 struct input_dev *input;
75 struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
76
77 if (is_not_elan_touchpad(hdev))
78 return 0;
79
80 input = devm_input_allocate_device(&hdev->dev);
81 if (!input)
82 return -ENOMEM;
83
84 input->name = "Elan Touchpad";
85 input->phys = hdev->phys;
86 input->uniq = hdev->uniq;
87 input->id.bustype = hdev->bus;
88 input->id.vendor = hdev->vendor;
89 input->id.product = hdev->product;
90 input->id.version = hdev->version;
91 input->dev.parent = &hdev->dev;
92
93 input_set_abs_params(input, ABS_MT_POSITION_X, 0,
94 drvdata->settings->max_x, 0, 0);
95 input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
96 drvdata->settings->max_y, 0, 0);
97 input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
98 drvdata->settings->max_fingers, 0, 0);
99 input_set_abs_params(input, ABS_TOOL_WIDTH, 0,
100 drvdata->settings->max_w, 0, 0);
101
102 __set_bit(BTN_LEFT, input->keybit);
103 __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
104
105 ret = input_mt_init_slots(input, drvdata->settings->max_fingers,
106 INPUT_MT_POINTER);
107 if (ret) {
108 hid_err(hdev, "Failed to init elan MT slots: %d\n", ret);
109 return ret;
110 }
111
112 ret = input_register_device(input);
113 if (ret) {
114 hid_err(hdev, "Failed to register elan input device: %d\n",
115 ret);
116 input_free_device(input);
117 return ret;
118 }
119
120 drvdata->input = input;
121
122 return 0;
123}
124
125static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
126 unsigned int slot_num)
127{
128 struct input_dev *input = drvdata->input;
129 int x, y, w;
130
131 bool active = !!data;
132
133 input_mt_slot(input, slot_num);
134 input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
135 if (active) {
136 x = ((data[0] & 0xF0) << 4) | data[1];
137 y = drvdata->settings->max_y -
138 (((data[0] & 0x07) << 8) | data[2]);
139 w = data[4];
140
141 input_report_abs(input, ABS_MT_POSITION_X, x);
142 input_report_abs(input, ABS_MT_POSITION_Y, y);
143 input_report_abs(input, ABS_TOOL_WIDTH, w);
144 }
145}
146
147static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
148{
149 int i;
150 struct input_dev *input = drvdata->input;
151
152 /*
153 * There is 3 types of reports: for single touch,
154 * for multitouch - first finger and for multitouch - second finger
155 *
156 * packet structure for ELAN_SINGLE_FINGER and ELAN_MT_FIRST_FINGER:
157 *
158 * byte 1: 1 0 0 0 0 0 0 1 // 0x81 or 0x82
159 * byte 2: 0 0 0 0 0 0 0 0 // looks like unused
160 * byte 3: f5 f4 f3 f2 f1 0 0 L
161 * byte 4: x12 x11 x10 x9 0? y11 y10 y9
162 * byte 5: x8 x7 x6 x5 x4 x3 x2 x1
163 * byte 6: y8 y7 y6 y5 y4 y3 y2 y1
164 * byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
165 * byte 8: w8 w7 w6 w5 w4 w3 w2 w1
166 *
167 * packet structure for ELAN_MT_SECOND_FINGER:
168 *
169 * byte 1: 1 0 0 0 0 0 1 1 // 0x83
170 * byte 2: x12 x11 x10 x9 0 y11 y10 y9
171 * byte 3: x8 x7 x6 x5 x4 x3 x2 x1
172 * byte 4: y8 y7 y6 y5 y4 y3 y2 y1
173 * byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
174 * byte 6: w8 w7 w6 w5 w4 w3 w2 w1
175 * byte 7: 0 0 0 0 0 0 0 0
176 * byte 8: 0 0 0 0 0 0 0 0
177 *
178 * f5-f1: finger touch bits
179 * L: clickpad button
180 * sy / sx: not sure yet, but this looks like rectangular
181 * area for finger
182 * w: looks like finger width
183 */
184
185 if (data[0] == ELAN_SINGLE_FINGER) {
186 for (i = 0; i < drvdata->settings->max_fingers; i++) {
187 if (data[2] & BIT(i + 3))
188 elan_report_mt_slot(drvdata, data + 3, i);
189 else
190 elan_report_mt_slot(drvdata, NULL, i);
191 }
192 input_report_key(input, BTN_LEFT, data[2] & 0x01);
193 }
194 /*
195 * When touched with two fingers Elan touchpad will emit two HID reports
196 * first is ELAN_MT_FIRST_FINGER and second is ELAN_MT_SECOND_FINGER
197 * we will save ELAN_MT_FIRST_FINGER report and wait for
198 * ELAN_MT_SECOND_FINGER to finish multitouch
199 */
200 if (data[0] == ELAN_MT_FIRST_FINGER) {
201 memcpy(drvdata->prev_report, data,
202 sizeof(drvdata->prev_report));
203 return;
204 }
205
206 if (data[0] == ELAN_MT_SECOND_FINGER) {
207 int first = 0;
208 u8 *prev_report = drvdata->prev_report;
209
210 if (prev_report[0] != ELAN_MT_FIRST_FINGER)
211 return;
212
213 for (i = 0; i < drvdata->settings->max_fingers; i++) {
214 if (prev_report[2] & BIT(i + 3)) {
215 if (!first) {
216 first = 1;
217 elan_report_mt_slot(drvdata, prev_report + 3, i);
218 } else {
219 elan_report_mt_slot(drvdata, data + 1, i);
220 }
221 } else {
222 elan_report_mt_slot(drvdata, NULL, i);
223 }
224 }
225 input_report_key(input, BTN_LEFT, prev_report[2] & 0x01);
226 }
227
228 input_mt_sync_frame(input);
229 input_sync(input);
230}
231
232static int elan_raw_event(struct hid_device *hdev,
233 struct hid_report *report, u8 *data, int size)
234{
235 struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
236
237 if (is_not_elan_touchpad(hdev))
238 return 0;
239
240 if (data[0] == ELAN_SINGLE_FINGER ||
241 data[0] == ELAN_MT_FIRST_FINGER ||
242 data[0] == ELAN_MT_SECOND_FINGER) {
243 if (size == ELAN_INPUT_REPORT_SIZE) {
244 elan_report_input(drvdata, data);
245 return 1;
246 }
247 }
248
249 return 0;
250}
251
252static int elan_start_multitouch(struct hid_device *hdev)
253{
254 int ret;
255
256 /*
257 * This byte sequence will enable multitouch mode and disable
258 * mouse emulation
259 */
260 const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 };
261 unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
262
263 if (!dmabuf)
264 return -ENOMEM;
265
266 ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf),
267 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
268
269 kfree(dmabuf);
270
271 if (ret != sizeof(buf)) {
272 hid_err(hdev, "Failed to start multitouch: %d\n", ret);
273 return ret;
274 }
275
276 return 0;
277}
278
279static enum led_brightness elan_mute_led_get_brigtness(struct led_classdev *led_cdev)
280{
281 struct device *dev = led_cdev->dev->parent;
282 struct hid_device *hdev = to_hid_device(dev);
283 struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
284
285 return drvdata->mute_led_state;
286}
287
288static int elan_mute_led_set_brigtness(struct led_classdev *led_cdev,
289 enum led_brightness value)
290{
291 int ret;
292 u8 led_state;
293 struct device *dev = led_cdev->dev->parent;
294 struct hid_device *hdev = to_hid_device(dev);
295 struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
296
297 unsigned char *dmabuf = kzalloc(ELAN_LED_REPORT_SIZE, GFP_KERNEL);
298
299 if (!dmabuf)
300 return -ENOMEM;
301
302 led_state = !!value;
303
304 dmabuf[0] = ELAN_MUTE_LED_REPORT;
305 dmabuf[1] = 0x02;
306 dmabuf[2] = led_state;
307
308 ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, ELAN_LED_REPORT_SIZE,
309 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
310
311 kfree(dmabuf);
312
313 if (ret != ELAN_LED_REPORT_SIZE) {
314 hid_err(hdev, "Failed to set mute led brightness: %d\n", ret);
315 return ret;
316 }
317
318 drvdata->mute_led_state = led_state;
319 return 0;
320}
321
322static int elan_init_mute_led(struct hid_device *hdev)
323{
324 struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
325 struct led_classdev *mute_led = &drvdata->mute_led;
326
327 mute_led->name = "elan:red:mute";
328 mute_led->brightness_get = elan_mute_led_get_brigtness;
329 mute_led->brightness_set_blocking = elan_mute_led_set_brigtness;
330 mute_led->max_brightness = LED_ON;
331 mute_led->dev = &hdev->dev;
332
333 return devm_led_classdev_register(&hdev->dev, mute_led);
334}
335
336static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
337{
338 int ret;
339 struct elan_drvdata *drvdata;
340
341 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
342
343 if (!drvdata)
344 return -ENOMEM;
345
346 drvdata->settings = (struct elan_touchpad_settings *)id->driver_data;
347 hid_set_drvdata(hdev, drvdata);
348
349 ret = hid_parse(hdev);
350 if (ret) {
351 hid_err(hdev, "Hid Parse failed\n");
352 return ret;
353 }
354
355 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
356 if (ret) {
357 hid_err(hdev, "Hid hw start failed\n");
358 return ret;
359 }
360
361 if (is_not_elan_touchpad(hdev))
362 return 0;
363
364 if (!drvdata->input) {
365 hid_err(hdev, "Input device is not registred\n");
366 ret = -ENAVAIL;
367 goto err;
368 }
369
370 ret = elan_start_multitouch(hdev);
371 if (ret)
372 goto err;
373
374 ret = elan_init_mute_led(hdev);
375 if (ret)
376 goto err;
377
378 return 0;
379err:
380 hid_hw_stop(hdev);
381 return ret;
382}
383
384static void elan_remove(struct hid_device *hdev)
385{
386 hid_hw_stop(hdev);
387}
388
389static const struct elan_touchpad_settings hp_x2_10_touchpad_data = {
390 .max_fingers = 5,
391 .max_x = 2930,
392 .max_y = 1250,
393 .max_area_x = 15,
394 .max_area_y = 15,
395 .max_w = 255,
396 .usb_bInterfaceNumber = 1,
397};
398
399static const struct hid_device_id elan_devices[] = {
400 { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER),
401 (kernel_ulong_t)&hp_x2_10_touchpad_data},
402 { }
403};
404
405MODULE_DEVICE_TABLE(hid, elan_devices);
406
407static struct hid_driver elan_driver = {
408 .name = "elan",
409 .id_table = elan_devices,
410 .input_mapping = elan_input_mapping,
411 .input_configured = elan_input_configured,
412 .raw_event = elan_raw_event,
413 .probe = elan_probe,
414 .remove = elan_remove,
415};
416
417module_hid_driver(elan_driver);
418
419MODULE_LICENSE("GPL");
420MODULE_AUTHOR("Alexandrov Stanislav");
421MODULE_DESCRIPTION("Driver for HID ELAN Touchpads");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 43ddcdfbd0da..450245efdcc2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -368,6 +368,7 @@
368#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 368#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
369 369
370#define USB_VENDOR_ID_ELAN 0x04f3 370#define USB_VENDOR_ID_ELAN 0x04f3
371#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
371 372
372#define USB_VENDOR_ID_ELECOM 0x056e 373#define USB_VENDOR_ID_ELECOM 0x056e
373#define USB_DEVICE_ID_ELECOM_BM084 0x0061 374#define USB_DEVICE_ID_ELECOM_BM084 0x0061
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 5f6035a5ce36..3d17ffb581ad 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -333,6 +333,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
333 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, 333 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
334 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, 334 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
335#endif 335#endif
336#if IS_ENABLED(CONFIG_HID_ELAN)
337 { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER) },
338#endif
336#if IS_ENABLED(CONFIG_HID_ELECOM) 339#if IS_ENABLED(CONFIG_HID_ELECOM)
337 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, 340 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
338 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRED) }, 341 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRED) },