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