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;
  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
  76static 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
  92static 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
 118static 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
 134static 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
 160static 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
 176static 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
 202static 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
 218static 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
 244static 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
 261static 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
 285static 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
 302static 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
 326static 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
 331static struct device_attribute dev_attr_pointer_dragging =
 332        __ATTR(dragging, S_IWUSR | S_IRUGO,
 333                        pointer_dragging_show,
 334                        pointer_dragging_store);
 335
 336static 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
 341static 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
 346static struct device_attribute dev_attr_pointer_sensitivity =
 347        __ATTR(sensitivity, S_IWUSR | S_IRUGO,
 348                        pointer_sensitivity_show,
 349                        pointer_sensitivity_store);
 350
 351static 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
 356static 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
 366static const struct attribute_group tpkbd_attr_group_pointer = {
 367        .attrs = tpkbd_attributes_pointer,
 368};
 369
 370static 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
 390static 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
 417static 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
 474err2:
 475        kfree(name_mute);
 476err:
 477        kfree(data_pointer);
 478        return ret;
 479}
 480
 481static 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;
 505err_free:
 506        return ret;
 507}
 508
 509static 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->led_micmute.name);
 523        kfree(data_pointer->led_mute.name);
 524        kfree(data_pointer);
 525}
 526
 527static void tpkbd_remove(struct hid_device *hdev)
 528{
 529        struct usbhid_device *uhdev;
 530
 531        uhdev = (struct usbhid_device *) hdev->driver_data;
 532        if (uhdev->ifnum == 1)
 533                tpkbd_remove_tp(hdev);
 534
 535        hid_hw_stop(hdev);
 536}
 537
 538static const struct hid_device_id tpkbd_devices[] = {
 539        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
 540        { }
 541};
 542
 543MODULE_DEVICE_TABLE(hid, tpkbd_devices);
 544
 545static struct hid_driver tpkbd_driver = {
 546        .name = "lenovo_tpkbd",
 547        .id_table = tpkbd_devices,
 548        .input_mapping = tpkbd_input_mapping,
 549        .probe = tpkbd_probe,
 550        .remove = tpkbd_remove,
 551};
 552
 553static int __init tpkbd_init(void)
 554{
 555        return hid_register_driver(&tpkbd_driver);
 556}
 557
 558static void __exit tpkbd_exit(void)
 559{
 560        hid_unregister_driver(&tpkbd_driver);
 561}
 562
 563module_init(tpkbd_init);
 564module_exit(tpkbd_exit);
 565
 566MODULE_LICENSE("GPL");
 567
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.