blob: f96bf095670ab88c2d2d297536252f84424e0c45 [file] [log] [blame]
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01001/*
Jamie Lentin6a5b4142014-07-23 23:30:46 +01002 * HID driver for Lenovo:
3 * - ThinkPad USB Keyboard with TrackPoint (tpkbd)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +01004 *
5 * Copyright (c) 2012 Bernhard Seibold
6 */
7
8/*
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 */
14
15#include <linux/module.h>
16#include <linux/sysfs.h>
17#include <linux/device.h>
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010018#include <linux/hid.h>
19#include <linux/input.h>
20#include <linux/leds.h>
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010021
22#include "hid-ids.h"
23
Jamie Lentin94723bf2014-07-23 23:30:45 +010024struct lenovo_drvdata_tpkbd {
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010025 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
Jamie Lentin94723bf2014-07-23 23:30:45 +010038static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010039 struct hid_input *hi, struct hid_field *field,
40 struct hid_usage *usage, unsigned long **bit, int *max)
41{
Benjamin Tissoires0c521832013-09-11 22:12:24 +020042 if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
Jamie Lentin6a5b4142014-07-23 23:30:46 +010043 /* This sub-device contains trackpoint, mark it */
Benjamin Tissoires0c521832013-09-11 22:12:24 +020044 hid_set_drvdata(hdev, (void *)1);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010045 map_key_clear(KEY_MICMUTE);
46 return 1;
47 }
48 return 0;
49}
50
Jamie Lentin6a5b4142014-07-23 23:30:46 +010051static int lenovo_input_mapping(struct hid_device *hdev,
52 struct hid_input *hi, struct hid_field *field,
53 struct hid_usage *usage, unsigned long **bit, int *max)
54{
55 switch (hdev->product) {
56 case USB_DEVICE_ID_LENOVO_TPKBD:
57 return lenovo_input_mapping_tpkbd(hdev, hi, field,
58 usage, bit, max);
59 default:
60 return 0;
61 }
62}
63
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010064#undef map_key_clear
65
Jamie Lentin94723bf2014-07-23 23:30:45 +010066static int lenovo_features_set_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010067{
68 struct hid_report *report;
Jamie Lentin94723bf2014-07-23 23:30:45 +010069 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010070
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010071 report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
72
73 report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
74 report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08;
75 report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
76 report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40;
77 report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
78 report->field[2]->value[0] = data_pointer->sensitivity;
79 report->field[3]->value[0] = data_pointer->press_speed;
80
Benjamin Tissoiresd88142722013-02-25 11:31:46 +010081 hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010082 return 0;
83}
84
Jamie Lentin94723bf2014-07-23 23:30:45 +010085static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010086 struct device_attribute *attr,
87 char *buf)
88{
Axel Lin832fbea2012-09-13 14:12:08 +080089 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +010090 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010091
92 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
93}
94
Jamie Lentin94723bf2014-07-23 23:30:45 +010095static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +010096 struct device_attribute *attr,
97 const char *buf,
98 size_t count)
99{
Axel Lin832fbea2012-09-13 14:12:08 +0800100 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100101 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100102 int value;
103
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100104 if (kstrtoint(buf, 10, &value))
105 return -EINVAL;
106 if (value < 0 || value > 1)
107 return -EINVAL;
108
109 data_pointer->press_to_select = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100110 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100111
112 return count;
113}
114
Jamie Lentin94723bf2014-07-23 23:30:45 +0100115static ssize_t attr_dragging_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100116 struct device_attribute *attr,
117 char *buf)
118{
Axel Lin832fbea2012-09-13 14:12:08 +0800119 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100120 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100121
122 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
123}
124
Jamie Lentin94723bf2014-07-23 23:30:45 +0100125static ssize_t attr_dragging_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100126 struct device_attribute *attr,
127 const char *buf,
128 size_t count)
129{
Axel Lin832fbea2012-09-13 14:12:08 +0800130 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100131 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100132 int value;
133
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100134 if (kstrtoint(buf, 10, &value))
135 return -EINVAL;
136 if (value < 0 || value > 1)
137 return -EINVAL;
138
139 data_pointer->dragging = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100140 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100141
142 return count;
143}
144
Jamie Lentin94723bf2014-07-23 23:30:45 +0100145static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100146 struct device_attribute *attr,
147 char *buf)
148{
Axel Lin832fbea2012-09-13 14:12:08 +0800149 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100150 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100151
152 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
153}
154
Jamie Lentin94723bf2014-07-23 23:30:45 +0100155static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100156 struct device_attribute *attr,
157 const char *buf,
158 size_t count)
159{
Axel Lin832fbea2012-09-13 14:12:08 +0800160 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100161 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100162 int value;
163
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100164 if (kstrtoint(buf, 10, &value))
165 return -EINVAL;
166 if (value < 0 || value > 1)
167 return -EINVAL;
168
169 data_pointer->release_to_select = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100170 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100171
172 return count;
173}
174
Jamie Lentin94723bf2014-07-23 23:30:45 +0100175static ssize_t attr_select_right_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100176 struct device_attribute *attr,
177 char *buf)
178{
Axel Lin832fbea2012-09-13 14:12:08 +0800179 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100180 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100181
182 return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
183}
184
Jamie Lentin94723bf2014-07-23 23:30:45 +0100185static ssize_t attr_select_right_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100186 struct device_attribute *attr,
187 const char *buf,
188 size_t count)
189{
Axel Lin832fbea2012-09-13 14:12:08 +0800190 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100191 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100192 int value;
193
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100194 if (kstrtoint(buf, 10, &value))
195 return -EINVAL;
196 if (value < 0 || value > 1)
197 return -EINVAL;
198
199 data_pointer->select_right = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100200 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100201
202 return count;
203}
204
Jamie Lentin94723bf2014-07-23 23:30:45 +0100205static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100206 struct device_attribute *attr,
207 char *buf)
208{
Axel Lin832fbea2012-09-13 14:12:08 +0800209 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100210 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100211
212 return snprintf(buf, PAGE_SIZE, "%u\n",
213 data_pointer->sensitivity);
214}
215
Jamie Lentin94723bf2014-07-23 23:30:45 +0100216static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100217 struct device_attribute *attr,
218 const char *buf,
219 size_t count)
220{
Axel Lin832fbea2012-09-13 14:12:08 +0800221 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100222 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100223 int value;
224
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100225 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
226 return -EINVAL;
227
228 data_pointer->sensitivity = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100229 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100230
231 return count;
232}
233
Jamie Lentin94723bf2014-07-23 23:30:45 +0100234static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100235 struct device_attribute *attr,
236 char *buf)
237{
Axel Lin832fbea2012-09-13 14:12:08 +0800238 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100239 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100240
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100241 return snprintf(buf, PAGE_SIZE, "%u\n",
242 data_pointer->press_speed);
243}
244
Jamie Lentin94723bf2014-07-23 23:30:45 +0100245static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100246 struct device_attribute *attr,
247 const char *buf,
248 size_t count)
249{
Axel Lin832fbea2012-09-13 14:12:08 +0800250 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100251 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100252 int value;
253
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100254 if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
255 return -EINVAL;
256
257 data_pointer->press_speed = value;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100258 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100259
260 return count;
261}
262
Jamie Lentin94723bf2014-07-23 23:30:45 +0100263static struct device_attribute dev_attr_press_to_select_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100264 __ATTR(press_to_select, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100265 attr_press_to_select_show_tpkbd,
266 attr_press_to_select_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100267
Jamie Lentin94723bf2014-07-23 23:30:45 +0100268static struct device_attribute dev_attr_dragging_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100269 __ATTR(dragging, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100270 attr_dragging_show_tpkbd,
271 attr_dragging_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100272
Jamie Lentin94723bf2014-07-23 23:30:45 +0100273static struct device_attribute dev_attr_release_to_select_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100274 __ATTR(release_to_select, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100275 attr_release_to_select_show_tpkbd,
276 attr_release_to_select_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100277
Jamie Lentin94723bf2014-07-23 23:30:45 +0100278static struct device_attribute dev_attr_select_right_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100279 __ATTR(select_right, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100280 attr_select_right_show_tpkbd,
281 attr_select_right_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100282
Jamie Lentin94723bf2014-07-23 23:30:45 +0100283static struct device_attribute dev_attr_sensitivity_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100284 __ATTR(sensitivity, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100285 attr_sensitivity_show_tpkbd,
286 attr_sensitivity_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100287
Jamie Lentin94723bf2014-07-23 23:30:45 +0100288static struct device_attribute dev_attr_press_speed_tpkbd =
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100289 __ATTR(press_speed, S_IWUSR | S_IRUGO,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100290 attr_press_speed_show_tpkbd,
291 attr_press_speed_store_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100292
Jamie Lentin94723bf2014-07-23 23:30:45 +0100293static struct attribute *lenovo_attributes_tpkbd[] = {
294 &dev_attr_press_to_select_tpkbd.attr,
295 &dev_attr_dragging_tpkbd.attr,
296 &dev_attr_release_to_select_tpkbd.attr,
297 &dev_attr_select_right_tpkbd.attr,
298 &dev_attr_sensitivity_tpkbd.attr,
299 &dev_attr_press_speed_tpkbd.attr,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100300 NULL
301};
302
Jamie Lentin94723bf2014-07-23 23:30:45 +0100303static const struct attribute_group lenovo_attr_group_tpkbd = {
304 .attrs = lenovo_attributes_tpkbd,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100305};
306
Jamie Lentin94723bf2014-07-23 23:30:45 +0100307static enum led_brightness lenovo_led_brightness_get_tpkbd(
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100308 struct led_classdev *led_cdev)
309{
Axel Lin832fbea2012-09-13 14:12:08 +0800310 struct device *dev = led_cdev->dev->parent;
311 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100312 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100313 int led_nr = 0;
314
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100315 if (led_cdev == &data_pointer->led_micmute)
316 led_nr = 1;
317
318 return data_pointer->led_state & (1 << led_nr)
319 ? LED_FULL
320 : LED_OFF;
321}
322
Jamie Lentin94723bf2014-07-23 23:30:45 +0100323static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100324 enum led_brightness value)
325{
Axel Lin832fbea2012-09-13 14:12:08 +0800326 struct device *dev = led_cdev->dev->parent;
327 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
Jamie Lentin94723bf2014-07-23 23:30:45 +0100328 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100329 struct hid_report *report;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100330 int led_nr = 0;
331
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100332 if (led_cdev == &data_pointer->led_micmute)
333 led_nr = 1;
334
335 if (value == LED_OFF)
336 data_pointer->led_state &= ~(1 << led_nr);
337 else
338 data_pointer->led_state |= 1 << led_nr;
339
340 report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
341 report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
342 report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
Benjamin Tissoiresd88142722013-02-25 11:31:46 +0100343 hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100344}
345
Jamie Lentin94723bf2014-07-23 23:30:45 +0100346static int lenovo_probe_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100347{
348 struct device *dev = &hdev->dev;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100349 struct lenovo_drvdata_tpkbd *data_pointer;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100350 size_t name_sz = strlen(dev_name(dev)) + 16;
351 char *name_mute, *name_micmute;
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200352 int i;
Jamie Lentine0a6aad2014-07-26 15:42:27 +0100353 int ret;
Kees Cook0a9cd0a2013-09-11 21:56:55 +0200354
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100355 /*
356 * Only register extra settings against subdevice where input_mapping
357 * set drvdata to 1, i.e. the trackpoint.
358 */
359 if (!hid_get_drvdata(hdev))
360 return 0;
361
362 hid_set_drvdata(hdev, NULL);
363
Kees Cook0a9cd0a2013-09-11 21:56:55 +0200364 /* Validate required reports. */
365 for (i = 0; i < 4; i++) {
366 if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1))
367 return -ENODEV;
368 }
369 if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
370 return -ENODEV;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100371
Jamie Lentine0a6aad2014-07-26 15:42:27 +0100372 ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
373 if (ret)
374 hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100375
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200376 data_pointer = devm_kzalloc(&hdev->dev,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100377 sizeof(struct lenovo_drvdata_tpkbd),
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200378 GFP_KERNEL);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100379 if (data_pointer == NULL) {
380 hid_err(hdev, "Could not allocate memory for driver data\n");
381 return -ENOMEM;
382 }
383
384 // set same default values as windows driver
385 data_pointer->sensitivity = 0xa0;
386 data_pointer->press_speed = 0x38;
387
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200388 name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
389 name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
390 if (name_mute == NULL || name_micmute == NULL) {
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100391 hid_err(hdev, "Could not allocate memory for led data\n");
Benjamin Tissoires01ab35f2013-09-11 22:12:23 +0200392 return -ENOMEM;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100393 }
394 snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100395 snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
396
397 hid_set_drvdata(hdev, data_pointer);
398
399 data_pointer->led_mute.name = name_mute;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100400 data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;
401 data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100402 data_pointer->led_mute.dev = dev;
403 led_classdev_register(dev, &data_pointer->led_mute);
404
405 data_pointer->led_micmute.name = name_micmute;
Jamie Lentin94723bf2014-07-23 23:30:45 +0100406 data_pointer->led_micmute.brightness_get =
407 lenovo_led_brightness_get_tpkbd;
408 data_pointer->led_micmute.brightness_set =
409 lenovo_led_brightness_set_tpkbd;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100410 data_pointer->led_micmute.dev = dev;
411 led_classdev_register(dev, &data_pointer->led_micmute);
412
Jamie Lentin94723bf2014-07-23 23:30:45 +0100413 lenovo_features_set_tpkbd(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100414
415 return 0;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100416}
417
Jamie Lentin94723bf2014-07-23 23:30:45 +0100418static int lenovo_probe(struct hid_device *hdev,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100419 const struct hid_device_id *id)
420{
421 int ret;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100422
423 ret = hid_parse(hdev);
424 if (ret) {
425 hid_err(hdev, "hid_parse failed\n");
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +0200426 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100427 }
428
429 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
430 if (ret) {
431 hid_err(hdev, "hid_hw_start failed\n");
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +0200432 goto err;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100433 }
434
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100435 switch (hdev->product) {
436 case USB_DEVICE_ID_LENOVO_TPKBD:
Jamie Lentin94723bf2014-07-23 23:30:45 +0100437 ret = lenovo_probe_tpkbd(hdev);
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100438 break;
439 default:
440 ret = 0;
441 break;
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +0200442 }
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100443 if (ret)
444 goto err_hid;
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100445
446 return 0;
Benjamin Tissoires0ccdd9e2013-09-11 21:56:59 +0200447err_hid:
448 hid_hw_stop(hdev);
449err:
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100450 return ret;
451}
452
Jamie Lentin94723bf2014-07-23 23:30:45 +0100453static void lenovo_remove_tpkbd(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100454{
Jamie Lentin94723bf2014-07-23 23:30:45 +0100455 struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100456
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100457 /*
458 * Only the trackpoint half of the keyboard has drvdata and stuff that
459 * needs unregistering.
460 */
461 if (data_pointer == NULL)
462 return;
463
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100464 sysfs_remove_group(&hdev->dev.kobj,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100465 &lenovo_attr_group_tpkbd);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100466
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100467 led_classdev_unregister(&data_pointer->led_micmute);
468 led_classdev_unregister(&data_pointer->led_mute);
469
470 hid_set_drvdata(hdev, NULL);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100471}
472
Jamie Lentin94723bf2014-07-23 23:30:45 +0100473static void lenovo_remove(struct hid_device *hdev)
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100474{
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100475 switch (hdev->product) {
476 case USB_DEVICE_ID_LENOVO_TPKBD:
Jamie Lentin94723bf2014-07-23 23:30:45 +0100477 lenovo_remove_tpkbd(hdev);
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100478 break;
479 }
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100480
481 hid_hw_stop(hdev);
482}
483
Jamie Lentin94723bf2014-07-23 23:30:45 +0100484static const struct hid_device_id lenovo_devices[] = {
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100485 { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
486 { }
487};
488
Jamie Lentin94723bf2014-07-23 23:30:45 +0100489MODULE_DEVICE_TABLE(hid, lenovo_devices);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100490
Jamie Lentin94723bf2014-07-23 23:30:45 +0100491static struct hid_driver lenovo_driver = {
492 .name = "lenovo",
493 .id_table = lenovo_devices,
Jamie Lentin6a5b4142014-07-23 23:30:46 +0100494 .input_mapping = lenovo_input_mapping,
Jamie Lentin94723bf2014-07-23 23:30:45 +0100495 .probe = lenovo_probe,
496 .remove = lenovo_remove,
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100497};
Jamie Lentin94723bf2014-07-23 23:30:45 +0100498module_hid_driver(lenovo_driver);
Bernhard Seiboldc1dcad2d2012-02-15 13:40:43 +0100499
500MODULE_LICENSE("GPL");