diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 11 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 40 | ||||
-rw-r--r-- | drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 1 | ||||
-rw-r--r-- | drivers/hid/hid-alps.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-asus.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-cp2112.c | 22 | ||||
-rw-r--r-- | drivers/hid/hid-debug.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-elan.c | 17 | ||||
-rw-r--r-- | drivers/hid/hid-ft260.c | 1054 | ||||
-rw-r--r-- | drivers/hid/hid-google-hammer.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 15 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 22 | ||||
-rw-r--r-- | drivers/hid/hid-lenovo.c | 147 | ||||
-rw-r--r-- | drivers/hid/hid-lg.c | 24 | ||||
-rw-r--r-- | drivers/hid/hid-logitech-dj.c | 131 | ||||
-rw-r--r-- | drivers/hid/hid-magicmouse.c | 158 | ||||
-rw-r--r-- | drivers/hid/hid-quirks.c | 2 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-acpi.c | 52 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 6 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 8 |
21 files changed, 1537 insertions, 181 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 786b71ef7738..b279c6130bc2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -351,6 +351,17 @@ config HID_EZKEY help Support for Ezkey BTC 8193 keyboard. +config HID_FT260 + tristate "FTDI FT260 USB HID to I2C host support" + depends on USB_HID && HIDRAW && I2C + help + Provides I2C host adapter functionality over USB-HID through FT260 + device. The customizable USB descriptor fields are exposed as sysfs + attributes. + + To compile this driver as a module, choose M here: the module + will be called hid-ft260. + config HID_GEMBIRD tristate "Gembird Joypad" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index c4f6d5c613dc..6e24c37132ec 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_HID_ELAN) += hid-elan.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o +obj-$(CONFIG_HID_FT260) += hid-ft260.o obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o obj-$(CONFIG_HID_GFRM) += hid-gfrm.o obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index dbac16641662..ddecc84fd6f0 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/dmi.h> #include <linux/interrupt.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/module.h> @@ -22,9 +23,13 @@ #define ACEL_EN BIT(0) #define GYRO_EN BIT(1) -#define MAGNO_EN BIT(2) +#define MAGNO_EN BIT(2) #define ALS_EN BIT(19) +static int sensor_mask_override = -1; +module_param_named(sensor_mask, sensor_mask_override, int, 0444); +MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask"); + void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) { union sfh_cmd_param cmd_param; @@ -73,12 +78,41 @@ void amd_stop_all_sensors(struct amd_mp2_dev *privdata) writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); } +static const struct dmi_system_id dmi_sensor_mask_overrides[] = { + { + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"), + }, + .driver_data = (void *)(ACEL_EN | MAGNO_EN), + }, + { + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"), + }, + .driver_data = (void *)(ACEL_EN | MAGNO_EN), + }, + { } +}; + int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) { int activestatus, num_of_sensors = 0; + const struct dmi_system_id *dmi_id; + u32 activecontrolstatus; + + if (sensor_mask_override == -1) { + dmi_id = dmi_first_match(dmi_sensor_mask_overrides); + if (dmi_id) + sensor_mask_override = (long)dmi_id->driver_data; + } + + if (sensor_mask_override >= 0) { + activestatus = sensor_mask_override; + } else { + activecontrolstatus = readl(privdata->mmio + AMD_P2C_MSG3); + activestatus = activecontrolstatus >> 4; + } - privdata->activecontrolstatus = readl(privdata->mmio + AMD_P2C_MSG3); - activestatus = privdata->activecontrolstatus >> 4; if (ACEL_EN & activestatus) sensor_id[num_of_sensors++] = accel_idx; diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h index 8f8d19b2cfe5..489415f7c22c 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h @@ -61,7 +61,6 @@ struct amd_mp2_dev { struct pci_dev *pdev; struct amdtp_cl_data *cl_data; void __iomem *mmio; - u32 activecontrolstatus; }; struct amd_mp2_sensor_info { diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 3feaece13ade..6b665931147d 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c @@ -761,6 +761,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) if (input_register_device(data->input2)) { input_free_device(input2); + ret = -ENOENT; goto exit; } } diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 1dfe184ebf5a..2ab22b925941 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -1222,6 +1222,9 @@ static const struct hid_device_id asus_devices[] = { USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD), QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 21e15627a461..477baa30889c 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -161,6 +161,7 @@ struct cp2112_device { atomic_t read_avail; atomic_t xfer_avail; struct gpio_chip gc; + struct irq_chip irq; u8 *in_out_buffer; struct mutex lock; @@ -1175,16 +1176,6 @@ static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type) return 0; } -static struct irq_chip cp2112_gpio_irqchip = { - .name = "cp2112-gpio", - .irq_startup = cp2112_gpio_irq_startup, - .irq_shutdown = cp2112_gpio_irq_shutdown, - .irq_ack = cp2112_gpio_irq_ack, - .irq_mask = cp2112_gpio_irq_mask, - .irq_unmask = cp2112_gpio_irq_unmask, - .irq_set_type = cp2112_gpio_irq_type, -}; - static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev, int pin) { @@ -1339,8 +1330,17 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id) dev->gc.can_sleep = 1; dev->gc.parent = &hdev->dev; + dev->irq.name = "cp2112-gpio"; + dev->irq.irq_startup = cp2112_gpio_irq_startup; + dev->irq.irq_shutdown = cp2112_gpio_irq_shutdown; + dev->irq.irq_ack = cp2112_gpio_irq_ack; + dev->irq.irq_mask = cp2112_gpio_irq_mask; + dev->irq.irq_unmask = cp2112_gpio_irq_unmask; + dev->irq.irq_set_type = cp2112_gpio_irq_type; + dev->irq.flags = IRQCHIP_MASK_ON_SUSPEND; + girq = &dev->gc.irq; - girq->chip = &cp2112_gpio_irqchip; + girq->chip = &dev->irq; /* The event comes from the outside so no parent handler */ girq->parent_handler = NULL; girq->num_parents = 0; diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index d7eaf9100370..59f8d716d78f 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -417,6 +417,7 @@ static const struct hid_usage_entry hid_usage_table[] = { { 0x85, 0x44, "Charging" }, { 0x85, 0x45, "Discharging" }, { 0x85, 0x4b, "NeedReplacement" }, + { 0x85, 0x65, "AbsoluteStateOfCharge" }, { 0x85, 0x66, "RemainingCapacity" }, { 0x85, 0x68, "RunTimeToEmpty" }, { 0x85, 0x6a, "AverageTimeToFull" }, diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index dae193749d44..021049805bb7 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -410,15 +410,6 @@ static int elan_start_multitouch(struct hid_device *hdev) return 0; } -static enum led_brightness elan_mute_led_get_brigtness(struct led_classdev *led_cdev) -{ - struct device *dev = led_cdev->dev->parent; - struct hid_device *hdev = to_hid_device(dev); - struct elan_drvdata *drvdata = hid_get_drvdata(hdev); - - return drvdata->mute_led_state; -} - static int elan_mute_led_set_brigtness(struct led_classdev *led_cdev, enum led_brightness value) { @@ -445,8 +436,9 @@ static int elan_mute_led_set_brigtness(struct led_classdev *led_cdev, kfree(dmabuf); if (ret != ELAN_LED_REPORT_SIZE) { - hid_err(hdev, "Failed to set mute led brightness: %d\n", ret); - return ret; + if (ret != -ENODEV) + hid_err(hdev, "Failed to set mute led brightness: %d\n", ret); + return ret < 0 ? ret : -EIO; } drvdata->mute_led_state = led_state; @@ -459,9 +451,10 @@ static int elan_init_mute_led(struct hid_device *hdev) struct led_classdev *mute_led = &drvdata->mute_led; mute_led->name = "elan:red:mute"; - mute_led->brightness_get = elan_mute_led_get_brigtness; + mute_led->default_trigger = "audio-mute"; mute_led->brightness_set_blocking = elan_mute_led_set_brigtness; mute_led->max_brightness = LED_ON; + mute_led->flags = LED_HW_PLUGGABLE; mute_led->dev = &hdev->dev; return devm_led_classdev_register(&hdev->dev, mute_led); diff --git a/drivers/hid/hid-ft260.c b/drivers/hid/hid-ft260.c new file mode 100644 index 000000000000..a5751607ce24 --- /dev/null +++ b/drivers/hid/hid-ft260.c @@ -0,0 +1,1054 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * hid-ft260.c - FTDI FT260 USB HID to I2C host bridge + * + * Copyright (c) 2021, Michael Zaidman <michaelz@xsightlabs.com> + * + * Data Sheet: + * https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT260.pdf + */ + +#include "hid-ids.h" +#include <linux/hidraw.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/usb.h> + +#ifdef DEBUG +static int ft260_debug = 1; +#else +static int ft260_debug; +#endif +module_param_named(debug, ft260_debug, int, 0600); +MODULE_PARM_DESC(debug, "Toggle FT260 debugging messages"); + +#define ft260_dbg(format, arg...) \ + do { \ + if (ft260_debug) \ + pr_info("%s: " format, __func__, ##arg); \ + } while (0) + +#define FT260_REPORT_MAX_LENGTH (64) +#define FT260_I2C_DATA_REPORT_ID(len) (FT260_I2C_REPORT_MIN + (len - 1) / 4) +/* + * The input report format assigns 62 bytes for the data payload, but ft260 + * returns 60 and 2 in two separate transactions. To minimize transfer time + * in reading chunks mode, set the maximum read payload length to 60 bytes. + */ +#define FT260_RD_DATA_MAX (60) +#define FT260_WR_DATA_MAX (60) + +/* + * Device interface configuration. + * The FT260 has 2 interfaces that are controlled by DCNF0 and DCNF1 pins. + * First implementes USB HID to I2C bridge function and + * second - USB HID to UART bridge function. + */ +enum { + FT260_MODE_ALL = 0x00, + FT260_MODE_I2C = 0x01, + FT260_MODE_UART = 0x02, + FT260_MODE_BOTH = 0x03, +}; + +/* Control pipe */ +enum { + FT260_GET_RQST_TYPE = 0xA1, + FT260_GET_REPORT = 0x01, + FT260_SET_RQST_TYPE = 0x21, + FT260_SET_REPORT = 0x09, + FT260_FEATURE = 0x03, +}; + +/* Report IDs / Feature In */ +enum { + FT260_CHIP_VERSION = 0xA0, + FT260_SYSTEM_SETTINGS = 0xA1, + FT260_I2C_STATUS = 0xC0, + FT260_I2C_READ_REQ = 0xC2, + FT260_I2C_REPORT_MIN = 0xD0, + FT260_I2C_REPORT_MAX = 0xDE, + FT260_GPIO = 0xB0, + FT260_UART_INTERRUPT_STATUS = 0xB1, + FT260_UART_STATUS = 0xE0, + FT260_UART_RI_DCD_STATUS = 0xE1, + FT260_UART_REPORT = 0xF0, +}; + +/* Feature Out */ +enum { + FT260_SET_CLOCK = 0x01, + FT260_SET_I2C_MODE = 0x02, + FT260_SET_UART_MODE = 0x03, + FT260_ENABLE_INTERRUPT = 0x05, + FT260_SELECT_GPIO2_FUNC = 0x06, + FT260_ENABLE_UART_DCD_RI = 0x07, + FT260_SELECT_GPIOA_FUNC = 0x08, + FT260_SELECT_GPIOG_FUNC = 0x09, + FT260_SET_INTERRUPT_TRIGGER = 0x0A, + FT260_SET_SUSPEND_OUT_POLAR = 0x0B, + FT260_ENABLE_UART_RI_WAKEUP = 0x0C, + FT260_SET_UART_RI_WAKEUP_CFG = 0x0D, + FT260_SET_I2C_RESET = 0x20, + FT260_SET_I2C_CLOCK_SPEED = 0x22, + FT260_SET_UART_RESET = 0x40, + FT260_SET_UART_CONFIG = 0x41, + FT260_SET_UART_BAUD_RATE = 0x42, + FT260_SET_UART_DATA_BIT = 0x43, + FT260_SET_UART_PARITY = 0x44, + FT260_SET_UART_STOP_BIT = 0x45, + FT260_SET_UART_BREAKING = 0x46, + FT260_SET_UART_XON_XOFF = 0x49, +}; + +/* Response codes in I2C status report */ +enum { + FT260_I2C_STATUS_SUCCESS = 0x00, + FT260_I2C_STATUS_CTRL_BUSY = 0x01, + FT260_I2C_STATUS_ERROR = 0x02, + FT260_I2C_STATUS_ADDR_NO_ACK = 0x04, + FT260_I2C_STATUS_DATA_NO_ACK = 0x08, + FT260_I2C_STATUS_ARBITR_LOST = 0x10, + FT260_I2C_STATUS_CTRL_IDLE = 0x20, + FT260_I2C_STATUS_BUS_BUSY = 0x40, +}; + +/* I2C Conditions flags */ +enum { + FT260_FLAG_NONE = 0x00, + FT260_FLAG_START = 0x02, + FT260_FLAG_START_REPEATED = 0x03, + FT260_FLAG_STOP = 0x04, + FT260_FLAG_START_STOP = 0x06, + FT260_FLAG_START_STOP_REPEATED = 0x07, +}; + +#define FT260_SET_REQUEST_VALUE(report_id) ((FT260_FEATURE << 8) | report_id) + +/* Feature In reports */ + +struct ft260_get_chip_version_report { + u8 report; /* FT260_CHIP_VERSION */ + u8 chip_code[4]; /* FTDI chip identification code */ + u8 reserved[8]; +} __packed; + +struct ft260_get_system_status_report { + u8 report; /* FT260_SYSTEM_SETTINGS */ + u8 chip_mode; /* DCNF0 and DCNF1 status, bits 0-1 */ + u8 clock_ctl; /* 0 - 12MHz, 1 - 24MHz, 2 - 48MHz */ + u8 suspend_status; /* 0 - not suspended, 1 - suspended */ + u8 pwren_status; /* 0 - FT260 is not ready, 1 - ready */ + u8 i2c_enable; /* 0 - disabled, 1 - enabled */ + u8 uart_mode; /* 0 - OFF; 1 - RTS_CTS, 2 - DTR_DSR, */ + /* 3 - XON_XOFF, 4 - No flow control */ + u8 hid_over_i2c_en; /* 0 - disabled, 1 - enabled */ + u8 gpio2_function; /* 0 - GPIO, 1 - SUSPOUT, */ + /* 2 - PWREN, 4 - TX_LED */ + u8 gpioA_function; /* 0 - GPIO, 3 - TX_ACTIVE, 4 - TX_LED */ + u8 gpioG_function; /* 0 - GPIO, 2 - PWREN, */ + /* 5 - RX_LED, 6 - BCD_DET */ + u8 suspend_out_pol; /* 0 - active-high, 1 - active-low */ + u8 enable_wakeup_int; /* 0 - disabled, 1 - enabled */ + u8 intr_cond; /* Interrupt trigger conditions */ + u8 power_saving_en; /* 0 - disabled, 1 - enabled */ + u8 reserved[10]; +} __packed; + +struct ft260_get_i2c_status_report { + u8 report; /* FT260_I2C_STATUS */ + u8 bus_status; /* I2C bus status */ + __le16 clock; /* I2C bus clock in range 60-3400 KHz */ + u8 reserved; +} __packed; + +/* Feature Out reports */ + +struct ft260_set_system_clock_report { + u8 report; /* FT260_SYSTEM_SETTINGS */ + u8 request; /* FT260_SET_CLOCK */ + u8 clock_ctl; /* 0 - 12MHz, 1 - 24MHz, 2 - 48MHz */ +} __packed; + +struct ft260_set_i2c_mode_report { + u8 report; /* FT260_SYSTEM_SETTINGS */ + u8 request; /* FT260_SET_I2C_MODE */ + u8 i2c_enable; /* 0 - disabled, 1 - enabled */ +} __packed; + +struct ft260_set_uart_mode_report { + u8 report; /* FT260_SYSTEM_SETTINGS */ + u8 request; /* FT260_SET_UART_MODE */ + u8 uart_mode; /* 0 - OFF; 1 - RTS_CTS, 2 - DTR_DSR, */ + /* 3 - XON_XOFF, 4 - No flow control */ +} __packed; + +struct ft260_set_i2c_reset_report { + u8 report; /* FT260_SYSTEM_SETTINGS */ + u8 request; /* FT260_SET_I2C_RESET */ +} __packed; + +struct ft260_set_i2c_speed_report { + u8 report; /* FT260_SYSTEM_SETTINGS */ + u8 request; /* FT260_SET_I2C_CLOCK_SPEED */ + __le16 clock; /* I2C bus clock in range 60-3400 KHz */ +} __packed; + +/* Data transfer reports */ + +struct ft260_i2c_write_request_report { + u8 report; /* FT260_I2C_REPORT */ + u8 address; /* 7-bit I2C address */ + u8 flag; /* I2C transaction condition */ + u8 length; /* data payload length */ + u8 data[60]; /* data payload */ +} __packed; + +struct ft260_i2c_read_request_report { + u8 report; /* FT260_I2C_READ_REQ */ + u8 address; /* 7-bit I2C address */ + u8 flag; /* I2C transaction condition */ + __le16 length; /* data payload length */ +} __packed; + +struct ft260_i2c_input_report { + u8 report; /* FT260_I2C_REPORT */ + u8 length; /* data payload length */ + u8 data[2]; /* data payload */ +} __packed; + +static const struct hid_device_id ft260_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY, + USB_DEVICE_ID_FT260) }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(hid, ft260_devices); + +struct ft260_device { + struct i2c_adapter adap; + struct hid_device *hdev; + struct completion wait; + struct mutex lock; + u8 write_buf[FT260_REPORT_MAX_LENGTH]; + u8 *read_buf; + u16 read_idx; + u16 read_len; + u16 clock; +}; + +static int ft260_hid_feature_report_get(struct hid_device *hdev, + unsigned char report_id, u8 *data, + size_t len) +{ + u8 *buf; + int ret; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, report_id, buf, len, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + memcpy(data, buf, len); + kfree(buf); + return ret; +} + +static int ft260_hid_feature_report_set(struct hid_device *hdev, u8 *data, + size_t len) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = FT260_SYSTEM_SETTINGS; + + ret = hid_hw_raw_request(hdev, buf[0], buf, len, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); + + kfree(buf); + return ret; +} + +static int ft260_i2c_reset(struct hid_device *hdev) +{ + struct ft260_set_i2c_reset_report report; + int ret; + + report.request = FT260_SET_I2C_RESET; + + ret = ft260_hid_feature_report_set(hdev, (u8 *)&report, sizeof(report)); + if (ret < 0) { + hid_err(hdev, "failed to reset I2C controller: %d\n", ret); + return ret; + } + + ft260_dbg("done\n"); + return ret; +} + +static int ft260_xfer_status(struct ft260_device *dev) +{ + struct hid_device *hdev = dev->hdev; + struct ft260_get_i2c_status_report report; + int ret; + + ret = ft260_hid_feature_report_get(hdev, FT260_I2C_STATUS, + (u8 *)&report, sizeof(report)); + if (ret < 0) { + hid_err(hdev, "failed to retrieve status: %d\n", ret); + return ret; + } + + dev->clock = le16_to_cpu(report.clock); + ft260_dbg("bus_status %#02x, clock %u\n", report.bus_status, + dev->clock); + + if (report.bus_status & FT260_I2C_STATUS_CTRL_BUSY) + return -EAGAIN; + + if (report.bus_status & FT260_I2C_STATUS_BUS_BUSY) + return -EBUSY; + + if (report.bus_status & FT260_I2C_STATUS_ERROR) + return -EIO; + + ret = -EIO; + + if (report.bus_status & FT260_I2C_STATUS_ADDR_NO_ACK) + ft260_dbg("unacknowledged address\n"); + + if (report.bus_status & FT260_I2C_STATUS_DATA_NO_ACK) + ft260_dbg("unacknowledged data\n"); + + if (report.bus_status & FT260_I2C_STATUS_ARBITR_LOST) + ft260_dbg("arbitration loss\n"); + + if (report.bus_status & FT260_I2C_STATUS_CTRL_IDLE) + ret = 0; + + return ret; +} + +static int ft260_hid_output_report(struct hid_device *hdev, u8 *data, + size_t len) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_output_report(hdev, buf, len); + + kfree(buf); + return ret; +} + +static int ft260_hid_output_report_check_status(struct ft260_device *dev, + u8 *data, int len) +{ + int ret, usec, try = 3; + struct hid_device *hdev = dev->hdev; + + ret = ft260_hid_output_report(hdev, data, len); + if (ret < 0) { + hid_err(hdev, "%s: failed to start transfer, ret %d\n", + __func__, ret); + ft260_i2c_reset(hdev); + return ret; + } + + /* transfer time = 1 / clock(KHz) * 10 bits * bytes */ + usec = 10000 / dev->clock * len; + usleep_range(usec, usec + 100); + ft260_dbg("wait %d usec, len %d\n", usec, len); + do { + ret = ft260_xfer_status(dev); + if (ret != -EAGAIN) + break; + } while (--try); + + if (ret == 0 || ret == -EBUSY) + return 0; + + ft260_i2c_reset(hdev); + return -EIO; +} + +static int ft260_i2c_write(struct ft260_device *dev, u8 addr, u8 *data, + int data_len, u8 flag) +{ + int len, ret, idx = 0; + struct hid_device *hdev = dev->hdev; + struct ft260_i2c_write_request_report *rep = + (struct ft260_i2c_write_request_report *)dev->write_buf; + + do { + if (data_len <= FT260_WR_DATA_MAX) + len = data_len; + else + len = FT260_WR_DATA_MAX; + + rep->report = FT260_I2C_DATA_REPORT_ID(len); + rep->address = addr; + rep->length = len; + rep->flag = flag; + + memcpy(rep->data, &data[idx], len); + + ft260_dbg("rep %#02x addr %#02x off %d len %d d[0] %#02x\n", + rep->report, addr, idx, len, data[0]); + + ret = ft260_hid_output_report_check_status(dev, (u8 *)rep, + len + 4); + if (ret < 0) { + hid_err(hdev, "%s: failed to start transfer, ret %d\n", + __func__, ret); + return ret; + } + + data_len -= len; + idx += len; + + } while (data_len > 0); + + return 0; +} + +static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd, + u8 *data, u8 data_len, u8 flag) +{ + int ret = 0; + int len = 4; + + struct ft260_i2c_write_request_report *rep = + (struct ft260_i2c_write_request_report *)dev->write_buf; + + rep->address = addr; + rep->data[0] = cmd; + rep->length = data_len + 1; + rep->flag = flag; + len += rep->length; + + rep->report = FT260_I2C_DATA_REPORT_ID(len); + + if (data_len > 0) + memcpy(&rep->data[1], data, data_len); + + ft260_dbg("rep %#02x addr %#02x cmd %#02x datlen %d replen %d\n", + rep->report, addr, cmd, rep->length, len); + + ret = ft260_hid_output_report_check_status(dev, (u8 *)rep, len); + + return ret; +} + +static int ft260_i2c_read(struct ft260_device *dev, u8 addr, u8 *data, + u16 len, u8 flag) +{ + struct ft260_i2c_read_request_report rep; + struct hid_device *hdev = dev->hdev; + int timeout; + int ret; + + if (len > FT260_RD_DATA_MAX) { + hid_err(hdev, "%s: unsupported rd len: %d\n", __func__, len); + return -EINVAL; + } + + dev->read_idx = 0; + dev->read_buf = data; + dev->read_len = len; + + rep.report = FT260_I2C_READ_REQ; + rep.length = cpu_to_le16(len); + rep.address = addr; + rep.flag = flag; + + ft260_dbg("rep %#02x addr %#02x len %d\n", rep.report, rep.address, + rep.length); + + reinit_completion(&dev->wait); + + ret = ft260_hid_output_report(hdev, (u8 *)&rep, sizeof(rep)); + if (ret < 0) { + hid_err(hdev, "%s: failed to start transaction, ret %d\n", + __func__, ret); + return ret; + } + + timeout = msecs_to_jiffies(5000); + if (!wait_for_completion_timeout(&dev->wait, timeout)) { + ft260_i2c_reset(hdev); + return -ETIMEDOUT; + } + + ret = ft260_xfer_status(dev); + if (ret == 0) + return 0; + + ft260_i2c_reset(hdev); + return -EIO; +} + +/* + * A random read operation is implemented as a dummy write operation, followed + * by a current address read operation. The dummy write operation is used to + * load the target byte address into the current byte address counter, from + * which the subsequent current address read operation then reads. + */ +static int ft260_i2c_write_read(struct ft260_device *dev, struct i2c_msg *msgs) +{ + int len, ret; + u16 left_len = msgs[1].len; + u8 *read_buf = msgs[1].buf; + u8 addr = msgs[0].addr; + u16 read_off = 0; + struct hid_device *hdev = dev->hdev; + + if (msgs[0].len > 2) { + hid_err(hdev, "%s: unsupported wr len: %d\n", __func__, + msgs[0].len); + return -EOPNOTSUPP; + } + + memcpy(&read_off, msgs[0].buf, msgs[0].len); + + do { + if (left_len <= FT260_RD_DATA_MAX) + len = left_len; + else + len = FT260_RD_DATA_MAX; + + ft260_dbg("read_off %#x left_len %d len %d\n", read_off, + left_len, len); + + ret = ft260_i2c_write(dev, addr, (u8 *)&read_off, msgs[0].len, + FT260_FLAG_START); + if (ret < 0) + return ret; + + ret = ft260_i2c_read(dev, addr, read_buf, len, + FT260_FLAG_START_STOP); + if (ret < 0) + return ret; + + left_len -= len; + read_buf += len; + read_off += len; + + } while (left_len > 0); + + return 0; +} + +static int ft260_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + int ret; + struct ft260_device *dev = i2c_get_adapdata(adapter); + struct hid_device *hdev = dev->hdev; + + mutex_lock(&dev->lock); + + ret = hid_hw_power(hdev, PM_HINT_FULLON); + if (ret < 0) { + hid_err(hdev, "failed to enter FULLON power mode: %d\n", ret); + mutex_unlock(&dev->lock); + return ret; + } + + if (num == 1) { + if (msgs->flags & I2C_M_RD) + ret = ft260_i2c_read(dev, msgs->addr, msgs->buf, + msgs->len, FT260_FLAG_START_STOP); + else + ret = ft260_i2c_write(dev, msgs->addr, msgs->buf, + msgs->len, FT260_FLAG_START_STOP); + if (ret < 0) + goto i2c_exit; + + } else { + /* Combined write then read message */ + ret = ft260_i2c_write_read(dev, msgs); + if (ret < 0) + goto i2c_exit; + } + + ret = num; +i2c_exit: + hid_hw_power(hdev, PM_HINT_NORMAL); + mutex_unlock(&dev->lock); + return ret; +} + +static int ft260_smbus_xfer(struct i2c_adapter *adapter, u16 addr, u16 flags, + char read_write, u8 cmd, int size, + union i2c_smbus_data *data) +{ + int ret; + struct ft260_device *dev = i2c_get_adapdata(adapter); + struct hid_device *hdev = dev->hdev; + + ft260_dbg("smbus size %d\n", size); + + mutex_lock(&dev->lock); + + ret = hid_hw_power(hdev, PM_HINT_FULLON); + if (ret < 0) { + hid_err(hdev, "power management error: %d\n", ret); + mutex_unlock(&dev->lock); + return ret; + } + + switch (size) { + case I2C_SMBUS_QUICK: + if (read_write == I2C_SMBUS_READ) + ret = ft260_i2c_read(dev, addr, &data->byte, 0, + FT260_FLAG_START_STOP); + else + ret = ft260_smbus_write(dev, addr, cmd, NULL, 0, + FT260_FLAG_START_STOP); + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) + ret = ft260_i2c_read(dev, addr, &data->byte, 1, + FT260_FLAG_START_STOP); + else + ret = ft260_smbus_write(dev, addr, cmd, NULL, 0, + FT260_FLAG_START_STOP); + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = ft260_smbus_write(dev, addr, cmd, NULL, 0, + FT260_FLAG_START); + if (ret) + goto smbus_exit; + + ret = ft260_i2c_read(dev, addr, &data->byte, 1, + FT260_FLAG_START_STOP_REPEATED); + } else { + ret = ft260_smbus_write(dev, addr, cmd, &data->byte, 1, + FT260_FLAG_START_STOP); + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = ft260_smbus_write(dev, addr, cmd, NULL, 0, + FT260_FLAG_START); + if (ret) + goto smbus_exit; + + ret = ft260_i2c_read(dev, addr, (u8 *)&data->word, 2, + FT260_FLAG_START_STOP_REPEATED); + } else { + ret = ft260_smbus_write(dev, addr, cmd, + (u8 *)&data->word, 2, + FT260_FLAG_START_STOP); + } + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = ft260_smbus_write(dev, addr, cmd, NULL, 0, + FT260_FLAG_START); + if (ret) + goto smbus_exit; + + ret = ft260_i2c_read(dev, addr, data->block, + data->block[0] + 1, + FT260_FLAG_START_STOP_REPEATED); + } else { + ret = ft260_smbus_write(dev, addr, cmd, data->block, + data->block[0] + 1, + FT260_FLAG_START_STOP); + } + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + ret = ft260_smbus_write(dev, addr, cmd, NULL, 0, + FT260_FLAG_START); + if (ret) + goto smbus_exit; + + ret = ft260_i2c_read(dev, addr, data->block + 1, + data->block[0], + FT260_FLAG_START_STOP_REPEATED); + } else { + ret = ft260_smbus_write(dev, addr, cmd, data->block + 1, + data->block[0], + FT260_FLAG_START_STOP); + } + break; + default: + hid_err(hdev, "unsupported smbus transaction size %d\n", size); + ret = -EOPNOTSUPP; + } + +smbus_exit: + hid_hw_power(hdev, PM_HINT_NORMAL); + mutex_unlock(&dev->lock); + return ret; +} + +static u32 ft260_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_adapter_quirks ft260_i2c_quirks = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_comb_1st_msg_len = 2, +}; + +static const struct i2c_algorithm ft260_i2c_algo = { + .master_xfer = ft260_i2c_xfer, + .smbus_xfer = ft260_smbus_xfer, + .functionality = ft260_functionality, +}; + +static int ft260_get_system_config(struct hid_device *hdev, + struct ft260_get_system_status_report *cfg) +{ + int ret; + int len = sizeof(struct ft260_get_system_status_report); + + ret = ft260_hid_feature_report_get(hdev, FT260_SYSTEM_SETTINGS, + (u8 *)cfg, len); + if (ret != len) { + hid_err(hdev, "failed to retrieve system status\n"); + if (ret >= 0) + return -EIO; + } + return 0; +} + +static int ft260_is_interface_enabled(struct hid_device *hdev) +{ + struct ft260_get_system_status_report cfg; + struct usb_interface *usbif = to_usb_interface(hdev->dev.parent); + int interface = usbif->cur_altsetting->desc.bInterfaceNumber; + int ret; + + ret = ft260_get_system_config(hdev, &cfg); + if (ret) + return ret; + + ft260_dbg("interface: 0x%02x\n", interface); + ft260_dbg("chip mode: 0x%02x\n", cfg.chip_mode); + ft260_dbg("clock_ctl: 0x%02x\n", cfg.clock_ctl); + ft260_dbg("i2c_enable: 0x%02x\n", cfg.i2c_enable); + ft260_dbg("uart_mode: 0x%02x\n", cfg.uart_mode); + + switch (cfg.chip_mode) { + case FT260_MODE_ALL: + case FT260_MODE_BOTH: + if (interface == 1) { + hid_info(hdev, "uart interface is not supported\n"); + return 0; + } + ret = 1; + break; + case FT260_MODE_UART: + if (interface == 0) { + hid_info(hdev, "uart is unsupported on interface 0\n"); + ret = 0; + } + break; + case FT260_MODE_I2C: + if (interface == 1) { + hid_info(hdev, "i2c is unsupported on interface 1\n"); + ret = 0; + } + break; + } + return ret; +} + +static int ft260_byte_show(struct hid_device *hdev, int id, u8 *cfg, int len, + u8 *field, u8 *buf) +{ + int ret; + + ret = ft260_hid_feature_report_get(hdev, id, cfg, len); + if (ret != len && ret >= 0) + return -EIO; + + return scnprintf(buf, PAGE_SIZE, "%hi\n", *field); +} + +static int ft260_word_show(struct hid_device *hdev, int id, u8 *cfg, int len, + u16 *field, u8 *buf) +{ + int ret; + + ret = ft260_hid_feature_report_get(hdev, id, cfg, len); + if (ret != len && ret >= 0) + return -EIO; + + return scnprintf(buf, PAGE_SIZE, "%hi\n", le16_to_cpu(*field)); +} + +#define FT260_ATTR_SHOW(name, reptype, id, type, func) \ + static ssize_t name##_show(struct device *kdev, \ + struct device_attribute *attr, char *buf) \ + { \ + struct reptype rep; \ + struct hid_device *hdev = to_hid_device(kdev); \ + type *field = &rep.name; \ + int len = sizeof(rep); \ + \ + return func(hdev, id, (u8 *)&rep, len, field, buf); \ + } + +#define FT260_SSTAT_ATTR_SHOW(name) \ + FT260_ATTR_SHOW(name, ft260_get_system_status_report, \ + FT260_SYSTEM_SETTINGS, u8, ft260_byte_show) + +#define FT260_I2CST_ATTR_SHOW(name) \ + FT260_ATTR_SHOW(name, ft260_get_i2c_status_report, \ + FT260_I2C_STATUS, u16, ft260_word_show) + +#define FT260_ATTR_STORE(name, reptype, id, req, type, func) \ + static ssize_t name##_store(struct device *kdev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + struct reptype rep; \ + struct hid_device *hdev = to_hid_device(kdev); \ + type name; \ + int ret; \ + \ + if (!func(buf, 10, &name)) { \ + rep.name = name; \ + rep.report = id; \ + rep.request = req; \ + ret = ft260_hid_feature_report_set(hdev, (u8 *)&rep, \ + sizeof(rep)); \ + if (!ret) \ + ret = count; \ + } else { \ + ret = -EINVAL; \ + } \ + return ret; \ + } + +#define FT260_BYTE_ATTR_STORE(name, reptype, req) \ + FT260_ATTR_STORE(name, reptype, FT260_SYSTEM_SETTINGS, req, \ + u8, kstrtou8) + +#define FT260_WORD_ATTR_STORE(name, reptype, req) \ + FT260_ATTR_STORE(name, reptype, FT260_SYSTEM_SETTINGS, req, \ + u16, kstrtou16) + +FT260_SSTAT_ATTR_SHOW(chip_mode); +static DEVICE_ATTR_RO(chip_mode); + +FT260_SSTAT_ATTR_SHOW(pwren_status); +static DEVICE_ATTR_RO(pwren_status); + +FT260_SSTAT_ATTR_SHOW(suspend_status); +static DEVICE_ATTR_RO(suspend_status); + +FT260_SSTAT_ATTR_SHOW(hid_over_i2c_en); +static DEVICE_ATTR_RO(hid_over_i2c_en); + +FT260_SSTAT_ATTR_SHOW(power_saving_en); +static DEVICE_ATTR_RO(power_saving_en); + +FT260_SSTAT_ATTR_SHOW(i2c_enable); +FT260_BYTE_ATTR_STORE(i2c_enable, ft260_set_i2c_mode_report, + FT260_SET_I2C_MODE); +static DEVICE_ATTR_RW(i2c_enable); + +FT260_SSTAT_ATTR_SHOW(uart_mode); +FT260_BYTE_ATTR_STORE(uart_mode, ft260_set_uart_mode_report, + FT260_SET_UART_MODE); +static DEVICE_ATTR_RW(uart_mode); + +FT260_SSTAT_ATTR_SHOW(clock_ctl); +FT260_BYTE_ATTR_STORE(clock_ctl, ft260_set_system_clock_report, + FT260_SET_CLOCK); +static DEVICE_ATTR_RW(clock_ctl); + +FT260_I2CST_ATTR_SHOW(clock); +FT260_WORD_ATTR_STORE(clock, ft260_set_i2c_speed_report, + FT260_SET_I2C_CLOCK_SPEED); +static DEVICE_ATTR_RW(clock); + +static ssize_t i2c_reset_store(struct device *kdev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct hid_device *hdev = to_hid_device(kdev); + int ret = ft260_i2c_reset(hdev); + + if (ret) + return ret; + return count; +} +static DEVICE_ATTR_WO(i2c_reset); + +static const struct attribute_group ft260_attr_group = { + .attrs = (struct attribute *[]) { + &dev_attr_chip_mode.attr, + &dev_attr_pwren_status.attr, + &dev_attr_suspend_status.attr, + &dev_attr_hid_over_i2c_en.attr, + &dev_attr_power_saving_en.attr, + &dev_attr_i2c_enable.attr, + &dev_attr_uart_mode.attr, + &dev_attr_clock_ctl.attr, + &dev_attr_i2c_reset.attr, + &dev_attr_clock.attr, + NULL + } +}; + +static int ft260_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct ft260_device *dev; + struct ft260_get_chip_version_report version; + int ret; + + dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "failed to parse HID\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "failed to start HID HW\n"); + return ret; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "failed to open HID HW\n"); + goto err_hid_stop; + } + + ret = ft260_hid_feature_report_get(hdev, FT260_CHIP_VERSION, + (u8 *)&version, sizeof(version)); + if (ret != sizeof(version)) { + hid_err(hdev, "failed to retrieve chip version\n"); + if (ret >= 0) + ret = -EIO; + goto err_hid_close; + } + + hid_info(hdev, "chip code: %02x%02x %02x%02x\n", + version.chip_code[0], version.chip_code[1], + version.chip_code[2], version.chip_code[3]); + + ret = ft260_is_interface_enabled(hdev); + if (ret <= 0) + goto err_hid_close; + + hid_set_drvdata(hdev, dev); + dev->hdev = hdev; + dev->adap.owner = THIS_MODULE; + dev->adap.class = I2C_CLASS_HWMON; + dev->adap.algo = &ft260_i2c_algo; + dev->adap.quirks = &ft260_i2c_quirks; + dev->adap.dev.parent = &hdev->dev; + snprintf(dev->adap.name, sizeof(dev->adap.name), + "FT260 usb-i2c bridge on hidraw%d", + ((struct hidraw *)hdev->hidraw)->minor); + + mutex_init(&dev->lock); + init_completion(&dev->wait); + + ret = i2c_add_adapter(&dev->adap); + if (ret) { + hid_err(hdev, "failed to add i2c adapter\n"); + goto err_hid_close; + } + + i2c_set_adapdata(&dev->adap, dev); + + ret = sysfs_create_group(&hdev->dev.kobj, &ft260_attr_group); + if (ret < 0) { + hid_err(hdev, "failed to create sysfs attrs\n"); + goto err_i2c_free; + } + + ret = ft260_xfer_status(dev); + if (ret) + ft260_i2c_reset(hdev); + + return 0; + +err_i2c_free: + i2c_del_adapter(&dev->adap); +err_hid_close: + hid_hw_close(hdev); +err_hid_stop: + hid_hw_stop(hdev); + return ret; +} + +static void ft260_remove(struct hid_device *hdev) +{ + int ret; + struct ft260_device *dev = hid_get_drvdata(hdev); + + ret = ft260_is_interface_enabled(hdev); + if (ret <= 0) + return; + + sysfs_remove_group(&hdev->dev.kobj, &ft260_attr_group); + i2c_del_adapter(&dev->adap); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static int ft260_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct ft260_device *dev = hid_get_drvdata(hdev); + struct ft260_i2c_input_report *xfer = (void *)data; + + if (xfer->report >= FT260_I2C_REPORT_MIN && + xfer->report <= FT260_I2C_REPORT_MAX) { + ft260_dbg("i2c resp: rep %#02x len %d\n", xfer->report, + xfer->length); + + memcpy(&dev->read_buf[dev->read_idx], &xfer->data, + xfer->length); + dev->read_idx += xfer->length; + + if (dev->read_idx == dev->read_len) + complete(&dev->wait); + + } else { + hid_err(hdev, "unknown report: %#02x\n", xfer->report); + return 0; + } + return 1; +} + +static struct hid_driver ft260_driver = { + .name = "ft260", + .id_table = ft260_devices, + .probe = ft260_probe, + .remove = ft260_remove, + .raw_event = ft260_raw_event, +}; + +module_hid_driver(ft260_driver); +MODULE_DESCRIPTION("FTDI FT260 USB HID to I2C host bridge"); +MODULE_AUTHOR("Michael Zaidman <michael.zaidman@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index d9319622da44..e60c31dd05ff 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -574,6 +574,8 @@ static void hammer_remove(struct hid_device *hdev) static const struct hid_device_id hammer_devices[] = { { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_DON) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MAGNEMITE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4c74d8706b51..84b8da3e7d09 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -93,6 +93,7 @@ #define BT_VENDOR_ID_APPLE 0x004c #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d +#define USB_DEVICE_ID_APPLE_MAGICMOUSE2 0x0269 #define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e #define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 0x0265 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e @@ -194,6 +195,7 @@ #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837 #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866 +#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6 #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869 #define USB_VENDOR_ID_ATEN 0x0557 @@ -430,6 +432,7 @@ #define USB_VENDOR_ID_FUTURE_TECHNOLOGY 0x0403 #define USB_DEVICE_ID_RETRODE2 0x97c1 +#define USB_DEVICE_ID_FT260 0x6030 #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 @@ -493,6 +496,7 @@ #define USB_DEVICE_ID_GOOGLE_MASTERBALL 0x503c #define USB_DEVICE_ID_GOOGLE_MAGNEMITE 0x503d #define USB_DEVICE_ID_GOOGLE_MOONBALL 0x5044 +#define USB_DEVICE_ID_GOOGLE_DON 0x5050 #define USB_VENDOR_ID_GOTOP 0x08f2 #define USB_DEVICE_ID_SUPER_Q2 0x007f @@ -806,6 +810,7 @@ #define USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER 0xc51b #define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER 0xc52b #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER 0xc52f +#define USB_DEVICE_ID_LOGITECH_G700_RECEIVER 0xc531 #define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2 0xc534 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539 @@ -814,8 +819,14 @@ #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 #define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 #define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 -#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 -#define USB_DEVICE_ID_DINOVO_MINI 0xc71f +#define USB_DEVICE_ID_MX5000_RECEIVER_MOUSE_DEV 0xc70a +#define USB_DEVICE_ID_MX5000_RECEIVER_KBD_DEV 0xc70e +#define USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_KBD_DEV 0xc713 +#define USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_MOUSE_DEV 0xc714 +#define USB_DEVICE_ID_MX5500_RECEIVER_KBD_DEV 0xc71b +#define USB_DEVICE_ID_MX5500_RECEIVER_MOUSE_DEV 0xc71c +#define USB_DEVICE_ID_DINOVO_MINI_RECEIVER_KBD_DEV 0xc71e +#define USB_DEVICE_ID_DINOVO_MINI_RECEIVER_MOUSE_DEV 0xc71f #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03 #define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL 0xca04 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 236bccd37760..18f5e28d475c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -435,7 +435,8 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) +static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, + struct hid_field *field, bool is_percentage) { struct power_supply_desc *psy_desc; struct power_supply_config psy_cfg = { .drv_data = dev, }; @@ -475,7 +476,7 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, min = field->logical_minimum; max = field->logical_maximum; - if (quirks & HID_BATTERY_QUIRK_PERCENT) { + if (is_percentage || (quirks & HID_BATTERY_QUIRK_PERCENT)) { min = 0; max = 100; } @@ -552,7 +553,7 @@ static void hidinput_update_battery(struct hid_device *dev, int value) } #else /* !CONFIG_HID_BATTERY_STRENGTH */ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, - struct hid_field *field) + struct hid_field *field, bool is_percentage) { return 0; } @@ -806,7 +807,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case 0x3b: /* Battery Strength */ - hidinput_setup_battery(device, HID_INPUT_REPORT, field); + hidinput_setup_battery(device, HID_INPUT_REPORT, field, false); usage->type = EV_PWR; return; @@ -1068,7 +1069,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_GENDEVCTRLS: switch (usage->hid) { case HID_DC_BATTERYSTRENGTH: - hidinput_setup_battery(device, HID_INPUT_REPORT, field); + hidinput_setup_battery(device, HID_INPUT_REPORT, field, false); + usage->type = EV_PWR; + return; + } + goto unknown; + + case HID_UP_BATTERY: + switch (usage->hid) { + case HID_BAT_ABSOLUTESTATEOFCHARGE: + hidinput_setup_battery(device, HID_INPUT_REPORT, field, true); usage->type = EV_PWR; return; } @@ -1672,7 +1682,7 @@ static void report_features(struct hid_device *hid) /* Verify if Battery Strength feature is available */ if (usage->hid == HID_DC_BATTERYSTRENGTH) hidinput_setup_battery(hid, HID_FEATURE_REPORT, - rep->field[i]); + rep->field[i], false); if (drv->feature_mapping) drv->feature_mapping(hid, rep->field[i], usage); diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index c6c8e20f3e8d..93b1f935e526 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -33,6 +33,9 @@ #include "hid-ids.h" +/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */ +#define LENOVO_KEY_MICMUTE KEY_F20 + struct lenovo_drvdata { u8 led_report[3]; /* Must be first for proper alignment */ int led_state; @@ -62,8 +65,8 @@ struct lenovo_drvdata { #define TP10UBKBD_LED_OFF 1 #define TP10UBKBD_LED_ON 2 -static void lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code, - enum led_brightness value) +static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code, + enum led_brightness value) { struct lenovo_drvdata *data = hid_get_drvdata(hdev); int ret; @@ -75,10 +78,18 @@ static void lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code, data->led_report[2] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF; ret = hid_hw_raw_request(hdev, data->led_report[0], data->led_report, 3, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); - if (ret) - hid_err(hdev, "Set LED output report error: %d\n", ret); + if (ret != 3) { + if (ret != -ENODEV) + hid_err(hdev, "Set LED output report error: %d\n", ret); + + ret = ret < 0 ? ret : -EIO; + } else { + ret = 0; + } mutex_unlock(&data->led_report_mutex); + + return ret; } static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work) @@ -126,7 +137,7 @@ static int lenovo_input_mapping_tpkbd(struct hid_device *hdev, if (usage->hid == (HID_UP_BUTTON | 0x0010)) { /* This sub-device contains trackpoint, mark it */ hid_set_drvdata(hdev, (void *)1); - map_key_clear(KEY_MICMUTE); + map_key_clear(LENOVO_KEY_MICMUTE); return 1; } return 0; @@ -141,7 +152,7 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) { switch (usage->hid & HID_USAGE) { case 0x00f1: /* Fn-F4: Mic mute */ - map_key_clear(KEY_MICMUTE); + map_key_clear(LENOVO_KEY_MICMUTE); return 1; case 0x00f2: /* Fn-F5: Brightness down */ map_key_clear(KEY_BRIGHTNESSDOWN); @@ -231,7 +242,7 @@ static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev, map_key_clear(KEY_FN_ESC); return 1; case 9: /* Fn-F4: Mic mute */ - map_key_clear(KEY_MICMUTE); + map_key_clear(LENOVO_KEY_MICMUTE); return 1; case 10: /* Fn-F7: Control panel */ map_key_clear(KEY_CONFIG); @@ -255,6 +266,54 @@ static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev, return 0; } +static int lenovo_input_mapping_x1_tab_kbd(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + /* + * The ThinkPad X1 Tablet Thin Keyboard uses 0x000c0001 usage for + * a bunch of keys which have no standard consumer page code. + */ + if (usage->hid == 0x000c0001) { + switch (usage->usage_index) { + case 0: /* Fn-F10: Enable/disable bluetooth */ + map_key_clear(KEY_BLUETOOTH); + return 1; + case 1: /* Fn-F11: Keyboard settings */ + map_key_clear(KEY_KEYBOARD); + return 1; + case 2: /* Fn-F12: User function / Cortana */ + map_key_clear(KEY_MACRO1); + return 1; + case 3: /* Fn-PrtSc: Snipping tool */ + map_key_clear(KEY_SELECTIVE_SCREENSHOT); + return 1; + case 8: /* Fn-Esc: Fn-lock toggle */ + map_key_clear(KEY_FN_ESC); + return 1; + case 9: /* Fn-F4: Mute/unmute microphone */ + map_key_clear(KEY_MICMUTE); + return 1; + case 10: /* Fn-F9: Settings */ + map_key_clear(KEY_CONFIG); + return 1; + case 13: /* Fn-F7: Manage external displays */ + map_key_clear(KEY_SWITCHVIDEOMODE); + return 1; + case 14: /* Fn-F8: Enable/disable wifi */ + map_key_clear(KEY_WLAN); + return 1; + } + } + + if (usage->hid == (HID_UP_KEYBOARD | 0x009a)) { + map_key_clear(KEY_SYSRQ); + return 1; + } + + return 0; +} + static int lenovo_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -278,6 +337,8 @@ static int lenovo_input_mapping(struct hid_device *hdev, case USB_DEVICE_ID_LENOVO_TP10UBKBD: return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field, usage, bit, max); + case USB_DEVICE_ID_LENOVO_X1_TAB: + return lenovo_input_mapping_x1_tab_kbd(hdev, hi, field, usage, bit, max); default: return 0; } @@ -349,7 +410,7 @@ static ssize_t attr_fn_lock_store(struct device *dev, { struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data = hid_get_drvdata(hdev); - int value; + int value, ret; if (kstrtoint(buf, 10, &value)) return -EINVAL; @@ -364,7 +425,10 @@ static ssize_t attr_fn_lock_store(struct device *dev, lenovo_features_set_cptkbd(hdev); break; case USB_DEVICE_ID_LENOVO_TP10UBKBD: - lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value); + case USB_DEVICE_ID_LENOVO_X1_TAB: + ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value); + if (ret) + return ret; break; } @@ -498,11 +562,15 @@ static int lenovo_event_cptkbd(struct hid_device *hdev, static int lenovo_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { + if (!hid_get_drvdata(hdev)) + return 0; + switch (hdev->product) { case USB_DEVICE_ID_LENOVO_CUSBKBD: case USB_DEVICE_ID_LENOVO_CBTKBD: return lenovo_event_cptkbd(hdev, field, usage, value); case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: return lenovo_event_tp10ubkbd(hdev, field, usage, value); default: return 0; @@ -761,23 +829,7 @@ static void lenovo_led_set_tpkbd(struct hid_device *hdev) hid_hw_request(hdev, report, HID_REQ_SET_REPORT); } -static enum led_brightness lenovo_led_brightness_get( - struct led_classdev *led_cdev) -{ - struct device *dev = led_cdev->dev->parent; - struct hid_device *hdev = to_hid_device(dev); - struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); - int led_nr = 0; - - if (led_cdev == &data_pointer->led_micmute) - led_nr = 1; - - return data_pointer->led_state & (1 << led_nr) - ? LED_FULL - : LED_OFF; -} - -static void lenovo_led_brightness_set(struct led_classdev *led_cdev, +static int lenovo_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { struct device *dev = led_cdev->dev->parent; @@ -785,6 +837,7 @@ static void lenovo_led_brightness_set(struct led_classdev *led_cdev, struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED }; int led_nr = 0; + int ret = 0; if (led_cdev == &data_pointer->led_micmute) led_nr = 1; @@ -799,9 +852,12 @@ static void lenovo_led_brightness_set(struct led_classdev *led_cdev, lenovo_led_set_tpkbd(hdev); break; case USB_DEVICE_ID_LENOVO_TP10UBKBD: - lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value); + case USB_DEVICE_ID_LENOVO_X1_TAB: + ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value); break; } + + return ret; } static int lenovo_register_leds(struct hid_device *hdev) @@ -821,16 +877,20 @@ static int lenovo_register_leds(struct hid_device *hdev) snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev)); data->led_mute.name = name_mute; - data->led_mute.brightness_get = lenovo_led_brightness_get; - data->led_mute.brightness_set = lenovo_led_brightness_set; + data->led_mute.default_trigger = "audio-mute"; + data->led_mute.brightness_set_blocking = lenovo_led_brightness_set; + data->led_mute.max_brightness = 1; + data->led_mute.flags = LED_HW_PLUGGABLE; data->led_mute.dev = &hdev->dev; ret = led_classdev_register(&hdev->dev, &data->led_mute); if (ret < 0) return ret; data->led_micmute.name = name_micm; - data->led_micmute.brightness_get = lenovo_led_brightness_get; - data->led_micmute.brightness_set = lenovo_led_brightness_set; + data->led_micmute.default_trigger = "audio-micmute"; + data->led_micmute.brightness_set_blocking = lenovo_led_brightness_set; + data->led_micmute.max_brightness = 1; + data->led_micmute.flags = LED_HW_PLUGGABLE; data->led_micmute.dev = &hdev->dev; ret = led_classdev_register(&hdev->dev, &data->led_micmute); if (ret < 0) { @@ -952,11 +1012,24 @@ static const struct attribute_group lenovo_attr_group_tp10ubkbd = { static int lenovo_probe_tp10ubkbd(struct hid_device *hdev) { + struct hid_report_enum *rep_enum; struct lenovo_drvdata *data; + struct hid_report *rep; + bool found; int ret; - /* All the custom action happens on the USBMOUSE device for USB */ - if (hdev->type != HID_TYPE_USBMOUSE) + /* + * The LEDs and the Fn-lock functionality use output report 9, + * with an application of 0xffa0001, add the LEDs on the interface + * with this output report. + */ + found = false; + rep_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + if (rep->application == 0xffa00001) + found = true; + } + if (!found) return 0; data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL); @@ -1018,6 +1091,7 @@ static int lenovo_probe(struct hid_device *hdev, ret = lenovo_probe_cptkbd(hdev); break; case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: ret = lenovo_probe_tp10ubkbd(hdev); break; default: @@ -1083,6 +1157,7 @@ static void lenovo_remove(struct hid_device *hdev) lenovo_remove_cptkbd(hdev); break; case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: lenovo_remove_tp10ubkbd(hdev); break; } @@ -1122,6 +1197,12 @@ static const struct hid_device_id lenovo_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) }, + /* + * Note bind to the HID_GROUP_GENERIC group, so that we only bind to the keyboard + * part, while letting hid-multitouch.c handle the touchpad and trackpoint. + */ + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB) }, { } }; diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 0dc7cdfc56f7..d40af911df63 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -568,22 +568,6 @@ static int lg_ultrax_remote_mapping(struct hid_input *hi, return 1; } -static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) - return 0; - - switch (usage->hid & HID_USAGE) { - - case 0x00d: lg_map_key_clear(KEY_MEDIA); break; - default: - return 0; - - } - return 1; -} - static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage, unsigned long **bit, int *max) { @@ -668,10 +652,6 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, lg_ultrax_remote_mapping(hi, usage, bit, max)) return 1; - if (hdev->product == USB_DEVICE_ID_DINOVO_MINI && - lg_dinovo_mapping(hi, usage, bit, max)) - return 1; - if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) return 1; @@ -879,10 +859,6 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP), .driver_data = LG_DUPLICATE_USAGES }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE), - .driver_data = LG_DUPLICATE_USAGES }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), - .driver_data = LG_DUPLICATE_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 271bd8d24339..fa835d565982 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -84,6 +84,7 @@ #define STD_MOUSE BIT(2) #define MULTIMEDIA BIT(3) #define POWER_KEYS BIT(4) +#define KBD_MOUSE BIT(5) #define MEDIA_CENTER BIT(8) #define KBD_LEDS BIT(14) /* Fake (bitnr > NUMBER_OF_HID_REPORTS) bit to track HID++ capability */ @@ -117,6 +118,7 @@ enum recvr_type { recvr_type_mouse_only, recvr_type_27mhz, recvr_type_bluetooth, + recvr_type_dinovo, }; struct dj_report { @@ -333,6 +335,47 @@ static const char mse_bluetooth_descriptor[] = { 0xC0, /* END_COLLECTION */ }; +/* Mouse descriptor (5) for Bluetooth receiver, normal-res hwheel, 8 buttons */ +static const char mse5_bluetooth_descriptor[] = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x02, /* Usage (Mouse) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x85, 0x05, /* Report ID (5) */ + 0x09, 0x01, /* Usage (Pointer) */ + 0xa1, 0x00, /* Collection (Physical) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (1) */ + 0x29, 0x08, /* Usage Maximum (8) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x95, 0x08, /* Report Count (8) */ + 0x75, 0x01, /* Report Size (1) */ + 0x81, 0x02, /* Input (Data,Var,Abs) */ + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x16, 0x01, 0xf8, /* Logical Minimum (-2047) */ + 0x26, 0xff, 0x07, /* Logical Maximum (2047) */ + 0x75, 0x0c, /* Report Size (12) */ + 0x95, 0x02, /* Report Count (2) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x31, /* Usage (Y) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x15, 0x81, /* Logical Minimum (-127) */ + 0x25, 0x7f, /* Logical Maximum (127) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x01, /* Report Count (1) */ + 0x09, 0x38, /* Usage (Wheel) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x05, 0x0c, /* Usage Page (Consumer Devices) */ + 0x0a, 0x38, 0x02, /* Usage (AC Pan) */ + 0x15, 0x81, /* Logical Minimum (-127) */ + 0x25, 0x7f, /* Logical Maximum (127) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x01, /* Report Count (1) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0xc0, /* End Collection */ + 0xc0, /* End Collection */ +}; + /* Gaming Mouse descriptor (2) */ static const char mse_high_res_descriptor[] = { 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ @@ -480,6 +523,7 @@ static const char hidpp_descriptor[] = { #define MAX_RDESC_SIZE \ (sizeof(kbd_descriptor) + \ sizeof(mse_bluetooth_descriptor) + \ + sizeof(mse5_bluetooth_descriptor) + \ sizeof(consumer_descriptor) + \ sizeof(syscontrol_descriptor) + \ sizeof(media_descriptor) + \ @@ -517,6 +561,11 @@ static void delayedwork_callback(struct work_struct *work); static LIST_HEAD(dj_hdev_list); static DEFINE_MUTEX(dj_hdev_list_lock); +static bool recvr_type_is_bluetooth(enum recvr_type type) +{ + return type == recvr_type_bluetooth || type == recvr_type_dinovo; +} + /* * dj/HID++ receivers are really a single logical entity, but for BIOS/Windows * compatibility they have multiple USB interfaces. On HID++ receivers we need @@ -534,7 +583,7 @@ static struct dj_receiver_dev *dj_find_receiver_dev(struct hid_device *hdev, * The bluetooth receiver contains a built-in hub and has separate * USB-devices for the keyboard and mouse interfaces. */ - sep = (type == recvr_type_bluetooth) ? '.' : '/'; + sep = recvr_type_is_bluetooth(type) ? '.' : '/'; /* Try to find an already-probed interface from the same device */ list_for_each_entry(djrcv_dev, &dj_hdev_list, list) { @@ -872,6 +921,14 @@ static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev, * touchpad to work we must also forward mouse input reports to the dj_hiddev * created for the keyboard (instead of forwarding them to a second paired * device with a device_type of REPORT_TYPE_MOUSE as we normally would). + * + * On Dinovo receivers the keyboard's touchpad and an optional paired actual + * mouse send separate input reports, INPUT(2) aka STD_MOUSE for the mouse + * and INPUT(5) aka KBD_MOUSE for the keyboard's touchpad. + * + * On MX5x00 receivers (which can also be paired with a Dinovo keyboard) + * INPUT(2) is used for both an optional paired actual mouse and for the + * keyboard's touchpad. */ static const u16 kbd_builtin_touchpad_ids[] = { 0xb309, /* Dinovo Edge */ @@ -898,7 +955,10 @@ static void logi_hidpp_dev_conn_notif_equad(struct hid_device *hdev, id = (workitem->quad_id_msb << 8) | workitem->quad_id_lsb; for (i = 0; i < ARRAY_SIZE(kbd_builtin_touchpad_ids); i++) { if (id == kbd_builtin_touchpad_ids[i]) { - workitem->reports_supported |= STD_MOUSE; + if (djrcv_dev->type == recvr_type_dinovo) + workitem->reports_supported |= KBD_MOUSE; + else + workitem->reports_supported |= STD_MOUSE; break; } } @@ -1367,7 +1427,7 @@ static int logi_dj_ll_parse(struct hid_device *hid) else if (djdev->dj_receiver_dev->type == recvr_type_27mhz) rdcat(rdesc, &rsize, mse_27mhz_descriptor, sizeof(mse_27mhz_descriptor)); - else if (djdev->dj_receiver_dev->type == recvr_type_bluetooth) + else if (recvr_type_is_bluetooth(djdev->dj_receiver_dev->type)) rdcat(rdesc, &rsize, mse_bluetooth_descriptor, sizeof(mse_bluetooth_descriptor)); else @@ -1375,6 +1435,13 @@ static int logi_dj_ll_parse(struct hid_device *hid) sizeof(mse_descriptor)); } + if (djdev->reports_supported & KBD_MOUSE) { + dbg_hid("%s: sending a kbd-mouse descriptor, reports_supported: %llx\n", + __func__, djdev->reports_supported); + rdcat(rdesc, &rsize, mse5_bluetooth_descriptor, + sizeof(mse5_bluetooth_descriptor)); + } + if (djdev->reports_supported & MULTIMEDIA) { dbg_hid("%s: sending a multimedia report descriptor: %llx\n", __func__, djdev->reports_supported); @@ -1692,6 +1759,7 @@ static int logi_dj_probe(struct hid_device *hdev, case recvr_type_mouse_only: no_dj_interfaces = 2; break; case recvr_type_27mhz: no_dj_interfaces = 2; break; case recvr_type_bluetooth: no_dj_interfaces = 2; break; + case recvr_type_dinovo: no_dj_interfaces = 2; break; } if (hid_is_using_ll_driver(hdev, &usb_hid_driver)) { intf = to_usb_interface(hdev->dev.parent); @@ -1857,23 +1925,27 @@ static void logi_dj_remove(struct hid_device *hdev) } static const struct hid_device_id logi_dj_receivers[] = { - {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + { /* Logitech unifying receiver (0xc52b) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER), .driver_data = recvr_type_dj}, - {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + { /* Logitech unifying receiver (0xc532) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2), .driver_data = recvr_type_dj}, - { /* Logitech Nano mouse only receiver */ + + { /* Logitech Nano mouse only receiver (0xc52f) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER), .driver_data = recvr_type_mouse_only}, - { /* Logitech Nano (non DJ) receiver */ + { /* Logitech Nano (non DJ) receiver (0xc534) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2), .driver_data = recvr_type_hidpp}, + { /* Logitech G700(s) receiver (0xc531) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - 0xc531), + USB_DEVICE_ID_LOGITECH_G700_RECEIVER), .driver_data = recvr_type_gaming_hidpp}, { /* Logitech G602 receiver (0xc537) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, @@ -1883,17 +1955,18 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1), .driver_data = recvr_type_gaming_hidpp}, + { /* Logitech powerplay receiver (0xc53a) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY), + .driver_data = recvr_type_gaming_hidpp}, { /* Logitech lightspeed receiver (0xc53f) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1), .driver_data = recvr_type_gaming_hidpp}, + { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), .driver_data = recvr_type_27mhz}, - { /* Logitech powerplay receiver (0xc53a) */ - HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY), - .driver_data = recvr_type_gaming_hidpp}, { /* Logitech 27 MHz HID++ 1.0 receiver (0xc517) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), @@ -1902,22 +1975,40 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER), .driver_data = recvr_type_27mhz}, - { /* Logitech MX5000 HID++ / bluetooth receiver keyboard intf. */ + + { /* Logitech MX5000 HID++ / bluetooth receiver keyboard intf. (0xc70e) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - 0xc70e), + USB_DEVICE_ID_MX5000_RECEIVER_KBD_DEV), .driver_data = recvr_type_bluetooth}, - { /* Logitech MX5000 HID++ / bluetooth receiver mouse intf. */ + { /* Logitech MX5000 HID++ / bluetooth receiver mouse intf. (0xc70a) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - 0xc70a), + USB_DEVICE_ID_MX5000_RECEIVER_MOUSE_DEV), .driver_data = recvr_type_bluetooth}, - { /* Logitech MX5500 HID++ / bluetooth receiver keyboard intf. */ + { /* Logitech MX5500 HID++ / bluetooth receiver keyboard intf. (0xc71b) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - 0xc71b), + USB_DEVICE_ID_MX5500_RECEIVER_KBD_DEV), .driver_data = recvr_type_bluetooth}, - { /* Logitech MX5500 HID++ / bluetooth receiver mouse intf. */ + { /* Logitech MX5500 HID++ / bluetooth receiver mouse intf. (0xc71c) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - 0xc71c), + USB_DEVICE_ID_MX5500_RECEIVER_MOUSE_DEV), .driver_data = recvr_type_bluetooth}, + + { /* Logitech Dinovo Edge HID++ / bluetooth receiver keyboard intf. (0xc713) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_KBD_DEV), + .driver_data = recvr_type_dinovo}, + { /* Logitech Dinovo Edge HID++ / bluetooth receiver mouse intf. (0xc714) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_MOUSE_DEV), + .driver_data = recvr_type_dinovo}, + { /* Logitech DiNovo Mini HID++ / bluetooth receiver mouse intf. (0xc71e) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_DINOVO_MINI_RECEIVER_KBD_DEV), + .driver_data = recvr_type_dinovo}, + { /* Logitech DiNovo Mini HID++ / bluetooth receiver keyboard intf. (0xc71f) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_DINOVO_MINI_RECEIVER_MOUSE_DEV), + .driver_data = recvr_type_dinovo}, {} }; diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index abd86903875f..2bb473d8c424 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -16,6 +16,7 @@ #include <linux/input/mt.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/workqueue.h> #include "hid-ids.h" @@ -54,6 +55,7 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define TRACKPAD2_USB_REPORT_ID 0x02 #define TRACKPAD2_BT_REPORT_ID 0x31 #define MOUSE_REPORT_ID 0x29 +#define MOUSE2_REPORT_ID 0x12 #define DOUBLE_REPORT_ID 0xf7 /* These definitions are not precise, but they're close enough. (Bits * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem @@ -127,6 +129,9 @@ struct magicmouse_sc { u8 size; } touches[16]; int tracking_ids[16]; + + struct hid_device *hdev; + struct delayed_work work; }; static int magicmouse_firm_touch(struct magicmouse_sc *msc) @@ -195,7 +200,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda int id, x, y, size, orientation, touch_major, touch_minor, state, down; int pressure = 0; - if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || + input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; x = (tdata[1] << 28 | tdata[0] << 20) >> 20; y = -((tdata[2] << 24 | tdata[1] << 16) >> 20); @@ -296,7 +302,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_PRESSURE, pressure); if (report_undeciphered) { - if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || + input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) input_event(input, EV_MSC, MSC_RAW, tdata[7]); else if (input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) @@ -380,6 +387,34 @@ static int magicmouse_raw_event(struct hid_device *hdev, * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; */ break; + case MOUSE2_REPORT_ID: + /* Size is either 8 or (14 + 8 * N) */ + if (size != 8 && (size < 14 || (size - 14) % 8 != 0)) + return 0; + npoints = (size - 14) / 8; + if (npoints > 15) { + hid_warn(hdev, "invalid size value (%d) for MOUSE2_REPORT_ID\n", + size); + return 0; + } + msc->ntouches = 0; + for (ii = 0; ii < npoints; ii++) + magicmouse_emit_touch(msc, ii, data + ii * 8 + 14); + + /* When emulating three-button mode, it is important + * to have the current touch information before + * generating a click event. + */ + x = (int)((data[3] << 24) | (data[2] << 16)) >> 16; + y = (int)((data[5] << 24) | (data[4] << 16)) >> 16; + clicks = data[1]; + + /* The following bits provide a device specific timestamp. They + * are unused here. + * + * ts = data[11] >> 6 | data[12] << 2 | data[13] << 10; + */ + break; case DOUBLE_REPORT_ID: /* Sometimes the trackpad sends two touch reports in one * packet. @@ -392,7 +427,8 @@ static int magicmouse_raw_event(struct hid_device *hdev, return 0; } - if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || + input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { magicmouse_emit_buttons(msc, clicks & 3); input_report_rel(input, REL_X, x); input_report_rel(input, REL_Y, y); @@ -408,6 +444,23 @@ static int magicmouse_raw_event(struct hid_device *hdev, return 1; } +static int magicmouse_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + if (msc->input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 && + field->report->id == MOUSE2_REPORT_ID) { + /* + * magic_mouse_raw_event has done all the work. Skip hidinput. + * + * Specifically, hidinput may modify BTN_LEFT and BTN_RIGHT, + * breaking emulate_3button. + */ + return 1; + } + return 0; +} + static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) { int error; @@ -415,7 +468,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd __set_bit(EV_KEY, input->evbit); - if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || + input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { __set_bit(BTN_LEFT, input->keybit); __set_bit(BTN_RIGHT, input->keybit); if (emulate_3button) @@ -480,7 +534,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd * the origin at the same position, and just uses the additive * inverse of the reported Y. */ - if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE || + input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); input_set_abs_params(input, ABS_MT_POSITION_X, MOUSE_MIN_X, MOUSE_MAX_X, 4, 0); @@ -580,19 +635,60 @@ static int magicmouse_input_configured(struct hid_device *hdev, return 0; } - -static int magicmouse_probe(struct hid_device *hdev, - const struct hid_device_id *id) +static int magicmouse_enable_multitouch(struct hid_device *hdev) { const u8 *feature; const u8 feature_mt[] = { 0xD7, 0x01 }; + const u8 feature_mt_mouse2[] = { 0xF1, 0x02, 0x01 }; const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 }; const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 }; u8 *buf; + int ret; + int feature_size; + + if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + if (hdev->vendor == BT_VENDOR_ID_APPLE) { + feature_size = sizeof(feature_mt_trackpad2_bt); + feature = feature_mt_trackpad2_bt; + } else { /* USB_VENDOR_ID_APPLE */ + feature_size = sizeof(feature_mt_trackpad2_usb); + feature = feature_mt_trackpad2_usb; + } + } else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { + feature_size = sizeof(feature_mt_mouse2); + feature = feature_mt_mouse2; + } else { + feature_size = sizeof(feature_mt); + feature = feature_mt; + } + + buf = kmemdup(feature, feature_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + kfree(buf); + return ret; +} + +static void magicmouse_enable_mt_work(struct work_struct *work) +{ + struct magicmouse_sc *msc = + container_of(work, struct magicmouse_sc, work.work); + int ret; + + ret = magicmouse_enable_multitouch(msc->hdev); + if (ret < 0) + hid_err(msc->hdev, "unable to request touch data (%d)\n", ret); +} + +static int magicmouse_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ struct magicmouse_sc *msc; struct hid_report *report; int ret; - int feature_size; if (id->vendor == USB_VENDOR_ID_APPLE && id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && @@ -606,6 +702,8 @@ static int magicmouse_probe(struct hid_device *hdev, } msc->scroll_accel = SCROLL_ACCEL_DEFAULT; + msc->hdev = hdev; + INIT_DEFERRABLE_WORK(&msc->work, magicmouse_enable_mt_work); msc->quirks = id->driver_data; hid_set_drvdata(hdev, msc); @@ -631,6 +729,9 @@ static int magicmouse_probe(struct hid_device *hdev, if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) report = hid_register_report(hdev, HID_INPUT_REPORT, MOUSE_REPORT_ID, 0); + else if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) + report = hid_register_report(hdev, HID_INPUT_REPORT, + MOUSE2_REPORT_ID, 0); else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { if (id->vendor == BT_VENDOR_ID_APPLE) report = hid_register_report(hdev, HID_INPUT_REPORT, @@ -652,25 +753,6 @@ static int magicmouse_probe(struct hid_device *hdev, } report->size = 6; - if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { - if (id->vendor == BT_VENDOR_ID_APPLE) { - feature_size = sizeof(feature_mt_trackpad2_bt); - feature = feature_mt_trackpad2_bt; - } else { /* USB_VENDOR_ID_APPLE */ - feature_size = sizeof(feature_mt_trackpad2_usb); - feature = feature_mt_trackpad2_usb; - } - } else { - feature_size = sizeof(feature_mt); - feature = feature_mt; - } - - buf = kmemdup(feature, feature_size, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto err_stop_hw; - } - /* * Some devices repond with 'invalid report id' when feature * report switching it into multitouch mode is sent to it. @@ -679,13 +761,14 @@ static int magicmouse_probe(struct hid_device *hdev, * but there seems to be no other way of switching the mode. * Thus the super-ugly hacky success check below. */ - ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size, - HID_FEATURE_REPORT, HID_REQ_SET_REPORT); - kfree(buf); - if (ret != -EIO && ret != feature_size) { + ret = magicmouse_enable_multitouch(hdev); + if (ret != -EIO && ret < 0) { hid_err(hdev, "unable to request touch data (%d)\n", ret); goto err_stop_hw; } + if (ret == -EIO && id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) { + schedule_delayed_work(&msc->work, msecs_to_jiffies(500)); + } return 0; err_stop_hw: @@ -693,9 +776,18 @@ err_stop_hw: return ret; } +static void magicmouse_remove(struct hid_device *hdev) +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + cancel_delayed_work_sync(&msc->work); + hid_hw_stop(hdev); +} + static const struct hid_device_id magic_mice[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, @@ -710,7 +802,9 @@ static struct hid_driver magicmouse_driver = { .name = "magicmouse", .id_table = magic_mice, .probe = magicmouse_probe, + .remove = magicmouse_remove, .raw_event = magicmouse_raw_event, + .event = magicmouse_event, .input_mapping = magicmouse_input_mapping, .input_configured = magicmouse_input_configured, }; diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 1a9daf03dbfa..3816be700f49 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -445,8 +445,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c index bb8c00e6be78..a6f0257a26de 100644 --- a/drivers/hid/i2c-hid/i2c-hid-acpi.c +++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c @@ -25,12 +25,13 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pm.h> +#include <linux/uuid.h> #include "i2c-hid.h" struct i2c_hid_acpi { struct i2chid_ops ops; - struct i2c_client *client; + struct acpi_device *adev; }; static const struct acpi_device_id i2c_hid_acpi_blacklist[] = { @@ -42,29 +43,24 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = { { }, }; -static int i2c_hid_acpi_get_descriptor(struct i2c_client *client) +/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */ +static guid_t i2c_hid_guid = + GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555, + 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE); + +static int i2c_hid_acpi_get_descriptor(struct acpi_device *adev) { - static guid_t i2c_hid_guid = - GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555, - 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE); + acpi_handle handle = acpi_device_handle(adev); union acpi_object *obj; - struct acpi_device *adev; - acpi_handle handle; u16 hid_descriptor_address; - handle = ACPI_HANDLE(&client->dev); - if (!handle || acpi_bus_get_device(handle, &adev)) { - dev_err(&client->dev, "Error could not get ACPI device\n"); - return -ENODEV; - } - if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0) return -ENODEV; obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL, ACPI_TYPE_INTEGER); if (!obj) { - dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n"); + acpi_handle_err(handle, "Error _DSM call to get HID descriptor address failed\n"); return -ENODEV; } @@ -76,14 +72,12 @@ static int i2c_hid_acpi_get_descriptor(struct i2c_client *client) static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops) { - struct i2c_hid_acpi *ihid_acpi = - container_of(ops, struct i2c_hid_acpi, ops); - struct device *dev = &ihid_acpi->client->dev; - acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D3_COLD); + struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops); + + acpi_device_set_power(ihid_acpi->adev, ACPI_STATE_D3_COLD); } -static int i2c_hid_acpi_probe(struct i2c_client *client, - const struct i2c_device_id *dev_id) +static int i2c_hid_acpi_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct i2c_hid_acpi *ihid_acpi; @@ -91,21 +85,25 @@ static int i2c_hid_acpi_probe(struct i2c_client *client, u16 hid_descriptor_address; int ret; + adev = ACPI_COMPANION(dev); + if (!adev) { + dev_err(&client->dev, "Error could not get ACPI device\n"); + return -ENODEV; + } + ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL); if (!ihid_acpi) return -ENOMEM; - ihid_acpi->client = client; + ihid_acpi->adev = adev; ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail; - ret = i2c_hid_acpi_get_descriptor(client); + ret = i2c_hid_acpi_get_descriptor(adev); if (ret < 0) return ret; hid_descriptor_address = ret; - adev = ACPI_COMPANION(dev); - if (adev) - acpi_device_fix_up_power(adev); + acpi_device_fix_up_power(adev); if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) { device_set_wakeup_capable(dev, true); @@ -128,10 +126,10 @@ static struct i2c_driver i2c_hid_acpi_driver = { .name = "i2c_hid_acpi", .pm = &i2c_hid_core_pm, .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .acpi_match_table = ACPI_PTR(i2c_hid_acpi_match), + .acpi_match_table = i2c_hid_acpi_match, }, - .probe = i2c_hid_acpi_probe, + .probe_new = i2c_hid_acpi_probe, .remove = i2c_hid_core_remove, .shutdown = i2c_hid_core_shutdown, }; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 45e0b1c75cb1..2fb2991dbe4c 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -887,11 +887,11 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) break; if (i == hid->maxcollection) - return -1; + return -EINVAL; } if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) - return -1; + return -ENOMEM; init_waitqueue_head(&hiddev->wait); INIT_LIST_HEAD(&hiddev->list); @@ -905,7 +905,7 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) hid_err(hid, "Not able to get a minor for this device\n"); hid->hiddev = NULL; kfree(hiddev); - return -1; + return retval; } /* diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 44d715c12f6a..2d70dc4bea65 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2533,7 +2533,7 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac, !wacom_wac->shared->is_touch_on) { if (!wacom_wac->shared->touch_down) return; - prox = 0; + prox = false; } wacom_wac->hid_data.num_received++; @@ -3574,8 +3574,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, { struct wacom_features *features = &wacom_wac->features; - input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - if (!(features->device_type & WACOM_DEVICETYPE_PEN)) return -ENODEV; @@ -3590,6 +3588,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, return 0; } + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(ABS_MISC, input_dev->absbit); @@ -3742,8 +3741,6 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, { struct wacom_features *features = &wacom_wac->features; - input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - if (!(features->device_type & WACOM_DEVICETYPE_TOUCH)) return -ENODEV; @@ -3756,6 +3753,7 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, /* setup has already been done */ return 0; + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); __set_bit(BTN_TOUCH, input_dev->keybit); if (features->touch_max == 1) { |