diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2020-07-21 19:02:33 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2020-07-21 19:02:33 -0700 |
commit | 9b031c86506cef9acae45e61339fcf9deaabb793 (patch) | |
tree | 9095d638ba9384f86df8d61dcf1f129c082481e1 /drivers/hid | |
parent | 53aab92dec447f93489e07924e310d605a389dea (diff) | |
parent | 04d5ce620f794f1df69b5f1b9ad62910fea547f1 (diff) | |
download | linux-9b031c86506cef9acae45e61339fcf9deaabb793.tar.gz linux-9b031c86506cef9acae45e61339fcf9deaabb793.tar.bz2 linux-9b031c86506cef9acae45e61339fcf9deaabb793.zip |
Merge branch 'elan-i2c' into next
Bring in update to Elan touchpad driver to support newer touchpads with
higher resolution.
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 20 | ||||
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/hid-alps.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-appleir.c | 12 | ||||
-rw-r--r-- | drivers/hid/hid-glorious.c | 86 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 13 | ||||
-rw-r--r-- | drivers/hid/hid-lg-g15.c | 10 | ||||
-rw-r--r-- | drivers/hid/hid-logitech-dj.c | 11 | ||||
-rw-r--r-- | drivers/hid/hid-mcp2221.c | 742 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-quirks.c | 4 | ||||
-rw-r--r-- | drivers/hid/hid-rmi.c | 1 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-core.c | 2 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/hbm.h | 2 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h | 2 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 37 | ||||
-rw-r--r-- | drivers/hid/usbhid/usbhid.h | 1 | ||||
-rw-r--r-- | drivers/hid/wacom_sys.c | 4 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 88 |
19 files changed, 946 insertions, 95 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 494a39e74939..34f07371716d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -362,6 +362,13 @@ config HID_GFRM ---help--- Support for Google Fiber TV Box remote controls +config HID_GLORIOUS + tristate "Glorious PC Gaming Race mice" + depends on HID + help + Support for Glorious PC Gaming Race mice such as + the Glorious Model O, O- and D. + config HID_HOLTEK tristate "Holtek HID devices" depends on USB_HID @@ -1039,7 +1046,7 @@ config HID_U2FZERO U2F Zero only supports blinking its LED, so this driver doesn't allow setting the brightness to anything but 1, which will - trigger a single blink and immediately reset to back 0. + trigger a single blink and immediately reset back to 0. config HID_WACOM tristate "Wacom Intuos/Graphire tablet support (USB)" @@ -1145,6 +1152,17 @@ config HID_ALPS Say Y here if you have a Alps touchpads over i2c-hid or usbhid and want support for its special functionalities. +config HID_MCP2221 + tristate "Microchip MCP2221 HID USB-to-I2C/SMbus host support" + depends on USB_HID && I2C + depends on GPIOLIB + ---help--- + Provides I2C and SMBUS host adapter functionality over USB-HID + through MCP2221 device. + + To compile this driver as a module, choose M here: the module + will be called hid-mcp2221.ko. + endmenu endif # HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index bfefa365b1ce..d8ea4b8c95af 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o obj-$(CONFIG_HID_GFRM) += hid-gfrm.o +obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o obj-$(CONFIG_HID_GT683R) += hid-gt683r.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o @@ -70,6 +71,7 @@ obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o obj-$(CONFIG_HID_MACALLY) += hid-macally.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MALTRON) += hid-maltron.o +obj-$(CONFIG_HID_MCP2221) += hid-mcp2221.o obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 28588f74425e..6f1fe7248d81 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c @@ -801,6 +801,7 @@ static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id) break; case HID_DEVICE_ID_ALPS_U1_DUAL: case HID_DEVICE_ID_ALPS_U1: + case HID_DEVICE_ID_ALPS_U1_UNICORN_LEGACY: data->dev_type = U1; break; default: diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index bf8d4afe0d6a..8deded185725 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -283,11 +283,9 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) int ret; struct appleir *appleir; - appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL); - if (!appleir) { - ret = -ENOMEM; - goto allocfail; - } + appleir = devm_kzalloc(&hid->dev, sizeof(struct appleir), GFP_KERNEL); + if (!appleir) + return -ENOMEM; appleir->hid = hid; @@ -313,8 +311,7 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) return 0; fail: - kfree(appleir); -allocfail: + devm_kfree(&hid->dev, appleir); return ret; } @@ -323,7 +320,6 @@ static void appleir_remove(struct hid_device *hid) struct appleir *appleir = hid_get_drvdata(hid); hid_hw_stop(hid); del_timer_sync(&appleir->key_up_timer); - kfree(appleir); } static const struct hid_device_id appleir_devices[] = { diff --git a/drivers/hid/hid-glorious.c b/drivers/hid/hid-glorious.c new file mode 100644 index 000000000000..558eb08c19ef --- /dev/null +++ b/drivers/hid/hid-glorious.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB HID driver for Glorious PC Gaming Race + * Glorious Model O, O- and D mice. + * + * Copyright (c) 2020 Samuel Čavoj <sammko@sammserver.com> + */ + +/* + */ + +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +MODULE_AUTHOR("Samuel Čavoj <sammko@sammserver.com>"); +MODULE_DESCRIPTION("HID driver for Glorious PC Gaming Race mice"); + +/* + * Glorious Model O and O- specify the const flag in the consumer input + * report descriptor, which leads to inputs being ignored. Fix this + * by patching the descriptor. + */ +static __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (*rsize == 213 && + rdesc[84] == 129 && rdesc[112] == 129 && rdesc[140] == 129 && + rdesc[85] == 3 && rdesc[113] == 3 && rdesc[141] == 3) { + hid_info(hdev, "patching Glorious Model O consumer control report descriptor\n"); + rdesc[85] = rdesc[113] = rdesc[141] = \ + HID_MAIN_ITEM_VARIABLE | HID_MAIN_ITEM_RELATIVE; + } + return rdesc; +} + +static void glorious_update_name(struct hid_device *hdev) +{ + const char *model = "Device"; + + switch (hdev->product) { + case USB_DEVICE_ID_GLORIOUS_MODEL_O: + model = "Model O"; break; + case USB_DEVICE_ID_GLORIOUS_MODEL_D: + model = "Model D"; break; + } + + snprintf(hdev->name, sizeof(hdev->name), "%s %s", "Glorious", model); +} + +static int glorious_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + hdev->quirks |= HID_QUIRK_INPUT_PER_APP; + + ret = hid_parse(hdev); + if (ret) + return ret; + + glorious_update_name(hdev); + + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); +} + +static const struct hid_device_id glorious_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_GLORIOUS, + USB_DEVICE_ID_GLORIOUS_MODEL_O) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLORIOUS, + USB_DEVICE_ID_GLORIOUS_MODEL_D) }, + { } +}; +MODULE_DEVICE_TABLE(hid, glorious_devices); + +static struct hid_driver glorious_driver = { + .name = "glorious", + .id_table = glorious_devices, + .probe = glorious_probe, + .report_fixup = glorious_report_fixup +}; + +module_hid_driver(glorious_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9f2213426556..1c71a1aa76b2 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -79,10 +79,10 @@ #define HID_DEVICE_ID_ALPS_U1_DUAL_PTP 0x121F #define HID_DEVICE_ID_ALPS_U1_DUAL_3BTN_PTP 0x1220 #define HID_DEVICE_ID_ALPS_U1 0x1215 +#define HID_DEVICE_ID_ALPS_U1_UNICORN_LEGACY 0x121E #define HID_DEVICE_ID_ALPS_T4_BTNLESS 0x120C #define HID_DEVICE_ID_ALPS_1222 0x1222 - #define USB_VENDOR_ID_AMI 0x046b #define USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE 0xff10 @@ -385,6 +385,7 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_C002 0xc002 #define USB_VENDOR_ID_ELAN 0x04f3 #define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401 @@ -464,6 +465,10 @@ #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100 +#define USB_VENDOR_ID_GLORIOUS 0x258a +#define USB_DEVICE_ID_GLORIOUS_MODEL_D 0x0033 +#define USB_DEVICE_ID_GLORIOUS_MODEL_O 0x0036 + #define I2C_VENDOR_ID_GOODIX 0x27c6 #define I2C_DEVICE_ID_GOODIX_01F0 0x01f0 @@ -755,6 +760,7 @@ #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 #define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222 +#define USB_DEVICE_ID_LOGITECH_G11 0xc225 #define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227 #define USB_DEVICE_ID_LOGITECH_G510 0xc22d #define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e @@ -821,6 +827,7 @@ #define USB_DEVICE_ID_PICK16F1454 0x0042 #define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7 #define USB_DEVICE_ID_LUXAFOR 0xf372 +#define USB_DEVICE_ID_MCP2221 0x00dd #define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b @@ -1092,6 +1099,9 @@ #define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300 #define USB_DEVICE_ID_SYMBOL_SCANNER_3 0x1200 +#define I2C_VENDOR_ID_SYNAPTICS 0x06cb +#define I2C_PRODUCT_ID_SYNAPTICS_SYNA2393 0x7a13 + #define USB_VENDOR_ID_SYNAPTICS 0x06cb #define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 #define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 @@ -1106,6 +1116,7 @@ #define USB_DEVICE_ID_SYNAPTICS_LTS2 0x1d10 #define USB_DEVICE_ID_SYNAPTICS_HD 0x0ac3 #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD 0x1ac3 +#define USB_DEVICE_ID_SYNAPTICS_DELL_K12A 0x2819 #define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012 0x2968 #define USB_DEVICE_ID_SYNAPTICS_TP_V103 0x5710 #define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5 0x81a7 diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index 8a9268a5c66a..ef0cbcd7540d 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -803,8 +803,10 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) } if (ret < 0) { - hid_err(hdev, "Error disabling keyboard emulation for the G-keys\n"); - goto error_hw_stop; + hid_err(hdev, "Error %d disabling keyboard emulation for the G-keys, falling back to generic hid-input driver\n", + ret); + hid_set_drvdata(hdev, NULL); + return 0; } /* Get initial brightness levels */ @@ -870,6 +872,10 @@ error_hw_stop: } static const struct hid_device_id lg_g15_devices[] = { + /* The G11 is a G15 without the LCD, treat it as a G15 */ + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_G11), + .driver_data = LG_G15 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G15_LCD), .driver_data = LG_G15 }, diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index bb50d6e7745b..ed9b1c1f460d 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -16,11 +16,11 @@ #include <asm/unaligned.h> #include "hid-ids.h" -#define DJ_MAX_PAIRED_DEVICES 6 +#define DJ_MAX_PAIRED_DEVICES 7 #define DJ_MAX_NUMBER_NOTIFS 8 #define DJ_RECEIVER_INDEX 0 #define DJ_DEVICE_INDEX_MIN 1 -#define DJ_DEVICE_INDEX_MAX 6 +#define DJ_DEVICE_INDEX_MAX 7 #define DJREPORT_SHORT_LENGTH 15 #define DJREPORT_LONG_LENGTH 32 @@ -980,6 +980,11 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, break; } + /* custom receiver device (eg. powerplay) */ + if (hidpp_report->device_index == 7) { + workitem.reports_supported |= HIDPP; + } + if (workitem.type == WORKITEM_TYPE_EMPTY) { hid_warn(hdev, "unusable device of type %s (0x%02x) connected on slot %d", @@ -1368,6 +1373,8 @@ static int logi_dj_ll_parse(struct hid_device *hid) } if (djdev->reports_supported & HIDPP) { + dbg_hid("%s: sending a HID++ descriptor, reports_supported: %llx\n", + __func__, djdev->reports_supported); rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor)); } diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c new file mode 100644 index 000000000000..d958475f8c81 --- /dev/null +++ b/drivers/hid/hid-mcp2221.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MCP2221A - Microchip USB to I2C Host Protocol Bridge + * + * Copyright (c) 2020, Rishi Gupta <gupt21@gmail.com> + * + * Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20005565B.pdf + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/hid.h> +#include <linux/hidraw.h> +#include <linux/i2c.h> +#include "hid-ids.h" + +/* Commands codes in a raw output report */ +enum { + MCP2221_I2C_WR_DATA = 0x90, + MCP2221_I2C_WR_NO_STOP = 0x94, + MCP2221_I2C_RD_DATA = 0x91, + MCP2221_I2C_RD_RPT_START = 0x93, + MCP2221_I2C_GET_DATA = 0x40, + MCP2221_I2C_PARAM_OR_STATUS = 0x10, + MCP2221_I2C_SET_SPEED = 0x20, + MCP2221_I2C_CANCEL = 0x10, +}; + +/* Response codes in a raw input report */ +enum { + MCP2221_SUCCESS = 0x00, + MCP2221_I2C_ENG_BUSY = 0x01, + MCP2221_I2C_START_TOUT = 0x12, + MCP2221_I2C_STOP_TOUT = 0x62, + MCP2221_I2C_WRADDRL_TOUT = 0x23, + MCP2221_I2C_WRDATA_TOUT = 0x44, + MCP2221_I2C_WRADDRL_NACK = 0x25, + MCP2221_I2C_MASK_ADDR_NACK = 0x40, + MCP2221_I2C_WRADDRL_SEND = 0x21, + MCP2221_I2C_ADDR_NACK = 0x25, + MCP2221_I2C_READ_COMPL = 0x55, +}; + +/* + * There is no way to distinguish responses. Therefore next command + * is sent only after response to previous has been received. Mutex + * lock is used for this purpose mainly. + */ +struct mcp2221 { + struct hid_device *hdev; + struct i2c_adapter adapter; + struct mutex lock; + struct completion wait_in_report; + u8 *rxbuf; + u8 txbuf[64]; + int rxbuf_idx; + int status; + u8 cur_i2c_clk_div; +}; + +/* + * Default i2c bus clock frequency 400 kHz. Modify this if you + * want to set some other frequency (min 50 kHz - max 400 kHz). + */ +static uint i2c_clk_freq = 400; + +/* Synchronously send output report to the device */ +static int mcp_send_report(struct mcp2221 *mcp, + u8 *out_report, size_t len) +{ + u8 *buf; + int ret; + + buf = kmemdup(out_report, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* mcp2221 uses interrupt endpoint for out reports */ + ret = hid_hw_output_report(mcp->hdev, buf, len); + kfree(buf); + + if (ret < 0) + return ret; + return 0; +} + +/* + * Send o/p report to the device and wait for i/p report to be + * received from the device. If the device does not respond, + * we timeout. + */ +static int mcp_send_data_req_status(struct mcp2221 *mcp, + u8 *out_report, int len) +{ + int ret; + unsigned long t; + + reinit_completion(&mcp->wait_in_report); + + ret = mcp_send_report(mcp, out_report, len); + if (ret) + return ret; + + t = wait_for_completion_timeout(&mcp->wait_in_report, + msecs_to_jiffies(4000)); + if (!t) + return -ETIMEDOUT; + + return mcp->status; +} + +/* Check pass/fail for actual communication with i2c slave */ +static int mcp_chk_last_cmd_status(struct mcp2221 *mcp) +{ + memset(mcp->txbuf, 0, 8); + mcp->txbuf[0] = MCP2221_I2C_PARAM_OR_STATUS; + + return mcp_send_data_req_status(mcp, mcp->txbuf, 8); +} + +/* Cancels last command releasing i2c bus just in case occupied */ +static int mcp_cancel_last_cmd(struct mcp2221 *mcp) +{ + memset(mcp->txbuf, 0, 8); + mcp->txbuf[0] = MCP2221_I2C_PARAM_OR_STATUS; + mcp->txbuf[2] = MCP2221_I2C_CANCEL; + + return mcp_send_data_req_status(mcp, mcp->txbuf, 8); +} + +static int mcp_set_i2c_speed(struct mcp2221 *mcp) +{ + int ret; + + memset(mcp->txbuf, 0, 8); + mcp->txbuf[0] = MCP2221_I2C_PARAM_OR_STATUS; + mcp->txbuf[3] = MCP2221_I2C_SET_SPEED; + mcp->txbuf[4] = mcp->cur_i2c_clk_div; + + ret = mcp_send_data_req_status(mcp, mcp->txbuf, 8); + if (ret) { + /* Small delay is needed here */ + usleep_range(980, 1000); + mcp_cancel_last_cmd(mcp); + } + + return 0; +} + +/* + * An output report can contain minimum 1 and maximum 60 user data + * bytes. If the number of data bytes is more then 60, we send it + * in chunks of 60 bytes. Last chunk may contain exactly 60 or less + * bytes. Total number of bytes is informed in very first report to + * mcp2221, from that point onwards it first collect all the data + * from host and then send to i2c slave device. + */ +static int mcp_i2c_write(struct mcp2221 *mcp, + struct i2c_msg *msg, int type, u8 last_status) +{ + int ret, len, idx, sent; + + idx = 0; + sent = 0; + if (msg->len < 60) + len = msg->len; + else + len = 60; + + do { + mcp->txbuf[0] = type; + mcp->txbuf[1] = msg->len & 0xff; + mcp->txbuf[2] = msg->len >> 8; + mcp->txbuf[3] = (u8)(msg->addr << 1); + + memcpy(&mcp->txbuf[4], &msg->buf[idx], len); + + ret = mcp_send_data_req_status(mcp, mcp->txbuf, len + 4); + if (ret) + return ret; + + usleep_range(980, 1000); + + if (last_status) { + ret = mcp_chk_last_cmd_status(mcp); + if (ret) + return ret; + } + + sent = sent + len; + if (sent >= msg->len) + break; + + idx = idx + len; + if ((msg->len - sent) < 60) + len = msg->len - sent; + else + len = 60; + + /* + * Testing shows delay is needed between successive writes + * otherwise next write fails on first-try from i2c core. + * This value is obtained through automated stress testing. + */ + usleep_range(980, 1000); + } while (len > 0); + + return ret; +} + +/* + * Device reads all data (0 - 65535 bytes) from i2c slave device and + * stores it in device itself. This data is read back from device to + * host in multiples of 60 bytes using input reports. + */ +static int mcp_i2c_smbus_read(struct mcp2221 *mcp, + struct i2c_msg *msg, int type, u16 smbus_addr, + u8 smbus_len, u8 *smbus_buf) +{ + int ret; + u16 total_len; + + mcp->txbuf[0] = type; + if (msg) { + mcp->txbuf[1] = msg->len & 0xff; + mcp->txbuf[2] = msg->len >> 8; + mcp->txbuf[3] = (u8)(msg->addr << 1); + total_len = msg->len; + mcp->rxbuf = msg->buf; + } else { + mcp->txbuf[1] = smbus_len; + mcp->txbuf[2] = 0; + mcp->txbuf[3] = (u8)(smbus_addr << 1); + total_len = smbus_len; + mcp->rxbuf = smbus_buf; + } + + ret = mcp_send_data_req_status(mcp, mcp->txbuf, 4); + if (ret) + return ret; + + mcp->rxbuf_idx = 0; + + do { + memset(mcp->txbuf, 0, 4); + mcp->txbuf[0] = MCP2221_I2C_GET_DATA; + + ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1); + if (ret) + return ret; + + ret = mcp_chk_last_cmd_status(mcp); + if (ret) + return ret; + + usleep_range(980, 1000); + } while (mcp->rxbuf_idx < total_len); + + return ret; +} + +static int mcp_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + int ret; + struct mcp2221 *mcp = i2c_get_adapdata(adapter); + + hid_hw_power(mcp->hdev, PM_HINT_FULLON); + + mutex_lock(&mcp->lock); + + /* Setting speed before every transaction is required for mcp2221 */ + ret = mcp_set_i2c_speed(mcp); + if (ret) + goto exit; + + if (num == 1) { + if (msgs->flags & I2C_M_RD) { + ret = mcp_i2c_smbus_read(mcp, msgs, MCP2221_I2C_RD_DATA, + 0, 0, NULL); + } else { + ret = mcp_i2c_write(mcp, msgs, MCP2221_I2C_WR_DATA, 1); + } + if (ret) + goto exit; + ret = num; + } else if (num == 2) { + /* Ex transaction; send reg address and read its contents */ + if (msgs[0].addr == msgs[1].addr && + !(msgs[0].flags & I2C_M_RD) && + (msgs[1].flags & I2C_M_RD)) { + + ret = mcp_i2c_write(mcp, &msgs[0], + MCP2221_I2C_WR_NO_STOP, 0); + if (ret) + goto exit; + + ret = mcp_i2c_smbus_read(mcp, &msgs[1], + MCP2221_I2C_RD_RPT_START, + 0, 0, NULL); + if (ret) + goto exit; + ret = num; + } else { + dev_err(&adapter->dev, + "unsupported multi-msg i2c transaction\n"); + ret = -EOPNOTSUPP; + } + } else { + dev_err(&adapter->dev, + "unsupported multi-msg i2c transaction\n"); + ret = -EOPNOTSUPP; + } + +exit: + hid_hw_power(mcp->hdev, PM_HINT_NORMAL); + mutex_unlock(&mcp->lock); + return ret; +} + +static int mcp_smbus_write(struct mcp2221 *mcp, u16 addr, + u8 command, u8 *buf, u8 len, int type, + u8 last_status) +{ + int data_len, ret; + + mcp->txbuf[0] = type; + mcp->txbuf[1] = len + 1; /* 1 is due to command byte itself */ + mcp->txbuf[2] = 0; + mcp->txbuf[3] = (u8)(addr << 1); + mcp->txbuf[4] = command; + + switch (len) { + case 0: + data_len = 5; + break; + case 1: + mcp->txbuf[5] = buf[0]; + data_len = 6; + break; + case 2: + mcp->txbuf[5] = buf[0]; + mcp->txbuf[6] = buf[1]; + data_len = 7; + break; + default: + memcpy(&mcp->txbuf[5], buf, len); + data_len = len + 5; + } + + ret = mcp_send_data_req_status(mcp, mcp->txbuf, data_len); + if (ret) + return ret; + + if (last_status) { + usleep_range(980, 1000); + + ret = mcp_chk_last_cmd_status(mcp); + if (ret) + return ret; + } + + return ret; +} + +static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data) +{ + int ret; + struct mcp2221 *mcp = i2c_get_adapdata(adapter); + + hid_hw_power(mcp->hdev, PM_HINT_FULLON); + + mutex_lock(&mcp->lock); + + ret = mcp_set_i2c_speed(mcp); + if (ret) + goto exit; + + switch (size) { + + case I2C_SMBUS_QUICK: + if (read_write == I2C_SMBUS_READ) + ret = mcp_i2c_smbus_read(mcp, NULL, MCP2221_I2C_RD_DATA, + addr, 0, &data->byte); + else + ret = mcp_smbus_write(mcp, addr, command, NULL, + 0, MCP2221_I2C_WR_DATA, 1); + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) + ret = mcp_i2c_smbus_read(mcp, NULL, MCP2221_I2C_RD_DATA, + addr, 1, &data->byte); + else + ret = mcp_smbus_write(mcp, addr, command, NULL, + 0, MCP2221_I2C_WR_DATA, 1); + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = mcp_smbus_write(mcp, addr, command, NULL, + 0, MCP2221_I2C_WR_NO_STOP, 0); + if (ret) + goto exit; + + ret = mcp_i2c_smbus_read(mcp, NULL, + MCP2221_I2C_RD_RPT_START, + addr, 1, &data->byte); + } else { + ret = mcp_smbus_write(mcp, addr, command, &data->byte, + 1, MCP2221_I2C_WR_DATA, 1); + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = mcp_smbus_write(mcp, addr, command, NULL, + 0, MCP2221_I2C_WR_NO_STOP, 0); + if (ret) + goto exit; + + ret = mcp_i2c_smbus_read(mcp, NULL, + MCP2221_I2C_RD_RPT_START, + addr, 2, (u8 *)&data->word); + } else { + ret = mcp_smbus_write(mcp, addr, command, + (u8 *)&data->word, 2, + MCP2221_I2C_WR_DATA, 1); + } + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = mcp_smbus_write(mcp, addr, command, NULL, + 0, MCP2221_I2C_WR_NO_STOP, 1); + if (ret) + goto exit; + + mcp->rxbuf_idx = 0; + mcp->rxbuf = data->block; + mcp->txbuf[0] = MCP2221_I2C_GET_DATA; + ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1); + if (ret) + goto exit; + } else { + if (!data->block[0]) { + ret = -EINVAL; + goto exit; + } + ret = mcp_smbus_write(mcp, addr, command, data->block, + data->block[0] + 1, + MCP2221_I2C_WR_DATA, 1); + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = mcp_smbus_write(mcp, addr, command, NULL, + 0, MCP2221_I2C_WR_NO_STOP, 1); + if (ret) + goto exit; + + mcp->rxbuf_idx = 0; + mcp->rxbuf = data->block; + mcp->txbuf[0] = MCP2221_I2C_GET_DATA; + ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1); + if (ret) + goto exit; + } else { + if (!data->block[0]) { + ret = -EINVAL; + goto exit; + } + ret = mcp_smbus_write(mcp, addr, command, + &data->block[1], data->block[0], + MCP2221_I2C_WR_DATA, 1); + } + break; + case I2C_SMBUS_PROC_CALL: + ret = mcp_smbus_write(mcp, addr, command, + (u8 *)&data->word, + 2, MCP2221_I2C_WR_NO_STOP, 0); + if (ret) + goto exit; + + ret = mcp_i2c_smbus_read(mcp, NULL, + MCP2221_I2C_RD_RPT_START, + addr, 2, (u8 *)&data->word); + break; + case I2C_SMBUS_BLOCK_PROC_CALL: + ret = mcp_smbus_write(mcp, addr, command, data->block, + data->block[0] + 1, + MCP2221_I2C_WR_NO_STOP, 0); + if (ret) + goto exit; + + ret = mcp_i2c_smbus_read(mcp, NULL, + MCP2221_I2C_RD_RPT_START, + addr, I2C_SMBUS_BLOCK_MAX, + data->block); + break; + default: + dev_err(&mcp->adapter.dev, + "unsupported smbus transaction size:%d\n", size); + ret = -EOPNOTSUPP; + } + +exit: + hid_hw_power(mcp->hdev, PM_HINT_NORMAL); + mutex_unlock(&mcp->lock); + return ret; +} + +static u32 mcp_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_PEC); +} + +static const struct i2c_algorithm mcp_i2c_algo = { + .master_xfer = mcp_i2c_xfer, + .smbus_xfer = mcp_smbus_xfer, + .functionality = mcp_i2c_func, +}; + +/* Gives current state of i2c engine inside mcp2221 */ +static int mcp_get_i2c_eng_state(struct mcp2221 *mcp, + u8 *data, u8 idx) +{ + int ret; + + switch (data[idx]) { + case MCP2221_I2C_WRADDRL_NACK: + case MCP2221_I2C_WRADDRL_SEND: + ret = -ENXIO; + break; + case MCP2221_I2C_START_TOUT: + case MCP2221_I2C_STOP_TOUT: + case MCP2221_I2C_WRADDRL_TOUT: + case MCP2221_I2C_WRDATA_TOUT: + ret = -ETIMEDOUT; + break; + case MCP2221_I2C_ENG_BUSY: + ret = -EAGAIN; + break; + case MCP2221_SUCCESS: + ret = 0x00; + break; + default: + ret = -EIO; + } + + return ret; +} + +/* + * MCP2221 uses interrupt endpoint for input reports. This function + * is called by HID layer when it receives i/p report from mcp2221, + * which is actually a response to the previously sent command. + * + * MCP2221A firmware specific return codes are parsed and 0 or + * appropriate negative error code is returned. Delayed response + * results in timeout error and stray reponses results in -EIO. + */ +static int mcp2221_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + u8 *buf; + struct mcp2221 *mcp = hid_get_drvdata(hdev); + + switch (data[0]) { + + case MCP2221_I2C_WR_DATA: + case MCP2221_I2C_WR_NO_STOP: + case MCP2221_I2C_RD_DATA: + case MCP2221_I2C_RD_RPT_START: + switch (data[1]) { + case MCP2221_SUCCESS: + mcp->status = 0; + break; + default: + mcp->status = mcp_get_i2c_eng_state(mcp, data, 2); + } + complete(&mcp->wait_in_report); + break; + + case MCP2221_I2C_PARAM_OR_STATUS: + switch (data[1]) { + case MCP2221_SUCCESS: + if ((mcp->txbuf[3] == MCP2221_I2C_SET_SPEED) && + (data[3] != MCP2221_I2C_SET_SPEED)) { + mcp->status = -EAGAIN; + break; + } + if (data[20] & MCP2221_I2C_MASK_ADDR_NACK) { + mcp->status = -ENXIO; + break; + } + mcp->status = mcp_get_i2c_eng_state(mcp, data, 8); + break; + default: + mcp->status = -EIO; + } + complete(&mcp->wait_in_report); + break; + + case MCP2221_I2C_GET_DATA: + switch (data[1]) { + case MCP2221_SUCCESS: + if (data[2] == MCP2221_I2C_ADDR_NACK) { + mcp->status = -ENXIO; + break; + } + if (!mcp_get_i2c_eng_state(mcp, data, 2) + && (data[3] == 0)) { + mcp->status = 0; + break; + } + if (data[3] == 127) { + mcp->status = -EIO; + break; + } + if (data[2] == MCP2221_I2C_READ_COMPL) { + buf = mcp->rxbuf; + memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]); + mcp->rxbuf_idx = mcp->rxbuf_idx + data[3]; + mcp->status = 0; + break; + } + mcp->status = -EIO; + break; + default: + mcp->status = -EIO; + } + complete(&mcp->wait_in_report); + break; + + default: + mcp->status = -EIO; + complete(&mcp->wait_in_report); + } + + return 1; +} + +static int mcp2221_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct mcp2221 *mcp; + + mcp = devm_kzalloc(&hdev->dev, sizeof(*mcp), GFP_KERNEL); + if (!mcp) + return -ENOMEM; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "can't parse reports\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "can't start hardware\n"); + return ret; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "can't open device\n"); + goto err_hstop; + } + + mutex_init(&mcp->lock); + init_completion(&mcp->wait_in_report); + hid_set_drvdata(hdev, mcp); + mcp->hdev = hdev; + + /* Set I2C bus clock diviser */ + if (i2c_clk_freq > 400) + i2c_clk_freq = 400; + if (i2c_clk_freq < 50) + i2c_clk_freq = 50; + mcp->cur_i2c_clk_div = (12000000 / (i2c_clk_freq * 1000)) - 3; + + mcp->adapter.owner = THIS_MODULE; + mcp->adapter.class = I2C_CLASS_HWMON; + mcp->adapter.algo = &mcp_i2c_algo; + mcp->adapter.retries = 1; + mcp->adapter.dev.parent = &hdev->dev; + snprintf(mcp->adapter.name, sizeof(mcp->adapter.name), + "MCP2221 usb-i2c bridge on hidraw%d", + ((struct hidraw *)hdev->hidraw)->minor); + + ret = i2c_add_adapter(&mcp->adapter); + if (ret) { + hid_err(hdev, "can't add usb-i2c adapter: %d\n", ret); + goto err_i2c; + } + i2c_set_adapdata(&mcp->adapter, mcp); + + return 0; + +err_i2c: + hid_hw_close(mcp->hdev); +err_hstop: + hid_hw_stop(mcp->hdev); + return ret; +} + +static void mcp2221_remove(struct hid_device *hdev) +{ + struct mcp2221 *mcp = hid_get_drvdata(hdev); + + i2c_del_adapter(&mcp->adapter); + hid_hw_close(mcp->hdev); + hid_hw_stop(mcp->hdev); +} + +static const struct hid_device_id mcp2221_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_MCP2221) }, + { } +}; +MODULE_DEVICE_TABLE(hid, mcp2221_devices); + +static struct hid_driver mcp2221_driver = { + .name = "mcp2221", + .id_table = mcp2221_devices, + .probe = mcp2221_probe, + .remove = mcp2221_remove, + .raw_event = mcp2221_raw_event, +}; + +/* Register with HID core */ +module_hid_driver(mcp2221_driver); + +MODULE_AUTHOR("Rishi Gupta <gupt21@gmail.com>"); +MODULE_DESCRIPTION("MCP2221 Microchip HID USB to I2C master bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e2ce790ff4a4..241740222199 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1920,6 +1920,9 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_C002) }, /* Elitegroup panel */ { .driver_data = MT_CLS_SERIAL, diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 3735546bb524..e4cb543de0cd 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -163,6 +163,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DELL_K12A), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD), HID_QUIRK_BADPAD }, { HID_USB_DEVICE(USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8882), HID_QUIRK_NOGET }, @@ -398,9 +399,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) }, #endif -#if IS_ENABLED(CONFIG_HID_ITE) - { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, -#endif #if IS_ENABLED(CONFIG_HID_ICADE) { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, #endif diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 9ce22acdfaca..8cffa84c9650 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -217,7 +217,6 @@ static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr, ret = rmi_write_report(hdev, data->writeReport, data->output_report_size); if (ret != data->output_report_size) { - clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); dev_err(&hdev->dev, "failed to write request output report (%d)\n", ret); diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 009000c5d55c..294c84e136d7 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -177,6 +177,8 @@ static const struct i2c_hid_quirks { I2C_HID_QUIRK_BOGUS_IRQ }, { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID, I2C_HID_QUIRK_RESET_ON_RESUME }, + { I2C_VENDOR_ID_SYNAPTICS, I2C_PRODUCT_ID_SYNAPTICS_SYNA2393, + I2C_HID_QUIRK_RESET_ON_RESUME }, { USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720, I2C_HID_QUIRK_BAD_INPUT_SIZE }, { 0, 0 } diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.h b/drivers/hid/intel-ish-hid/ishtp/hbm.h index bb85985b1620..7c445b203f2a 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.h +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.h @@ -82,7 +82,7 @@ struct ishtp_msg_hdr { struct ishtp_bus_message { uint8_t hbm_cmd; - uint8_t data[0]; + uint8_t data[]; } __packed; /** diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h index 39e0e6c73adf..1cc6364aa957 100644 --- a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h +++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h @@ -214,7 +214,7 @@ struct ishtp_device { const struct ishtp_hw_ops *ops; size_t mtu; uint32_t ishtp_msg_hdr; - char hw[0] __aligned(sizeof(void *)); + char hw[] __aligned(sizeof(void *)); }; static inline unsigned long ishtp_secs_to_jiffies(unsigned long sec) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index c7bc9db5b192..17a638f15082 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -682,16 +682,21 @@ static int usbhid_open(struct hid_device *hid) struct usbhid_device *usbhid = hid->driver_data; int res; + mutex_lock(&usbhid->mutex); + set_bit(HID_OPENED, &usbhid->iofl); - if (hid->quirks & HID_QUIRK_ALWAYS_POLL) - return 0; + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) { + res = 0; + goto Done; + } res = usb_autopm_get_interface(usbhid->intf); /* the device must be awake to reliably request remote wakeup */ if (res < 0) { clear_bit(HID_OPENED, &usbhid->iofl); - return -EIO; + res = -EIO; + goto Done; } usbhid->intf->needs_remote_wakeup = 1; @@ -725,6 +730,9 @@ static int usbhid_open(struct hid_device *hid) msleep(50); clear_bit(HID_RESUME_RUNNING, &usbhid->iofl); + + Done: + mutex_unlock(&usbhid->mutex); return res; } @@ -732,6 +740,8 @@ static void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; + mutex_lock(&usbhid->mutex); + /* * Make sure we don't restart data acquisition due to * a resumption we no longer care about by avoiding racing @@ -743,12 +753,13 @@ static void usbhid_close(struct hid_device *hid) clear_bit(HID_IN_POLLING, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); - if (hid->quirks & HID_QUIRK_ALWAYS_POLL) - return; + if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) { + hid_cancel_delayed_stuff(usbhid); + usb_kill_urb(usbhid->urbin); + usbhid->intf->needs_remote_wakeup = 0; + } - hid_cancel_delayed_stuff(usbhid); - usb_kill_urb(usbhid->urbin); - usbhid->intf->needs_remote_wakeup = 0; + mutex_unlock(&usbhid->mutex); } /* @@ -1057,6 +1068,8 @@ static int usbhid_start(struct hid_device *hid) unsigned int n, insize = 0; int ret; + mutex_lock(&usbhid->mutex); + clear_bit(HID_DISCONNECTED, &usbhid->iofl); usbhid->bufsize = HID_MIN_BUFFER_SIZE; @@ -1177,6 +1190,8 @@ static int usbhid_start(struct hid_device *hid) usbhid_set_leds(hid); device_set_wakeup_enable(&dev->dev, 1); } + + mutex_unlock(&usbhid->mutex); return 0; fail: @@ -1187,6 +1202,7 @@ fail: usbhid->urbout = NULL; usbhid->urbctrl = NULL; hid_free_buffers(dev, hid); + mutex_unlock(&usbhid->mutex); return ret; } @@ -1202,6 +1218,8 @@ static void usbhid_stop(struct hid_device *hid) usbhid->intf->needs_remote_wakeup = 0; } + mutex_lock(&usbhid->mutex); + clear_bit(HID_STARTED, &usbhid->iofl); spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ set_bit(HID_DISCONNECTED, &usbhid->iofl); @@ -1222,6 +1240,8 @@ static void usbhid_stop(struct hid_device *hid) usbhid->urbout = NULL; hid_free_buffers(hid_to_usb_dev(hid), hid); + + mutex_unlock(&usbhid->mutex); } static int usbhid_power(struct hid_device *hid, int lvl) @@ -1382,6 +1402,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * INIT_WORK(&usbhid->reset_work, hid_reset); timer_setup(&usbhid->io_retry, hid_retry_timeout, 0); spin_lock_init(&usbhid->lock); + mutex_init(&usbhid->mutex); ret = hid_add_device(hid); if (ret) { diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 8620408bd7af..75fe85d3d27a 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -80,6 +80,7 @@ struct usbhid_device { dma_addr_t outbuf_dma; /* Output buffer dma */ unsigned long last_out; /* record of last output for timeouts */ + struct mutex mutex; /* start/stop/open/close */ spinlock_t lock; /* fifo spinlock */ unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ struct timer_list io_retry; /* Retry timer */ diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 5ded94b7bf68..cd71e7133944 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -319,9 +319,11 @@ static void wacom_feature_mapping(struct hid_device *hdev, data[0] = field->report->id; ret = wacom_get_report(hdev, HID_FEATURE_REPORT, data, n, WAC_CMD_RETRIES); - if (ret == n) { + if (ret == n && features->type == HID_GENERIC) { ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, data, n, 0); + } else if (ret == 2 && features->type != HID_GENERIC) { + features->touch_max = data[1]; } else { features->touch_max = 16; hid_warn(hdev, "wacom_feature_mapping: " diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index d99a9d407671..1c96809b51c9 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1427,11 +1427,13 @@ static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom) { struct input_dev *pad_input = wacom->pad_input; unsigned char *data = wacom->data; + int nbuttons = wacom->features.numbered_buttons; - int buttons = data[282] | ((data[281] & 0x40) << 2); + int expresskeys = data[282]; + int center = (data[281] & 0x40) >> 6; int ring = data[285] & 0x7F; bool ringstatus = data[285] & 0x80; - bool prox = buttons || ringstatus; + bool prox = expresskeys || center || ringstatus; /* Fix touchring data: userspace expects 0 at left and increasing clockwise */ ring = 71 - ring; @@ -1439,7 +1441,8 @@ static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom) if (ring > 71) ring -= 72; - wacom_report_numbered_buttons(pad_input, 9, buttons); + wacom_report_numbered_buttons(pad_input, nbuttons, + expresskeys | (center << (nbuttons - 1))); input_report_abs(pad_input, ABS_WHEEL, ringstatus ? ring : 0); @@ -2637,9 +2640,25 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev, case HID_DG_TIPSWITCH: hid_data->last_slot_field = equivalent_usage; break; + case HID_DG_CONTACTCOUNT: + hid_data->cc_report = report->id; + hid_data->cc_index = i; + hid_data->cc_value_index = j; + break; } } } + + if (hid_data->cc_report != 0 && + hid_data->cc_index >= 0) { + struct hid_field *field = report->field[hid_data->cc_index]; + int value = field->value[hid_data->cc_value_index]; + if (value) + hid_data->num_expected = value; + } + else { + hid_data->num_expected = wacom_wac->features.touch_max; + } } static void wacom_wac_finger_report(struct hid_device *hdev, @@ -2649,7 +2668,6 @@ static void wacom_wac_finger_report(struct hid_device *hdev, struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct input_dev *input = wacom_wac->touch_input; unsigned touch_max = wacom_wac->features.touch_max; - struct hid_data *hid_data = &wacom_wac->hid_data; /* If more packets of data are expected, give us a chance to * process them rather than immediately syncing a partial @@ -2663,7 +2681,6 @@ static void wacom_wac_finger_report(struct hid_device *hdev, input_sync(input); wacom_wac->hid_data.num_received = 0; - hid_data->num_expected = 0; /* keep touch state for pen event */ wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(wacom_wac); @@ -2738,73 +2755,12 @@ static void wacom_report_events(struct hid_device *hdev, } } -static void wacom_set_num_expected(struct hid_device *hdev, - struct hid_report *report, - int collection_index, - struct hid_field *field, - int field_index) -{ - struct wacom *wacom = hid_get_drvdata(hdev); - struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct hid_data *hid_data = &wacom_wac->hid_data; - unsigned int original_collection_level = - hdev->collection[collection_index].level; - bool end_collection = false; - int i; - - if (hid_data->num_expected) - return; - - // find the contact count value for this segment - for (i = field_index; i < report->maxfield && !end_collection; i++) { - struct hid_field *field = report->field[i]; - unsigned int field_level = - hdev->collection[field->usage[0].collection_index].level; - unsigned int j; - - if (field_level != original_collection_level) - continue; - - for (j = 0; j < field->maxusage; j++) { - struct hid_usage *usage = &field->usage[j]; - - if (usage->collection_index != collection_index) { - end_collection = true; - break; - } - if (wacom_equivalent_usage(usage->hid) == HID_DG_CONTACTCOUNT) { - hid_data->cc_report = report->id; - hid_data->cc_index = i; - hid_data->cc_value_index = j; - - if (hid_data->cc_report != 0 && - hid_data->cc_index >= 0) { - - struct hid_field *field = - report->field[hid_data->cc_index]; - int value = - field->value[hid_data->cc_value_index]; - - if (value) - hid_data->num_expected = value; - } - } - } - } - - if (hid_data->cc_report == 0 || hid_data->cc_index < 0) - hid_data->num_expected = wacom_wac->features.touch_max; -} - static int wacom_wac_collection(struct hid_device *hdev, struct hid_report *report, int collection_index, struct hid_field *field, int field_index) { struct wacom *wacom = hid_get_drvdata(hdev); - if (WACOM_FINGER_FIELD(field)) - wacom_set_num_expected(hdev, report, collection_index, field, - field_index); wacom_report_events(hdev, report, collection_index, field_index); /* |