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/hid.h>
  18#include <linux/input.h>
  19#include <linux/leds.h>
  20
  21#include "hid-ids.h"
  22
  23/* This is only used for the trackpoint part of the driver, hence _tp */
  24struct tpkbd_data_pointer {
  25        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
  38static int tpkbd_input_mapping(struct hid_device *hdev,
  39                struct hid_input *hi, struct hid_field *field,
  40                struct hid_usage *usage, unsigned long **bit, int *max)
  41{
  42        if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
  43                /* mark the device as pointer */
  44                hid_set_drvdata(hdev, (void *)1);
  45                map_key_clear(KEY_MICMUTE);
  46                return 1;
  47        }
  48        return 0;
  49}
  50
  51#undef map_key_clear
  52
  53static int tpkbd_features_set(struct hid_device *hdev)
  54{
  55        struct hid_report *report;
  56        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
  57
  58        report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
  59
  60        report->field[0]->value[0]  = data_pointer->press_to_select   ? 0x01 : 0x02;
  61        report->field[0]->value[0] |= data_pointer->dragging          ? 0x04 : 0x08;
  62        report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
  63        report->field[0]->value[0] |= data_pointer->select_right      ? 0x80 : 0x40;
  64        report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
  65        report->field[2]->value[0] = data_pointer->sensitivity;
  66        report->field[3]->value[0] = data_pointer->press_speed;
  67
  68        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
  69        return 0;
  70}
  71
  72static ssize_t pointer_press_to_select_show(struct device *dev,
  73                struct device_attribute *attr,
  74                char *buf)
  75{
  76        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
  77        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
  78
  79        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
  80}
  81
  82static ssize_t pointer_press_to_select_store(struct device *dev,
  83                struct device_attribute *attr,
  84                const char *buf,
  85                size_t count)
  86{
  87        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
  88        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
  89        int value;
  90
  91        if (kstrtoint(buf, 10, &value))
  92                return -EINVAL;
  93        if (value < 0 || value > 1)
  94                return -EINVAL;
  95
  96        data_pointer->press_to_select = value;
  97        tpkbd_features_set(hdev);
  98
  99        return count;
 100}
 101
 102static ssize_t pointer_dragging_show(struct device *dev,
 103                struct device_attribute *attr,
 104                char *buf)
 105{
 106        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 107        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 108
 109        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
 110}
 111
 112static ssize_t pointer_dragging_store(struct device *dev,
 113                struct device_attribute *attr,
 114                const char *buf,
 115                size_t count)
 116{
 117        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 118        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 119        int value;
 120
 121        if (kstrtoint(buf, 10, &value))
 122                return -EINVAL;
 123        if (value < 0 || value > 1)
 124                return -EINVAL;
 125
 126        data_pointer->dragging = value;
 127        tpkbd_features_set(hdev);
 128
 129        return count;
 130}
 131
 132static ssize_t pointer_release_to_select_show(struct device *dev,
 133                struct device_attribute *attr,
 134                char *buf)
 135{
 136        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 137        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 138
 139        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
 140}
 141
 142static ssize_t pointer_release_to_select_store(struct device *dev,
 143                struct device_attribute *attr,
 144                const char *buf,
 145                size_t count)
 146{
 147        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 148        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 149        int value;
 150
 151        if (kstrtoint(buf, 10, &value))
 152                return -EINVAL;
 153        if (value < 0 || value > 1)
 154                return -EINVAL;
 155
 156        data_pointer->release_to_select = value;
 157        tpkbd_features_set(hdev);
 158
 159        return count;
 160}
 161
 162static ssize_t pointer_select_right_show(struct device *dev,
 163                struct device_attribute *attr,
 164                char *buf)
 165{
 166        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 167        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 168
 169        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
 170}
 171
 172static ssize_t pointer_select_right_store(struct device *dev,
 173                struct device_attribute *attr,
 174                const char *buf,
 175                size_t count)
 176{
 177        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 178        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 179        int value;
 180
 181        if (kstrtoint(buf, 10, &value))
 182                return -EINVAL;
 183        if (value < 0 || value > 1)
 184                return -EINVAL;
 185
 186        data_pointer->select_right = value;
 187        tpkbd_features_set(hdev);
 188
 189        return count;
 190}
 191
 192static ssize_t pointer_sensitivity_show(struct device *dev,
 193                struct device_attribute *attr,
 194                char *buf)
 195{
 196        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 197        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 198
 199        return snprintf(buf, PAGE_SIZE, "%u\n",
 200                data_pointer->sensitivity);
 201}
 202
 203static ssize_t pointer_sensitivity_store(struct device *dev,
 204                struct device_attribute *attr,
 205                const char *buf,
 206                size_t count)
 207{
 208        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 209        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 210        int value;
 211
 212        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
 213                return -EINVAL;
 214
 215        data_pointer->sensitivity = value;
 216        tpkbd_features_set(hdev);
 217
 218        return count;
 219}
 220
 221static ssize_t pointer_press_speed_show(struct device *dev,
 222                struct device_attribute *attr,
 223                char *buf)
 224{
 225        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 226        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 227
 228        return snprintf(buf, PAGE_SIZE, "%u\n",
 229                data_pointer->press_speed);
 230}
 231
 232static ssize_t pointer_press_speed_store(struct device *dev,
 233                struct device_attribute *attr,
 234                const char *buf,
 235                size_t count)
 236{
 237        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 238        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 239        int value;
 240
 241        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
 242                return -EINVAL;
 243
 244        data_pointer->press_speed = value;
 245        tpkbd_features_set(hdev);
 246
 247        return count;
 248}
 249
 250static struct device_attribute dev_attr_pointer_press_to_select =
 251        __ATTR(press_to_select, S_IWUSR | S_IRUGO,
 252                        pointer_press_to_select_show,
 253                        pointer_press_to_select_store);
 254
 255static struct device_attribute dev_attr_pointer_dragging =
 256        __ATTR(dragging, S_IWUSR | S_IRUGO,
 257                        pointer_dragging_show,
 258                        pointer_dragging_store);
 259
 260static struct device_attribute dev_attr_pointer_release_to_select =
 261        __ATTR(release_to_select, S_IWUSR | S_IRUGO,
 262                        pointer_release_to_select_show,
 263                        pointer_release_to_select_store);
 264
 265static struct device_attribute dev_attr_pointer_select_right =
 266        __ATTR(select_right, S_IWUSR | S_IRUGO,
 267                        pointer_select_right_show,
 268                        pointer_select_right_store);
 269
 270static struct device_attribute dev_attr_pointer_sensitivity =
 271        __ATTR(sensitivity, S_IWUSR | S_IRUGO,
 272                        pointer_sensitivity_show,
 273                        pointer_sensitivity_store);
 274
 275static struct device_attribute dev_attr_pointer_press_speed =
 276        __ATTR(press_speed, S_IWUSR | S_IRUGO,
 277                        pointer_press_speed_show,
 278                        pointer_press_speed_store);
 279
 280static struct attribute *tpkbd_attributes_pointer[] = {
 281        &dev_attr_pointer_press_to_select.attr,
 282        &dev_attr_pointer_dragging.attr,
 283        &dev_attr_pointer_release_to_select.attr,
 284        &dev_attr_pointer_select_right.attr,
 285        &dev_attr_pointer_sensitivity.attr,
 286        &dev_attr_pointer_press_speed.attr,
 287        NULL
 288};
 289
 290static const struct attribute_group tpkbd_attr_group_pointer = {
 291        .attrs = tpkbd_attributes_pointer,
 292};
 293
 294static enum led_brightness tpkbd_led_brightness_get(
 295                        struct led_classdev *led_cdev)
 296{
 297        struct device *dev = led_cdev->dev->parent;
 298        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 299        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 300        int led_nr = 0;
 301
 302        if (led_cdev == &data_pointer->led_micmute)
 303                led_nr = 1;
 304
 305        return data_pointer->led_state & (1 << led_nr)
 306                                ? LED_FULL
 307                                : LED_OFF;
 308}
 309
 310static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
 311                        enum led_brightness value)
 312{
 313        struct device *dev = led_cdev->dev->parent;
 314        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 315        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 316        struct hid_report *report;
 317        int led_nr = 0;
 318
 319        if (led_cdev == &data_pointer->led_micmute)
 320                led_nr = 1;
 321
 322        if (value == LED_OFF)
 323                data_pointer->led_state &= ~(1 << led_nr);
 324        else
 325                data_pointer->led_state |= 1 << led_nr;
 326
 327        report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
 328        report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
 329        report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
 330        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 331}
 332
 333static int tpkbd_probe_tp(struct hid_device *hdev)
 334{
 335        struct device *dev = &hdev->dev;
 336        struct tpkbd_data_pointer *data_pointer;
 337        size_t name_sz = strlen(dev_name(dev)) + 16;
 338        char *name_mute, *name_micmute;
 339        int i;
 340
 341        /* Validate required reports. */
 342        for (i = 0; i < 4; i++) {
 343                if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1))
 344                        return -ENODEV;
 345        }
 346        if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
 347                return -ENODEV;
 348
 349        if (sysfs_create_group(&hdev->dev.kobj,
 350                                &tpkbd_attr_group_pointer)) {
 351                hid_warn(hdev, "Could not create sysfs group\n");
 352        }
 353
 354        data_pointer = devm_kzalloc(&hdev->dev,
 355                                    sizeof(struct tpkbd_data_pointer),
 356                                    GFP_KERNEL);
 357        if (data_pointer == NULL) {
 358                hid_err(hdev, "Could not allocate memory for driver data\n");
 359                return -ENOMEM;
 360        }
 361
 362        // set same default values as windows driver
 363        data_pointer->sensitivity = 0xa0;
 364        data_pointer->press_speed = 0x38;
 365
 366        name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
 367        name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
 368        if (name_mute == NULL || name_micmute == NULL) {
 369                hid_err(hdev, "Could not allocate memory for led data\n");
 370                return -ENOMEM;
 371        }
 372        snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
 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}
 393
 394static int tpkbd_probe(struct hid_device *hdev,
 395                const struct hid_device_id *id)
 396{
 397        int ret;
 398
 399        ret = hid_parse(hdev);
 400        if (ret) {
 401                hid_err(hdev, "hid_parse failed\n");
 402                goto err;
 403        }
 404
 405        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 406        if (ret) {
 407                hid_err(hdev, "hid_hw_start failed\n");
 408                goto err;
 409        }
 410
 411        if (hid_get_drvdata(hdev)) {
 412                hid_set_drvdata(hdev, NULL);
 413                ret = tpkbd_probe_tp(hdev);
 414                if (ret)
 415                        goto err_hid;
 416        }
 417
 418        return 0;
 419err_hid:
 420        hid_hw_stop(hdev);
 421err:
 422        return ret;
 423}
 424
 425static void tpkbd_remove_tp(struct hid_device *hdev)
 426{
 427        struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 428
 429        sysfs_remove_group(&hdev->dev.kobj,
 430                        &tpkbd_attr_group_pointer);
 431
 432        led_classdev_unregister(&data_pointer->led_micmute);
 433        led_classdev_unregister(&data_pointer->led_mute);
 434
 435        hid_set_drvdata(hdev, NULL);
 436}
 437
 438static void tpkbd_remove(struct hid_device *hdev)
 439{
 440        if (hid_get_drvdata(hdev))
 441                tpkbd_remove_tp(hdev);
 442
 443        hid_hw_stop(hdev);
 444}
 445
 446static const struct hid_device_id tpkbd_devices[] = {
 447        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
 448        { }
 449};
 450
 451MODULE_DEVICE_TABLE(hid, tpkbd_devices);
 452
 453static struct hid_driver tpkbd_driver = {
 454        .name = "lenovo_tpkbd",
 455        .id_table = tpkbd_devices,
 456        .input_mapping = tpkbd_input_mapping,
 457        .probe = tpkbd_probe,
 458        .remove = tpkbd_remove,
 459};
 460module_hid_driver(tpkbd_driver);
 461
 462MODULE_LICENSE("GPL");
 463
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.