Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: Driver for Lenovo Keyboard with Trackpoint

This driver for the "Lenovo ThinkPad USB Keyboard with Trackpoint" supports
setting various device attributes, controlling mute and microphone mute
LEDs and enables use of the microphone mute key.

Signed-off-by: Bernhard Seibold <mail@bernhard-seibold.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Bernhard Seibold and committed by
Jiri Kosina
c1dcad2d e39fe251

+619
+38
Documentation/ABI/testing/sysfs-driver-hid-lenovo-tpkbd
··· 1 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_to_select 2 + Date: July 2011 3 + Contact: linux-input@vger.kernel.org 4 + Description: This controls if mouse clicks should be generated if the trackpoint is quickly pressed. How fast this press has to be 5 + is being controlled by press_speed. 6 + Values are 0 or 1. 7 + 8 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/dragging 9 + Date: July 2011 10 + Contact: linux-input@vger.kernel.org 11 + Description: If this setting is enabled, it is possible to do dragging by pressing the trackpoint. This requires press_to_select to be enabled. 12 + Values are 0 or 1. 13 + 14 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/release_to_select 15 + Date: July 2011 16 + Contact: linux-input@vger.kernel.org 17 + Description: For details regarding this setting please refer to http://www.pc.ibm.com/ww/healthycomputing/trkpntb.html 18 + Values are 0 or 1. 19 + 20 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/select_right 21 + Date: July 2011 22 + Contact: linux-input@vger.kernel.org 23 + Description: This setting controls if the mouse click events generated by pressing the trackpoint (if press_to_select is enabled) generate 24 + a left or right mouse button click. 25 + Values are 0 or 1. 26 + 27 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/sensitivity 28 + Date: July 2011 29 + Contact: linux-input@vger.kernel.org 30 + Description: This file contains the trackpoint sensitivity. 31 + Values are decimal integers from 1 (lowest sensitivity) to 255 (highest sensitivity). 32 + 33 + What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_speed 34 + Date: July 2011 35 + Contact: linux-input@vger.kernel.org 36 + Description: This setting controls how fast the trackpoint needs to be pressed to generate a mouse click if press_to_select is enabled. 37 + Values are decimal integers from 1 (slowest) to 255 (fastest). 38 +
+12
drivers/hid/Kconfig
··· 268 268 ---help--- 269 269 Support for LC-Power RC1000MCE RF remote control. 270 270 271 + config HID_LENOVO_TPKBD 272 + tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" 273 + depends on USB_HID 274 + select LEDS_CLASS 275 + ---help--- 276 + Support for the Lenovo ThinkPad USB Keyboard with TrackPoint. 277 + 278 + Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint 279 + and would like to use device-specific features like changing the 280 + sensitivity of the trackpoint, using the microphone mute button or 281 + controlling the mute and microphone mute LEDs. 282 + 271 283 config HID_LOGITECH 272 284 tristate "Logitech devices" if EXPERT 273 285 depends on USB_HID
+1
drivers/hid/Makefile
··· 54 54 obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o 55 55 obj-$(CONFIG_HID_KYE) += hid-kye.o 56 56 obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o 57 + obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o 57 58 obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o 58 59 obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o 59 60 obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
+1
drivers/hid/hid-core.c
··· 1544 1544 { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, 1545 1545 { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, 1546 1546 { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, 1547 + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, 1547 1548 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, 1548 1549 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, 1549 1550 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
+3
drivers/hid/hid-ids.h
··· 473 473 #define USB_DEVICE_ID_LD_HYBRID 0x2090 474 474 #define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 475 475 476 + #define USB_VENDOR_ID_LENOVO 0x17ef 477 + #define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 478 + 476 479 #define USB_VENDOR_ID_LG 0x1fd2 477 480 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 478 481
+564
drivers/hid/hid-lenovo-tpkbd.c
··· 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; 60 + 61 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 62 + report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; 63 + 64 + report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; 65 + report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; 66 + report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; 67 + report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; 68 + report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver 69 + report->field[2]->value[0] = data_pointer->sensitivity; 70 + report->field[3]->value[0] = data_pointer->press_speed; 71 + 72 + usbhid_submit_report(hdev, report, USB_DIR_OUT); 73 + return 0; 74 + } 75 + 76 + static ssize_t pointer_press_to_select_show(struct device *dev, 77 + struct device_attribute *attr, 78 + char *buf) 79 + { 80 + struct hid_device *hdev; 81 + struct tpkbd_data_pointer *data_pointer; 82 + 83 + hdev = container_of(dev, struct hid_device, dev); 84 + if (hdev == NULL) 85 + return -ENODEV; 86 + 87 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 88 + 89 + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); 90 + } 91 + 92 + static ssize_t pointer_press_to_select_store(struct device *dev, 93 + struct device_attribute *attr, 94 + const char *buf, 95 + size_t count) 96 + { 97 + struct hid_device *hdev; 98 + struct tpkbd_data_pointer *data_pointer; 99 + int value; 100 + 101 + hdev = container_of(dev, struct hid_device, dev); 102 + if (hdev == NULL) 103 + return -ENODEV; 104 + 105 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 106 + 107 + if (kstrtoint(buf, 10, &value)) 108 + return -EINVAL; 109 + if (value < 0 || value > 1) 110 + return -EINVAL; 111 + 112 + data_pointer->press_to_select = value; 113 + tpkbd_features_set(hdev); 114 + 115 + return count; 116 + } 117 + 118 + static ssize_t pointer_dragging_show(struct device *dev, 119 + struct device_attribute *attr, 120 + char *buf) 121 + { 122 + struct hid_device *hdev; 123 + struct tpkbd_data_pointer *data_pointer; 124 + 125 + hdev = container_of(dev, struct hid_device, dev); 126 + if (hdev == NULL) 127 + return -ENODEV; 128 + 129 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 130 + 131 + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); 132 + } 133 + 134 + static ssize_t pointer_dragging_store(struct device *dev, 135 + struct device_attribute *attr, 136 + const char *buf, 137 + size_t count) 138 + { 139 + struct hid_device *hdev; 140 + struct tpkbd_data_pointer *data_pointer; 141 + int value; 142 + 143 + hdev = container_of(dev, struct hid_device, dev); 144 + if (hdev == NULL) 145 + return -ENODEV; 146 + 147 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 148 + 149 + if (kstrtoint(buf, 10, &value)) 150 + return -EINVAL; 151 + if (value < 0 || value > 1) 152 + return -EINVAL; 153 + 154 + data_pointer->dragging = value; 155 + tpkbd_features_set(hdev); 156 + 157 + return count; 158 + } 159 + 160 + static ssize_t pointer_release_to_select_show(struct device *dev, 161 + struct device_attribute *attr, 162 + char *buf) 163 + { 164 + struct hid_device *hdev; 165 + struct tpkbd_data_pointer *data_pointer; 166 + 167 + hdev = container_of(dev, struct hid_device, dev); 168 + if (hdev == NULL) 169 + return -ENODEV; 170 + 171 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 172 + 173 + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); 174 + } 175 + 176 + static ssize_t pointer_release_to_select_store(struct device *dev, 177 + struct device_attribute *attr, 178 + const char *buf, 179 + size_t count) 180 + { 181 + struct hid_device *hdev; 182 + struct tpkbd_data_pointer *data_pointer; 183 + int value; 184 + 185 + hdev = container_of(dev, struct hid_device, dev); 186 + if (hdev == NULL) 187 + return -ENODEV; 188 + 189 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 190 + 191 + if (kstrtoint(buf, 10, &value)) 192 + return -EINVAL; 193 + if (value < 0 || value > 1) 194 + return -EINVAL; 195 + 196 + data_pointer->release_to_select = value; 197 + tpkbd_features_set(hdev); 198 + 199 + return count; 200 + } 201 + 202 + static ssize_t pointer_select_right_show(struct device *dev, 203 + struct device_attribute *attr, 204 + char *buf) 205 + { 206 + struct hid_device *hdev; 207 + struct tpkbd_data_pointer *data_pointer; 208 + 209 + hdev = container_of(dev, struct hid_device, dev); 210 + if (hdev == NULL) 211 + return -ENODEV; 212 + 213 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 214 + 215 + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); 216 + } 217 + 218 + static ssize_t pointer_select_right_store(struct device *dev, 219 + struct device_attribute *attr, 220 + const char *buf, 221 + size_t count) 222 + { 223 + struct hid_device *hdev; 224 + struct tpkbd_data_pointer *data_pointer; 225 + int value; 226 + 227 + hdev = container_of(dev, struct hid_device, dev); 228 + if (hdev == NULL) 229 + return -ENODEV; 230 + 231 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 232 + 233 + if (kstrtoint(buf, 10, &value)) 234 + return -EINVAL; 235 + if (value < 0 || value > 1) 236 + return -EINVAL; 237 + 238 + data_pointer->select_right = value; 239 + tpkbd_features_set(hdev); 240 + 241 + return count; 242 + } 243 + 244 + static ssize_t pointer_sensitivity_show(struct device *dev, 245 + struct device_attribute *attr, 246 + char *buf) 247 + { 248 + struct hid_device *hdev; 249 + struct tpkbd_data_pointer *data_pointer; 250 + 251 + hdev = container_of(dev, struct hid_device, dev); 252 + if (hdev == NULL) 253 + return -ENODEV; 254 + 255 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 256 + 257 + return snprintf(buf, PAGE_SIZE, "%u\n", 258 + data_pointer->sensitivity); 259 + } 260 + 261 + static ssize_t pointer_sensitivity_store(struct device *dev, 262 + struct device_attribute *attr, 263 + const char *buf, 264 + size_t count) 265 + { 266 + struct hid_device *hdev; 267 + struct tpkbd_data_pointer *data_pointer; 268 + int value; 269 + 270 + hdev = container_of(dev, struct hid_device, dev); 271 + if (hdev == NULL) 272 + return -ENODEV; 273 + 274 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 275 + 276 + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) 277 + return -EINVAL; 278 + 279 + data_pointer->sensitivity = value; 280 + tpkbd_features_set(hdev); 281 + 282 + return count; 283 + } 284 + 285 + static ssize_t pointer_press_speed_show(struct device *dev, 286 + struct device_attribute *attr, 287 + char *buf) 288 + { 289 + struct hid_device *hdev; 290 + struct tpkbd_data_pointer *data_pointer; 291 + 292 + hdev = container_of(dev, struct hid_device, dev); 293 + if (hdev == NULL) 294 + return -ENODEV; 295 + 296 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 297 + 298 + return snprintf(buf, PAGE_SIZE, "%u\n", 299 + data_pointer->press_speed); 300 + } 301 + 302 + static ssize_t pointer_press_speed_store(struct device *dev, 303 + struct device_attribute *attr, 304 + const char *buf, 305 + size_t count) 306 + { 307 + struct hid_device *hdev; 308 + struct tpkbd_data_pointer *data_pointer; 309 + int value; 310 + 311 + hdev = container_of(dev, struct hid_device, dev); 312 + if (hdev == NULL) 313 + return -ENODEV; 314 + 315 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 316 + 317 + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) 318 + return -EINVAL; 319 + 320 + data_pointer->press_speed = value; 321 + tpkbd_features_set(hdev); 322 + 323 + return count; 324 + } 325 + 326 + static struct device_attribute dev_attr_pointer_press_to_select = 327 + __ATTR(press_to_select, S_IWUSR | S_IRUGO, 328 + pointer_press_to_select_show, 329 + pointer_press_to_select_store); 330 + 331 + static struct device_attribute dev_attr_pointer_dragging = 332 + __ATTR(dragging, S_IWUSR | S_IRUGO, 333 + pointer_dragging_show, 334 + pointer_dragging_store); 335 + 336 + static struct device_attribute dev_attr_pointer_release_to_select = 337 + __ATTR(release_to_select, S_IWUSR | S_IRUGO, 338 + pointer_release_to_select_show, 339 + pointer_release_to_select_store); 340 + 341 + static struct device_attribute dev_attr_pointer_select_right = 342 + __ATTR(select_right, S_IWUSR | S_IRUGO, 343 + pointer_select_right_show, 344 + pointer_select_right_store); 345 + 346 + static struct device_attribute dev_attr_pointer_sensitivity = 347 + __ATTR(sensitivity, S_IWUSR | S_IRUGO, 348 + pointer_sensitivity_show, 349 + pointer_sensitivity_store); 350 + 351 + static struct device_attribute dev_attr_pointer_press_speed = 352 + __ATTR(press_speed, S_IWUSR | S_IRUGO, 353 + pointer_press_speed_show, 354 + pointer_press_speed_store); 355 + 356 + static struct attribute *tpkbd_attributes_pointer[] = { 357 + &dev_attr_pointer_press_to_select.attr, 358 + &dev_attr_pointer_dragging.attr, 359 + &dev_attr_pointer_release_to_select.attr, 360 + &dev_attr_pointer_select_right.attr, 361 + &dev_attr_pointer_sensitivity.attr, 362 + &dev_attr_pointer_press_speed.attr, 363 + NULL 364 + }; 365 + 366 + static const struct attribute_group tpkbd_attr_group_pointer = { 367 + .attrs = tpkbd_attributes_pointer, 368 + }; 369 + 370 + static enum led_brightness tpkbd_led_brightness_get( 371 + struct led_classdev *led_cdev) 372 + { 373 + struct device *dev; 374 + struct hid_device *hdev; 375 + struct tpkbd_data_pointer *data_pointer; 376 + int led_nr = 0; 377 + 378 + dev = led_cdev->dev->parent; 379 + hdev = container_of(dev, struct hid_device, dev); 380 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 381 + 382 + if (led_cdev == &data_pointer->led_micmute) 383 + led_nr = 1; 384 + 385 + return data_pointer->led_state & (1 << led_nr) 386 + ? LED_FULL 387 + : LED_OFF; 388 + } 389 + 390 + static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, 391 + enum led_brightness value) 392 + { 393 + struct device *dev; 394 + struct hid_device *hdev; 395 + struct hid_report *report; 396 + struct tpkbd_data_pointer *data_pointer; 397 + int led_nr = 0; 398 + 399 + dev = led_cdev->dev->parent; 400 + hdev = container_of(dev, struct hid_device, dev); 401 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 402 + 403 + if (led_cdev == &data_pointer->led_micmute) 404 + led_nr = 1; 405 + 406 + if (value == LED_OFF) 407 + data_pointer->led_state &= ~(1 << led_nr); 408 + else 409 + data_pointer->led_state |= 1 << led_nr; 410 + 411 + report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; 412 + report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; 413 + report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; 414 + usbhid_submit_report(hdev, report, USB_DIR_OUT); 415 + } 416 + 417 + static int tpkbd_probe_tp(struct hid_device *hdev) 418 + { 419 + struct device *dev = &hdev->dev; 420 + struct tpkbd_data_pointer *data_pointer; 421 + size_t name_sz = strlen(dev_name(dev)) + 16; 422 + char *name_mute, *name_micmute; 423 + int ret; 424 + 425 + if (sysfs_create_group(&hdev->dev.kobj, 426 + &tpkbd_attr_group_pointer)) { 427 + hid_warn(hdev, "Could not create sysfs group\n"); 428 + } 429 + 430 + data_pointer = kzalloc(sizeof(struct tpkbd_data_pointer), GFP_KERNEL); 431 + if (data_pointer == NULL) { 432 + hid_err(hdev, "Could not allocate memory for driver data\n"); 433 + return -ENOMEM; 434 + } 435 + 436 + // set same default values as windows driver 437 + data_pointer->sensitivity = 0xa0; 438 + data_pointer->press_speed = 0x38; 439 + 440 + name_mute = kzalloc(name_sz, GFP_KERNEL); 441 + if (name_mute == NULL) { 442 + hid_err(hdev, "Could not allocate memory for led data\n"); 443 + ret = -ENOMEM; 444 + goto err; 445 + } 446 + snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); 447 + 448 + name_micmute = kzalloc(name_sz, GFP_KERNEL); 449 + if (name_micmute == NULL) { 450 + hid_err(hdev, "Could not allocate memory for led data\n"); 451 + ret = -ENOMEM; 452 + goto err2; 453 + } 454 + snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); 455 + 456 + hid_set_drvdata(hdev, data_pointer); 457 + 458 + data_pointer->led_mute.name = name_mute; 459 + data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get; 460 + data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set; 461 + data_pointer->led_mute.dev = dev; 462 + led_classdev_register(dev, &data_pointer->led_mute); 463 + 464 + data_pointer->led_micmute.name = name_micmute; 465 + data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get; 466 + data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set; 467 + data_pointer->led_micmute.dev = dev; 468 + led_classdev_register(dev, &data_pointer->led_micmute); 469 + 470 + tpkbd_features_set(hdev); 471 + 472 + return 0; 473 + 474 + err2: 475 + kfree(name_mute); 476 + err: 477 + kfree(data_pointer); 478 + return ret; 479 + } 480 + 481 + static int tpkbd_probe(struct hid_device *hdev, 482 + const struct hid_device_id *id) 483 + { 484 + int ret; 485 + struct usbhid_device *uhdev; 486 + 487 + ret = hid_parse(hdev); 488 + if (ret) { 489 + hid_err(hdev, "hid_parse failed\n"); 490 + goto err_free; 491 + } 492 + 493 + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 494 + if (ret) { 495 + hid_err(hdev, "hid_hw_start failed\n"); 496 + goto err_free; 497 + } 498 + 499 + uhdev = (struct usbhid_device *) hdev->driver_data; 500 + 501 + if (uhdev->ifnum == 1) 502 + return tpkbd_probe_tp(hdev); 503 + 504 + return 0; 505 + err_free: 506 + return ret; 507 + } 508 + 509 + static void tpkbd_remove_tp(struct hid_device *hdev) 510 + { 511 + struct tpkbd_data_pointer *data_pointer; 512 + 513 + sysfs_remove_group(&hdev->dev.kobj, 514 + &tpkbd_attr_group_pointer); 515 + 516 + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); 517 + 518 + led_classdev_unregister(&data_pointer->led_micmute); 519 + led_classdev_unregister(&data_pointer->led_mute); 520 + 521 + hid_set_drvdata(hdev, NULL); 522 + kfree(data_pointer); 523 + } 524 + 525 + static void tpkbd_remove(struct hid_device *hdev) 526 + { 527 + struct usbhid_device *uhdev; 528 + 529 + uhdev = (struct usbhid_device *) hdev->driver_data; 530 + if (uhdev->ifnum == 1) 531 + tpkbd_remove_tp(hdev); 532 + 533 + hid_hw_stop(hdev); 534 + } 535 + 536 + static const struct hid_device_id tpkbd_devices[] = { 537 + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, 538 + { } 539 + }; 540 + 541 + MODULE_DEVICE_TABLE(hid, tpkbd_devices); 542 + 543 + static struct hid_driver tpkbd_driver = { 544 + .name = "lenovo_tpkbd", 545 + .id_table = tpkbd_devices, 546 + .input_mapping = tpkbd_input_mapping, 547 + .probe = tpkbd_probe, 548 + .remove = tpkbd_remove, 549 + }; 550 + 551 + static int __init tpkbd_init(void) 552 + { 553 + return hid_register_driver(&tpkbd_driver); 554 + } 555 + 556 + static void __exit tpkbd_exit(void) 557 + { 558 + hid_unregister_driver(&tpkbd_driver); 559 + } 560 + 561 + module_init(tpkbd_init); 562 + module_exit(tpkbd_exit); 563 + 564 + MODULE_LICENSE("GPL");