HID: lenovo: Rename hid-lenovo-tpkbd to hid-lenovo
authorJamie Lentin <jm@lentin.co.uk>
Wed, 23 Jul 2014 22:30:45 +0000 (23:30 +0100)
committerJiri Kosina <jkosina@suse.cz>
Tue, 29 Jul 2014 09:24:46 +0000 (11:24 +0200)
Rename module and all functions within so we can add support for other
keyboards in the same file. Rename the _tp postfix to _tpkbd, to
signify functions relevant to the TP USB keyboard.

Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
Reviewed-by: Antonio Ospite <ao2@ao2.it>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Documentation/ABI/testing/sysfs-driver-hid-lenovo [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-lenovo-tpkbd [deleted file]
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-lenovo-tpkbd.c [deleted file]
drivers/hid/hid-lenovo.c [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-lenovo b/Documentation/ABI/testing/sysfs-driver-hid-lenovo
new file mode 100644 (file)
index 0000000..57b92cb
--- /dev/null
@@ -0,0 +1,38 @@
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_to_select
+Date:          July 2011
+Contact:       linux-input@vger.kernel.org
+Description:   This controls if mouse clicks should be generated if the trackpoint is quickly pressed. How fast this press has to be
+               is being controlled by press_speed.
+               Values are 0 or 1.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/dragging
+Date:          July 2011
+Contact:       linux-input@vger.kernel.org
+Description:   If this setting is enabled, it is possible to do dragging by pressing the trackpoint. This requires press_to_select to be enabled.
+               Values are 0 or 1.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/release_to_select
+Date:          July 2011
+Contact:       linux-input@vger.kernel.org
+Description:   For details regarding this setting please refer to http://www.pc.ibm.com/ww/healthycomputing/trkpntb.html
+               Values are 0 or 1.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/select_right
+Date:          July 2011
+Contact:       linux-input@vger.kernel.org
+Description:   This setting controls if the mouse click events generated by pressing the trackpoint (if press_to_select is enabled) generate
+               a left or right mouse button click.
+               Values are 0 or 1.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/sensitivity
+Date:          July 2011
+Contact:       linux-input@vger.kernel.org
+Description:   This file contains the trackpoint sensitivity.
+               Values are decimal integers from 1 (lowest sensitivity) to 255 (highest sensitivity).
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_speed
+Date:          July 2011
+Contact:       linux-input@vger.kernel.org
+Description:   This setting controls how fast the trackpoint needs to be pressed to generate a mouse click if press_to_select is enabled.
+               Values are decimal integers from 1 (slowest) to 255 (fastest).
+
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-lenovo-tpkbd b/Documentation/ABI/testing/sysfs-driver-hid-lenovo-tpkbd
deleted file mode 100644 (file)
index 57b92cb..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_to_select
-Date:          July 2011
-Contact:       linux-input@vger.kernel.org
-Description:   This controls if mouse clicks should be generated if the trackpoint is quickly pressed. How fast this press has to be
-               is being controlled by press_speed.
-               Values are 0 or 1.
-
-What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/dragging
-Date:          July 2011
-Contact:       linux-input@vger.kernel.org
-Description:   If this setting is enabled, it is possible to do dragging by pressing the trackpoint. This requires press_to_select to be enabled.
-               Values are 0 or 1.
-
-What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/release_to_select
-Date:          July 2011
-Contact:       linux-input@vger.kernel.org
-Description:   For details regarding this setting please refer to http://www.pc.ibm.com/ww/healthycomputing/trkpntb.html
-               Values are 0 or 1.
-
-What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/select_right
-Date:          July 2011
-Contact:       linux-input@vger.kernel.org
-Description:   This setting controls if the mouse click events generated by pressing the trackpoint (if press_to_select is enabled) generate
-               a left or right mouse button click.
-               Values are 0 or 1.
-
-What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/sensitivity
-Date:          July 2011
-Contact:       linux-input@vger.kernel.org
-Description:   This file contains the trackpoint sensitivity.
-               Values are decimal integers from 1 (lowest sensitivity) to 255 (highest sensitivity).
-
-What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/press_speed
-Date:          July 2011
-Contact:       linux-input@vger.kernel.org
-Description:   This setting controls how fast the trackpoint needs to be pressed to generate a mouse click if press_to_select is enabled.
-               Values are decimal integers from 1 (slowest) to 255 (fastest).
-
index 5e79c6a..687d7a1 100644 (file)
@@ -331,18 +331,18 @@ config HID_LCPOWER
        ---help---
        Support for LC-Power RC1000MCE RF remote control.
 
-config HID_LENOVO_TPKBD
-       tristate "Lenovo ThinkPad USB Keyboard with TrackPoint"
+config HID_LENOVO
+       tristate "Lenovo / Thinkpad devices"
        depends on HID
        select NEW_LEDS
        select LEDS_CLASS
        ---help---
-       Support for the Lenovo ThinkPad USB Keyboard with TrackPoint.
+       Support for Lenovo devices that are not fully compliant with HID standard.
 
-       Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint
-       and would like to use device-specific features like changing the
-       sensitivity of the trackpoint, using the microphone mute button or
-       controlling the mute and microphone mute LEDs.
+       Say Y if you want support for the non-compliant features of the Lenovo
+       Thinkpad standalone keyboards, e.g:
+       - ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint
+         configuration)
 
 config HID_LOGITECH
        tristate "Logitech devices" if EXPERT
index a6fa6ba..5e96be3 100644 (file)
@@ -59,7 +59,7 @@ obj-$(CONFIG_HID_KENSINGTON)  += hid-kensington.o
 obj-$(CONFIG_HID_KEYTOUCH)     += hid-keytouch.o
 obj-$(CONFIG_HID_KYE)          += hid-kye.o
 obj-$(CONFIG_HID_LCPOWER)       += hid-lcpower.o
-obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o
+obj-$(CONFIG_HID_LENOVO)       += hid-lenovo.o
 obj-$(CONFIG_HID_LOGITECH)     += hid-logitech.o
 obj-$(CONFIG_HID_LOGITECH_DJ)  += hid-logitech-dj.o
 obj-$(CONFIG_HID_MAGICMOUSE)    += hid-magicmouse.o
index 8ed66fd..55841bd 100644 (file)
@@ -1796,7 +1796,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
-#if IS_ENABLED(CONFIG_HID_LENOVO_TPKBD)
+#if IS_ENABLED(CONFIG_HID_LENOVO)
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
 #endif
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c
deleted file mode 100644 (file)
index 2d25b6c..0000000
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- *  HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint
- *
- *  Copyright (c) 2012 Bernhard Seibold
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- */
-
-#include <linux/module.h>
-#include <linux/sysfs.h>
-#include <linux/device.h>
-#include <linux/hid.h>
-#include <linux/input.h>
-#include <linux/leds.h>
-
-#include "hid-ids.h"
-
-/* This is only used for the trackpoint part of the driver, hence _tp */
-struct tpkbd_data_pointer {
-       int led_state;
-       struct led_classdev led_mute;
-       struct led_classdev led_micmute;
-       int press_to_select;
-       int dragging;
-       int release_to_select;
-       int select_right;
-       int sensitivity;
-       int press_speed;
-};
-
-#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
-
-static int tpkbd_input_mapping(struct hid_device *hdev,
-               struct hid_input *hi, struct hid_field *field,
-               struct hid_usage *usage, unsigned long **bit, int *max)
-{
-       if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
-               /* mark the device as pointer */
-               hid_set_drvdata(hdev, (void *)1);
-               map_key_clear(KEY_MICMUTE);
-               return 1;
-       }
-       return 0;
-}
-
-#undef map_key_clear
-
-static int tpkbd_features_set(struct hid_device *hdev)
-{
-       struct hid_report *report;
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
-
-       report->field[0]->value[0]  = data_pointer->press_to_select   ? 0x01 : 0x02;
-       report->field[0]->value[0] |= data_pointer->dragging          ? 0x04 : 0x08;
-       report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
-       report->field[0]->value[0] |= data_pointer->select_right      ? 0x80 : 0x40;
-       report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
-       report->field[2]->value[0] = data_pointer->sensitivity;
-       report->field[3]->value[0] = data_pointer->press_speed;
-
-       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
-       return 0;
-}
-
-static ssize_t pointer_press_to_select_show(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
-}
-
-static ssize_t pointer_press_to_select_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       int value;
-
-       if (kstrtoint(buf, 10, &value))
-               return -EINVAL;
-       if (value < 0 || value > 1)
-               return -EINVAL;
-
-       data_pointer->press_to_select = value;
-       tpkbd_features_set(hdev);
-
-       return count;
-}
-
-static ssize_t pointer_dragging_show(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
-}
-
-static ssize_t pointer_dragging_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       int value;
-
-       if (kstrtoint(buf, 10, &value))
-               return -EINVAL;
-       if (value < 0 || value > 1)
-               return -EINVAL;
-
-       data_pointer->dragging = value;
-       tpkbd_features_set(hdev);
-
-       return count;
-}
-
-static ssize_t pointer_release_to_select_show(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
-}
-
-static ssize_t pointer_release_to_select_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       int value;
-
-       if (kstrtoint(buf, 10, &value))
-               return -EINVAL;
-       if (value < 0 || value > 1)
-               return -EINVAL;
-
-       data_pointer->release_to_select = value;
-       tpkbd_features_set(hdev);
-
-       return count;
-}
-
-static ssize_t pointer_select_right_show(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
-}
-
-static ssize_t pointer_select_right_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       int value;
-
-       if (kstrtoint(buf, 10, &value))
-               return -EINVAL;
-       if (value < 0 || value > 1)
-               return -EINVAL;
-
-       data_pointer->select_right = value;
-       tpkbd_features_set(hdev);
-
-       return count;
-}
-
-static ssize_t pointer_sensitivity_show(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       return snprintf(buf, PAGE_SIZE, "%u\n",
-               data_pointer->sensitivity);
-}
-
-static ssize_t pointer_sensitivity_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       int value;
-
-       if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
-               return -EINVAL;
-
-       data_pointer->sensitivity = value;
-       tpkbd_features_set(hdev);
-
-       return count;
-}
-
-static ssize_t pointer_press_speed_show(struct device *dev,
-               struct device_attribute *attr,
-               char *buf)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       return snprintf(buf, PAGE_SIZE, "%u\n",
-               data_pointer->press_speed);
-}
-
-static ssize_t pointer_press_speed_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buf,
-               size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       int value;
-
-       if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
-               return -EINVAL;
-
-       data_pointer->press_speed = value;
-       tpkbd_features_set(hdev);
-
-       return count;
-}
-
-static struct device_attribute dev_attr_pointer_press_to_select =
-       __ATTR(press_to_select, S_IWUSR | S_IRUGO,
-                       pointer_press_to_select_show,
-                       pointer_press_to_select_store);
-
-static struct device_attribute dev_attr_pointer_dragging =
-       __ATTR(dragging, S_IWUSR | S_IRUGO,
-                       pointer_dragging_show,
-                       pointer_dragging_store);
-
-static struct device_attribute dev_attr_pointer_release_to_select =
-       __ATTR(release_to_select, S_IWUSR | S_IRUGO,
-                       pointer_release_to_select_show,
-                       pointer_release_to_select_store);
-
-static struct device_attribute dev_attr_pointer_select_right =
-       __ATTR(select_right, S_IWUSR | S_IRUGO,
-                       pointer_select_right_show,
-                       pointer_select_right_store);
-
-static struct device_attribute dev_attr_pointer_sensitivity =
-       __ATTR(sensitivity, S_IWUSR | S_IRUGO,
-                       pointer_sensitivity_show,
-                       pointer_sensitivity_store);
-
-static struct device_attribute dev_attr_pointer_press_speed =
-       __ATTR(press_speed, S_IWUSR | S_IRUGO,
-                       pointer_press_speed_show,
-                       pointer_press_speed_store);
-
-static struct attribute *tpkbd_attributes_pointer[] = {
-       &dev_attr_pointer_press_to_select.attr,
-       &dev_attr_pointer_dragging.attr,
-       &dev_attr_pointer_release_to_select.attr,
-       &dev_attr_pointer_select_right.attr,
-       &dev_attr_pointer_sensitivity.attr,
-       &dev_attr_pointer_press_speed.attr,
-       NULL
-};
-
-static const struct attribute_group tpkbd_attr_group_pointer = {
-       .attrs = tpkbd_attributes_pointer,
-};
-
-static enum led_brightness tpkbd_led_brightness_get(
-                       struct led_classdev *led_cdev)
-{
-       struct device *dev = led_cdev->dev->parent;
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       int led_nr = 0;
-
-       if (led_cdev == &data_pointer->led_micmute)
-               led_nr = 1;
-
-       return data_pointer->led_state & (1 << led_nr)
-                               ? LED_FULL
-                               : LED_OFF;
-}
-
-static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
-                       enum led_brightness value)
-{
-       struct device *dev = led_cdev->dev->parent;
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-       struct hid_report *report;
-       int led_nr = 0;
-
-       if (led_cdev == &data_pointer->led_micmute)
-               led_nr = 1;
-
-       if (value == LED_OFF)
-               data_pointer->led_state &= ~(1 << led_nr);
-       else
-               data_pointer->led_state |= 1 << led_nr;
-
-       report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
-       report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
-       report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
-       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
-}
-
-static int tpkbd_probe_tp(struct hid_device *hdev)
-{
-       struct device *dev = &hdev->dev;
-       struct tpkbd_data_pointer *data_pointer;
-       size_t name_sz = strlen(dev_name(dev)) + 16;
-       char *name_mute, *name_micmute;
-       int i;
-
-       /* Validate required reports. */
-       for (i = 0; i < 4; i++) {
-               if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1))
-                       return -ENODEV;
-       }
-       if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
-               return -ENODEV;
-
-       if (sysfs_create_group(&hdev->dev.kobj,
-                               &tpkbd_attr_group_pointer)) {
-               hid_warn(hdev, "Could not create sysfs group\n");
-       }
-
-       data_pointer = devm_kzalloc(&hdev->dev,
-                                   sizeof(struct tpkbd_data_pointer),
-                                   GFP_KERNEL);
-       if (data_pointer == NULL) {
-               hid_err(hdev, "Could not allocate memory for driver data\n");
-               return -ENOMEM;
-       }
-
-       // set same default values as windows driver
-       data_pointer->sensitivity = 0xa0;
-       data_pointer->press_speed = 0x38;
-
-       name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
-       name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
-       if (name_mute == NULL || name_micmute == NULL) {
-               hid_err(hdev, "Could not allocate memory for led data\n");
-               return -ENOMEM;
-       }
-       snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
-       snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
-
-       hid_set_drvdata(hdev, data_pointer);
-
-       data_pointer->led_mute.name = name_mute;
-       data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get;
-       data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set;
-       data_pointer->led_mute.dev = dev;
-       led_classdev_register(dev, &data_pointer->led_mute);
-
-       data_pointer->led_micmute.name = name_micmute;
-       data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get;
-       data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set;
-       data_pointer->led_micmute.dev = dev;
-       led_classdev_register(dev, &data_pointer->led_micmute);
-
-       tpkbd_features_set(hdev);
-
-       return 0;
-}
-
-static int tpkbd_probe(struct hid_device *hdev,
-               const struct hid_device_id *id)
-{
-       int ret;
-
-       ret = hid_parse(hdev);
-       if (ret) {
-               hid_err(hdev, "hid_parse failed\n");
-               goto err;
-       }
-
-       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-       if (ret) {
-               hid_err(hdev, "hid_hw_start failed\n");
-               goto err;
-       }
-
-       if (hid_get_drvdata(hdev)) {
-               hid_set_drvdata(hdev, NULL);
-               ret = tpkbd_probe_tp(hdev);
-               if (ret)
-                       goto err_hid;
-       }
-
-       return 0;
-err_hid:
-       hid_hw_stop(hdev);
-err:
-       return ret;
-}
-
-static void tpkbd_remove_tp(struct hid_device *hdev)
-{
-       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
-
-       sysfs_remove_group(&hdev->dev.kobj,
-                       &tpkbd_attr_group_pointer);
-
-       led_classdev_unregister(&data_pointer->led_micmute);
-       led_classdev_unregister(&data_pointer->led_mute);
-
-       hid_set_drvdata(hdev, NULL);
-}
-
-static void tpkbd_remove(struct hid_device *hdev)
-{
-       if (hid_get_drvdata(hdev))
-               tpkbd_remove_tp(hdev);
-
-       hid_hw_stop(hdev);
-}
-
-static const struct hid_device_id tpkbd_devices[] = {
-       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
-       { }
-};
-
-MODULE_DEVICE_TABLE(hid, tpkbd_devices);
-
-static struct hid_driver tpkbd_driver = {
-       .name = "lenovo_tpkbd",
-       .id_table = tpkbd_devices,
-       .input_mapping = tpkbd_input_mapping,
-       .probe = tpkbd_probe,
-       .remove = tpkbd_remove,
-};
-module_hid_driver(tpkbd_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c
new file mode 100644 (file)
index 0000000..0320b96
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ *  HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint
+ *
+ *  Copyright (c) 2012 Bernhard Seibold
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+
+#include "hid-ids.h"
+
+struct lenovo_drvdata_tpkbd {
+       int led_state;
+       struct led_classdev led_mute;
+       struct led_classdev led_micmute;
+       int press_to_select;
+       int dragging;
+       int release_to_select;
+       int select_right;
+       int sensitivity;
+       int press_speed;
+};
+
+#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
+
+static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       if (usage->hid == (HID_UP_BUTTON | 0x0010)) {
+               /* mark the device as pointer */
+               hid_set_drvdata(hdev, (void *)1);
+               map_key_clear(KEY_MICMUTE);
+               return 1;
+       }
+       return 0;
+}
+
+#undef map_key_clear
+
+static int lenovo_features_set_tpkbd(struct hid_device *hdev)
+{
+       struct hid_report *report;
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
+
+       report->field[0]->value[0]  = data_pointer->press_to_select   ? 0x01 : 0x02;
+       report->field[0]->value[0] |= data_pointer->dragging          ? 0x04 : 0x08;
+       report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20;
+       report->field[0]->value[0] |= data_pointer->select_right      ? 0x80 : 0x40;
+       report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver
+       report->field[2]->value[0] = data_pointer->sensitivity;
+       report->field[3]->value[0] = data_pointer->press_speed;
+
+       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+       return 0;
+}
+
+static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
+}
+
+static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       int value;
+
+       if (kstrtoint(buf, 10, &value))
+               return -EINVAL;
+       if (value < 0 || value > 1)
+               return -EINVAL;
+
+       data_pointer->press_to_select = value;
+       lenovo_features_set_tpkbd(hdev);
+
+       return count;
+}
+
+static ssize_t attr_dragging_show_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
+}
+
+static ssize_t attr_dragging_store_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       int value;
+
+       if (kstrtoint(buf, 10, &value))
+               return -EINVAL;
+       if (value < 0 || value > 1)
+               return -EINVAL;
+
+       data_pointer->dragging = value;
+       lenovo_features_set_tpkbd(hdev);
+
+       return count;
+}
+
+static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
+}
+
+static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       int value;
+
+       if (kstrtoint(buf, 10, &value))
+               return -EINVAL;
+       if (value < 0 || value > 1)
+               return -EINVAL;
+
+       data_pointer->release_to_select = value;
+       lenovo_features_set_tpkbd(hdev);
+
+       return count;
+}
+
+static ssize_t attr_select_right_show_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
+}
+
+static ssize_t attr_select_right_store_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       int value;
+
+       if (kstrtoint(buf, 10, &value))
+               return -EINVAL;
+       if (value < 0 || value > 1)
+               return -EINVAL;
+
+       data_pointer->select_right = value;
+       lenovo_features_set_tpkbd(hdev);
+
+       return count;
+}
+
+static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               data_pointer->sensitivity);
+}
+
+static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       int value;
+
+       if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
+               return -EINVAL;
+
+       data_pointer->sensitivity = value;
+       lenovo_features_set_tpkbd(hdev);
+
+       return count;
+}
+
+static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+               data_pointer->press_speed);
+}
+
+static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf,
+               size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       int value;
+
+       if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
+               return -EINVAL;
+
+       data_pointer->press_speed = value;
+       lenovo_features_set_tpkbd(hdev);
+
+       return count;
+}
+
+static struct device_attribute dev_attr_press_to_select_tpkbd =
+       __ATTR(press_to_select, S_IWUSR | S_IRUGO,
+                       attr_press_to_select_show_tpkbd,
+                       attr_press_to_select_store_tpkbd);
+
+static struct device_attribute dev_attr_dragging_tpkbd =
+       __ATTR(dragging, S_IWUSR | S_IRUGO,
+                       attr_dragging_show_tpkbd,
+                       attr_dragging_store_tpkbd);
+
+static struct device_attribute dev_attr_release_to_select_tpkbd =
+       __ATTR(release_to_select, S_IWUSR | S_IRUGO,
+                       attr_release_to_select_show_tpkbd,
+                       attr_release_to_select_store_tpkbd);
+
+static struct device_attribute dev_attr_select_right_tpkbd =
+       __ATTR(select_right, S_IWUSR | S_IRUGO,
+                       attr_select_right_show_tpkbd,
+                       attr_select_right_store_tpkbd);
+
+static struct device_attribute dev_attr_sensitivity_tpkbd =
+       __ATTR(sensitivity, S_IWUSR | S_IRUGO,
+                       attr_sensitivity_show_tpkbd,
+                       attr_sensitivity_store_tpkbd);
+
+static struct device_attribute dev_attr_press_speed_tpkbd =
+       __ATTR(press_speed, S_IWUSR | S_IRUGO,
+                       attr_press_speed_show_tpkbd,
+                       attr_press_speed_store_tpkbd);
+
+static struct attribute *lenovo_attributes_tpkbd[] = {
+       &dev_attr_press_to_select_tpkbd.attr,
+       &dev_attr_dragging_tpkbd.attr,
+       &dev_attr_release_to_select_tpkbd.attr,
+       &dev_attr_select_right_tpkbd.attr,
+       &dev_attr_sensitivity_tpkbd.attr,
+       &dev_attr_press_speed_tpkbd.attr,
+       NULL
+};
+
+static const struct attribute_group lenovo_attr_group_tpkbd = {
+       .attrs = lenovo_attributes_tpkbd,
+};
+
+static enum led_brightness lenovo_led_brightness_get_tpkbd(
+                       struct led_classdev *led_cdev)
+{
+       struct device *dev = led_cdev->dev->parent;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       int led_nr = 0;
+
+       if (led_cdev == &data_pointer->led_micmute)
+               led_nr = 1;
+
+       return data_pointer->led_state & (1 << led_nr)
+                               ? LED_FULL
+                               : LED_OFF;
+}
+
+static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
+                       enum led_brightness value)
+{
+       struct device *dev = led_cdev->dev->parent;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct hid_report *report;
+       int led_nr = 0;
+
+       if (led_cdev == &data_pointer->led_micmute)
+               led_nr = 1;
+
+       if (value == LED_OFF)
+               data_pointer->led_state &= ~(1 << led_nr);
+       else
+               data_pointer->led_state |= 1 << led_nr;
+
+       report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
+       report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
+       report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
+       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+}
+
+static int lenovo_probe_tpkbd(struct hid_device *hdev)
+{
+       struct device *dev = &hdev->dev;
+       struct lenovo_drvdata_tpkbd *data_pointer;
+       size_t name_sz = strlen(dev_name(dev)) + 16;
+       char *name_mute, *name_micmute;
+       int i;
+
+       /* Validate required reports. */
+       for (i = 0; i < 4; i++) {
+               if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1))
+                       return -ENODEV;
+       }
+       if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
+               return -ENODEV;
+
+       if (sysfs_create_group(&hdev->dev.kobj,
+                               &lenovo_attr_group_tpkbd)) {
+               hid_warn(hdev, "Could not create sysfs group\n");
+       }
+
+       data_pointer = devm_kzalloc(&hdev->dev,
+                                   sizeof(struct lenovo_drvdata_tpkbd),
+                                   GFP_KERNEL);
+       if (data_pointer == NULL) {
+               hid_err(hdev, "Could not allocate memory for driver data\n");
+               return -ENOMEM;
+       }
+
+       // set same default values as windows driver
+       data_pointer->sensitivity = 0xa0;
+       data_pointer->press_speed = 0x38;
+
+       name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+       name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+       if (name_mute == NULL || name_micmute == NULL) {
+               hid_err(hdev, "Could not allocate memory for led data\n");
+               return -ENOMEM;
+       }
+       snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
+       snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
+
+       hid_set_drvdata(hdev, data_pointer);
+
+       data_pointer->led_mute.name = name_mute;
+       data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;
+       data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;
+       data_pointer->led_mute.dev = dev;
+       led_classdev_register(dev, &data_pointer->led_mute);
+
+       data_pointer->led_micmute.name = name_micmute;
+       data_pointer->led_micmute.brightness_get =
+               lenovo_led_brightness_get_tpkbd;
+       data_pointer->led_micmute.brightness_set =
+               lenovo_led_brightness_set_tpkbd;
+       data_pointer->led_micmute.dev = dev;
+       led_classdev_register(dev, &data_pointer->led_micmute);
+
+       lenovo_features_set_tpkbd(hdev);
+
+       return 0;
+}
+
+static int lenovo_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "hid_parse failed\n");
+               goto err;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               hid_err(hdev, "hid_hw_start failed\n");
+               goto err;
+       }
+
+       if (hid_get_drvdata(hdev)) {
+               hid_set_drvdata(hdev, NULL);
+               ret = lenovo_probe_tpkbd(hdev);
+               if (ret)
+                       goto err_hid;
+       }
+
+       return 0;
+err_hid:
+       hid_hw_stop(hdev);
+err:
+       return ret;
+}
+
+static void lenovo_remove_tpkbd(struct hid_device *hdev)
+{
+       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+
+       sysfs_remove_group(&hdev->dev.kobj,
+                       &lenovo_attr_group_tpkbd);
+
+       led_classdev_unregister(&data_pointer->led_micmute);
+       led_classdev_unregister(&data_pointer->led_mute);
+
+       hid_set_drvdata(hdev, NULL);
+}
+
+static void lenovo_remove(struct hid_device *hdev)
+{
+       if (hid_get_drvdata(hdev))
+               lenovo_remove_tpkbd(hdev);
+
+       hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id lenovo_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(hid, lenovo_devices);
+
+static struct hid_driver lenovo_driver = {
+       .name = "lenovo",
+       .id_table = lenovo_devices,
+       .input_mapping = lenovo_input_mapping_tpkbd,
+       .probe = lenovo_probe,
+       .remove = lenovo_remove,
+};
+module_hid_driver(lenovo_driver);
+
+MODULE_LICENSE("GPL");