Commit | Line | Data |
---|---|---|
9a6a4193 AS |
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 | ||
30 | struct 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 | ||
40 | struct 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 | ||
48 | static 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 | ||
56 | static 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 | ||
71 | static 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 | ||
125 | static 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 | ||
147 | static 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 | ||
232 | static 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 | ||
252 | static 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 | ||
279 | static 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 | ||
288 | static 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 | ||
322 | static 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 | ||
336 | static 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; | |
379 | err: | |
380 | hid_hw_stop(hdev); | |
381 | return ret; | |
382 | } | |
383 | ||
384 | static void elan_remove(struct hid_device *hdev) | |
385 | { | |
386 | hid_hw_stop(hdev); | |
387 | } | |
388 | ||
389 | static 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 | ||
399 | static 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 | ||
405 | MODULE_DEVICE_TABLE(hid, elan_devices); | |
406 | ||
407 | static 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 | ||
417 | module_hid_driver(elan_driver); | |
418 | ||
419 | MODULE_LICENSE("GPL"); | |
420 | MODULE_AUTHOR("Alexandrov Stanislav"); | |
421 | MODULE_DESCRIPTION("Driver for HID ELAN Touchpads"); |