summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig10
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c45
-rw-r--r--drivers/hid/hid-cougar.c312
-rw-r--r--drivers/hid/hid-debug.c8
-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/i2c-hid/i2c-hid.c59
-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/usbhid/hiddev.c11
-rw-r--r--drivers/hid/wacom_sys.c18
-rw-r--r--drivers/hid/wacom_wac.c10
16 files changed, 1268 insertions, 509 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 f858cc72011d..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);
@@ -1952,6 +1986,8 @@ static int hid_device_probe(struct device *dev)
}
hdev->io_started = false;
+ clear_bit(ffs(HID_STAT_REPROBED), &hdev->status);
+
if (!hdev->driver) {
id = hid_match_device(hdev, hdrv);
if (id == NULL) {
@@ -2215,7 +2251,8 @@ static int __hid_bus_reprobe_drivers(struct device *dev, void *data)
struct hid_device *hdev = to_hid_device(dev);
if (hdev->driver == hdrv &&
- !hdrv->match(hdev, hid_ignore_special_drivers))
+ !hdrv->match(hdev, hid_ignore_special_drivers) &&
+ !test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status))
return device_reprobe(dev);
return 0;
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-debug.c b/drivers/hid/hid-debug.c
index 8469b6964ff6..b48100236df8 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -1154,6 +1154,8 @@ copy_rest:
goto out;
if (list->tail > list->head) {
len = list->tail - list->head;
+ if (len > count)
+ len = count;
if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) {
ret = -EFAULT;
@@ -1163,6 +1165,8 @@ copy_rest:
list->head += len;
} else {
len = HID_DEBUG_BUFSIZE - list->head;
+ if (len > count)
+ len = count;
if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) {
ret = -EFAULT;
@@ -1170,7 +1174,9 @@ copy_rest:
}
list->head = 0;
ret += len;
- goto copy_rest;
+ count -= len;
+ if (count > 0)
+ goto copy_rest;
}
}
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_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
.driver_data = MS_PRESENTER },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
+ .driver_data = MS_SURFACE_DIAL },
{ }
};
MODULE_DEVICE_TABLE(hid, ms_devices);
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 45968f7970f8..40fbb7c52723 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -28,14 +28,11 @@
*/
/*
- * This driver is regularly tested thanks to the tool hid-test[1].
- * This tool relies on hid-replay[2] and a database of hid devices[3].
+ * This driver is regularly tested thanks to the test suite in hid-tools[1].
* Please run these regression tests before patching this module so that
* your patch won't break existing known devices.
*
- * [1] https://github.com/bentiss/hid-test
- * [2] https://github.com/bentiss/hid-replay
- * [3] https://github.com/bentiss/hid-devices
+ * [1] https://gitlab.freedesktop.org/libevdev/hid-tools
*/
#include <linux/device.h>
@@ -90,13 +87,54 @@ enum latency_mode {
#define MT_IO_FLAGS_ACTIVE_SLOTS 1
#define MT_IO_FLAGS_PENDING_SLOTS 2
-struct mt_slot {
- __s32 x, y, cx, cy, p, w, h, a;
- __s32 contactid; /* the device ContactID assigned to this slot */
- bool touch_state; /* is the touch valid? */
- bool inrange_state; /* is the finger in proximity of the sensor? */
- bool confidence_state; /* is the touch made by a finger? */
- bool has_azimuth; /* the contact reports azimuth */
+static const bool mtrue = true; /* default for true */
+static const bool mfalse; /* default for false */
+static const __s32 mzero; /* default for 0 */
+
+#define DEFAULT_TRUE ((void *)&mtrue)
+#define DEFAULT_FALSE ((void *)&mfalse)
+#define DEFAULT_ZERO ((void *)&mzero)
+
+struct mt_usages {
+ struct list_head list;
+ __s32 *x, *y, *cx, *cy, *p, *w, *h, *a;
+ __s32 *contactid; /* the device ContactID assigned to this slot */
+ bool *tip_state; /* is the touch valid? */
+ bool *inrange_state; /* is the finger in proximity of the sensor? */
+ bool *confidence_state; /* is the touch made by a finger? */
+};
+
+struct mt_application {
+ struct list_head list;
+ unsigned int application;
+ struct list_head mt_usages; /* mt usages list */
+
+ __s32 quirks;
+
+ __s32 *scantime; /* scantime reported */
+ __s32 scantime_logical_max; /* max value for raw scantime */
+
+ __s32 *raw_cc; /* contact count in the report */
+ int left_button_state; /* left button state */
+ unsigned int mt_flags; /* flags to pass to input-mt */
+
+ unsigned long *pending_palm_slots; /* slots where we reported palm
+ * and need to release */
+
+ __u8 num_received; /* how many contacts we received */
+ __u8 num_expected; /* expected last contact index */
+ __u8 buttons_count; /* number of physical buttons per touchpad */
+ __u8 touches_by_report; /* how many touches are present in one report:
+ * 1 means we should use a serial protocol
+ * > 1 means hybrid (multitouch) protocol
+ */
+
+ __s32 dev_time; /* the scan time provided by the device */
+ unsigned long jiffies; /* the frame's jiffies */
+ int timestamp; /* the timestamp to be sent */
+ int prev_scantime; /* scantime reported previously */
+
+ bool have_contact_count;
};
struct mt_class {
@@ -111,46 +149,30 @@ struct mt_class {
bool export_all_inputs; /* do not ignore mouse, keyboards, etc... */
};
-struct mt_fields {
- unsigned usages[HID_MAX_FIELDS];
- unsigned int length;
+struct mt_report_data {
+ struct list_head list;
+ struct hid_report *report;
+ struct mt_application *application;
+ bool is_mt_collection;
};
struct mt_device {
- struct mt_slot curdata; /* placeholder of incoming data */
struct mt_class mtclass; /* our mt device class */
struct timer_list release_timer; /* to release sticky fingers */
struct hid_device *hdev; /* hid_device we're attached to */
- struct mt_fields *fields; /* temporary placeholder for storing the
- multitouch fields */
unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */
- int cc_index; /* contact count field index in the report */
- int cc_value_index; /* contact count value index in the field */
- int scantime_index; /* scantime field index in the report */
- int scantime_val_index; /* scantime value index in the field */
- int prev_scantime; /* scantime reported in the previous packet */
- int left_button_state; /* left button state */
- unsigned last_slot_field; /* the last field of a slot */
- unsigned mt_report_id; /* the report ID of the multitouch device */
__u8 inputmode_value; /* InputMode HID feature value */
- __u8 num_received; /* how many contacts we received */
- __u8 num_expected; /* expected last contact index */
__u8 maxcontacts;
- __u8 touches_by_report; /* how many touches are present in one report:
- * 1 means we should use a serial protocol
- * > 1 means hybrid (multitouch) protocol */
- __u8 buttons_count; /* number of physical buttons per touchpad */
bool is_buttonpad; /* is this device a button pad? */
bool serial_maybe; /* need to check for serial protocol */
- bool curvalid; /* is the current contact valid? */
- unsigned mt_flags; /* flags to pass to input-mt */
- __s32 dev_time; /* the scan time provided by the device */
- unsigned long jiffies; /* the frame's jiffies */
- int timestamp; /* the timestamp to be sent */
+
+ struct list_head applications;
+ struct list_head reports;
};
-static void mt_post_parse_default_settings(struct mt_device *td);
-static void mt_post_parse(struct mt_device *td);
+static void mt_post_parse_default_settings(struct mt_device *td,
+ struct mt_application *app);
+static void mt_post_parse(struct mt_device *td, struct mt_application *app);
/* classes of device behavior */
#define MT_CLS_DEFAULT 0x0001
@@ -203,15 +225,16 @@ static void mt_post_parse(struct mt_device *td);
* to a valid contact that was just read.
*/
-static int cypress_compute_slot(struct mt_device *td)
+static int cypress_compute_slot(struct mt_application *application,
+ struct mt_usages *slot)
{
- if (td->curdata.contactid != 0 || td->num_received == 0)
- return td->curdata.contactid;
+ if (*slot->contactid != 0 || application->num_received == 0)
+ return *slot->contactid;
else
return -1;
}
-static struct mt_class mt_classes[] = {
+static const struct mt_class mt_classes[] = {
{ .name = MT_CLS_DEFAULT,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE },
@@ -353,6 +376,7 @@ static ssize_t mt_set_quirks(struct device *dev,
{
struct hid_device *hdev = to_hid_device(dev);
struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_application *application;
unsigned long val;
@@ -361,8 +385,11 @@ static ssize_t mt_set_quirks(struct device *dev,
td->mtclass.quirks = val;
- if (td->cc_index < 0)
- td->mtclass.quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
+ list_for_each_entry(application, &td->applications, list) {
+ application->quirks = val;
+ if (!application->have_contact_count)
+ application->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
+ }
return count;
}
@@ -457,41 +484,199 @@ static void set_abs(struct input_dev *input, unsigned int code,
input_abs_set_res(input, code, hidinput_calc_abs_res(field, code));
}
-static void mt_store_field(struct hid_usage *usage, struct mt_device *td,
- struct hid_input *hi)
+static struct mt_usages *mt_allocate_usage(struct hid_device *hdev,
+ struct mt_application *application)
+{
+ struct mt_usages *usage;
+
+ usage = devm_kzalloc(&hdev->dev, sizeof(*usage), GFP_KERNEL);
+ if (!usage)
+ return NULL;
+
+ /* set some defaults so we do not need to check for null pointers */
+ usage->x = DEFAULT_ZERO;
+ usage->y = DEFAULT_ZERO;
+ usage->cx = DEFAULT_ZERO;
+ usage->cy = DEFAULT_ZERO;
+ usage->p = DEFAULT_ZERO;
+ usage->w = DEFAULT_ZERO;
+ usage->h = DEFAULT_ZERO;
+ usage->a = DEFAULT_ZERO;
+ usage->contactid = DEFAULT_ZERO;
+ usage->tip_state = DEFAULT_FALSE;
+ usage->inrange_state = DEFAULT_FALSE;
+ usage->confidence_state = DEFAULT_TRUE;
+
+ list_add_tail(&usage->list, &application->mt_usages);
+
+ return usage;
+}
+
+static struct mt_application *mt_allocate_application(struct mt_device *td,
+ unsigned int application)
+{
+ struct mt_application *mt_application;
+
+ mt_application = devm_kzalloc(&td->hdev->dev, sizeof(*mt_application),
+ GFP_KERNEL);
+ if (!mt_application)
+ return NULL;
+
+ mt_application->application = application;
+ INIT_LIST_HEAD(&mt_application->mt_usages);
+
+ if (application == HID_DG_TOUCHSCREEN)
+ mt_application->mt_flags |= INPUT_MT_DIRECT;
+
+ /*
+ * Model touchscreens providing buttons as touchpads.
+ */
+ if (application == HID_DG_TOUCHPAD) {
+ mt_application->mt_flags |= INPUT_MT_POINTER;
+ td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+ }
+
+ mt_application->scantime = DEFAULT_ZERO;
+ mt_application->raw_cc = DEFAULT_ZERO;
+ mt_application->quirks = td->mtclass.quirks;
+
+ list_add_tail(&mt_application->list, &td->applications);
+
+ return mt_application;
+}
+
+static struct mt_application *mt_find_application(struct mt_device *td,
+ unsigned int application)
+{
+ struct mt_application *tmp, *mt_application = NULL;
+
+ list_for_each_entry(tmp, &td->applications, list) {
+ if (application == tmp->application) {
+ mt_application = tmp;
+ break;
+ }
+ }
+
+ if (!mt_application)
+ mt_application = mt_allocate_application(td, application);
+
+ return mt_application;
+}
+
+static struct mt_report_data *mt_allocate_report_data(struct mt_device *td,
+ struct hid_report *report)
+{
+ struct mt_report_data *rdata;
+ struct hid_field *field;
+ int r, n;
+
+ rdata = devm_kzalloc(&td->hdev->dev, sizeof(*rdata), GFP_KERNEL);
+ if (!rdata)
+ return NULL;
+
+ rdata->report = report;
+ rdata->application = mt_find_application(td, report->application);
+
+ if (!rdata->application) {
+ devm_kfree(&td->hdev->dev, rdata);
+ return NULL;
+ }
+
+ for (r = 0; r < report->maxfield; r++) {
+ field = report->field[r];
+
+ if (!(HID_MAIN_ITEM_VARIABLE & field->flags))
+ continue;
+
+ for (n = 0; n < field->report_count; n++) {
+ if (field->usage[n].hid == HID_DG_CONTACTID)
+ rdata->is_mt_collection = true;
+ }
+ }
+
+ list_add_tail(&rdata->list, &td->reports);
+
+ return rdata;
+}
+
+static struct mt_report_data *mt_find_report_data(struct mt_device *td,
+ struct hid_report *report)
{
- struct mt_fields *f = td->fields;
+ struct mt_report_data *tmp, *rdata = NULL;
- if (f->length >= HID_MAX_FIELDS)
+ list_for_each_entry(tmp, &td->reports, list) {
+ if (report == tmp->report) {
+ rdata = tmp;
+ break;
+ }
+ }
+
+ if (!rdata)
+ rdata = mt_allocate_report_data(td, report);
+
+ return rdata;
+}
+
+static void mt_store_field(struct hid_device *hdev,
+ struct mt_application *application,
+ __s32 *value,
+ size_t offset)
+{
+ struct mt_usages *usage;
+ __s32 **target;
+
+ if (list_empty(&application->mt_usages))
+ usage = mt_allocate_usage(hdev, application);
+ else
+ usage = list_last_entry(&application->mt_usages,
+ struct mt_usages,
+ list);
+
+ if (!usage)
return;
- f->usages[f->length++] = usage->hid;
+ target = (__s32 **)((char *)usage + offset);
+
+ /* the value has already been filled, create a new slot */
+ if (*target != DEFAULT_TRUE &&
+ *target != DEFAULT_FALSE &&
+ *target != DEFAULT_ZERO) {
+ usage = mt_allocate_usage(hdev, application);
+ if (!usage)
+ return;
+
+ target = (__s32 **)((char *)usage + offset);
+ }
+
+ *target = value;
}
+#define MT_STORE_FIELD(__name) \
+ mt_store_field(hdev, app, \
+ &field->value[usage->usage_index], \
+ offsetof(struct mt_usages, __name))
+
static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
- unsigned long **bit, int *max)
+ unsigned long **bit, int *max, struct mt_application *app)
{
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
int code;
struct hid_usage *prev_usage = NULL;
- if (field->application == HID_DG_TOUCHSCREEN)
- td->mt_flags |= INPUT_MT_DIRECT;
-
/*
* Model touchscreens providing buttons as touchpads.
*/
- if (field->application == HID_DG_TOUCHPAD ||
+ if (field->application == HID_DG_TOUCHSCREEN &&
(usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
- td->mt_flags |= INPUT_MT_POINTER;
+ app->mt_flags |= INPUT_MT_POINTER;
td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
}
/* count the buttons on touchpads */
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
- td->buttons_count++;
+ app->buttons_count++;
if (usage->usage_index)
prev_usage = &field->usage[usage->usage_index - 1];
@@ -502,33 +687,40 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
switch (usage->hid) {
case HID_GD_X:
if (prev_usage && (prev_usage->hid == usage->hid)) {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOOL_X);
- set_abs(hi->input, ABS_MT_TOOL_X, field,
- cls->sn_move);
+ code = ABS_MT_TOOL_X;
+ MT_STORE_FIELD(cx);
} else {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_POSITION_X);
- set_abs(hi->input, ABS_MT_POSITION_X, field,
- cls->sn_move);
+ code = ABS_MT_POSITION_X;
+ MT_STORE_FIELD(x);
+ }
+
+ set_abs(hi->input, code, field, cls->sn_move);
+
+ /*
+ * A system multi-axis that exports X and Y has a high
+ * chance of being used directly on a surface
+ */
+ if (field->application == HID_GD_SYSTEM_MULTIAXIS) {
+ __set_bit(INPUT_PROP_DIRECT,
+ hi->input->propbit);
+ input_set_abs_params(hi->input,
+ ABS_MT_TOOL_TYPE,
+ MT_TOOL_DIAL,
+ MT_TOOL_DIAL, 0, 0);
}
- mt_store_field(usage, td, hi);
return 1;
case HID_GD_Y:
if (prev_usage && (prev_usage->hid == usage->hid)) {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOOL_Y);
- set_abs(hi->input, ABS_MT_TOOL_Y, field,
- cls->sn_move);
+ code = ABS_MT_TOOL_Y;
+ MT_STORE_FIELD(cy);
} else {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_POSITION_Y);
- set_abs(hi->input, ABS_MT_POSITION_Y, field,
- cls->sn_move);
+ code = ABS_MT_POSITION_Y;
+ MT_STORE_FIELD(y);
}
- mt_store_field(usage, td, hi);
+ set_abs(hi->input, code, field, cls->sn_move);
+
return 1;
}
return 0;
@@ -536,43 +728,45 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER:
switch (usage->hid) {
case HID_DG_INRANGE:
- if (cls->quirks & MT_QUIRK_HOVERING) {
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_DISTANCE);
+ if (app->quirks & MT_QUIRK_HOVERING) {
input_set_abs_params(hi->input,
ABS_MT_DISTANCE, 0, 1, 0, 0);
}
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(inrange_state);
return 1;
case HID_DG_CONFIDENCE:
if ((cls->name == MT_CLS_WIN_8 ||
cls->name == MT_CLS_WIN_8_DUAL) &&
- field->application == HID_DG_TOUCHPAD)
- cls->quirks |= MT_QUIRK_CONFIDENCE;
- mt_store_field(usage, td, hi);
+ (field->application == HID_DG_TOUCHPAD ||
+ field->application == HID_DG_TOUCHSCREEN))
+ app->quirks |= MT_QUIRK_CONFIDENCE;
+
+ if (app->quirks & MT_QUIRK_CONFIDENCE)
+ input_set_abs_params(hi->input,
+ ABS_MT_TOOL_TYPE,
+ MT_TOOL_FINGER,
+ MT_TOOL_PALM, 0, 0);
+
+ MT_STORE_FIELD(confidence_state);
return 1;
case HID_DG_TIPSWITCH:
- hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
- input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
- mt_store_field(usage, td, hi);
+ if (field->application != HID_GD_SYSTEM_MULTIAXIS)
+ input_set_capability(hi->input,
+ EV_KEY, BTN_TOUCH);
+ MT_STORE_FIELD(tip_state);
return 1;
case HID_DG_CONTACTID:
- mt_store_field(usage, td, hi);
- td->touches_by_report++;
- td->mt_report_id = field->report->id;
+ MT_STORE_FIELD(contactid);
+ app->touches_by_report++;
return 1;
case HID_DG_WIDTH:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOUCH_MAJOR);
- if (!(cls->quirks & MT_QUIRK_NO_AREA))
+ if (!(app->quirks & MT_QUIRK_NO_AREA))
set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
cls->sn_width);
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(w);
return 1;
case HID_DG_HEIGHT:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_TOUCH_MINOR);
- if (!(cls->quirks & MT_QUIRK_NO_AREA)) {
+ if (!(app->quirks & MT_QUIRK_NO_AREA)) {
set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
cls->sn_height);
@@ -585,41 +779,23 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0);
}
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(h);
return 1;
case HID_DG_TIPPRESSURE:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_PRESSURE);
set_abs(hi->input, ABS_MT_PRESSURE, field,
cls->sn_pressure);
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(p);
return 1;
case HID_DG_SCANTIME:
- hid_map_usage(hi, usage, bit, max,
- EV_MSC, MSC_TIMESTAMP);
input_set_capability(hi->input, EV_MSC, MSC_TIMESTAMP);
- /* Ignore if indexes are out of bounds. */
- if (field->index >= field->report->maxfield ||
- usage->usage_index >= field->report_count)
- return 1;
- td->scantime_index = field->index;
- td->scantime_val_index = usage->usage_index;
- /*
- * We don't set td->last_slot_field as scan time is
- * global to the report.
- */
+ app->scantime = &field->value[usage->usage_index];
+ app->scantime_logical_max = field->logical_maximum;
return 1;
case HID_DG_CONTACTCOUNT:
- /* Ignore if indexes are out of bounds. */
- if (field->index >= field->report->maxfield ||
- usage->usage_index >= field->report_count)
- return 1;
- td->cc_index = field->index;
- td->cc_value_index = usage->usage_index;
+ app->have_contact_count = true;
+ app->raw_cc = &field->value[usage->usage_index];
return 1;
case HID_DG_AZIMUTH:
- hid_map_usage(hi, usage, bit, max,
- EV_ABS, ABS_MT_ORIENTATION);
/*
* Azimuth has the range of [0, MAX) representing a full
* revolution. Set ABS_MT_ORIENTATION to a quarter of
@@ -630,11 +806,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
field->logical_maximum / 4,
cls->sn_move ?
field->logical_maximum / cls->sn_move : 0, 0);
- mt_store_field(usage, td, hi);
+ MT_STORE_FIELD(a);
return 1;
case HID_DG_CONTACTMAX:
- /* we don't set td->last_slot_field as contactcount and
- * contact max are global to the report */
+ /* contact max are global to the report */
return -1;
case HID_DG_TOUCH:
/* Legacy devices use TIPSWITCH and not TOUCH.
@@ -650,10 +825,14 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* MS PTP spec says that external buttons left and right have
* usages 2 and 3.
*/
- if ((cls->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
+ if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
field->application == HID_DG_TOUCHPAD &&
(usage->hid & HID_USAGE) > 1)
code--;
+
+ if (field->application == HID_GD_SYSTEM_MULTIAXIS)
+ code = BTN_0 + ((usage->hid - 1) & HID_USAGE);
+
hid_map_usage(hi, usage, bit, max, EV_KEY, code);
input_set_capability(hi->input, EV_KEY, code);
return 1;
@@ -666,110 +845,68 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
-static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
+static int mt_compute_slot(struct mt_device *td, struct mt_application *app,
+ struct mt_usages *slot,
+ struct input_dev *input)
{
- __s32 quirks = td->mtclass.quirks;
+ __s32 quirks = app->quirks;
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID)
- return td->curdata.contactid;
+ return *slot->contactid;
if (quirks & MT_QUIRK_CYPRESS)
- return cypress_compute_slot(td);
+ return cypress_compute_slot(app, slot);
if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER)
- return td->num_received;
+ return app->num_received;
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE)
- return td->curdata.contactid - 1;
+ return *slot->contactid - 1;
- return input_mt_get_slot_by_key(input, td->curdata.contactid);
+ return input_mt_get_slot_by_key(input, *slot->contactid);
}
-/*
- * this function is called when a whole contact has been processed,
- * so that it can assign it to a slot and store the data there
- */
-static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
+static void mt_release_pending_palms(struct mt_device *td,
+ struct mt_application *app,
+ struct input_dev *input)
{
- if ((td->mtclass.quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) &&
- td->num_received >= td->num_expected)
- return;
-
- if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) {
- int active;
- int slotnum = mt_compute_slot(td, input);
- struct mt_slot *s = &td->curdata;
- struct input_mt *mt = input->mt;
+ int slotnum;
+ bool need_sync = false;
- if (slotnum < 0 || slotnum >= td->maxcontacts)
- return;
-
- if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) {
- struct input_mt_slot *slot = &mt->slots[slotnum];
- if (input_mt_is_active(slot) &&
- input_mt_is_used(mt, slot))
- return;
- }
-
- if (!(td->mtclass.quirks & MT_QUIRK_CONFIDENCE))
- s->confidence_state = true;
- active = (s->touch_state || s->inrange_state) &&
- s->confidence_state;
+ for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) {
+ clear_bit(slotnum, app->pending_palm_slots);
input_mt_slot(input, slotnum);
- input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
- if (active) {
- /* this finger is in proximity of the sensor */
- int wide = (s->w > s->h);
- int major = max(s->w, s->h);
- int minor = min(s->w, s->h);
- int orientation = wide;
+ input_mt_report_slot_state(input, MT_TOOL_PALM, false);
- if (s->has_azimuth)
- orientation = s->a;
-
- /*
- * divided by two to match visual scale of touch
- * for devices with this quirk
- */
- if (td->mtclass.quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
- major = major >> 1;
- minor = minor >> 1;
- }
-
- input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
- input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
- input_event(input, EV_ABS, ABS_MT_TOOL_X, s->cx);
- input_event(input, EV_ABS, ABS_MT_TOOL_Y, s->cy);
- input_event(input, EV_ABS, ABS_MT_DISTANCE,
- !s->touch_state);
- input_event(input, EV_ABS, ABS_MT_ORIENTATION,
- orientation);
- input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
- input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
- input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
-
- set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags);
- }
+ need_sync = true;
}
- td->num_received++;
+ if (need_sync) {
+ input_mt_sync_frame(input);
+ input_sync(input);
+ }
}
/*
* this function is called when a whole packet has been received and processed,
* so that it can decide what to send to the input layer.
*/
-static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
+static void mt_sync_frame(struct mt_device *td, struct mt_application *app,
+ struct input_dev *input)
{
- if (td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS)
- input_event(input, EV_KEY, BTN_LEFT, td->left_button_state);
+ if (app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS)
+ input_event(input, EV_KEY, BTN_LEFT, app->left_button_state);
input_mt_sync_frame(input);
- input_event(input, EV_MSC, MSC_TIMESTAMP, td->timestamp);
+ input_event(input, EV_MSC, MSC_TIMESTAMP, app->timestamp);
input_sync(input);
- td->num_received = 0;
- td->left_button_state = 0;
+
+ mt_release_pending_palms(td, app, input);
+
+ app->num_received = 0;
+ app->left_button_state = 0;
+
if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags))
set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags);
else
@@ -777,17 +914,15 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags);
}
-static int mt_compute_timestamp(struct mt_device *td, struct hid_field *field,
- __s32 value)
+static int mt_compute_timestamp(struct mt_application *app, __s32 value)
{
- long delta = value - td->dev_time;
- unsigned long jdelta = jiffies_to_usecs(jiffies - td->jiffies);
+ long delta = value - app->prev_scantime;
+ unsigned long jdelta = jiffies_to_usecs(jiffies - app->jiffies);
- td->jiffies = jiffies;
- td->dev_time = value;
+ app->jiffies = jiffies;
if (delta < 0)
- delta += field->logical_maximum;
+ delta += app->scantime_logical_max;
/* HID_DG_SCANTIME is expressed in 100us, we want it in us. */
delta *= 100;
@@ -796,7 +931,7 @@ static int mt_compute_timestamp(struct mt_device *td, struct hid_field *field,
/* No data received for a while, resync the timestamp. */
return 0;
else
- return td->timestamp + delta;
+ return app->timestamp + delta;
}
static int mt_touch_event(struct hid_device *hid, struct hid_field *field,
@@ -809,63 +944,90 @@ static int mt_touch_event(struct hid_device *hid, struct hid_field *field,
return 1;
}
-static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
- struct hid_usage *usage, __s32 value,
- bool first_packet)
+static int mt_process_slot(struct mt_device *td, struct input_dev *input,
+ struct mt_application *app,
+ struct mt_usages *slot)
{
- struct mt_device *td = hid_get_drvdata(hid);
- __s32 quirks = td->mtclass.quirks;
- struct input_dev *input = field->hidinput->input;
+ struct input_mt *mt = input->mt;
+ __s32 quirks = app->quirks;
+ bool valid = true;
+ bool confidence_state = true;
+ bool inrange_state = false;
+ int active;
+ int slotnum;
+ int tool = MT_TOOL_FINGER;
+
+ if (!slot)
+ return -EINVAL;
- if (hid->claimed & HID_CLAIMED_INPUT) {
- switch (usage->hid) {
- case HID_DG_INRANGE:
- if (quirks & MT_QUIRK_VALID_IS_INRANGE)
- td->curvalid = value;
- if (quirks & MT_QUIRK_HOVERING)
- td->curdata.inrange_state = value;
- break;
- case HID_DG_TIPSWITCH:
- if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
- td->curvalid = value;
- td->curdata.touch_state = value;
- break;
- case HID_DG_CONFIDENCE:
- if (quirks & MT_QUIRK_CONFIDENCE)
- td->curdata.confidence_state = value;
- if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE)
- td->curvalid = value;
- break;
- case HID_DG_CONTACTID:
- td->curdata.contactid = value;
- break;
- case HID_DG_TIPPRESSURE:
- td->curdata.p = value;
- break;
- case HID_GD_X:
- if (usage->code == ABS_MT_TOOL_X)
- td->curdata.cx = value;
- else
- td->curdata.x = value;
- break;
- case HID_GD_Y:
- if (usage->code == ABS_MT_TOOL_Y)
- td->curdata.cy = value;
- else
- td->curdata.y = value;
- break;
- case HID_DG_WIDTH:
- td->curdata.w = value;
- break;
- case HID_DG_HEIGHT:
- td->curdata.h = value;
- break;
- case HID_DG_SCANTIME:
- td->timestamp = mt_compute_timestamp(td, field, value);
- break;
- case HID_DG_CONTACTCOUNT:
- break;
- case HID_DG_AZIMUTH:
+ if ((quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) &&
+ app->num_received >= app->num_expected)
+ return -EAGAIN;
+
+ if (!(quirks & MT_QUIRK_ALWAYS_VALID)) {
+ if (quirks & MT_QUIRK_VALID_IS_INRANGE)
+ valid = *slot->inrange_state;
+ if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
+ valid = *slot->tip_state;
+ if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE)
+ valid = *slot->confidence_state;
+
+ if (!valid)
+ return 0;
+ }
+
+ slotnum = mt_compute_slot(td, app, slot, input);
+ if (slotnum < 0 || slotnum >= td->maxcontacts)
+ return 0;
+
+ if ((quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) {
+ struct input_mt_slot *i_slot = &mt->slots[slotnum];
+
+ if (input_mt_is_active(i_slot) &&
+ input_mt_is_used(mt, i_slot))
+ return -EAGAIN;
+ }
+
+ if (quirks & MT_QUIRK_CONFIDENCE)
+ confidence_state = *slot->confidence_state;
+
+ if (quirks & MT_QUIRK_HOVERING)
+ inrange_state = *slot->inrange_state;
+
+ active = *slot->tip_state || inrange_state;
+
+ if (app->application == HID_GD_SYSTEM_MULTIAXIS)
+ tool = MT_TOOL_DIAL;
+ else if (unlikely(!confidence_state)) {
+ tool = MT_TOOL_PALM;
+ if (!active &&
+ input_mt_is_active(&mt->slots[slotnum])) {
+ /*
+ * The non-confidence was reported for
+ * previously valid contact that is also no
+ * longer valid. We can't simply report
+ * lift-off as userspace will not be aware
+ * of non-confidence, so we need to split
+ * it into 2 events: active MT_TOOL_PALM
+ * and a separate liftoff.
+ */
+ active = true;
+ set_bit(slotnum, app->pending_palm_slots);
+ }
+ }
+
+ input_mt_slot(input, slotnum);
+ input_mt_report_slot_state(input, tool, active);
+ if (active) {
+ /* this finger is in proximity of the sensor */
+ int wide = (*slot->w > *slot->h);
+ int major = max(*slot->w, *slot->h);
+ int minor = min(*slot->w, *slot->h);
+ int orientation = wide;
+ int max_azimuth;
+ int azimuth;
+
+ if (slot->a != DEFAULT_ZERO) {
/*
* Azimuth is counter-clockwise and ranges from [0, MAX)
* (a full revolution). Convert it to clockwise ranging
@@ -876,77 +1038,107 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
* out of range to [-MAX/2, MAX/2] to report an upside
* down ellipsis.
*/
- if (value > field->logical_maximum / 2)
- value -= field->logical_maximum;
- td->curdata.a = -value;
- td->curdata.has_azimuth = true;
- break;
- case HID_DG_TOUCH:
- /* do nothing */
- break;
+ azimuth = *slot->a;
+ max_azimuth = input_abs_get_max(input,
+ ABS_MT_ORIENTATION);
+ if (azimuth > max_azimuth * 2)
+ azimuth -= max_azimuth * 4;
+ orientation = -azimuth;
+ }
- default:
+ if (quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
/*
- * For Win8 PTP touchpads we should only look at
- * non finger/touch events in the first_packet of
- * a (possible) multi-packet frame.
+ * divided by two to match visual scale of touch
+ * for devices with this quirk
*/
- if ((quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
- !first_packet)
- return;
+ major = major >> 1;
+ minor = minor >> 1;
+ }
- /*
- * For Win8 PTP touchpads we map both the clickpad click
- * and any "external" left buttons to BTN_LEFT if a
- * device claims to have both we need to report 1 for
- * BTN_LEFT if either is pressed, so we or all values
- * together and report the result in mt_sync_frame().
- */
- if ((quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
- usage->type == EV_KEY && usage->code == BTN_LEFT) {
- td->left_button_state |= value;
- return;
- }
+ input_event(input, EV_ABS, ABS_MT_POSITION_X, *slot->x);
+ input_event(input, EV_ABS, ABS_MT_POSITION_Y, *slot->y);
+ input_event(input, EV_ABS, ABS_MT_TOOL_X, *slot->cx);
+ input_event(input, EV_ABS, ABS_MT_TOOL_Y, *slot->cy);
+ input_event(input, EV_ABS, ABS_MT_DISTANCE, !*slot->tip_state);
+ input_event(input, EV_ABS, ABS_MT_ORIENTATION, orientation);
+ input_event(input, EV_ABS, ABS_MT_PRESSURE, *slot->p);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+ input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
+
+ set_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags);
+ }
- if (usage->type)
- input_event(input, usage->type, usage->code,
- value);
+ return 0;
+}
+
+static void mt_process_mt_event(struct hid_device *hid,
+ struct mt_application *app,
+ struct hid_field *field,
+ struct hid_usage *usage,
+ __s32 value,
+ bool first_packet)
+{
+ __s32 quirks = app->quirks;
+ struct input_dev *input = field->hidinput->input;
+
+ if (!usage->type || !(hid->claimed & HID_CLAIMED_INPUT))
+ return;
+
+ if (quirks & MT_QUIRK_WIN8_PTP_BUTTONS) {
+
+ /*
+ * For Win8 PTP touchpads we should only look at
+ * non finger/touch events in the first_packet of a
+ * (possible) multi-packet frame.
+ */
+ if (!first_packet)
return;
- }
- if (usage->usage_index + 1 == field->report_count) {
- /* we only take into account the last report. */
- if (usage->hid == td->last_slot_field)
- mt_complete_slot(td, field->hidinput->input);
+ /*
+ * For Win8 PTP touchpads we map both the clickpad click
+ * and any "external" left buttons to BTN_LEFT if a
+ * device claims to have both we need to report 1 for
+ * BTN_LEFT if either is pressed, so we or all values
+ * together and report the result in mt_sync_frame().
+ */
+ if (usage->type == EV_KEY && usage->code == BTN_LEFT) {
+ app->left_button_state |= value;
+ return;
}
-
}
+
+ input_event(input, usage->type, usage->code, value);
}
-static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
+static void mt_touch_report(struct hid_device *hid,
+ struct mt_report_data *rdata)
{
struct mt_device *td = hid_get_drvdata(hid);
+ struct hid_report *report = rdata->report;
+ struct mt_application *app = rdata->application;
struct hid_field *field;
+ struct input_dev *input;
+ struct mt_usages *slot;
bool first_packet;
unsigned count;
- int r, n, scantime = 0;
+ int r, n;
+ int scantime = 0;
+ int contact_count = -1;
/* sticky fingers release in progress, abort */
if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
return;
+ scantime = *app->scantime;
+ app->timestamp = mt_compute_timestamp(app, scantime);
+ if (app->raw_cc != DEFAULT_ZERO)
+ contact_count = *app->raw_cc;
+
/*
* Includes multi-packet support where subsequent
* packets are sent with zero contactcount.
*/
- if (td->scantime_index >= 0) {
- field = report->field[td->scantime_index];
- scantime = field->value[td->scantime_val_index];
- }
- if (td->cc_index >= 0) {
- struct hid_field *field = report->field[td->cc_index];
- int value = field->value[td->cc_value_index];
-
+ if (contact_count >= 0) {
/*
* For Win8 PTPs the first packet (td->num_received == 0) may
* have a contactcount of 0 if there only is a button event.
@@ -954,16 +1146,25 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
* of a possible multi-packet frame be checking that the
* timestamp has changed.
*/
- if ((td->mtclass.quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
- td->num_received == 0 && td->prev_scantime != scantime)
- td->num_expected = value;
+ if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
+ app->num_received == 0 &&
+ app->prev_scantime != scantime)
+ app->num_expected = contact_count;
/* A non 0 contact count always indicates a first packet */
- else if (value)
- td->num_expected = value;
+ else if (contact_count)
+ app->num_expected = contact_count;
+ }
+ app->prev_scantime = scantime;
+
+ first_packet = app->num_received == 0;
+
+ input = report->field[0]->hidinput->input;
+
+ list_for_each_entry(slot, &app->mt_usages, list) {
+ if (!mt_process_slot(td, input, app, slot))
+ app->num_received++;
}
- td->prev_scantime = scantime;
- first_packet = td->num_received == 0;
for (r = 0; r < report->maxfield; r++) {
field = report->field[r];
count = field->report_count;
@@ -972,12 +1173,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
continue;
for (n = 0; n < count; n++)
- mt_process_mt_event(hid, field, &field->usage[n],
- field->value[n], first_packet);
+ mt_process_mt_event(hid, app, field,
+ &field->usage[n], field->value[n],
+ first_packet);
}
- if (td->num_received >= td->num_expected)
- mt_sync_frame(td, report->field[0]->hidinput->input);
+ if (app->num_received >= app->num_expected)
+ mt_sync_frame(td, app, input);
/*
* Windows 8 specs says 2 things:
@@ -997,7 +1199,7 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
* only affect laggish machines and the ones that have a firmware
* defect.
*/
- if (td->mtclass.quirks & MT_QUIRK_STICKY_FINGERS) {
+ if (app->quirks & MT_QUIRK_STICKY_FINGERS) {
if (test_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags))
mod_timer(&td->release_timer,
jiffies + msecs_to_jiffies(100));
@@ -1009,7 +1211,8 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
}
static int mt_touch_input_configured(struct hid_device *hdev,
- struct hid_input *hi)
+ struct hid_input *hi,
+ struct mt_application *app)
{
struct mt_device *td = hid_get_drvdata(hdev);
struct mt_class *cls = &td->mtclass;
@@ -1019,28 +1222,36 @@ static int mt_touch_input_configured(struct hid_device *hdev,
if (!td->maxcontacts)
td->maxcontacts = MT_DEFAULT_MAXCONTACT;
- mt_post_parse(td);
+ mt_post_parse(td, app);
if (td->serial_maybe)
- mt_post_parse_default_settings(td);
+ mt_post_parse_default_settings(td, app);
if (cls->is_indirect)
- td->mt_flags |= INPUT_MT_POINTER;
+ app->mt_flags |= INPUT_MT_POINTER;
- if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
- td->mt_flags |= INPUT_MT_DROP_UNUSED;
+ if (app->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
+ app->mt_flags |= INPUT_MT_DROP_UNUSED;
/* check for clickpads */
- if ((td->mt_flags & INPUT_MT_POINTER) && (td->buttons_count == 1))
+ if ((app->mt_flags & INPUT_MT_POINTER) &&
+ (app->buttons_count == 1))
td->is_buttonpad = true;
if (td->is_buttonpad)
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
- ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+ app->pending_palm_slots = devm_kcalloc(&hi->input->dev,
+ BITS_TO_LONGS(td->maxcontacts),
+ sizeof(long),
+ GFP_KERNEL);
+ if (!app->pending_palm_slots)
+ return -ENOMEM;
+
+ ret = input_mt_init_slots(input, td->maxcontacts, app->mt_flags);
if (ret)
return ret;
- td->mt_flags = 0;
+ app->mt_flags = 0;
return 0;
}
@@ -1051,6 +1262,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
unsigned long **bit, int *max)
{
struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_application *application;
+ struct mt_report_data *rdata;
+
+ rdata = mt_find_report_data(td, field->report);
+ if (!rdata) {
+ hid_err(hdev, "failed to allocate data for report\n");
+ return 0;
+ }
+
+ application = rdata->application;
/*
* If mtclass.export_all_inputs is not set, only map fields from
@@ -1066,8 +1287,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
field->application != HID_GD_SYSTEM_CONTROL &&
field->application != HID_CP_CONSUMER_CONTROL &&
field->application != HID_GD_WIRELESS_RADIO_CTLS &&
+ field->application != HID_GD_SYSTEM_MULTIAXIS &&
!(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS &&
- td->mtclass.quirks & MT_QUIRK_ASUS_CUSTOM_UP))
+ application->quirks & MT_QUIRK_ASUS_CUSTOM_UP))
return -1;
/*
@@ -1076,7 +1298,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* map usages to input keys.
*/
if (field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS &&
- td->mtclass.quirks & MT_QUIRK_ASUS_CUSTOM_UP &&
+ application->quirks & MT_QUIRK_ASUS_CUSTOM_UP &&
(usage->hid & HID_USAGE_PAGE) == HID_UP_CUSTOM) {
set_bit(EV_REP, hi->input->evbit);
if (field->flags & HID_MAIN_ITEM_VARIABLE)
@@ -1093,23 +1315,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 1;
}
- /*
- * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
- * for the stylus.
- * The check for mt_report_id ensures we don't process
- * HID_DG_CONTACTCOUNT from the pen report as it is outside the physical
- * collection, but within the report ID.
- */
- if (field->physical == HID_DG_STYLUS)
- return 0;
- else if ((field->physical == 0) &&
- (field->report->id != td->mt_report_id) &&
- (td->mt_report_id != -1))
- return 0;
-
- if (field->application == HID_DG_TOUCHSCREEN ||
- field->application == HID_DG_TOUCHPAD)
- return mt_touch_input_mapping(hdev, hi, field, usage, bit, max);
+ if (rdata->is_mt_collection)
+ return mt_touch_input_mapping(hdev, hi, field, usage, bit, max,
+ application);
/* let hid-core decide for the others */
return 0;
@@ -1119,15 +1327,11 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- /*
- * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN"
- * for the stylus.
- */
- if (field->physical == HID_DG_STYLUS)
- return 0;
+ struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_report_data *rdata;
- if (field->application == HID_DG_TOUCHSCREEN ||
- field->application == HID_DG_TOUCHPAD) {
+ rdata = mt_find_report_data(td, field->report);
+ if (rdata && rdata->is_mt_collection) {
/* We own these mappings, tell hid-input to ignore them */
return -1;
}
@@ -1140,8 +1344,10 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct mt_device *td = hid_get_drvdata(hid);
+ struct mt_report_data *rdata;
- if (field->report->id == td->mt_report_id)
+ rdata = mt_find_report_data(td, field->report);
+ if (rdata && rdata->is_mt_collection)
return mt_touch_event(hid, field, usage, value);
return 0;
@@ -1151,12 +1357,14 @@ static void mt_report(struct hid_device *hid, struct hid_report *report)
{
struct mt_device *td = hid_get_drvdata(hid);
struct hid_field *field = report->field[0];
+ struct mt_report_data *rdata;
if (!(hid->claimed & HID_CLAIMED_INPUT))
return;
- if (report->id == td->mt_report_id)
- return mt_touch_report(hid, report);
+ rdata = mt_find_report_data(td, report);
+ if (rdata && rdata->is_mt_collection)
+ return mt_touch_report(hid, rdata);
if (field && field->hidinput && field->hidinput->input)
input_sync(field->hidinput->input);
@@ -1197,9 +1405,9 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev,
return true;
case HID_DG_CONTACTMAX:
- if (td->mtclass.maxcontacts) {
+ if (cls->maxcontacts) {
max = min_t(int, field->logical_maximum,
- td->mtclass.maxcontacts);
+ cls->maxcontacts);
if (field->value[index] != max) {
field->value[index] = max;
return true;
@@ -1259,12 +1467,13 @@ static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency,
}
}
-static void mt_post_parse_default_settings(struct mt_device *td)
+static void mt_post_parse_default_settings(struct mt_device *td,
+ struct mt_application *app)
{
- __s32 quirks = td->mtclass.quirks;
+ __s32 quirks = app->quirks;
/* unknown serial device needs special quirks */
- if (td->touches_by_report == 1) {
+ if (list_is_singular(&app->mt_usages)) {
quirks |= MT_QUIRK_ALWAYS_VALID;
quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP;
quirks &= ~MT_QUIRK_VALID_IS_INRANGE;
@@ -1272,21 +1481,13 @@ static void mt_post_parse_default_settings(struct mt_device *td)
quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
}
- td->mtclass.quirks = quirks;
+ app->quirks = quirks;
}
-static void mt_post_parse(struct mt_device *td)
+static void mt_post_parse(struct mt_device *td, struct mt_application *app)
{
- struct mt_fields *f = td->fields;
- struct mt_class *cls = &td->mtclass;
-
- if (td->touches_by_report > 0) {
- int field_count_per_touch = f->length / td->touches_by_report;
- td->last_slot_field = f->usages[field_count_per_touch - 1];
- }
-
- if (td->cc_index < 0)
- cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
+ if (!app->have_contact_count)
+ app->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE;
}
static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
@@ -1295,13 +1496,24 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
char *name;
const char *suffix = NULL;
unsigned int application = 0;
+ struct mt_report_data *rdata;
+ struct mt_application *mt_application = NULL;
struct hid_report *report;
int ret;
list_for_each_entry(report, &hi->reports, hidinput_list) {
application = report->application;
- if (report->id == td->mt_report_id) {
- ret = mt_touch_input_configured(hdev, hi);
+ rdata = mt_find_report_data(td, report);
+ if (!rdata) {
+ hid_err(hdev, "failed to allocate data for report\n");
+ return -ENOMEM;
+ }
+
+ mt_application = rdata->application;
+
+ if (rdata->is_mt_collection) {
+ ret = mt_touch_input_configured(hdev, hi,
+ mt_application);
if (ret)
return ret;
}
@@ -1327,6 +1539,7 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
case HID_GD_SYSTEM_CONTROL:
case HID_CP_CONSUMER_CONTROL:
case HID_GD_WIRELESS_RADIO_CTLS:
+ case HID_GD_SYSTEM_MULTIAXIS:
/* already handled by hid core */
break;
case HID_DG_TOUCHSCREEN:
@@ -1390,6 +1603,7 @@ static void mt_fix_const_fields(struct hid_device *hdev, unsigned int usage)
static void mt_release_contacts(struct hid_device *hid)
{
struct hid_input *hidinput;
+ struct mt_application *application;
struct mt_device *td = hid_get_drvdata(hid);
list_for_each_entry(hidinput, &hid->inputs, list) {
@@ -1409,7 +1623,9 @@ static void mt_release_contacts(struct hid_device *hid)
}
}
- td->num_received = 0;
+ list_for_each_entry(application, &td->applications, list) {
+ application->num_received = 0;
+ }
}
static void mt_expired_timeout(struct timer_list *t)
@@ -1432,7 +1648,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
struct mt_device *td;
- struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
+ const struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
for (i = 0; mt_classes[i].name ; i++) {
if (id->driver_data == mt_classes[i].name) {
@@ -1449,17 +1665,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
td->hdev = hdev;
td->mtclass = *mtclass;
td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
- td->cc_index = -1;
- td->scantime_index = -1;
- td->mt_report_id = -1;
hid_set_drvdata(hdev, td);
- td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields),
- GFP_KERNEL);
- if (!td->fields) {
- dev_err(&hdev->dev, "cannot allocate multitouch fields data\n");
- return -ENOMEM;
- }
+ INIT_LIST_HEAD(&td->applications);
+ INIT_LIST_HEAD(&td->reports);
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
td->serial_maybe = true;
@@ -1496,10 +1705,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
- /* release .fields memory as it is not used anymore */
- devm_kfree(&hdev->dev, td->fields);
- td->fields = NULL;
-
return 0;
}
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index c1652bb7bd15..2ce194a84868 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -484,7 +484,7 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
return;
}
- if ((ret_size > size) || (ret_size <= 2)) {
+ if ((ret_size > size) || (ret_size < 2)) {
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
__func__, size, ret_size);
return;
@@ -1002,18 +1002,18 @@ static int i2c_hid_probe(struct i2c_client *client,
return client->irq;
}
- ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL);
+ ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL);
if (!ihid)
return -ENOMEM;
if (client->dev.of_node) {
ret = i2c_hid_of_probe(client, &ihid->pdata);
if (ret)
- goto err;
+ return ret;
} else if (!platform_data) {
ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
if (ret)
- goto err;
+ return ret;
} else {
ihid->pdata = *platform_data;
}
@@ -1021,21 +1021,20 @@ static int i2c_hid_probe(struct i2c_client *client,
/* Parse platform agnostic common properties from ACPI / device tree */
i2c_hid_fwnode_probe(client, &ihid->pdata);
- ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd");
- if (IS_ERR(ihid->pdata.supply)) {
- ret = PTR_ERR(ihid->pdata.supply);
- if (ret != -EPROBE_DEFER)
- dev_err(&client->dev, "Failed to get regulator: %d\n",
- ret);
- goto err;
- }
+ ihid->pdata.supplies[0].supply = "vdd";
+ ihid->pdata.supplies[1].supply = "vddl";
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(ihid->pdata.supplies),
+ ihid->pdata.supplies);
+ if (ret)
+ return ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
+ ihid->pdata.supplies);
+ if (ret < 0)
+ return ret;
- ret = regulator_enable(ihid->pdata.supply);
- if (ret < 0) {
- dev_err(&client->dev, "Failed to enable regulator: %d\n",
- ret);
- goto err;
- }
if (ihid->pdata.post_power_delay_ms)
msleep(ihid->pdata.post_power_delay_ms);
@@ -1122,11 +1121,9 @@ err_pm:
pm_runtime_disable(&client->dev);
err_regulator:
- regulator_disable(ihid->pdata.supply);
-
-err:
+ regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
+ ihid->pdata.supplies);
i2c_hid_free_buffers(ihid);
- kfree(ihid);
return ret;
}
@@ -1148,9 +1145,8 @@ static int i2c_hid_remove(struct i2c_client *client)
if (ihid->bufsize)
i2c_hid_free_buffers(ihid);
- regulator_disable(ihid->pdata.supply);
-
- kfree(ihid);
+ regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
+ ihid->pdata.supplies);
return 0;
}
@@ -1201,9 +1197,8 @@ static int i2c_hid_suspend(struct device *dev)
hid_warn(hid, "Failed to enable irq wake: %d\n",
wake_status);
} else {
- ret = regulator_disable(ihid->pdata.supply);
- if (ret < 0)
- hid_warn(hid, "Failed to disable supply: %d\n", ret);
+ regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
+ ihid->pdata.supplies);
}
return 0;
@@ -1218,9 +1213,11 @@ static int i2c_hid_resume(struct device *dev)
int wake_status;
if (!device_may_wakeup(&client->dev)) {
- ret = regulator_enable(ihid->pdata.supply);
- if (ret < 0)
- hid_warn(hid, "Failed to enable supply: %d\n", ret);
+ ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
+ ihid->pdata.supplies);
+ if (ret)
+ hid_warn(hid, "Failed to enable supplies: %d\n", ret);
+
if (ihid->pdata.post_power_delay_ms)
msleep(ihid->pdata.post_power_delay_ms);
} else if (ihid->irq_wake_enabled) {
diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c
index 9a60ec13cb10..bfbca7ec54ce 100644
--- a/drivers/hid/intel-ish-hid/ipc/ipc.c
+++ b/drivers/hid/intel-ish-hid/ipc/ipc.c
@@ -907,8 +907,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
struct ishtp_device *dev;
int i;
- dev = kzalloc(sizeof(struct ishtp_device) + sizeof(struct ish_hw),
- GFP_KERNEL);
+ dev = devm_kzalloc(&pdev->dev,
+ sizeof(struct ishtp_device) + sizeof(struct ish_hw),
+ GFP_KERNEL);
if (!dev)
return NULL;
@@ -925,7 +926,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) {
struct wr_msg_ctl_info *tx_buf;
- tx_buf = kzalloc(sizeof(struct wr_msg_ctl_info), GFP_KERNEL);
+ tx_buf = devm_kzalloc(&pdev->dev,
+ sizeof(struct wr_msg_ctl_info),
+ GFP_KERNEL);
if (!tx_buf) {
/*
* IPC buffers may be limited or not available
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
index a2c53ea3b5ed..050f9872f5c0 100644
--- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c
+++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
@@ -95,6 +95,13 @@ static int ish_init(struct ishtp_device *dev)
return 0;
}
+static const struct pci_device_id ish_invalid_pci_ids[] = {
+ /* Mehlow platform special pci ids */
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)},
+ {}
+};
+
/**
* ish_probe() - PCI driver probe callback
* @pdev: pci device
@@ -110,6 +117,10 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct ish_hw *hw;
int ret;
+ /* Check for invalid platforms for ISH support */
+ if (pci_dev_present(ish_invalid_pci_ids))
+ return -ENODEV;
+
/* enable pci dev */
ret = pci_enable_device(pdev);
if (ret) {
@@ -172,7 +183,6 @@ free_irq:
free_irq(pdev->irq, dev);
free_device:
pci_iounmap(pdev, hw->mem_addr);
- kfree(dev);
release_regions:
pci_release_regions(pdev);
disable_device:
@@ -202,7 +212,6 @@ static void ish_remove(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_clear_master(pdev);
pci_disable_device(pdev);
- kfree(ishtp_dev);
}
static struct device __maybe_unused *ish_resume_device;
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index e3ce233f8bdc..23872d08308c 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -36,6 +36,7 @@
#include <linux/hiddev.h>
#include <linux/compat.h>
#include <linux/vmalloc.h>
+#include <linux/nospec.h>
#include "usbhid.h"
#ifdef CONFIG_USB_DYNAMIC_MINORS
@@ -469,10 +470,14 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
if (uref->field_index >= report->maxfield)
goto inval;
+ uref->field_index = array_index_nospec(uref->field_index,
+ report->maxfield);
field = report->field[uref->field_index];
if (uref->usage_index >= field->maxusage)
goto inval;
+ uref->usage_index = array_index_nospec(uref->usage_index,
+ field->maxusage);
uref->usage_code = field->usage[uref->usage_index].hid;
@@ -499,6 +504,8 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
if (uref->field_index >= report->maxfield)
goto inval;
+ uref->field_index = array_index_nospec(uref->field_index,
+ report->maxfield);
field = report->field[uref->field_index];
@@ -753,6 +760,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (finfo.field_index >= report->maxfield)
break;
+ finfo.field_index = array_index_nospec(finfo.field_index,
+ report->maxfield);
field = report->field[finfo.field_index];
memset(&finfo, 0, sizeof(finfo));
@@ -797,6 +806,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (cinfo.index >= hid->maxcollection)
break;
+ cinfo.index = array_index_nospec(cinfo.index,
+ hid->maxcollection);
cinfo.type = hid->collection[cinfo.index].type;
cinfo.usage = hid->collection[cinfo.index].usage;
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index d6797535fff9..ffe59a19b3a3 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -703,18 +703,6 @@ struct wacom_hdev_data {
static LIST_HEAD(wacom_udev_list);
static DEFINE_MUTEX(wacom_udev_list_lock);
-static bool 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);
-}
-
static bool wacom_are_sibling(struct hid_device *hdev,
struct hid_device *sibling)
{
@@ -737,10 +725,10 @@ static bool wacom_are_sibling(struct hid_device *hdev,
* the same physical parent device path.
*/
if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) {
- if (!compare_device_paths(hdev, sibling, '/'))
+ if (!hid_compare_device_paths(hdev, sibling, '/'))
return false;
} else {
- if (!compare_device_paths(hdev, sibling, '.'))
+ if (!hid_compare_device_paths(hdev, sibling, '.'))
return false;
}
@@ -787,7 +775,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
/* Try to find an already-probed interface from the same device */
list_for_each_entry(data, &wacom_udev_list, list) {
- if (compare_device_paths(hdev, data->dev, '/')) {
+ if (hid_compare_device_paths(hdev, data->dev, '/')) {
kref_get(&data->kref);
return data;
}
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 0bb44d0088ed..ad7afa74d365 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -3365,8 +3365,14 @@ void wacom_setup_device_quirks(struct wacom *wacom)
if (features->type >= INTUOSHT && features->type <= BAMBOO_PT)
features->device_type |= WACOM_DEVICETYPE_PAD;
- features->x_max = 4096;
- features->y_max = 4096;
+ if (features->type == INTUOSHT2) {
+ features->x_max = features->x_max / 10;
+ features->y_max = features->y_max / 10;
+ }
+ else {
+ features->x_max = 4096;
+ features->y_max = 4096;
+ }
}
else if (features->pktlen == WACOM_PKGLEN_BBTOUCH) {
features->device_type |= WACOM_DEVICETYPE_PAD;