xref: /openbmc/linux/drivers/hid/hid-asus.c (revision 79a93295)
1 /*
2  *  HID driver for Asus notebook built-in keyboard.
3  *  Fixes small logical maximum to match usage maximum.
4  *
5  *  Currently supported devices are:
6  *    EeeBook X205TA
7  *    VivoBook E200HA
8  *
9  *  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
14  *
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>
20  */
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 
29 #include <linux/hid.h>
30 #include <linux/module.h>
31 #include <linux/input/mt.h>
32 
33 #include "hid-ids.h"
34 
35 MODULE_AUTHOR("Yusuke Fujimaki <usk.fujimaki@gmail.com>");
36 MODULE_AUTHOR("Brendan McGrath <redmcg@redmandi.dyndns.org>");
37 MODULE_AUTHOR("Victor Vlasenko <victor.vlasenko@sysgears.com>");
38 MODULE_AUTHOR("Frederik Wenigwieser <frederik.wenigwieser@gmail.com>");
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 
303 static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
304 		unsigned int *rsize)
305 {
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) {
310 		hid_info(hdev, "Fixing up Asus notebook report descriptor\n");
311 		rdesc[55] = 0xdd;
312 	}
313 	return rdesc;
314 }
315 
316 static const struct hid_device_id asus_devices[] = {
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 },
321 	{ }
322 };
323 MODULE_DEVICE_TABLE(hid, asus_devices);
324 
325 static struct hid_driver asus_driver = {
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
336 };
337 module_hid_driver(asus_driver);
338 
339 MODULE_LICENSE("GPL");
340