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        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
  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        return snprintf(buf, PAGE_SIZE, "%u\n",
 232                data_pointer->press_speed);
 233}
 234
 235static ssize_t pointer_press_speed_store(struct device *dev,
 236                struct device_attribute *attr,
 237                const char *buf,
 238                size_t count)
 239{
 240        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 241        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 242        int value;
 243
 244        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
 245                return -EINVAL;
 246
 247        data_pointer->press_speed = value;
 248        tpkbd_features_set(hdev);
 249
 250        return count;
 251}
 252
 253static struct device_attribute dev_attr_pointer_press_to_select =
 254        __ATTR(press_to_select, S_IWUSR | S_IRUGO,
 255                        pointer_press_to_select_show,
 256                        pointer_press_to_select_store);
 257
 258static struct device_attribute dev_attr_pointer_dragging =
 259        __ATTR(dragging, S_IWUSR | S_IRUGO,
 260                        pointer_dragging_show,
 261                        pointer_dragging_store);
 262
 263static struct device_attribute dev_attr_pointer_release_to_select =
 264        __ATTR(release_to_select, S_IWUSR | S_IRUGO,
 265                        pointer_release_to_select_show,
 266                        pointer_release_to_select_store);
 267
 268static struct device_attribute dev_attr_pointer_select_right =
 269        __ATTR(select_right, S_IWUSR | S_IRUGO,
 270                        pointer_select_right_show,
 271                        pointer_select_right_store);
 272
 273static struct device_attribute dev_attr_pointer_sensitivity =
 274        __ATTR(sensitivity, S_IWUSR | S_IRUGO,
 275                        pointer_sensitivity_show,
 276                        pointer_sensitivity_store);
 277
 278static struct device_attribute dev_attr_pointer_press_speed =
 279        __ATTR(press_speed, S_IWUSR | S_IRUGO,
 280                        pointer_press_speed_show,
 281                        pointer_press_speed_store);
 282
 283static struct attribute *tpkbd_attributes_pointer[] = {
 284        &dev_attr_pointer_press_to_select.attr,
 285        &dev_attr_pointer_dragging.attr,
 286        &dev_attr_pointer_release_to_select.attr,
 287        &dev_attr_pointer_select_right.attr,
 288        &dev_attr_pointer_sensitivity.attr,
 289        &dev_attr_pointer_press_speed.attr,
 290        NULL
 291};
 292
 293static const struct attribute_group tpkbd_attr_group_pointer = {
 294        .attrs = tpkbd_attributes_pointer,
 295};
 296
 297static enum led_brightness tpkbd_led_brightness_get(
 298                        struct led_classdev *led_cdev)
 299{
 300        struct device *dev = led_cdev->dev->parent;
 301        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 302        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 303        int led_nr = 0;
 304
 305        if (led_cdev == &data_pointer->led_micmute)
 306                led_nr = 1;
 307
 308        return data_pointer->led_state & (1 << led_nr)
 309                                ? LED_FULL
 310                                : LED_OFF;
 311}
 312
 313static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
 314                        enum led_brightness value)
 315{
 316        struct device *dev = led_cdev->dev->parent;
 317        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 318        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 319        struct hid_report *report;
 320        int led_nr = 0;
 321
 322        if (led_cdev == &data_pointer->led_micmute)
 323                led_nr = 1;
 324
 325        if (value == LED_OFF)
 326                data_pointer->led_state &= ~(1 << led_nr);
 327        else
 328                data_pointer->led_state |= 1 << led_nr;
 329
 330        report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
 331        report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
 332        report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
 333        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 334}
 335
 336static int tpkbd_probe_tp(struct hid_device *hdev)
 337{
 338        struct device *dev = &hdev->dev;
 339        struct tpkbd_data_pointer *data_pointer;
 340        size_t name_sz = strlen(dev_name(dev)) + 16;
 341        char *name_mute, *name_micmute;
 342        int ret;
 343
 344        if (sysfs_create_group(&hdev->dev.kobj,
 345                                &tpkbd_attr_group_pointer)) {
 346                hid_warn(hdev, "Could not create sysfs group\n");
 347        }
 348
 349        data_pointer = kzalloc(sizeof(struct tpkbd_data_pointer), GFP_KERNEL);
 350        if (data_pointer == NULL) {
 351                hid_err(hdev, "Could not allocate memory for driver data\n");
 352                return -ENOMEM;
 353        }
 354
 355        // set same default values as windows driver
 356        data_pointer->sensitivity = 0xa0;
 357        data_pointer->press_speed = 0x38;
 358
 359        name_mute = kzalloc(name_sz, GFP_KERNEL);
 360        if (name_mute == NULL) {
 361                hid_err(hdev, "Could not allocate memory for led data\n");
 362                ret = -ENOMEM;
 363                goto err;
 364        }
 365        snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
 366
 367        name_micmute = kzalloc(name_sz, GFP_KERNEL);
 368        if (name_micmute == NULL) {
 369                hid_err(hdev, "Could not allocate memory for led data\n");
 370                ret = -ENOMEM;
 371                goto err2;
 372        }
 373        snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
 374
 375        hid_set_drvdata(hdev, data_pointer);
 376
 377        data_pointer->led_mute.name = name_mute;
 378        data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get;
 379        data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set;
 380        data_pointer->led_mute.dev = dev;
 381        led_classdev_register(dev, &data_pointer->led_mute);
 382
 383        data_pointer->led_micmute.name = name_micmute;
 384        data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get;
 385        data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set;
 386        data_pointer->led_micmute.dev = dev;
 387        led_classdev_register(dev, &data_pointer->led_micmute);
 388
 389        tpkbd_features_set(hdev);
 390
 391        return 0;
 392
 393err2:
 394        kfree(name_mute);
 395err:
 396        kfree(data_pointer);
 397        return ret;
 398}
 399
 400static int tpkbd_probe(struct hid_device *hdev,
 401                const struct hid_device_id *id)
 402{
 403        int ret;
 404        struct usbhid_device *uhdev;
 405
 406        ret = hid_parse(hdev);
 407        if (ret) {
 408                hid_err(hdev, "hid_parse failed\n");
 409                goto err_free;
 410        }
 411
 412        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 413        if (ret) {
 414                hid_err(hdev, "hid_hw_start failed\n");
 415                goto err_free;
 416        }
 417
 418        uhdev = (struct usbhid_device *) hdev->driver_data;
 419
 420        if (uhdev->ifnum == 1)
 421                return tpkbd_probe_tp(hdev);
 422
 423        return 0;
 424err_free:
 425        return ret;
 426}
 427
 428static void tpkbd_remove_tp(struct hid_device *hdev)
 429{
 430        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 431
 432        sysfs_remove_group(&hdev->dev.kobj,
 433                        &tpkbd_attr_group_pointer);
 434
 435        led_classdev_unregister(&data_pointer->led_micmute);
 436        led_classdev_unregister(&data_pointer->led_mute);
 437
 438        hid_set_drvdata(hdev, NULL);
 439        kfree(data_pointer->led_micmute.name);
 440        kfree(data_pointer->led_mute.name);
 441        kfree(data_pointer);
 442}
 443
 444static void tpkbd_remove(struct hid_device *hdev)
 445{
 446        struct usbhid_device *uhdev;
 447
 448        uhdev = (struct usbhid_device *) hdev->driver_data;
 449        if (uhdev->ifnum == 1)
 450                tpkbd_remove_tp(hdev);
 451
 452        hid_hw_stop(hdev);
 453}
 454
 455static const struct hid_device_id tpkbd_devices[] = {
 456        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
 457        { }
 458};
 459
 460MODULE_DEVICE_TABLE(hid, tpkbd_devices);
 461
 462static struct hid_driver tpkbd_driver = {
 463        .name = "lenovo_tpkbd",
 464        .id_table = tpkbd_devices,
 465        .input_mapping = tpkbd_input_mapping,
 466        .probe = tpkbd_probe,
 467        .remove = tpkbd_remove,
 468};
 469module_hid_driver(tpkbd_driver);
 470
 471MODULE_LICENSE("GPL");
 472
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.