blob: e0de54b4f499dcf92e08fb5696ddb56d58d27630 [file] [log] [blame]
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +09001/*
Yusuke Fujimakib94f7d52016-04-03 23:15:16 +09002 * HID driver for Asus notebook built-in keyboard.
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +09003 * Fixes small logical maximum to match usage maximum.
4 *
Yusuke Fujimakib94f7d52016-04-03 23:15:16 +09005 * Currently supported devices are:
6 * EeeBook X205TA
7 * VivoBook E200HA
8 *
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +09009 * Copyright (c) 2016 Yusuke Fujimaki <usk.fujimaki@gmail.com>
10 *
11 * This module based on hid-ortek by
12 * Copyright (c) 2010 Johnathon Harris <jmharris@gmail.com>
13 * Copyright (c) 2011 Jiri Kosina
Brendan McGrath9ce12d82016-11-29 18:59:25 +110014 *
15 * This module has been updated to add support for Asus i2c touchpad.
16 *
17 * Copyright (c) 2016 Brendan McGrath <redmcg@redmandi.dyndns.org>
18 * Copyright (c) 2016 Victor Vlasenko <victor.vlasenko@sysgears.com>
19 * Copyright (c) 2016 Frederik Wenigwieser <frederik.wenigwieser@gmail.com>
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +090020 */
21
22/*
23 * This program is free software; you can redistribute it and/or modify it
24 * under the terms of the GNU General Public License as published by the Free
25 * Software Foundation; either version 2 of the License, or (at your option)
26 * any later version.
27 */
28
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +090029#include <linux/hid.h>
30#include <linux/module.h>
Brendan McGrath9ce12d82016-11-29 18:59:25 +110031#include <linux/input/mt.h>
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +090032
33#include "hid-ids.h"
34
Brendan McGrath9ce12d82016-11-29 18:59:25 +110035MODULE_AUTHOR("Yusuke Fujimaki <usk.fujimaki@gmail.com>");
36MODULE_AUTHOR("Brendan McGrath <redmcg@redmandi.dyndns.org>");
37MODULE_AUTHOR("Victor Vlasenko <victor.vlasenko@sysgears.com>");
38MODULE_AUTHOR("Frederik Wenigwieser <frederik.wenigwieser@gmail.com>");
39MODULE_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)
Matjaz Hegedic0485b1e2017-03-09 00:31:14 +010066#define QUIRK_NO_CONSUMER_USAGES BIT(4)
Brendan McGrath9ce12d82016-11-29 18:59:25 +110067
Matjaz Hegedica93913e2017-03-09 00:31:13 +010068#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
Matjaz Hegedic0485b1e2017-03-09 00:31:14 +010069 QUIRK_NO_INIT_REPORTS | \
70 QUIRK_NO_CONSUMER_USAGES)
Matjaz Hegedica93913e2017-03-09 00:31:13 +010071#define I2C_TOUCHPAD_QUIRKS (QUIRK_NO_INIT_REPORTS | \
Brendan McGrath9ce12d82016-11-29 18:59:25 +110072 QUIRK_SKIP_INPUT_MAPPING | \
73 QUIRK_IS_MULTITOUCH)
74
75#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
76
77struct asus_drvdata {
78 unsigned long quirks;
79 struct input_dev *input;
80};
81
82static void asus_report_contact_down(struct input_dev *input,
83 int toolType, u8 *data)
84{
85 int touch_major, pressure;
86 int x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1];
87 int y = MAX_Y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]);
88
89 if (toolType == MT_TOOL_PALM) {
90 touch_major = MAX_TOUCH_MAJOR;
91 pressure = MAX_PRESSURE;
92 } else {
93 touch_major = (data[3] >> 4) & CONTACT_TOUCH_MAJOR_MASK;
94 pressure = data[4] & CONTACT_PRESSURE_MASK;
95 }
96
97 input_report_abs(input, ABS_MT_POSITION_X, x);
98 input_report_abs(input, ABS_MT_POSITION_Y, y);
99 input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
100 input_report_abs(input, ABS_MT_PRESSURE, pressure);
101}
102
103/* Required for Synaptics Palm Detection */
104static void asus_report_tool_width(struct input_dev *input)
105{
106 struct input_mt *mt = input->mt;
107 struct input_mt_slot *oldest;
108 int oldid, count, i;
109
110 oldest = NULL;
111 oldid = mt->trkid;
112 count = 0;
113
114 for (i = 0; i < mt->num_slots; ++i) {
115 struct input_mt_slot *ps = &mt->slots[i];
116 int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
117
118 if (id < 0)
119 continue;
120 if ((id - oldid) & TRKID_SGN) {
121 oldest = ps;
122 oldid = id;
123 }
124 count++;
125 }
126
127 if (oldest) {
128 input_report_abs(input, ABS_TOOL_WIDTH,
129 input_mt_get_value(oldest, ABS_MT_TOUCH_MAJOR));
130 }
131}
132
133static void asus_report_input(struct input_dev *input, u8 *data)
134{
135 int i;
136 u8 *contactData = data + 2;
137
138 for (i = 0; i < MAX_CONTACTS; i++) {
139 bool down = !!(data[1] & BIT(i+3));
140 int toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ?
141 MT_TOOL_PALM : MT_TOOL_FINGER;
142
143 input_mt_slot(input, i);
144 input_mt_report_slot_state(input, toolType, down);
145
146 if (down) {
147 asus_report_contact_down(input, toolType, contactData);
148 contactData += CONTACT_DATA_SIZE;
149 }
150 }
151
152 input_report_key(input, BTN_LEFT, data[1] & BTN_LEFT_MASK);
153 asus_report_tool_width(input);
154
155 input_mt_sync_frame(input);
156 input_sync(input);
157}
158
159static int asus_raw_event(struct hid_device *hdev,
160 struct hid_report *report, u8 *data, int size)
161{
162 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
163
164 if (drvdata->quirks & QUIRK_IS_MULTITOUCH &&
165 data[0] == INPUT_REPORT_ID &&
166 size == INPUT_REPORT_SIZE) {
167 asus_report_input(drvdata->input, data);
168 return 1;
169 }
170
171 return 0;
172}
173
174static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
175{
Brendan McGrathc8b1b3d2016-12-10 21:20:42 +1100176 struct input_dev *input = hi->input;
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100177 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
178
179 if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
180 int ret;
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100181
182 input_set_abs_params(input, ABS_MT_POSITION_X, 0, MAX_X, 0, 0);
183 input_set_abs_params(input, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0);
184 input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MAX_TOUCH_MAJOR, 0, 0);
185 input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0);
186 input_set_abs_params(input, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0);
187
188 __set_bit(BTN_LEFT, input->keybit);
189 __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
190
191 ret = input_mt_init_slots(input, MAX_CONTACTS, INPUT_MT_POINTER);
192
193 if (ret) {
194 hid_err(hdev, "Asus input mt init slots failed: %d\n", ret);
195 return ret;
196 }
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100197 }
198
Brendan McGrathc8b1b3d2016-12-10 21:20:42 +1100199 drvdata->input = input;
200
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100201 return 0;
202}
203
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100204#define asus_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, \
Chris Chiu1caccc22017-03-01 15:48:51 -0600205 max, EV_KEY, (c))
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100206static int asus_input_mapping(struct hid_device *hdev,
207 struct hid_input *hi, struct hid_field *field,
208 struct hid_usage *usage, unsigned long **bit,
209 int *max)
210{
211 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
212
213 if (drvdata->quirks & QUIRK_SKIP_INPUT_MAPPING) {
214 /* Don't map anything from the HID report.
215 * We do it all manually in asus_input_configured
216 */
217 return -1;
218 }
219
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100220 /* ASUS-specific keyboard hotkeys */
Chris Chiu1caccc22017-03-01 15:48:51 -0600221 if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
222 set_bit(EV_REP, hi->input->evbit);
223 switch (usage->hid & HID_USAGE) {
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100224 case 0x10: asus_map_key_clear(KEY_BRIGHTNESSDOWN); break;
225 case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
226 case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
227 case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
228 case 0x82: asus_map_key_clear(KEY_CAMERA); break;
229 case 0x88: asus_map_key_clear(KEY_WLAN); break;
230 case 0xb5: asus_map_key_clear(KEY_CALC); break;
231 case 0xc4: asus_map_key_clear(KEY_KBDILLUMUP); break;
232 case 0xc5: asus_map_key_clear(KEY_KBDILLUMDOWN); break;
Chris Chiu1caccc22017-03-01 15:48:51 -0600233
234 /* ASUS touchpad toggle */
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100235 case 0x6b: asus_map_key_clear(KEY_F21); break;
Chris Chiu1caccc22017-03-01 15:48:51 -0600236
237 /* ROG key */
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100238 case 0x38: asus_map_key_clear(KEY_PROG1); break;
Chris Chiu1caccc22017-03-01 15:48:51 -0600239
240 /* Fn+C ASUS Splendid */
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100241 case 0xba: asus_map_key_clear(KEY_PROG2); break;
Chris Chiu1caccc22017-03-01 15:48:51 -0600242
243 /* Fn+Space Power4Gear Hybrid */
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100244 case 0x5c: asus_map_key_clear(KEY_PROG3); break;
Chris Chiu1caccc22017-03-01 15:48:51 -0600245
246 default:
Matjaz Hegedic0485b1e2017-03-09 00:31:14 +0100247 /* ASUS lazily declares 256 usages, ignore the rest,
248 * as some make the keyboard appear as a pointer device. */
249 return -1;
Chris Chiu1caccc22017-03-01 15:48:51 -0600250 }
251 return 1;
252 }
253
Matjaz Hegedic0485b1e2017-03-09 00:31:14 +0100254 if (drvdata->quirks & QUIRK_NO_CONSUMER_USAGES &&
255 (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
256 switch (usage->hid & HID_USAGE) {
257 case 0xe2: /* Mute */
258 case 0xe9: /* Volume up */
259 case 0xea: /* Volume down */
260 return 0;
261 default:
262 /* Ignore dummy Consumer usages which make the
263 * keyboard incorrectly appear as a pointer device.
264 */
265 return -1;
266 }
267 }
268
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100269 return 0;
270}
271
272static int asus_start_multitouch(struct hid_device *hdev)
273{
274 int ret;
275 const unsigned char buf[] = { FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00 };
276 unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
277
278 if (!dmabuf) {
279 ret = -ENOMEM;
280 hid_err(hdev, "Asus failed to alloc dma buf: %d\n", ret);
281 return ret;
282 }
283
284 ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf),
285 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
286
287 kfree(dmabuf);
288
289 if (ret != sizeof(buf)) {
290 hid_err(hdev, "Asus failed to start multitouch: %d\n", ret);
291 return ret;
292 }
293
294 return 0;
295}
296
297static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
298{
299 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
300
301 if (drvdata->quirks & QUIRK_IS_MULTITOUCH)
302 return asus_start_multitouch(hdev);
303
304 return 0;
305}
306
307static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
308{
309 int ret;
310 struct asus_drvdata *drvdata;
311
312 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
313 if (drvdata == NULL) {
314 hid_err(hdev, "Can't alloc Asus descriptor\n");
315 return -ENOMEM;
316 }
317
318 hid_set_drvdata(hdev, drvdata);
319
320 drvdata->quirks = id->driver_data;
321
322 if (drvdata->quirks & QUIRK_NO_INIT_REPORTS)
323 hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
324
325 ret = hid_parse(hdev);
326 if (ret) {
327 hid_err(hdev, "Asus hid parse failed: %d\n", ret);
328 return ret;
329 }
330
331 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
332 if (ret) {
333 hid_err(hdev, "Asus hw start failed: %d\n", ret);
334 return ret;
335 }
336
337 if (!drvdata->input) {
338 hid_err(hdev, "Asus input not registered\n");
339 ret = -ENOMEM;
340 goto err_stop_hw;
341 }
342
Brendan McGrathc8b1b3d2016-12-10 21:20:42 +1100343 if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
344 drvdata->input->name = "Asus TouchPad";
345 } else {
346 drvdata->input->name = "Asus Keyboard";
347 }
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100348
349 if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
350 ret = asus_start_multitouch(hdev);
351 if (ret)
352 goto err_stop_hw;
353 }
354
355 return 0;
356err_stop_hw:
357 hid_hw_stop(hdev);
358 return ret;
359}
360
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +0900361static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
362 unsigned int *rsize)
363{
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100364 struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
365
366 if (drvdata->quirks & QUIRK_FIX_NOTEBOOK_REPORT &&
367 *rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x65) {
Yusuke Fujimakib94f7d52016-04-03 23:15:16 +0900368 hid_info(hdev, "Fixing up Asus notebook report descriptor\n");
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +0900369 rdesc[55] = 0xdd;
370 }
371 return rdesc;
372}
373
374static const struct hid_device_id asus_devices[] = {
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100375 { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100376 USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD), I2C_KEYBOARD_QUIRKS},
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100377 { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
Matjaz Hegedica93913e2017-03-09 00:31:13 +0100378 USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD), I2C_TOUCHPAD_QUIRKS },
Chris Chiu1caccc22017-03-01 15:48:51 -0600379 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
380 USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1) },
381 { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
382 USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2) },
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +0900383 { }
384};
385MODULE_DEVICE_TABLE(hid, asus_devices);
386
387static struct hid_driver asus_driver = {
Brendan McGrath9ce12d82016-11-29 18:59:25 +1100388 .name = "asus",
389 .id_table = asus_devices,
390 .report_fixup = asus_report_fixup,
391 .probe = asus_probe,
392 .input_mapping = asus_input_mapping,
393 .input_configured = asus_input_configured,
394#ifdef CONFIG_PM
395 .reset_resume = asus_reset_resume,
396#endif
397 .raw_event = asus_raw_event
Yusuke Fujimakieeb01a52016-03-21 16:18:42 +0900398};
399module_hid_driver(asus_driver);
400
401MODULE_LICENSE("GPL");