summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-08-20 15:59:01 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-08-20 15:59:01 -0700
commit7a324b3f0535ceb0f6676fa20ca2a7b6213008cb (patch)
tree4c03a2f9382ee8264ddbe79a60293f825364fac3 /drivers/hid
parent61c4fc1eaf736344904767d201b0d4f7a2ebaf79 (diff)
parent1429b47bfe98e63421d207aaae8038809c3f90ce (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina: - touch_max detection improvements and quirk handling fixes in wacom driver from Jason Gerecke and Ping Cheng - Palm rejection from Dmitry Torokhov and _dial support from Benjamin Tissoires for hid-multitouch driver - Low voltage support for i2c-hid driver from Stephen Boyd - Guitar-Hero support from Nicolas Adenis-Lamarre - other assorted small fixes and device ID additions * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (40 commits) HID: intel_ish-hid: tx_buf memory leak on probe/remove HID: intel-ish-hid: Prevent loading of driver on Mehlow HID: cougar: Add support for the Cougar 500k Gaming Keyboard HID: cougar: make compare_device_paths reusable HID: intel-ish-hid: remove redundant variable num_frags HID: multitouch: handle palm for touchscreens HID: multitouch: touchscreens also use confidence reports HID: multitouch: report MT_TOOL_PALM for non-confident touches HID: microsoft: support the Surface Dial HID: core: do not upper bound the collection stack HID: input: enable Totem on the Dell Canvas 27 HID: multitouch: remove one copy of values HID: multitouch: ditch mt_report_id HID: multitouch: store a per application quirks value HID: multitouch: Store per collection multitouch data HID: multitouch: make sure the static list of class is not changed input: add MT_TOOL_DIAL HID: elan: Add support for touchpad on the Toshiba Click Mini L9W HID: elan: Add USB-id for HP x2 10-n000nd touchpad HID: elan: Add a flag for selecting if the touchpad has a LED ...
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig10
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c40
-rw-r--r--drivers/hid/hid-cougar.c312
-rw-r--r--drivers/hid/hid-elan.c235
-rw-r--r--drivers/hid/hid-ids.h5
-rw-r--r--drivers/hid/hid-input.c3
-rw-r--r--drivers/hid/hid-microsoft.c49
-rw-r--r--drivers/hid/hid-multitouch.c989
-rw-r--r--drivers/hid/hid-ntrig.c2
-rw-r--r--drivers/hid/hid-redragon.c26
-rw-r--r--drivers/hid/hid-sony.c164
-rw-r--r--drivers/hid/hid-wiimote-core.c14
-rw-r--r--drivers/hid/hid-wiimote-modules.c440
-rw-r--r--drivers/hid/hid-wiimote.h3
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c57
-rw-r--r--drivers/hid/intel-ish-hid/ipc/ipc.c9
-rw-r--r--drivers/hid/intel-ish-hid/ipc/pci-ish.c13
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/hbm.c2
-rw-r--r--drivers/hid/usbhid/hid-core.c7
-rw-r--r--drivers/hid/wacom_sys.c123
-rw-r--r--drivers/hid/wacom_wac.c20
22 files changed, 1795 insertions, 729 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index a49a10437c40..61e1953ff921 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -207,6 +207,16 @@ config HID_CORSAIR
- Vengeance K90
- Scimitar PRO RGB
+config HID_COUGAR
+ tristate "Cougar devices"
+ depends on HID
+ help
+ Support for Cougar devices that are not fully compliant with the
+ HID standard.
+
+ Supported devices:
+ - Cougar 500k Gaming Keyboard
+
config HID_PRODIKEYS
tristate "Prodikeys PC-MIDI Keyboard support"
depends on HID && SND
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 511e1cbff768..bd7ac53b75c5 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o
obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o
+obj-$(CONFIG_HID_COUGAR) += hid-cougar.o
obj-$(CONFIG_HID_CP2112) += hid-cp2112.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 3942ee61bd1c..3da354af7a0a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -128,9 +128,19 @@ static int open_collection(struct hid_parser *parser, unsigned type)
usage = parser->local.usage[0];
- if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
- hid_err(parser->device, "collection stack overflow\n");
- return -EINVAL;
+ if (parser->collection_stack_ptr == parser->collection_stack_size) {
+ unsigned int *collection_stack;
+ unsigned int new_size = parser->collection_stack_size +
+ HID_COLLECTION_STACK_SIZE;
+
+ collection_stack = krealloc(parser->collection_stack,
+ new_size * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!collection_stack)
+ return -ENOMEM;
+
+ parser->collection_stack = collection_stack;
+ parser->collection_stack_size = new_size;
}
if (parser->device->maxcollection == parser->device->collection_size) {
@@ -840,6 +850,7 @@ static int hid_scan_report(struct hid_device *hid)
break;
}
+ kfree(parser->collection_stack);
vfree(parser);
return 0;
}
@@ -1939,6 +1950,29 @@ static int hid_bus_match(struct device *dev, struct device_driver *drv)
return hid_match_device(hdev, hdrv) != NULL;
}
+/**
+ * hid_compare_device_paths - check if both devices share the same path
+ * @hdev_a: hid device
+ * @hdev_b: hid device
+ * @separator: char to use as separator
+ *
+ * Check if two devices share the same path up to the last occurrence of
+ * the separator char. Both paths must exist (i.e., zero-length paths
+ * don't match).
+ */
+bool hid_compare_device_paths(struct hid_device *hdev_a,
+ struct hid_device *hdev_b, char separator)
+{
+ int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys;
+ int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys;
+
+ if (n1 != n2 || n1 <= 0 || n2 <= 0)
+ return false;
+
+ return !strncmp(hdev_a->phys, hdev_b->phys, n1);
+}
+EXPORT_SYMBOL_GPL(hid_compare_device_paths);
+
static int hid_device_probe(struct device *dev)
{
struct hid_driver *hdrv = to_hid_driver(dev->driver);
diff --git a/drivers/hid/hid-cougar.c b/drivers/hid/hid-cougar.c
new file mode 100644
index 000000000000..ad2e87de7dc5
--- /dev/null
+++ b/drivers/hid/hid-cougar.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * HID driver for Cougar 500k Gaming Keyboard
+ *
+ * Copyright (c) 2018 Daniel M. Lambea <dmlambea@gmail.com>
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+MODULE_AUTHOR("Daniel M. Lambea <dmlambea@gmail.com>");
+MODULE_DESCRIPTION("Cougar 500k Gaming Keyboard");
+MODULE_LICENSE("GPL");
+MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18");
+
+static int cougar_g6_is_space = 1;
+module_param_named(g6_is_space, cougar_g6_is_space, int, 0600);
+MODULE_PARM_DESC(g6_is_space,
+ "If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)");
+
+
+#define COUGAR_VENDOR_USAGE 0xff00ff00
+
+#define COUGAR_FIELD_CODE 1
+#define COUGAR_FIELD_ACTION 2
+
+#define COUGAR_KEY_G1 0x83
+#define COUGAR_KEY_G2 0x84
+#define COUGAR_KEY_G3 0x85
+#define COUGAR_KEY_G4 0x86
+#define COUGAR_KEY_G5 0x87
+#define COUGAR_KEY_G6 0x78
+#define COUGAR_KEY_FN 0x0d
+#define COUGAR_KEY_MR 0x6f
+#define COUGAR_KEY_M1 0x80
+#define COUGAR_KEY_M2 0x81
+#define COUGAR_KEY_M3 0x82
+#define COUGAR_KEY_LEDS 0x67
+#define COUGAR_KEY_LOCK 0x6e
+
+
+/* Default key mappings. The special key COUGAR_KEY_G6 is defined first
+ * because it is more frequent to use the spacebar rather than any other
+ * special keys. Depending on the value of the parameter 'g6_is_space',
+ * the mapping will be updated in the probe function.
+ */
+static unsigned char cougar_mapping[][2] = {
+ { COUGAR_KEY_G6, KEY_SPACE },
+ { COUGAR_KEY_G1, KEY_F13 },
+ { COUGAR_KEY_G2, KEY_F14 },
+ { COUGAR_KEY_G3, KEY_F15 },
+ { COUGAR_KEY_G4, KEY_F16 },
+ { COUGAR_KEY_G5, KEY_F17 },
+ { COUGAR_KEY_LOCK, KEY_SCREENLOCK },
+/* The following keys are handled by the hardware itself, so no special
+ * treatment is required:
+ { COUGAR_KEY_FN, KEY_RESERVED },
+ { COUGAR_KEY_MR, KEY_RESERVED },
+ { COUGAR_KEY_M1, KEY_RESERVED },
+ { COUGAR_KEY_M2, KEY_RESERVED },
+ { COUGAR_KEY_M3, KEY_RESERVED },
+ { COUGAR_KEY_LEDS, KEY_RESERVED },
+*/
+ { 0, 0 },
+};
+
+struct cougar_shared {
+ struct list_head list;
+ struct kref kref;
+ bool enabled;
+ struct hid_device *dev;
+ struct input_dev *input;
+};
+
+struct cougar {
+ bool special_intf;
+ struct cougar_shared *shared;
+};
+
+static LIST_HEAD(cougar_udev_list);
+static DEFINE_MUTEX(cougar_udev_list_lock);
+
+static void cougar_fix_g6_mapping(struct hid_device *hdev)
+{
+ int i;
+
+ for (i = 0; cougar_mapping[i][0]; i++) {
+ if (cougar_mapping[i][0] == COUGAR_KEY_G6) {
+ cougar_mapping[i][1] =
+ cougar_g6_is_space ? KEY_SPACE : KEY_F18;
+ hid_info(hdev, "G6 mapped to %s\n",
+ cougar_g6_is_space ? "space" : "F18");
+ return;
+ }
+ }
+ hid_warn(hdev, "no mapping defined for G6/spacebar");
+}
+
+/*
+ * Constant-friendly rdesc fixup for mouse interface
+ */
+static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ if (rdesc[2] == 0x09 && rdesc[3] == 0x02 &&
+ (rdesc[115] | rdesc[116] << 8) >= HID_MAX_USAGES) {
+ hid_info(hdev,
+ "usage count exceeds max: fixing up report descriptor\n");
+ rdesc[115] = ((HID_MAX_USAGES-1) & 0xff);
+ rdesc[116] = ((HID_MAX_USAGES-1) >> 8);
+ }
+ return rdesc;
+}
+
+static struct cougar_shared *cougar_get_shared_data(struct hid_device *hdev)
+{
+ struct cougar_shared *shared;
+
+ /* Try to find an already-probed interface from the same device */
+ list_for_each_entry(shared, &cougar_udev_list, list) {
+ if (hid_compare_device_paths(hdev, shared->dev, '/')) {
+ kref_get(&shared->kref);
+ return shared;
+ }
+ }
+ return NULL;
+}
+
+static void cougar_release_shared_data(struct kref *kref)
+{
+ struct cougar_shared *shared = container_of(kref,
+ struct cougar_shared, kref);
+
+ mutex_lock(&cougar_udev_list_lock);
+ list_del(&shared->list);
+ mutex_unlock(&cougar_udev_list_lock);
+
+ kfree(shared);
+}
+
+static void cougar_remove_shared_data(void *resource)
+{
+ struct cougar *cougar = resource;
+
+ if (cougar->shared) {
+ kref_put(&cougar->shared->kref, cougar_release_shared_data);
+ cougar->shared = NULL;
+ }
+}
+
+/*
+ * Bind the device group's shared data to this cougar struct.
+ * If no shared data exists for this group, create and initialize it.
+ */
+static int cougar_bind_shared_data(struct hid_device *hdev, struct cougar *cougar)
+{
+ struct cougar_shared *shared;
+ int error = 0;
+
+ mutex_lock(&cougar_udev_list_lock);
+
+ shared = cougar_get_shared_data(hdev);
+ if (!shared) {
+ shared = kzalloc(sizeof(*shared), GFP_KERNEL);
+ if (!shared) {
+ error = -ENOMEM;
+ goto out;
+ }
+
+ kref_init(&shared->kref);
+ shared->dev = hdev;
+ list_add_tail(&shared->list, &cougar_udev_list);
+ }
+
+ cougar->shared = shared;
+
+ error = devm_add_action(&hdev->dev, cougar_remove_shared_data, cougar);
+ if (error) {
+ mutex_unlock(&cougar_udev_list_lock);
+ cougar_remove_shared_data(cougar);
+ return error;
+ }
+
+out:
+ mutex_unlock(&cougar_udev_list_lock);
+ return error;
+}
+
+static int cougar_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct cougar *cougar;
+ struct hid_input *next, *hidinput = NULL;
+ unsigned int connect_mask;
+ int error;
+
+ cougar = devm_kzalloc(&hdev->dev, sizeof(*cougar), GFP_KERNEL);
+ if (!cougar)
+ return -ENOMEM;
+ hid_set_drvdata(hdev, cougar);
+
+ error = hid_parse(hdev);
+ if (error) {
+ hid_err(hdev, "parse failed\n");
+ goto fail;
+ }
+
+ if (hdev->collection->usage == COUGAR_VENDOR_USAGE) {
+ cougar->special_intf = true;
+ connect_mask = HID_CONNECT_HIDRAW;
+ } else
+ connect_mask = HID_CONNECT_DEFAULT;
+
+ error = hid_hw_start(hdev, connect_mask);
+ if (error) {
+ hid_err(hdev, "hw start failed\n");
+ goto fail;
+ }
+
+ error = cougar_bind_shared_data(hdev, cougar);
+ if (error)
+ goto fail_stop_and_cleanup;
+
+ /* The custom vendor interface will use the hid_input registered
+ * for the keyboard interface, in order to send translated key codes
+ * to it.
+ */
+ if (hdev->collection->usage == HID_GD_KEYBOARD) {
+ cougar_fix_g6_mapping(hdev);
+ list_for_each_entry_safe(hidinput, next, &hdev->inputs, list) {
+ if (hidinput->registered && hidinput->input != NULL) {
+ cougar->shared->input = hidinput->input;
+ cougar->shared->enabled = true;
+ break;
+ }
+ }
+ } else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) {
+ error = hid_hw_open(hdev);
+ if (error)
+ goto fail_stop_and_cleanup;
+ }
+ return 0;
+
+fail_stop_and_cleanup:
+ hid_hw_stop(hdev);
+fail:
+ hid_set_drvdata(hdev, NULL);
+ return error;
+}
+
+/*
+ * Convert events from vendor intf to input key events
+ */
+static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *data, int size)
+{
+ struct cougar *cougar;
+ unsigned char code, action;
+ int i;
+
+ cougar = hid_get_drvdata(hdev);
+ if (!cougar->special_intf || !cougar->shared ||
+ !cougar->shared->input || !cougar->shared->enabled)
+ return 0;
+
+ code = data[COUGAR_FIELD_CODE];
+ action = data[COUGAR_FIELD_ACTION];
+ for (i = 0; cougar_mapping[i][0]; i++) {
+ if (code == cougar_mapping[i][0]) {
+ input_event(cougar->shared->input, EV_KEY,
+ cougar_mapping[i][1], action);
+ input_sync(cougar->shared->input);
+ return 0;
+ }
+ }
+ hid_warn(hdev, "unmapped special key code %x: ignoring\n", code);
+ return 0;
+}
+
+static void cougar_remove(struct hid_device *hdev)
+{
+ struct cougar *cougar = hid_get_drvdata(hdev);
+
+ if (cougar) {
+ /* Stop the vendor intf to process more events */
+ if (cougar->shared)
+ cougar->shared->enabled = false;
+ if (cougar->special_intf)
+ hid_hw_close(hdev);
+ }
+ hid_hw_stop(hdev);
+}
+
+static struct hid_device_id cougar_id_table[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
+ USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) },
+ {}
+};
+MODULE_DEVICE_TABLE(hid, cougar_id_table);
+
+static struct hid_driver cougar_driver = {
+ .name = "cougar",
+ .id_table = cougar_id_table,
+ .report_fixup = cougar_report_fixup,
+ .probe = cougar_probe,
+ .remove = cougar_remove,
+ .raw_event = cougar_raw_event,
+};
+
+module_hid_driver(cougar_driver);
diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c
index 803a725785cf..07e26c3567eb 100644
--- a/drivers/hid/hid-elan.c
+++ b/drivers/hid/hid-elan.c
@@ -19,38 +19,49 @@
#include "hid-ids.h"
+#define ELAN_MT_I2C 0x5d
#define ELAN_SINGLE_FINGER 0x81
#define ELAN_MT_FIRST_FINGER 0x82
#define ELAN_MT_SECOND_FINGER 0x83
#define ELAN_INPUT_REPORT_SIZE 8
+#define ELAN_I2C_REPORT_SIZE 32
+#define ELAN_FINGER_DATA_LEN 5
+#define ELAN_MAX_FINGERS 5
+#define ELAN_MAX_PRESSURE 255
+#define ELAN_TP_USB_INTF 1
+
+#define ELAN_FEATURE_REPORT 0x0d
+#define ELAN_FEATURE_SIZE 5
+#define ELAN_PARAM_MAX_X 6
+#define ELAN_PARAM_MAX_Y 7
+#define ELAN_PARAM_RES 8
#define ELAN_MUTE_LED_REPORT 0xBC
#define ELAN_LED_REPORT_SIZE 8
-struct elan_touchpad_settings {
- u8 max_fingers;
- u16 max_x;
- u16 max_y;
- u8 max_area_x;
- u8 max_area_y;
- u8 max_w;
- int usb_bInterfaceNumber;
-};
+#define ELAN_HAS_LED BIT(0)
struct elan_drvdata {
struct input_dev *input;
u8 prev_report[ELAN_INPUT_REPORT_SIZE];
struct led_classdev mute_led;
u8 mute_led_state;
- struct elan_touchpad_settings *settings;
+ u16 max_x;
+ u16 max_y;
+ u16 res_x;
+ u16 res_y;
};
static int is_not_elan_touchpad(struct hid_device *hdev)
{
- struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
- struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+ if (hdev->bus == BUS_USB) {
+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+
+ return (intf->altsetting->desc.bInterfaceNumber !=
+ ELAN_TP_USB_INTF);
+ }
- return (intf->altsetting->desc.bInterfaceNumber != drvdata->settings->usb_bInterfaceNumber);
+ return 0;
}
static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -62,12 +73,86 @@ static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if (field->report->id == ELAN_SINGLE_FINGER ||
field->report->id == ELAN_MT_FIRST_FINGER ||
- field->report->id == ELAN_MT_SECOND_FINGER)
+ field->report->id == ELAN_MT_SECOND_FINGER ||
+ field->report->id == ELAN_MT_I2C)
return -1;
return 0;
}
+static int elan_get_device_param(struct hid_device *hdev,
+ unsigned char *dmabuf, unsigned char param)
+{
+ int ret;
+
+ dmabuf[0] = ELAN_FEATURE_REPORT;
+ dmabuf[1] = 0x05;
+ dmabuf[2] = 0x03;
+ dmabuf[3] = param;
+ dmabuf[4] = 0x01;
+
+ ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf,
+ ELAN_FEATURE_SIZE, HID_FEATURE_REPORT,
+ HID_REQ_SET_REPORT);
+ if (ret != ELAN_FEATURE_SIZE) {
+ hid_err(hdev, "Set report error for parm %d: %d\n", param, ret);
+ return ret;
+ }
+
+ ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf,
+ ELAN_FEATURE_SIZE, HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
+ if (ret != ELAN_FEATURE_SIZE) {
+ hid_err(hdev, "Get report error for parm %d: %d\n", param, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static unsigned int elan_convert_res(char val)
+{
+ /*
+ * (value from firmware) * 10 + 790 = dpi
+ * dpi * 10 / 254 = dots/mm
+ */
+ return (val * 10 + 790) * 10 / 254;
+}
+
+static int elan_get_device_params(struct hid_device *hdev)
+{
+ struct elan_drvdata *drvdata = hid_get_drvdata(hdev);
+ unsigned char *dmabuf;
+ int ret;
+
+ dmabuf = kmalloc(ELAN_FEATURE_SIZE, GFP_KERNEL);
+ if (!dmabuf)
+ return -ENOMEM;
+
+ ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_X);
+ if (ret)
+ goto err;
+
+ drvdata->max_x = (dmabuf[4] << 8) | dmabuf[3];
+
+ ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_Y);
+ if (ret)
+ goto err;
+
+ drvdata->max_y = (dmabuf[4] << 8) | dmabuf[3];
+
+ ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_RES);
+ if (ret)
+ goto err;
+
+ drvdata->res_x = elan_convert_res(dmabuf[3]);
+ drvdata->res_y = elan_convert_res(dmabuf[4]);
+
+err:
+ kfree(dmabuf);
+ return ret;
+}
+
static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
int ret;
@@ -77,6 +162,10 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
if (is_not_elan_touchpad(hdev))
return 0;
+ ret = elan_get_device_params(hdev);
+ if (ret)
+ return ret;
+
input = devm_input_allocate_device(&hdev->dev);
if (!input)
return -ENOMEM;
@@ -90,25 +179,25 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi)
input->id.version = hdev->version;
input->dev.parent = &hdev->dev;
- input_set_abs_params(input, ABS_MT_POSITION_X, 0,
- drvdata->settings->max_x, 0, 0);
- input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
- drvdata->settings->max_y, 0, 0);
- input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
- drvdata->settings->max_fingers, 0, 0);
- input_set_abs_params(input, ABS_TOOL_WIDTH, 0,
- drvdata->settings->max_w, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, drvdata->max_x,
+ 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->max_y,
+ 0, 0);
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, ELAN_MAX_PRESSURE,
+ 0, 0);
__set_bit(BTN_LEFT, input->keybit);
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
- ret = input_mt_init_slots(input, drvdata->settings->max_fingers,
- INPUT_MT_POINTER);
+ ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER);
if (ret) {
hid_err(hdev, "Failed to init elan MT slots: %d\n", ret);
return ret;
}
+ input_abs_set_res(input, ABS_X, drvdata->res_x);
+ input_abs_set_res(input, ABS_Y, drvdata->res_y);
+
ret = input_register_device(input);
if (ret) {
hid_err(hdev, "Failed to register elan input device: %d\n",
@@ -126,7 +215,7 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
unsigned int slot_num)
{
struct input_dev *input = drvdata->input;
- int x, y, w;
+ int x, y, p;
bool active = !!data;
@@ -134,17 +223,17 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data,
input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
if (active) {
x = ((data[0] & 0xF0) << 4) | data[1];
- y = drvdata->settings->max_y -
+ y = drvdata->max_y -
(((data[0] & 0x07) << 8) | data[2]);
- w = data[4];
+ p = data[4];
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
- input_report_abs(input, ABS_TOOL_WIDTH, w);
+ input_report_abs(input, ABS_MT_PRESSURE, p);
}
}
-static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
+static void elan_usb_report_input(struct elan_drvdata *drvdata, u8 *data)
{
int i;
struct input_dev *input = drvdata->input;
@@ -162,7 +251,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
* byte 5: x8 x7 x6 x5 x4 x3 x2 x1
* byte 6: y8 y7 y6 y5 y4 y3 y2 y1
* byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
- * byte 8: w8 w7 w6 w5 w4 w3 w2 w1
+ * byte 8: p8 p7 p6 p5 p4 p3 p2 p1
*
* packet structure for ELAN_MT_SECOND_FINGER:
*
@@ -171,19 +260,21 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
* byte 3: x8 x7 x6 x5 x4 x3 x2 x1
* byte 4: y8 y7 y6 y5 y4 y3 y2 y1
* byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
- * byte 6: w8 w7 w6 w5 w4 w3 w2 w1
+ * byte 6: p8 p7 p6 p5 p4 p3 p2 p1
* byte 7: 0 0 0 0 0 0 0 0
* byte 8: 0 0 0 0 0 0 0 0
*
* f5-f1: finger touch bits
* L: clickpad button
- * sy / sx: not sure yet, but this looks like rectangular
- * area for finger
- * w: looks like finger width
+ * sy / sx: finger width / height expressed in traces, the total number
+ * of traces can be queried by doing a HID_REQ_SET_REPORT
+ * { 0x0d, 0x05, 0x03, 0x05, 0x01 } followed by a GET, in the
+ * returned buf, buf[3]=no-x-traces, buf[4]=no-y-traces.
+ * p: pressure
*/
if (data[0] == ELAN_SINGLE_FINGER) {
- for (i = 0; i < drvdata->settings->max_fingers; i++) {
+ for (i = 0; i < ELAN_MAX_FINGERS; i++) {
if (data[2] & BIT(i + 3))
elan_report_mt_slot(drvdata, data + 3, i);
else
@@ -210,7 +301,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
if (prev_report[0] != ELAN_MT_FIRST_FINGER)
return;
- for (i = 0; i < drvdata->settings->max_fingers; i++) {
+ for (i = 0; i < ELAN_MAX_FINGERS; i++) {
if (prev_report[2] & BIT(i + 3)) {
if (!first) {
first = 1;
@@ -229,6 +320,46 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data)
input_sync(input);
}
+static void elan_i2c_report_input(struct elan_drvdata *drvdata, u8 *data)
+{
+ struct input_dev *input = drvdata->input;
+ u8 *finger_data;
+ int i;
+
+ /*
+ * Elan MT touchpads in i2c mode send finger data in the same format
+ * as in USB mode, but then with all fingers in a single packet.
+ *
+ * packet structure for ELAN_MT_I2C:
+ *
+ * byte 1: 1 0 0 1 1 1 0 1 // 0x5d
+ * byte 2: f5 f4 f3 f2 f1 0 0 L
+ * byte 3: x12 x11 x10 x9 0? y11 y10 y9
+ * byte 4: x8 x7 x6 x5 x4 x3 x2 x1
+ * byte 5: y8 y7 y6 y5 y4 y3 y2 y1
+ * byte 6: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1
+ * byte 7: p8 p7 p6 p5 p4 p3 p2 p1
+ * byte 8-12: Same as byte 3-7 for second finger down
+ * byte 13-17: Same as byte 3-7 for third finger down
+ * byte 18-22: Same as byte 3-7 for fourth finger down
+ * byte 23-27: Same as byte 3-7 for fifth finger down
+ */
+
+ finger_data = data + 2;
+ for (i = 0; i < ELAN_MAX_FINGERS; i++) {
+ if (data[1] & BIT(i + 3)) {
+ elan_report_mt_slot(drvdata, finger_data, i);
+ finger_data += ELAN_FINGER_DATA_LEN;
+ } else {
+ elan_report_mt_slot(drvdata, NULL, i);
+ }
+ }
+
+ input_report_key(input, BTN_LEFT, data[1] & 0x01);
+ input_mt_sync_frame(input);
+ input_sync(input);
+}
+
static int elan_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data, int size)
{
@@ -241,11 +372,16 @@ static int elan_raw_event(struct hid_device *hdev,
data[0] == ELAN_MT_FIRST_FINGER ||
data[0] == ELAN_MT_SECOND_FINGER) {
if (size == ELAN_INPUT_REPORT_SIZE) {
- elan_report_input(drvdata, data);
+ elan_usb_report_input(drvdata, data);
return 1;
}
}
+ if (data[0] == ELAN_MT_I2C && size == ELAN_I2C_REPORT_SIZE) {
+ elan_i2c_report_input(drvdata, data);
+ return 1;
+ }
+
return 0;
}
@@ -343,7 +479,6 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!drvdata)
return -ENOMEM;
- drvdata->settings = (struct elan_touchpad_settings *)id->driver_data;
hid_set_drvdata(hdev, drvdata);
ret = hid_parse(hdev);
@@ -371,9 +506,11 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto err;
- ret = elan_init_mute_led(hdev);
- if (ret)
- goto err;
+ if (id->driver_data & ELAN_HAS_LED) {
+ ret = elan_init_mute_led(hdev);
+ if (ret)
+ goto err;
+ }
return 0;
err:
@@ -386,22 +523,14 @@ static void elan_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
}
-static const struct elan_touchpad_settings hp_x2_10_touchpad_data = {
- .max_fingers = 5,
- .max_x = 2930,
- .max_y = 1250,
- .max_area_x = 15,
- .max_area_y = 15,
- .max_w = 255,
- .usb_bInterfaceNumber = 1,
-};
-
static const struct hid_device_id elan_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2),
+ .driver_data = ELAN_HAS_LED },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER),
- (kernel_ulong_t)&hp_x2_10_touchpad_data},
+ .driver_data = ELAN_HAS_LED },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) },
{ }
};
-
MODULE_DEVICE_TABLE(hid, elan_devices);
static struct hid_driver elan_driver = {
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index c7981ddd8776..79bdf0c7e351 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -369,6 +369,8 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
#define USB_VENDOR_ID_ELAN 0x04f3
+#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401
+#define USB_DEVICE_ID_HP_X2 0x074d
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
#define USB_VENDOR_ID_ELECOM 0x056e
@@ -1001,6 +1003,9 @@
#define USB_VENDOR_ID_SINO_LITE 0x1345
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
+#define USB_VENDOR_ID_SOLID_YEAR 0x060b
+#define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a
+
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index ab93dd5927c3..4e94ea3e280a 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1550,6 +1550,9 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
case HID_GD_WIRELESS_RADIO_CTLS:
suffix = "Wireless Radio Control";
break;
+ case HID_GD_SYSTEM_MULTIAXIS:
+ suffix = "System Multi Axis";
+ break;
default:
break;
}
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 96e7d3231d2f..72d983626afd 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -22,12 +22,13 @@
#include "hid-ids.h"
-#define MS_HIDINPUT 0x01
-#define MS_ERGONOMY 0x02
-#define MS_PRESENTER 0x04
-#define MS_RDESC 0x08
-#define MS_NOGET 0x10
-#define MS_DUPLICATE_USAGES 0x20
+#define MS_HIDINPUT BIT(0)
+#define MS_ERGONOMY BIT(1)
+#define MS_PRESENTER BIT(2)
+#define MS_RDESC BIT(3)
+#define MS_NOGET BIT(4)
+#define MS_DUPLICATE_USAGES BIT(5)
+#define MS_SURFACE_DIAL BIT(6)
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
@@ -130,6 +131,30 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
return 1;
}
+static int ms_surface_dial_quirk(struct hid_input *hi, struct hid_field *field,
+ struct hid_usage *usage, unsigned long **bit, int *max)
+{
+ switch (usage->hid & HID_USAGE_PAGE) {
+ case 0xff070000:
+ /* fall-through */
+ case HID_UP_DIGITIZER:
+ /* ignore those axis */
+ return -1;
+ case HID_UP_GENDESK:
+ switch (usage->hid) {
+ case HID_GD_X:
+ /* fall-through */
+ case HID_GD_Y:
+ /* fall-through */
+ case HID_GD_RFKILL_BTN:
+ /* ignore those axis */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
@@ -146,6 +171,13 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
ms_presenter_8k_quirk(hi, usage, bit, max))
return 1;
+ if (quirks & MS_SURFACE_DIAL) {
+ int ret = ms_surface_dial_quirk(hi, field, usage, bit, max);
+
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -229,6 +261,9 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (quirks & MS_NOGET)
hdev->quirks |= HID_QUIRK_NOGET;
+ if (quirks & MS_SURFACE_DIAL)
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
+
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
@@ -281,6 +316,8 @@ static const struct hid_device_id ms_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDO