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