linux/drivers/hid/hid-lenovo-tpkbd.c
<<
>>
Prefs
   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 */
  26struct 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
  40static 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
  56static 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
  75static 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
  85static 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
 105static 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
 115static 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
 135static 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
 145static 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
 165static 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
 175static 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
 195static 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
 206static 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
 224static 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
 237static 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
 255static 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
 260static struct device_attribute dev_attr_pointer_dragging =
 261        __ATTR(dragging, S_IWUSR | S_IRUGO,
 262                        pointer_dragging_show,
 263                        pointer_dragging_store);
 264
 265static 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
 270static 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
 275static struct device_attribute dev_attr_pointer_sensitivity =
 276        __ATTR(sensitivity, S_IWUSR | S_IRUGO,
 277                        pointer_sensitivity_show,
 278                        pointer_sensitivity_store);
 279
 280static 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
 285static 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
 295static const struct attribute_group tpkbd_attr_group_pointer = {
 296        .attrs = tpkbd_attributes_pointer,
 297};
 298
 299static 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
 315static 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
 338static 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
 395err2:
 396        kfree(name_mute);
 397err:
 398        kfree(data_pointer);
 399        return ret;
 400}
 401
 402static 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;
 426err_free:
 427        return ret;
 428}
 429
 430static 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
 446static 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
 457static const struct hid_device_id tpkbd_devices[] = {
 458        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
 459        { }
 460};
 461
 462MODULE_DEVICE_TABLE(hid, tpkbd_devices);
 463
 464static 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
 472static int __init tpkbd_init(void)
 473{
 474        return hid_register_driver(&tpkbd_driver);
 475}
 476
 477static void __exit tpkbd_exit(void)
 478{
 479        hid_unregister_driver(&tpkbd_driver);
 480}
 481
 482module_init(tpkbd_init);
 483module_exit(tpkbd_exit);
 484
 485MODULE_LICENSE("GPL");
 486
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.