diff options
Diffstat (limited to 'drivers/hid/hid-lenovo-tpkbd.c')
-rw-r--r-- | drivers/hid/hid-lenovo-tpkbd.c | 462 |
1 files changed, 0 insertions, 462 deletions
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c deleted file mode 100644 index 2d25b6cbbc05..000000000000 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ /dev/null | |||
@@ -1,462 +0,0 @@ | |||
1 | /* | ||
2 | * HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint | ||
3 | * | ||
4 | * Copyright (c) 2012 Bernhard Seibold | ||
5 | */ | ||
6 | |||
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/module.h> | ||
15 | #include <linux/sysfs.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/hid.h> | ||
18 | #include <linux/input.h> | ||
19 | #include <linux/leds.h> | ||
20 | |||
21 | #include "hid-ids.h" | ||
22 | |||
23 | /* This is only used for the trackpoint part of the driver, hence _tp */ | ||
24 | struct tpkbd_data_pointer { | ||
25 | int led_state; | ||
26 | struct led_classdev led_mute; | ||
27 | struct led_classdev led_micmute; | ||
28 | int press_to_select; | ||
29 | int dragging; | ||
30 | int release_to_select; | ||
31 | int select_right; | ||
32 | int sensitivity; | ||
33 | int press_speed; | ||
34 | }; | ||
35 | |||
36 | #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) | ||
37 | |||
38 | static int tpkbd_input_mapping(struct hid_device *hdev, | ||
39 | struct hid_input *hi, struct hid_field *field, | ||
40 | struct hid_usage *usage, unsigned long **bit, int *max) | ||
41 | { | ||
42 | if (usage->hid == (HID_UP_BUTTON | 0x0010)) { | ||
43 | /* mark the device as pointer */ | ||
44 | hid_set_drvdata(hdev, (void *)1); | ||
45 | map_key_clear(KEY_MICMUTE); | ||
46 | return 1; | ||
47 | } | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | #undef map_key_clear | ||
52 | |||
53 | static int tpkbd_features_set(struct hid_device *hdev) | ||
54 | { | ||
55 | struct hid_report *report; | ||
56 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
57 | |||
58 | report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; | ||
59 | |||
60 | report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; | ||
61 | report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; | ||
62 | report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; | ||
63 | report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; | ||
64 | report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver | ||
65 | report->field[2]->value[0] = data_pointer->sensitivity; | ||
66 | report->field[3]->value[0] = data_pointer->press_speed; | ||
67 | |||
68 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static ssize_t pointer_press_to_select_show(struct device *dev, | ||
73 | struct device_attribute *attr, | ||
74 | char *buf) | ||
75 | { | ||
76 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
77 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
78 | |||
79 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); | ||
80 | } | ||
81 | |||
82 | static ssize_t pointer_press_to_select_store(struct device *dev, | ||
83 | struct device_attribute *attr, | ||
84 | const char *buf, | ||
85 | size_t count) | ||
86 | { | ||
87 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
88 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
89 | int value; | ||
90 | |||
91 | if (kstrtoint(buf, 10, &value)) | ||
92 | return -EINVAL; | ||
93 | if (value < 0 || value > 1) | ||
94 | return -EINVAL; | ||
95 | |||
96 | data_pointer->press_to_select = value; | ||
97 | tpkbd_features_set(hdev); | ||
98 | |||
99 | return count; | ||
100 | } | ||
101 | |||
102 | static ssize_t pointer_dragging_show(struct device *dev, | ||
103 | struct device_attribute *attr, | ||
104 | char *buf) | ||
105 | { | ||
106 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
107 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
108 | |||
109 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); | ||
110 | } | ||
111 | |||
112 | static ssize_t pointer_dragging_store(struct device *dev, | ||
113 | struct device_attribute *attr, | ||
114 | const char *buf, | ||
115 | size_t count) | ||
116 | { | ||
117 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
118 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
119 | int value; | ||
120 | |||
121 | if (kstrtoint(buf, 10, &value)) | ||
122 | return -EINVAL; | ||
123 | if (value < 0 || value > 1) | ||
124 | return -EINVAL; | ||
125 | |||
126 | data_pointer->dragging = value; | ||
127 | tpkbd_features_set(hdev); | ||
128 | |||
129 | return count; | ||
130 | } | ||
131 | |||
132 | static ssize_t pointer_release_to_select_show(struct device *dev, | ||
133 | struct device_attribute *attr, | ||
134 | char *buf) | ||
135 | { | ||
136 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
137 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
138 | |||
139 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); | ||
140 | } | ||
141 | |||
142 | static ssize_t pointer_release_to_select_store(struct device *dev, | ||
143 | struct device_attribute *attr, | ||
144 | const char *buf, | ||
145 | size_t count) | ||
146 | { | ||
147 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
148 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
149 | int value; | ||
150 | |||
151 | if (kstrtoint(buf, 10, &value)) | ||
152 | return -EINVAL; | ||
153 | if (value < 0 || value > 1) | ||
154 | return -EINVAL; | ||
155 | |||
156 | data_pointer->release_to_select = value; | ||
157 | tpkbd_features_set(hdev); | ||
158 | |||
159 | return count; | ||
160 | } | ||
161 | |||
162 | static ssize_t pointer_select_right_show(struct device *dev, | ||
163 | struct device_attribute *attr, | ||
164 | char *buf) | ||
165 | { | ||
166 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
167 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
168 | |||
169 | return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); | ||
170 | } | ||
171 | |||
172 | static ssize_t pointer_select_right_store(struct device *dev, | ||
173 | struct device_attribute *attr, | ||
174 | const char *buf, | ||
175 | size_t count) | ||
176 | { | ||
177 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
178 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
179 | int value; | ||
180 | |||
181 | if (kstrtoint(buf, 10, &value)) | ||
182 | return -EINVAL; | ||
183 | if (value < 0 || value > 1) | ||
184 | return -EINVAL; | ||
185 | |||
186 | data_pointer->select_right = value; | ||
187 | tpkbd_features_set(hdev); | ||
188 | |||
189 | return count; | ||
190 | } | ||
191 | |||
192 | static ssize_t pointer_sensitivity_show(struct device *dev, | ||
193 | struct device_attribute *attr, | ||
194 | char *buf) | ||
195 | { | ||
196 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
197 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
198 | |||
199 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
200 | data_pointer->sensitivity); | ||
201 | } | ||
202 | |||
203 | static ssize_t pointer_sensitivity_store(struct device *dev, | ||
204 | struct device_attribute *attr, | ||
205 | const char *buf, | ||
206 | size_t count) | ||
207 | { | ||
208 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
209 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
210 | int value; | ||
211 | |||
212 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | ||
213 | return -EINVAL; | ||
214 | |||
215 | data_pointer->sensitivity = value; | ||
216 | tpkbd_features_set(hdev); | ||
217 | |||
218 | return count; | ||
219 | } | ||
220 | |||
221 | static ssize_t pointer_press_speed_show(struct device *dev, | ||
222 | struct device_attribute *attr, | ||
223 | char *buf) | ||
224 | { | ||
225 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
226 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
227 | |||
228 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
229 | data_pointer->press_speed); | ||
230 | } | ||
231 | |||
232 | static ssize_t pointer_press_speed_store(struct device *dev, | ||
233 | struct device_attribute *attr, | ||
234 | const char *buf, | ||
235 | size_t count) | ||
236 | { | ||
237 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
238 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
239 | int value; | ||
240 | |||
241 | if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) | ||
242 | return -EINVAL; | ||
243 | |||
244 | data_pointer->press_speed = value; | ||
245 | tpkbd_features_set(hdev); | ||
246 | |||
247 | return count; | ||
248 | } | ||
249 | |||
250 | static struct device_attribute dev_attr_pointer_press_to_select = | ||
251 | __ATTR(press_to_select, S_IWUSR | S_IRUGO, | ||
252 | pointer_press_to_select_show, | ||
253 | pointer_press_to_select_store); | ||
254 | |||
255 | static struct device_attribute dev_attr_pointer_dragging = | ||
256 | __ATTR(dragging, S_IWUSR | S_IRUGO, | ||
257 | pointer_dragging_show, | ||
258 | pointer_dragging_store); | ||
259 | |||
260 | static struct device_attribute dev_attr_pointer_release_to_select = | ||
261 | __ATTR(release_to_select, S_IWUSR | S_IRUGO, | ||
262 | pointer_release_to_select_show, | ||
263 | pointer_release_to_select_store); | ||
264 | |||
265 | static struct device_attribute dev_attr_pointer_select_right = | ||
266 | __ATTR(select_right, S_IWUSR | S_IRUGO, | ||
267 | pointer_select_right_show, | ||
268 | pointer_select_right_store); | ||
269 | |||
270 | static struct device_attribute dev_attr_pointer_sensitivity = | ||
271 | __ATTR(sensitivity, S_IWUSR | S_IRUGO, | ||
272 | pointer_sensitivity_show, | ||
273 | pointer_sensitivity_store); | ||
274 | |||
275 | static struct device_attribute dev_attr_pointer_press_speed = | ||
276 | __ATTR(press_speed, S_IWUSR | S_IRUGO, | ||
277 | pointer_press_speed_show, | ||
278 | pointer_press_speed_store); | ||
279 | |||
280 | static struct attribute *tpkbd_attributes_pointer[] = { | ||
281 | &dev_attr_pointer_press_to_select.attr, | ||
282 | &dev_attr_pointer_dragging.attr, | ||
283 | &dev_attr_pointer_release_to_select.attr, | ||
284 | &dev_attr_pointer_select_right.attr, | ||
285 | &dev_attr_pointer_sensitivity.attr, | ||
286 | &dev_attr_pointer_press_speed.attr, | ||
287 | NULL | ||
288 | }; | ||
289 | |||
290 | static const struct attribute_group tpkbd_attr_group_pointer = { | ||
291 | .attrs = tpkbd_attributes_pointer, | ||
292 | }; | ||
293 | |||
294 | static enum led_brightness tpkbd_led_brightness_get( | ||
295 | struct led_classdev *led_cdev) | ||
296 | { | ||
297 | struct device *dev = led_cdev->dev->parent; | ||
298 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
299 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
300 | int led_nr = 0; | ||
301 | |||
302 | if (led_cdev == &data_pointer->led_micmute) | ||
303 | led_nr = 1; | ||
304 | |||
305 | return data_pointer->led_state & (1 << led_nr) | ||
306 | ? LED_FULL | ||
307 | : LED_OFF; | ||
308 | } | ||
309 | |||
310 | static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, | ||
311 | enum led_brightness value) | ||
312 | { | ||
313 | struct device *dev = led_cdev->dev->parent; | ||
314 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | ||
315 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
316 | struct hid_report *report; | ||
317 | int led_nr = 0; | ||
318 | |||
319 | if (led_cdev == &data_pointer->led_micmute) | ||
320 | led_nr = 1; | ||
321 | |||
322 | if (value == LED_OFF) | ||
323 | data_pointer->led_state &= ~(1 << led_nr); | ||
324 | else | ||
325 | data_pointer->led_state |= 1 << led_nr; | ||
326 | |||
327 | report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; | ||
328 | report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; | ||
329 | report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; | ||
330 | hid_hw_request(hdev, report, HID_REQ_SET_REPORT); | ||
331 | } | ||
332 | |||
333 | static int tpkbd_probe_tp(struct hid_device *hdev) | ||
334 | { | ||
335 | struct device *dev = &hdev->dev; | ||
336 | struct tpkbd_data_pointer *data_pointer; | ||
337 | size_t name_sz = strlen(dev_name(dev)) + 16; | ||
338 | char *name_mute, *name_micmute; | ||
339 | int i; | ||
340 | |||
341 | /* Validate required reports. */ | ||
342 | for (i = 0; i < 4; i++) { | ||
343 | if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) | ||
344 | return -ENODEV; | ||
345 | } | ||
346 | if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) | ||
347 | return -ENODEV; | ||
348 | |||
349 | if (sysfs_create_group(&hdev->dev.kobj, | ||
350 | &tpkbd_attr_group_pointer)) { | ||
351 | hid_warn(hdev, "Could not create sysfs group\n"); | ||
352 | } | ||
353 | |||
354 | data_pointer = devm_kzalloc(&hdev->dev, | ||
355 | sizeof(struct tpkbd_data_pointer), | ||
356 | GFP_KERNEL); | ||
357 | if (data_pointer == NULL) { | ||
358 | hid_err(hdev, "Could not allocate memory for driver data\n"); | ||
359 | return -ENOMEM; | ||
360 | } | ||
361 | |||
362 | // set same default values as windows driver | ||
363 | data_pointer->sensitivity = 0xa0; | ||
364 | data_pointer->press_speed = 0x38; | ||
365 | |||
366 | name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); | ||
367 | name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); | ||
368 | if (name_mute == NULL || name_micmute == NULL) { | ||
369 | hid_err(hdev, "Could not allocate memory for led data\n"); | ||
370 | return -ENOMEM; | ||
371 | } | ||
372 | snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); | ||
373 | snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); | ||
374 | |||
375 | hid_set_drvdata(hdev, data_pointer); | ||
376 | |||
377 | data_pointer->led_mute.name = name_mute; | ||
378 | data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get; | ||
379 | data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set; | ||
380 | data_pointer->led_mute.dev = dev; | ||
381 | led_classdev_register(dev, &data_pointer->led_mute); | ||
382 | |||
383 | data_pointer->led_micmute.name = name_micmute; | ||
384 | data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get; | ||
385 | data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set; | ||
386 | data_pointer->led_micmute.dev = dev; | ||
387 | led_classdev_register(dev, &data_pointer->led_micmute); | ||
388 | |||
389 | tpkbd_features_set(hdev); | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static int tpkbd_probe(struct hid_device *hdev, | ||
395 | const struct hid_device_id *id) | ||
396 | { | ||
397 | int ret; | ||
398 | |||
399 | ret = hid_parse(hdev); | ||
400 | if (ret) { | ||
401 | hid_err(hdev, "hid_parse failed\n"); | ||
402 | goto err; | ||
403 | } | ||
404 | |||
405 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
406 | if (ret) { | ||
407 | hid_err(hdev, "hid_hw_start failed\n"); | ||
408 | goto err; | ||
409 | } | ||
410 | |||
411 | if (hid_get_drvdata(hdev)) { | ||
412 | hid_set_drvdata(hdev, NULL); | ||
413 | ret = tpkbd_probe_tp(hdev); | ||
414 | if (ret) | ||
415 | goto err_hid; | ||
416 | } | ||
417 | |||
418 | return 0; | ||
419 | err_hid: | ||
420 | hid_hw_stop(hdev); | ||
421 | err: | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | static void tpkbd_remove_tp(struct hid_device *hdev) | ||
426 | { | ||
427 | struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); | ||
428 | |||
429 | sysfs_remove_group(&hdev->dev.kobj, | ||
430 | &tpkbd_attr_group_pointer); | ||
431 | |||
432 | led_classdev_unregister(&data_pointer->led_micmute); | ||
433 | led_classdev_unregister(&data_pointer->led_mute); | ||
434 | |||
435 | hid_set_drvdata(hdev, NULL); | ||
436 | } | ||
437 | |||
438 | static void tpkbd_remove(struct hid_device *hdev) | ||
439 | { | ||
440 | if (hid_get_drvdata(hdev)) | ||
441 | tpkbd_remove_tp(hdev); | ||
442 | |||
443 | hid_hw_stop(hdev); | ||
444 | } | ||
445 | |||
446 | static const struct hid_device_id tpkbd_devices[] = { | ||
447 | { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, | ||
448 | { } | ||
449 | }; | ||
450 | |||
451 | MODULE_DEVICE_TABLE(hid, tpkbd_devices); | ||
452 | |||
453 | static struct hid_driver tpkbd_driver = { | ||
454 | .name = "lenovo_tpkbd", | ||
455 | .id_table = tpkbd_devices, | ||
456 | .input_mapping = tpkbd_input_mapping, | ||
457 | .probe = tpkbd_probe, | ||
458 | .remove = tpkbd_remove, | ||
459 | }; | ||
460 | module_hid_driver(tpkbd_driver); | ||
461 | |||
462 | MODULE_LICENSE("GPL"); | ||