]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
Merge branches 'for-3.15/multitouch', 'for-3.15/sony' and 'for-3.15/uhid' into for...
authorJiri Kosina <jkosina@suse.cz>
Tue, 1 Apr 2014 17:06:50 +0000 (19:06 +0200)
committerJiri Kosina <jkosina@suse.cz>
Tue, 1 Apr 2014 17:06:50 +0000 (19:06 +0200)
26 files changed:
Documentation/hid/uhid.txt
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-apple.c
drivers/hid/hid-core.c
drivers/hid/hid-cp2112.c [new file with mode: 0644]
drivers/hid/hid-hyperv.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lg4ff.c
drivers/hid/hid-microsoft.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-sony.c
drivers/hid/hidraw.c
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/uhid.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
drivers/iio/common/hid-sensors/hid-sensor-trigger.c
include/linux/hid-sensor-hub.h
include/linux/hid-sensor-ids.h
include/linux/hid.h
include/uapi/linux/uhid.h
net/bluetooth/hidp/core.c
net/bluetooth/hidp/hidp.h

index dc35a2b75eeec08743b5e55614508a881589d3c0..ee6593608c8efd00ab4201a886efb46a3a30cad0 100644 (file)
@@ -93,6 +93,11 @@ the request was handled successfully.
   event to the kernel. The payload is of type struct uhid_create_req and
   contains information about your device. You can start I/O now.
 
+  UHID_CREATE2:
+  Same as UHID_CREATE, but the HID report descriptor data (rd_data) is an array
+  inside struct uhid_create2_req, instead of a pointer to a separate array.
+  Enables use from languages that don't support pointers, e.g. Python.
+
   UHID_DESTROY:
   This destroys the internal HID device. No further I/O will be accepted. There
   may still be pending messages that you can receive with read() but no further
@@ -105,6 +110,12 @@ the request was handled successfully.
   contains a data-payload. This is the raw data that you read from your device.
   The kernel will parse the HID reports and react on it.
 
+  UHID_INPUT2:
+  Same as UHID_INPUT, but the data array is the last field of uhid_input2_req.
+  Enables userspace to write only the required bytes to kernel (ev.type +
+  ev.u.input2.size + the part of the data array that matters), instead of
+  the entire struct uhid_input2_req.
+
   UHID_FEATURE_ANSWER:
   If you receive a UHID_FEATURE request you must answer with this request. You
   must copy the "id" field from the request into the answer. Set the "err" field
index f7220011a00b7cde8c16754743517ca17301366a..7af9d0b5dea16f427987e234952c44cd53c540fc 100644 (file)
@@ -175,6 +175,15 @@ config HID_PRODIKEYS
          multimedia keyboard, but will lack support for the musical keyboard
          and some additional multimedia keys.
 
+config HID_CP2112
+       tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
+       depends on USB_HID && I2C && GPIOLIB
+       ---help---
+       Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge.
+       This is a HID device driver which registers as an i2c adapter
+       and gpiochip to expose these functions of the CP2112. The
+       customizable USB descriptor fields are exposed as sysfs attributes.
+
 config HID_CYPRESS
        tristate "Cypress mouse and barcode readers" if EXPERT
        depends on HID
@@ -608,25 +617,27 @@ config HID_SAMSUNG
        Support for Samsung InfraRed remote control or keyboards.
 
 config HID_SONY
-       tristate "Sony PS2/3 accessories"
+       tristate "Sony PS2/3/4 accessories"
        depends on USB_HID
        depends on NEW_LEDS
        depends on LEDS_CLASS
+       select POWER_SUPPLY
        ---help---
        Support for
 
          * Sony PS3 6-axis controllers
+         * Sony PS4 DualShock 4 controllers
          * Buzz controllers
          * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
          * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
 
 config SONY_FF
-       bool "Sony PS2/3 accessories force feedback support"
+       bool "Sony PS2/3/4 accessories force feedback support" 
        depends on HID_SONY
        select INPUT_FF_MEMLESS
        ---help---
-       Say Y here if you have a Sony PS2/3 accessory and want to enable force
-       feedback support for it.
+       Say Y here if you have a Sony PS2/3/4 accessory and want to enable
+       force feedback support for it.
 
 config HID_SPEEDLINK
        tristate "Speedlink VAD Cezanne mouse support"
index 30e44318f87f6c72742f6b6f21584a7ac3af200e..fc712dde02a48f185dcdb4ff01e5dd0aadc96aa6 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_AUREAL)        += hid-aureal.o
 obj-$(CONFIG_HID_BELKIN)       += hid-belkin.o
 obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)      += hid-chicony.o
+obj-$(CONFIG_HID_CP2112)       += hid-cp2112.o
 obj-$(CONFIG_HID_CYPRESS)      += hid-cypress.o
 obj-$(CONFIG_HID_DRAGONRISE)   += hid-dr.o
 obj-$(CONFIG_HID_EMS_FF)       += hid-emsff.o
index 497558127bb3e63609af2b5e1f75265f9e7b1423..f822fd2a1adabc4b3e53d86155c2d9e50d3d241e 100644 (file)
@@ -469,6 +469,9 @@ static const struct hid_device_id apple_devices[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
                                USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI),
                .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+                               USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
                .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
index 0b57babe3f9f7a20bff6d92328d2542f966c52ab..dbe548b22b2a09a13ab45d04811f54557938861a 100644 (file)
@@ -1719,6 +1719,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
        { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
@@ -1732,6 +1733,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
@@ -1819,6 +1821,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
        { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
        { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
new file mode 100644 (file)
index 0000000..56be85a
--- /dev/null
@@ -0,0 +1,1073 @@
+/*
+ * hid-cp2112.c - Silicon Labs HID USB to SMBus master bridge
+ * Copyright (c) 2013,2014 Uplogix, Inc.
+ * David Barksdale <dbarksdale@uplogix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * The Silicon Labs CP2112 chip is a USB HID device which provides an
+ * SMBus controller for talking to slave devices and 8 GPIO pins. The
+ * host communicates with the CP2112 via raw HID reports.
+ *
+ * Data Sheet:
+ *   http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
+ * Programming Interface Specification:
+ *   http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf
+ */
+
+#include <linux/gpio.h>
+#include <linux/hid.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/nls.h>
+#include <linux/usb/ch9.h>
+#include "hid-ids.h"
+
+enum {
+       CP2112_GPIO_CONFIG              = 0x02,
+       CP2112_GPIO_GET                 = 0x03,
+       CP2112_GPIO_SET                 = 0x04,
+       CP2112_GET_VERSION_INFO         = 0x05,
+       CP2112_SMBUS_CONFIG             = 0x06,
+       CP2112_DATA_READ_REQUEST        = 0x10,
+       CP2112_DATA_WRITE_READ_REQUEST  = 0x11,
+       CP2112_DATA_READ_FORCE_SEND     = 0x12,
+       CP2112_DATA_READ_RESPONSE       = 0x13,
+       CP2112_DATA_WRITE_REQUEST       = 0x14,
+       CP2112_TRANSFER_STATUS_REQUEST  = 0x15,
+       CP2112_TRANSFER_STATUS_RESPONSE = 0x16,
+       CP2112_CANCEL_TRANSFER          = 0x17,
+       CP2112_LOCK_BYTE                = 0x20,
+       CP2112_USB_CONFIG               = 0x21,
+       CP2112_MANUFACTURER_STRING      = 0x22,
+       CP2112_PRODUCT_STRING           = 0x23,
+       CP2112_SERIAL_STRING            = 0x24,
+};
+
+enum {
+       STATUS0_IDLE            = 0x00,
+       STATUS0_BUSY            = 0x01,
+       STATUS0_COMPLETE        = 0x02,
+       STATUS0_ERROR           = 0x03,
+};
+
+enum {
+       STATUS1_TIMEOUT_NACK            = 0x00,
+       STATUS1_TIMEOUT_BUS             = 0x01,
+       STATUS1_ARBITRATION_LOST        = 0x02,
+       STATUS1_READ_INCOMPLETE         = 0x03,
+       STATUS1_WRITE_INCOMPLETE        = 0x04,
+       STATUS1_SUCCESS                 = 0x05,
+};
+
+struct cp2112_smbus_config_report {
+       u8 report;              /* CP2112_SMBUS_CONFIG */
+       __be32 clock_speed;     /* Hz */
+       u8 device_address;      /* Stored in the upper 7 bits */
+       u8 auto_send_read;      /* 1 = enabled, 0 = disabled */
+       __be16 write_timeout;   /* ms, 0 = no timeout */
+       __be16 read_timeout;    /* ms, 0 = no timeout */
+       u8 scl_low_timeout;     /* 1 = enabled, 0 = disabled */
+       __be16 retry_time;      /* # of retries, 0 = no limit */
+} __packed;
+
+struct cp2112_usb_config_report {
+       u8 report;      /* CP2112_USB_CONFIG */
+       __le16 vid;     /* Vendor ID */
+       __le16 pid;     /* Product ID */
+       u8 max_power;   /* Power requested in 2mA units */
+       u8 power_mode;  /* 0x00 = bus powered
+                          0x01 = self powered & regulator off
+                          0x02 = self powered & regulator on */
+       u8 release_major;
+       u8 release_minor;
+       u8 mask;        /* What fields to program */
+} __packed;
+
+struct cp2112_read_req_report {
+       u8 report;      /* CP2112_DATA_READ_REQUEST */
+       u8 slave_address;
+       __be16 length;
+} __packed;
+
+struct cp2112_write_read_req_report {
+       u8 report;      /* CP2112_DATA_WRITE_READ_REQUEST */
+       u8 slave_address;
+       __be16 length;
+       u8 target_address_length;
+       u8 target_address[16];
+} __packed;
+
+struct cp2112_write_req_report {
+       u8 report;      /* CP2112_DATA_WRITE_REQUEST */
+       u8 slave_address;
+       u8 length;
+       u8 data[61];
+} __packed;
+
+struct cp2112_force_read_report {
+       u8 report;      /* CP2112_DATA_READ_FORCE_SEND */
+       __be16 length;
+} __packed;
+
+struct cp2112_xfer_status_report {
+       u8 report;      /* CP2112_TRANSFER_STATUS_RESPONSE */
+       u8 status0;     /* STATUS0_* */
+       u8 status1;     /* STATUS1_* */
+       __be16 retries;
+       __be16 length;
+} __packed;
+
+struct cp2112_string_report {
+       u8 dummy;               /* force .string to be aligned */
+       u8 report;              /* CP2112_*_STRING */
+       u8 length;              /* length in bytes of everyting after .report */
+       u8 type;                /* USB_DT_STRING */
+       wchar_t string[30];     /* UTF16_LITTLE_ENDIAN string */
+} __packed;
+
+/* Number of times to request transfer status before giving up waiting for a
+   transfer to complete. This may need to be changed if SMBUS clock, retries,
+   or read/write/scl_low timeout settings are changed. */
+static const int XFER_STATUS_RETRIES = 10;
+
+/* Time in ms to wait for a CP2112_DATA_READ_RESPONSE or
+   CP2112_TRANSFER_STATUS_RESPONSE. */
+static const int RESPONSE_TIMEOUT = 50;
+
+static const struct hid_device_id cp2112_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, cp2112_devices);
+
+struct cp2112_device {
+       struct i2c_adapter adap;
+       struct hid_device *hdev;
+       wait_queue_head_t wait;
+       u8 read_data[61];
+       u8 read_length;
+       int xfer_status;
+       atomic_t read_avail;
+       atomic_t xfer_avail;
+       struct gpio_chip gc;
+};
+
+static int gpio_push_pull = 0xFF;
+module_param(gpio_push_pull, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask");
+
+static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+       struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+                                                gc);
+       struct hid_device *hdev = dev->hdev;
+       u8 buf[5];
+       int ret;
+
+       ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
+                                      sizeof(buf), HID_FEATURE_REPORT,
+                                      HID_REQ_GET_REPORT);
+       if (ret != sizeof(buf)) {
+               hid_err(hdev, "error requesting GPIO config: %d\n", ret);
+               return ret;
+       }
+
+       buf[1] &= ~(1 << offset);
+       buf[2] = gpio_push_pull;
+
+       ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
+                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       if (ret < 0) {
+               hid_err(hdev, "error setting GPIO config: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+                                                gc);
+       struct hid_device *hdev = dev->hdev;
+       u8 buf[3];
+       int ret;
+
+       buf[0] = CP2112_GPIO_SET;
+       buf[1] = value ? 0xff : 0;
+       buf[2] = 1 << offset;
+
+       ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf, sizeof(buf),
+                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       if (ret < 0)
+               hid_err(hdev, "error setting GPIO values: %d\n", ret);
+}
+
+static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+                                                gc);
+       struct hid_device *hdev = dev->hdev;
+       u8 buf[2];
+       int ret;
+
+       ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf, sizeof(buf),
+                                      HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+       if (ret != sizeof(buf)) {
+               hid_err(hdev, "error requesting GPIO values: %d\n", ret);
+               return ret;
+       }
+
+       return (buf[1] >> offset) & 1;
+}
+
+static int cp2112_gpio_direction_output(struct gpio_chip *chip,
+                                       unsigned offset, int value)
+{
+       struct cp2112_device *dev = container_of(chip, struct cp2112_device,
+                                                gc);
+       struct hid_device *hdev = dev->hdev;
+       u8 buf[5];
+       int ret;
+
+       cp2112_gpio_set(chip, offset, value);
+
+       ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
+                                      sizeof(buf), HID_FEATURE_REPORT,
+                                      HID_REQ_GET_REPORT);
+       if (ret != sizeof(buf)) {
+               hid_err(hdev, "error requesting GPIO config: %d\n", ret);
+               return ret;
+       }
+
+       buf[1] |= 1 << offset;
+       buf[2] = gpio_push_pull;
+
+       ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf),
+                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       if (ret < 0) {
+               hid_err(hdev, "error setting GPIO config: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cp2112_hid_get(struct hid_device *hdev, unsigned char report_number,
+                         u8 *data, size_t count, unsigned char report_type)
+{
+       u8 *buf;
+       int ret;
+
+       buf = kmalloc(count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = hid_hw_raw_request(hdev, report_number, buf, count,
+                                      report_type, HID_REQ_GET_REPORT);
+       memcpy(data, buf, count);
+       kfree(buf);
+       return ret;
+}
+
+static int cp2112_hid_output(struct hid_device *hdev, u8 *data, size_t count,
+                            unsigned char report_type)
+{
+       u8 *buf;
+       int ret;
+
+       buf = kmemdup(data, count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (report_type == HID_OUTPUT_REPORT)
+               ret = hid_hw_output_report(hdev, buf, count);
+       else
+               ret = hid_hw_raw_request(hdev, buf[0], buf, count, report_type,
+                               HID_REQ_SET_REPORT);
+
+       kfree(buf);
+       return ret;
+}
+
+static int cp2112_wait(struct cp2112_device *dev, atomic_t *avail)
+{
+       int ret = 0;
+
+       /* We have sent either a CP2112_TRANSFER_STATUS_REQUEST or a
+        * CP2112_DATA_READ_FORCE_SEND and we are waiting for the response to
+        * come in cp2112_raw_event or timeout. There will only be one of these
+        * in flight at any one time. The timeout is extremely large and is a
+        * last resort if the CP2112 has died. If we do timeout we don't expect
+        * to receive the response which would cause data races, it's not like
+        * we can do anything about it anyway.
+        */
+       ret = wait_event_interruptible_timeout(dev->wait,
+               atomic_read(avail), msecs_to_jiffies(RESPONSE_TIMEOUT));
+       if (-ERESTARTSYS == ret)
+               return ret;
+       if (!ret)
+               return -ETIMEDOUT;
+
+       atomic_set(avail, 0);
+       return 0;
+}
+
+static int cp2112_xfer_status(struct cp2112_device *dev)
+{
+       struct hid_device *hdev = dev->hdev;
+       u8 buf[2];
+       int ret;
+
+       buf[0] = CP2112_TRANSFER_STATUS_REQUEST;
+       buf[1] = 0x01;
+       atomic_set(&dev->xfer_avail, 0);
+
+       ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
+       if (ret < 0) {
+               hid_warn(hdev, "Error requesting status: %d\n", ret);
+               return ret;
+       }
+
+       ret = cp2112_wait(dev, &dev->xfer_avail);
+       if (ret)
+               return ret;
+
+       return dev->xfer_status;
+}
+
+static int cp2112_read(struct cp2112_device *dev, u8 *data, size_t size)
+{
+       struct hid_device *hdev = dev->hdev;
+       struct cp2112_force_read_report report;
+       int ret;
+
+       report.report = CP2112_DATA_READ_FORCE_SEND;
+       report.length = cpu_to_be16(size);
+
+       atomic_set(&dev->read_avail, 0);
+
+       ret = cp2112_hid_output(hdev, &report.report, sizeof(report),
+                               HID_OUTPUT_REPORT);
+       if (ret < 0) {
+               hid_warn(hdev, "Error requesting data: %d\n", ret);
+               return ret;
+       }
+
+       ret = cp2112_wait(dev, &dev->read_avail);
+       if (ret)
+               return ret;
+
+       hid_dbg(hdev, "read %d of %zd bytes requested\n",
+               dev->read_length, size);
+
+       if (size > dev->read_length)
+               size = dev->read_length;
+
+       memcpy(data, dev->read_data, size);
+       return dev->read_length;
+}
+
+static int cp2112_read_req(void *buf, u8 slave_address, u16 length)
+{
+       struct cp2112_read_req_report *report = buf;
+
+       if (length < 1 || length > 512)
+               return -EINVAL;
+
+       report->report = CP2112_DATA_READ_REQUEST;
+       report->slave_address = slave_address << 1;
+       report->length = cpu_to_be16(length);
+       return sizeof(*report);
+}
+
+static int cp2112_write_read_req(void *buf, u8 slave_address, u16 length,
+                                u8 command, u8 *data, u8 data_length)
+{
+       struct cp2112_write_read_req_report *report = buf;
+
+       if (length < 1 || length > 512
+           || data_length > sizeof(report->target_address) - 1)
+               return -EINVAL;
+
+       report->report = CP2112_DATA_WRITE_READ_REQUEST;
+       report->slave_address = slave_address << 1;
+       report->length = cpu_to_be16(length);
+       report->target_address_length = data_length + 1;
+       report->target_address[0] = command;
+       memcpy(&report->target_address[1], data, data_length);
+       return data_length + 6;
+}
+
+static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data,
+                           u8 data_length)
+{
+       struct cp2112_write_req_report *report = buf;
+
+       if (data_length > sizeof(report->data) - 1)
+               return -EINVAL;
+
+       report->report = CP2112_DATA_WRITE_REQUEST;
+       report->slave_address = slave_address << 1;
+       report->length = data_length + 1;
+       report->data[0] = command;
+       memcpy(&report->data[1], data, data_length);
+       return data_length + 4;
+}
+
+static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
+                      unsigned short flags, char read_write, u8 command,
+                      int size, union i2c_smbus_data *data)
+{
+       struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data;
+       struct hid_device *hdev = dev->hdev;
+       u8 buf[64];
+       __be16 word;
+       ssize_t count;
+       size_t read_length = 0;
+       unsigned int retries;
+       int ret;
+
+       hid_dbg(hdev, "%s addr 0x%x flags 0x%x cmd 0x%x size %d\n",
+               read_write == I2C_SMBUS_WRITE ? "write" : "read",
+               addr, flags, command, size);
+
+       switch (size) {
+       case I2C_SMBUS_BYTE:
+               read_length = 1;
+
+               if (I2C_SMBUS_READ == read_write)
+                       count = cp2112_read_req(buf, addr, read_length);
+               else
+                       count = cp2112_write_req(buf, addr, data->byte, NULL,
+                                                0);
+               break;
+       case I2C_SMBUS_BYTE_DATA:
+               read_length = 1;
+
+               if (I2C_SMBUS_READ == read_write)
+                       count = cp2112_write_read_req(buf, addr, read_length,
+                                                     command, NULL, 0);
+               else
+                       count = cp2112_write_req(buf, addr, command,
+                                                &data->byte, 1);
+               break;
+       case I2C_SMBUS_WORD_DATA:
+               read_length = 2;
+               word = cpu_to_be16(data->word);
+
+               if (I2C_SMBUS_READ == read_write)
+                       count = cp2112_write_read_req(buf, addr, read_length,
+                                                     command, NULL, 0);
+               else
+                       count = cp2112_write_req(buf, addr, command,
+                                                (u8 *)&word, 2);
+               break;
+       case I2C_SMBUS_PROC_CALL:
+               size = I2C_SMBUS_WORD_DATA;
+               read_write = I2C_SMBUS_READ;
+               read_length = 2;
+               word = cpu_to_be16(data->word);
+
+               count = cp2112_write_read_req(buf, addr, read_length, command,
+                                             (u8 *)&word, 2);
+               break;
+       case I2C_SMBUS_I2C_BLOCK_DATA:
+               size = I2C_SMBUS_BLOCK_DATA;
+               /* fallthrough */
+       case I2C_SMBUS_BLOCK_DATA:
+               if (I2C_SMBUS_READ == read_write) {
+                       count = cp2112_write_read_req(buf, addr,
+                                                     I2C_SMBUS_BLOCK_MAX,
+                                                     command, NULL, 0);
+               } else {
+                       count = cp2112_write_req(buf, addr, command,
+                                                data->block,
+                                                data->block[0] + 1);
+               }
+               break;
+       case I2C_SMBUS_BLOCK_PROC_CALL:
+               size = I2C_SMBUS_BLOCK_DATA;
+               read_write = I2C_SMBUS_READ;
+
+               count = cp2112_write_read_req(buf, addr, I2C_SMBUS_BLOCK_MAX,
+                                             command, data->block,
+                                             data->block[0] + 1);
+               break;
+       default:
+               hid_warn(hdev, "Unsupported transaction %d\n", size);
+               return -EOPNOTSUPP;
+       }
+
+       if (count < 0)
+               return count;
+
+       ret = hid_hw_power(hdev, PM_HINT_FULLON);
+       if (ret < 0) {
+               hid_err(hdev, "power management error: %d\n", ret);
+               return ret;
+       }
+
+       ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT);
+       if (ret < 0) {
+               hid_warn(hdev, "Error starting transaction: %d\n", ret);
+               goto power_normal;
+       }
+
+       for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) {
+               ret = cp2112_xfer_status(dev);
+               if (-EBUSY == ret)
+                       continue;
+               if (ret < 0)
+                       goto power_normal;
+               break;
+       }
+
+       if (XFER_STATUS_RETRIES <= retries) {
+               hid_warn(hdev, "Transfer timed out, cancelling.\n");
+               buf[0] = CP2112_CANCEL_TRANSFER;
+               buf[1] = 0x01;
+
+               ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
+               if (ret < 0)
+                       hid_warn(hdev, "Error cancelling transaction: %d\n",
+                                ret);
+
+               ret = -ETIMEDOUT;
+               goto power_normal;
+       }
+
+       if (I2C_SMBUS_WRITE == read_write) {
+               ret = 0;
+               goto power_normal;
+       }
+
+       if (I2C_SMBUS_BLOCK_DATA == size)
+               read_length = ret;
+
+       ret = cp2112_read(dev, buf, read_length);
+       if (ret < 0)
+               goto power_normal;
+       if (ret != read_length) {
+               hid_warn(hdev, "short read: %d < %zd\n", ret, read_length);
+               ret = -EIO;
+               goto power_normal;
+       }
+
+       switch (size) {
+       case I2C_SMBUS_BYTE:
+       case I2C_SMBUS_BYTE_DATA:
+               data->byte = buf[0];
+               break;
+       case I2C_SMBUS_WORD_DATA:
+               data->word = be16_to_cpup((__be16 *)buf);
+               break;
+       case I2C_SMBUS_BLOCK_DATA:
+               if (read_length > I2C_SMBUS_BLOCK_MAX) {
+                       ret = -EPROTO;
+                       goto power_normal;
+               }
+
+               memcpy(data->block, buf, read_length);
+               break;
+       }
+
+       ret = 0;
+power_normal:
+       hid_hw_power(hdev, PM_HINT_NORMAL);
+       hid_dbg(hdev, "transfer finished: %d\n", ret);
+       return ret;
+}
+
+static u32 cp2112_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_SMBUS_BYTE |
+               I2C_FUNC_SMBUS_BYTE_DATA |
+               I2C_FUNC_SMBUS_WORD_DATA |
+               I2C_FUNC_SMBUS_BLOCK_DATA |
+               I2C_FUNC_SMBUS_I2C_BLOCK |
+               I2C_FUNC_SMBUS_PROC_CALL |
+               I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
+}
+
+static const struct i2c_algorithm smbus_algorithm = {
+       .smbus_xfer     = cp2112_xfer,
+       .functionality  = cp2112_functionality,
+};
+
+static int cp2112_get_usb_config(struct hid_device *hdev,
+                                struct cp2112_usb_config_report *cfg)
+{
+       int ret;
+
+       ret = cp2112_hid_get(hdev, CP2112_USB_CONFIG, (u8 *)cfg, sizeof(*cfg),
+                            HID_FEATURE_REPORT);
+       if (ret != sizeof(*cfg)) {
+               hid_err(hdev, "error reading usb config: %d\n", ret);
+               if (ret < 0)
+                       return ret;
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int cp2112_set_usb_config(struct hid_device *hdev,
+                                struct cp2112_usb_config_report *cfg)
+{
+       int ret;
+
+       BUG_ON(cfg->report != CP2112_USB_CONFIG);
+
+       ret = cp2112_hid_output(hdev, (u8 *)cfg, sizeof(*cfg),
+                               HID_FEATURE_REPORT);
+       if (ret != sizeof(*cfg)) {
+               hid_err(hdev, "error writing usb config: %d\n", ret);
+               if (ret < 0)
+                       return ret;
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void chmod_sysfs_attrs(struct hid_device *hdev);
+
+#define CP2112_CONFIG_ATTR(name, store, format, ...) \
+static ssize_t name##_store(struct device *kdev, \
+                           struct device_attribute *attr, const char *buf, \
+                           size_t count) \
+{ \
+       struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \
+       struct cp2112_usb_config_report cfg; \
+       int ret = cp2112_get_usb_config(hdev, &cfg); \
+       if (ret) \
+               return ret; \
+       store; \
+       ret = cp2112_set_usb_config(hdev, &cfg); \
+       if (ret) \
+               return ret; \
+       chmod_sysfs_attrs(hdev); \
+       return count; \
+} \
+static ssize_t name##_show(struct device *kdev, \
+                          struct device_attribute *attr, char *buf) \
+{ \
+       struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \
+       struct cp2112_usb_config_report cfg; \
+       int ret = cp2112_get_usb_config(hdev, &cfg); \
+       if (ret) \
+               return ret; \
+       return scnprintf(buf, PAGE_SIZE, format, ##__VA_ARGS__); \
+} \
+static DEVICE_ATTR_RW(name);
+
+CP2112_CONFIG_ATTR(vendor_id, ({
+       u16 vid;
+
+       if (sscanf(buf, "%hi", &vid) != 1)
+               return -EINVAL;
+
+       cfg.vid = cpu_to_le16(vid);
+       cfg.mask = 0x01;
+}), "0x%04x\n", le16_to_cpu(cfg.vid));
+
+CP2112_CONFIG_ATTR(product_id, ({
+       u16 pid;
+
+       if (sscanf(buf, "%hi", &pid) != 1)
+               return -EINVAL;
+
+       cfg.pid = cpu_to_le16(pid);
+       cfg.mask = 0x02;
+}), "0x%04x\n", le16_to_cpu(cfg.pid));
+
+CP2112_CONFIG_ATTR(max_power, ({
+       int mA;
+
+       if (sscanf(buf, "%i", &mA) != 1)
+               return -EINVAL;
+
+       cfg.max_power = (mA + 1) / 2;
+       cfg.mask = 0x04;
+}), "%u mA\n", cfg.max_power * 2);
+
+CP2112_CONFIG_ATTR(power_mode, ({
+       if (sscanf(buf, "%hhi", &cfg.power_mode) != 1)
+               return -EINVAL;
+
+       cfg.mask = 0x08;
+}), "%u\n", cfg.power_mode);
+
+CP2112_CONFIG_ATTR(release_version, ({
+       if (sscanf(buf, "%hhi.%hhi", &cfg.release_major, &cfg.release_minor)
+           != 2)
+               return -EINVAL;
+
+       cfg.mask = 0x10;
+}), "%u.%u\n", cfg.release_major, cfg.release_minor);
+
+#undef CP2112_CONFIG_ATTR
+
+struct cp2112_pstring_attribute {
+       struct device_attribute attr;
+       unsigned char report;
+};
+
+static ssize_t pstr_store(struct device *kdev,
+                         struct device_attribute *kattr, const char *buf,
+                         size_t count)
+{
+       struct hid_device *hdev = container_of(kdev, struct hid_device, dev);
+       struct cp2112_pstring_attribute *attr =
+               container_of(kattr, struct cp2112_pstring_attribute, attr);
+       struct cp2112_string_report report;
+       int ret;
+
+       memset(&report, 0, sizeof(report));
+
+       ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN,
+                             report.string, ARRAY_SIZE(report.string));
+       report.report = attr->report;
+       report.length = ret * sizeof(report.string[0]) + 2;
+       report.type = USB_DT_STRING;
+
+       ret = cp2112_hid_output(hdev, &report.report, report.length + 1,
+                               HID_FEATURE_REPORT);
+       if (ret != report.length + 1) {
+               hid_err(hdev, "error writing %s string: %d\n", kattr->attr.name,
+                       ret);
+               if (ret < 0)
+                       return ret;
+               return -EIO;
+       }
+
+       chmod_sysfs_attrs(hdev);
+       return count;
+}
+
+static ssize_t pstr_show(struct device *kdev,
+                        struct device_attribute *kattr, char *buf)
+{
+       struct hid_device *hdev = container_of(kdev, struct hid_device, dev);
+       struct cp2112_pstring_attribute *attr =
+               container_of(kattr, struct cp2112_pstring_attribute, attr);
+       struct cp2112_string_report report;
+       u8 length;
+       int ret;
+
+       ret = cp2112_hid_get(hdev, attr->report, &report.report,
+                            sizeof(report) - 1, HID_FEATURE_REPORT);
+       if (ret < 3) {
+               hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name,
+                       ret);
+               if (ret < 0)
+                       return ret;
+               return -EIO;
+       }
+
+       if (report.length < 2) {
+               hid_err(hdev, "invalid %s string length: %d\n",
+                       kattr->attr.name, report.length);
+               return -EIO;
+       }
+
+       length = report.length > ret - 1 ? ret - 1 : report.length;
+       length = (length - 2) / sizeof(report.string[0]);
+       ret = utf16s_to_utf8s(report.string, length, UTF16_LITTLE_ENDIAN, buf,
+                             PAGE_SIZE - 1);
+       buf[ret++] = '\n';
+       return ret;
+}
+
+#define CP2112_PSTR_ATTR(name, _report) \
+static struct cp2112_pstring_attribute dev_attr_##name = { \
+       .attr = __ATTR(name, (S_IWUSR | S_IRUGO), pstr_show, pstr_store), \
+       .report = _report, \
+};
+
+CP2112_PSTR_ATTR(manufacturer, CP2112_MANUFACTURER_STRING);
+CP2112_PSTR_ATTR(product,      CP2112_PRODUCT_STRING);
+CP2112_PSTR_ATTR(serial,       CP2112_SERIAL_STRING);
+
+#undef CP2112_PSTR_ATTR
+
+static const struct attribute_group cp2112_attr_group = {
+       .attrs = (struct attribute *[]){
+               &dev_attr_vendor_id.attr,
+               &dev_attr_product_id.attr,
+               &dev_attr_max_power.attr,
+               &dev_attr_power_mode.attr,
+               &dev_attr_release_version.attr,
+               &dev_attr_manufacturer.attr.attr,
+               &dev_attr_product.attr.attr,
+               &dev_attr_serial.attr.attr,
+               NULL
+       }
+};
+
+/* Chmoding our sysfs attributes is simply a way to expose which fields in the
+ * PROM have already been programmed. We do not depend on this preventing
+ * writing to these attributes since the CP2112 will simply ignore writes to
+ * already-programmed fields. This is why there is no sense in fixing this
+ * racy behaviour.
+ */
+static void chmod_sysfs_attrs(struct hid_device *hdev)
+{
+       struct attribute **attr;
+       u8 buf[2];
+       int ret;
+
+       ret = cp2112_hid_get(hdev, CP2112_LOCK_BYTE, buf, sizeof(buf),
+                            HID_FEATURE_REPORT);
+       if (ret != sizeof(buf)) {
+               hid_err(hdev, "error reading lock byte: %d\n", ret);
+               return;
+       }
+
+       for (attr = cp2112_attr_group.attrs; *attr; ++attr) {
+               umode_t mode = (buf[1] & 1) ? S_IWUSR | S_IRUGO : S_IRUGO;
+               ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode);
+               if (ret < 0)
+                       hid_err(hdev, "error chmoding sysfs file %s\n",
+                               (*attr)->name);
+               buf[1] >>= 1;
+       }
+}
+
+static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct cp2112_device *dev;
+       u8 buf[3];
+       struct cp2112_smbus_config_report config;
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       ret = hid_hw_open(hdev);
+       if (ret) {
+               hid_err(hdev, "hw open failed\n");
+               goto err_hid_stop;
+       }
+
+       ret = hid_hw_power(hdev, PM_HINT_FULLON);
+       if (ret < 0) {
+               hid_err(hdev, "power management error: %d\n", ret);
+               goto err_hid_close;
+       }
+
+       ret = cp2112_hid_get(hdev, CP2112_GET_VERSION_INFO, buf, sizeof(buf),
+                            HID_FEATURE_REPORT);
+       if (ret != sizeof(buf)) {
+               hid_err(hdev, "error requesting version\n");
+               if (ret >= 0)
+                       ret = -EIO;
+               goto err_power_normal;
+       }
+
+       hid_info(hdev, "Part Number: 0x%02X Device Version: 0x%02X\n",
+                buf[1], buf[2]);
+
+       ret = cp2112_hid_get(hdev, CP2112_SMBUS_CONFIG, (u8 *)&config,
+                            sizeof(config), HID_FEATURE_REPORT);
+       if (ret != sizeof(config)) {
+               hid_err(hdev, "error requesting SMBus config\n");
+               if (ret >= 0)
+                       ret = -EIO;
+               goto err_power_normal;
+       }
+
+       config.retry_time = cpu_to_be16(1);
+
+       ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config),
+                               HID_FEATURE_REPORT);
+       if (ret != sizeof(config)) {
+               hid_err(hdev, "error setting SMBus config\n");
+               if (ret >= 0)
+                       ret = -EIO;
+               goto err_power_normal;
+       }
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev) {
+               ret = -ENOMEM;
+               goto err_power_normal;
+       }
+
+       hid_set_drvdata(hdev, (void *)dev);
+       dev->hdev               = hdev;
+       dev->adap.owner         = THIS_MODULE;
+       dev->adap.class         = I2C_CLASS_HWMON;
+       dev->adap.algo          = &smbus_algorithm;
+       dev->adap.algo_data     = dev;
+       dev->adap.dev.parent    = &hdev->dev;
+       snprintf(dev->adap.name, sizeof(dev->adap.name),
+                "CP2112 SMBus Bridge on hiddev%d", hdev->minor);
+       init_waitqueue_head(&dev->wait);
+
+       hid_device_io_start(hdev);
+       ret = i2c_add_adapter(&dev->adap);
+       hid_device_io_stop(hdev);
+
+       if (ret) {
+               hid_err(hdev, "error registering i2c adapter\n");
+               goto err_free_dev;
+       }
+
+       hid_dbg(hdev, "adapter registered\n");
+
+       dev->gc.label                   = "cp2112_gpio";
+       dev->gc.direction_input         = cp2112_gpio_direction_input;
+       dev->gc.direction_output        = cp2112_gpio_direction_output;
+       dev->gc.set                     = cp2112_gpio_set;
+       dev->gc.get                     = cp2112_gpio_get;
+       dev->gc.base                    = -1;
+       dev->gc.ngpio                   = 8;
+       dev->gc.can_sleep               = 1;
+       dev->gc.dev                     = &hdev->dev;
+
+       ret = gpiochip_add(&dev->gc);
+       if (ret < 0) {
+               hid_err(hdev, "error registering gpio chip\n");
+               goto err_free_i2c;
+       }
+
+       ret = sysfs_create_group(&hdev->dev.kobj, &cp2112_attr_group);
+       if (ret < 0) {
+               hid_err(hdev, "error creating sysfs attrs\n");
+               goto err_gpiochip_remove;
+       }
+
+       chmod_sysfs_attrs(hdev);
+       hid_hw_power(hdev, PM_HINT_NORMAL);
+
+       return ret;
+
+err_gpiochip_remove:
+       if (gpiochip_remove(&dev->gc) < 0)
+               hid_err(hdev, "error removing gpio chip\n");
+err_free_i2c:
+       i2c_del_adapter(&dev->adap);
+err_free_dev:
+       kfree(dev);
+err_power_normal:
+       hid_hw_power(hdev, PM_HINT_NORMAL);
+err_hid_close:
+       hid_hw_close(hdev);
+err_hid_stop:
+       hid_hw_stop(hdev);
+       return ret;
+}
+
+static void cp2112_remove(struct hid_device *hdev)
+{
+       struct cp2112_device *dev = hid_get_drvdata(hdev);
+
+       sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
+       if (gpiochip_remove(&dev->gc))
+               hid_err(hdev, "unable to remove gpio chip\n");
+       i2c_del_adapter(&dev->adap);
+       /* i2c_del_adapter has finished removing all i2c devices from our
+        * adapter. Well behaved devices should no longer call our cp2112_xfer
+        * and should have waited for any pending calls to finish. It has also
+        * waited for device_unregister(&adap->dev) to complete. Therefore we
+        * can safely free our struct cp2112_device.
+        */
+       hid_hw_close(hdev);
+       hid_hw_stop(hdev);
+       kfree(dev);
+}
+
+static int cp2112_raw_event(struct hid_device *hdev, struct hid_report *report,
+                           u8 *data, int size)
+{
+       struct cp2112_device *dev = hid_get_drvdata(hdev);
+       struct cp2112_xfer_status_report *xfer = (void *)data;
+
+       switch (data[0]) {
+       case CP2112_TRANSFER_STATUS_RESPONSE:
+               hid_dbg(hdev, "xfer status: %02x %02x %04x %04x\n",
+                       xfer->status0, xfer->status1,
+                       be16_to_cpu(xfer->retries), be16_to_cpu(xfer->length));
+
+               switch (xfer->status0) {
+               case STATUS0_IDLE:
+                       dev->xfer_status = -EAGAIN;
+                       break;
+               case STATUS0_BUSY:
+                       dev->xfer_status = -EBUSY;
+                       break;
+               case STATUS0_COMPLETE:
+                       dev->xfer_status = be16_to_cpu(xfer->length);
+                       break;
+               case STATUS0_ERROR:
+                       switch (xfer->status1) {
+                       case STATUS1_TIMEOUT_NACK:
+                       case STATUS1_TIMEOUT_BUS:
+                               dev->xfer_status = -ETIMEDOUT;
+                               break;
+                       default:
+                               dev->xfer_status = -EIO;
+                               break;
+                       }
+                       break;
+               default:
+                       dev->xfer_status = -EINVAL;
+                       break;
+               }
+
+               atomic_set(&dev->xfer_avail, 1);
+               break;
+       case CP2112_DATA_READ_RESPONSE:
+               hid_dbg(hdev, "read response: %02x %02x\n", data[1], data[2]);
+
+               dev->read_length = data[2];
+               if (dev->read_length > sizeof(dev->read_data))
+                       dev->read_length = sizeof(dev->read_data);
+
+               memcpy(dev->read_data, &data[3], dev->read_length);
+               atomic_set(&dev->read_avail, 1);
+               break;
+       default:
+               hid_err(hdev, "unknown report\n");
+
+               return 0;
+       }
+
+       wake_up_interruptible(&dev->wait);
+       return 1;
+}
+
+static struct hid_driver cp2112_driver = {
+       .name           = "cp2112",
+       .id_table       = cp2112_devices,
+       .probe          = cp2112_probe,
+       .remove         = cp2112_remove,
+       .raw_event      = cp2112_raw_event,
+};
+
+module_hid_driver(cp2112_driver);
+MODULE_DESCRIPTION("Silicon Labs HID USB to SMBus master bridge");
+MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");
+MODULE_LICENSE("GPL");
+
index 8fae6d1414cca83bc7af2386b7db5dc4cbd3f27b..f52dbcb7133b4fac11f798638c4911fa896e2e55 100644 (file)
@@ -157,6 +157,7 @@ struct mousevsc_dev {
        u32                     report_desc_size;
        struct hv_input_dev_info hid_dev_info;
        struct hid_device       *hid_device;
+       u8                      input_buf[HID_MAX_BUFFER_SIZE];
 };
 
 
@@ -256,6 +257,7 @@ static void mousevsc_on_receive(struct hv_device *device,
        struct synthhid_msg *hid_msg;
        struct mousevsc_dev *input_dev = hv_get_drvdata(device);
        struct synthhid_input_report *input_report;
+       size_t len;
 
        pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet +
                                                (packet->offset8 << 3));
@@ -300,9 +302,12 @@ static void mousevsc_on_receive(struct hv_device *device,
                        (struct synthhid_input_report *)pipe_msg->data;
                if (!input_dev->init_complete)
                        break;
-               hid_input_report(input_dev->hid_device,
-                               HID_INPUT_REPORT, input_report->buffer,
-                               input_report->header.size, 1);
+
+               len = min(input_report->header.size,
+                         (u32)sizeof(input_dev->input_buf));
+               memcpy(input_dev->input_buf, input_report->buffer, len);
+               hid_input_report(input_dev->hid_device, HID_INPUT_REPORT,
+                                input_dev->input_buf, len, 1);
                break;
        default:
                pr_err("unsupported hid msg type - type %d len %d",
@@ -455,12 +460,22 @@ static void mousevsc_hid_stop(struct hid_device *hid)
 {
 }
 
+static int mousevsc_hid_raw_request(struct hid_device *hid,
+                                   unsigned char report_num,
+                                   __u8 *buf, size_t len,
+                                   unsigned char rtype,
+                                   int reqtype)
+{
+       return 0;
+}
+
 static struct hid_ll_driver mousevsc_ll_driver = {
        .parse = mousevsc_hid_parse,
        .open = mousevsc_hid_open,
        .close = mousevsc_hid_close,
        .start = mousevsc_hid_start,
        .stop = mousevsc_hid_stop,
+       .raw_request = mousevsc_hid_raw_request,
 };
 
 static struct hid_driver mousevsc_hid_driver;
index ca9b206a01c1c87bb3c8c686bfdb1363de3d4ff7..548c1a51959334e867b27c73ae20d682515c9149 100644 (file)
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS   0x023b
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI  0x0255
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO   0x0256
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS   0x0257
 #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI   0x0290
 #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO    0x0291
 #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS    0x0292
 
 #define USB_VENDOR_ID_CYGNAL           0x10c4
 #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X      0x818a
+#define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH      0x81b9
+#define USB_DEVICE_ID_CYGNAL_CP2112    0xea90
 
 #define USB_VENDOR_ID_CYPRESS          0x04b4
 #define USB_DEVICE_ID_CYPRESS_MOUSE    0x0001
 #define USB_VENDOR_ID_INTEL_1          0x8087
 #define USB_DEVICE_ID_INTEL_HID_SENSOR 0x09fa
 
+#define USB_VENDOR_ID_STM_0             0x0483
+#define USB_DEVICE_ID_STM_HID_SENSOR    0x91d1
+
 #define USB_VENDOR_ID_ION              0x15e4
 #define USB_DEVICE_ID_ICADE            0x0132
 
 
 #define USB_VENDOR_ID_MICROSOFT                0x045e
 #define USB_DEVICE_ID_SIDEWINDER_GV    0x003b
+#define USB_DEVICE_ID_MS_OFFICE_KB     0x0048
 #define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
 #define USB_DEVICE_ID_MS_NE4K          0x00db
 #define USB_DEVICE_ID_MS_NE4K_JP       0x00dc
 #define USB_DEVICE_ID_MS_PRESENTER_8K_USB      0x0713
 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K      0x0730
 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500    0x076c
+#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7
+#define USB_DEVICE_ID_MS_TYPE_COVER_2  0x07a9
 
 #define USB_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER    0x3201
 
 #define USB_VENDOR_ID_NEXIO            0x1870
 #define USB_DEVICE_ID_NEXIO_MULTITOUCH_420     0x010d
+#define USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750 0x0110
 
 #define USB_VENDOR_ID_NEXTWINDOW       0x1926
 #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN   0x0003
index f5aef792f13b6dd235bdfcaf0b43cf6f9802cc48..e7e8b19a928433becdfc7455e2b05733cf763736 100644 (file)
@@ -789,10 +789,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x199: map_key_clear(KEY_CHAT);            break;
                case 0x19c: map_key_clear(KEY_LOGOFF);          break;
                case 0x19e: map_key_clear(KEY_COFFEE);          break;
+               case 0x1a3: map_key_clear(KEY_NEXT);            break;
+               case 0x1a4: map_key_clear(KEY_PREVIOUS);        break;
                case 0x1a6: map_key_clear(KEY_HELP);            break;
                case 0x1a7: map_key_clear(KEY_DOCUMENTS);       break;
                case 0x1ab: map_key_clear(KEY_SPELLCHECK);      break;
                case 0x1ae: map_key_clear(KEY_KEYBOARD);        break;
+               case 0x1b4: map_key_clear(KEY_FILE);            break;
                case 0x1b6: map_key_clear(KEY_IMAGES);          break;
                case 0x1b7: map_key_clear(KEY_AUDIO);           break;
                case 0x1b8: map_key_clear(KEY_VIDEO);           break;
@@ -1178,7 +1181,7 @@ static void hidinput_led_worker(struct work_struct *work)
 
        /* fall back to generic raw-output-report */
        len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-       buf = kmalloc(len, GFP_KERNEL);
+       buf = hid_alloc_report_buf(report, GFP_KERNEL);
        if (!buf)
                return;
 
index befe0e336471b0f36e34473ee97c3ba72759ddf9..24883b4d1a49d40a2bb3462ee36ed094f474b3cd 100644 (file)
@@ -43,6 +43,7 @@
 #define G25_REV_MIN 0x22
 #define G27_REV_MAJ 0x12
 #define G27_REV_MIN 0x38
+#define G27_2_REV_MIN 0x39
 
 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
 
@@ -130,6 +131,7 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {
        {DFP_REV_MAJ,  DFP_REV_MIN,  &native_dfp},      /* Driving Force Pro */
        {G25_REV_MAJ,  G25_REV_MIN,  &native_g25},      /* G25 */
        {G27_REV_MAJ,  G27_REV_MIN,  &native_g27},      /* G27 */
+       {G27_REV_MAJ,  G27_2_REV_MIN,  &native_g27},    /* G27 v2 */
 };
 
 /* Recalculates X axis value accordingly to currently selected range */
index c6ef6eed30915e272edf7249001d3005b9e1b667..6fd58175a291bb65fd15cf555a60d1773dd3ae58 100644 (file)
@@ -62,9 +62,48 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
 {
        struct input_dev *input = hi->input;
 
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+               switch (usage->hid & HID_USAGE) {
+               /*
+                * Microsoft uses these 2 reserved usage ids for 2 keys on
+                * the MS office kb labelled "Office Home" and "Task Pane".
+                */
+               case 0x29d:
+                       ms_map_key_clear(KEY_PROG1);
+                       return 1;
+               case 0x29e:
+                       ms_map_key_clear(KEY_PROG2);
+                       return 1;
+               }
+               return 0;
+       }
+
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+               return 0;
+
        switch (usage->hid & HID_USAGE) {
        case 0xfd06: ms_map_key_clear(KEY_CHAT);        break;
        case 0xfd07: ms_map_key_clear(KEY_PHONE);       break;
+       case 0xff00:
+               /* Special keypad keys */
+               ms_map_key_clear(KEY_KPEQUAL);
+               set_bit(KEY_KPLEFTPAREN, input->keybit);
+               set_bit(KEY_KPRIGHTPAREN, input->keybit);
+               break;
+       case 0xff01:
+               /* Scroll wheel */
+               hid_map_usage_clear(hi, usage, bit, max, EV_REL, REL_WHEEL);
+               break;
+       case 0xff02:
+               /*
+                * This byte contains a copy of the modifier keys byte of a
+                * standard hid keyboard report, as send by interface 0
+                * (this usage is found on interface 1).
+                *
+                * This byte only gets send when another key in the same report
+                * changes state, and as such is useless, ignore it.
+                */
+               return -1;
        case 0xff05:
                set_bit(EV_REP, input->evbit);
                ms_map_key_clear(KEY_F13);
@@ -83,6 +122,9 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
 static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+               return 0;
+
        set_bit(EV_REP, hi->input->evbit);
        switch (usage->hid & HID_USAGE) {
        case 0xfd08: ms_map_key_clear(KEY_FORWARD);     break;
@@ -102,9 +144,6 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 {
        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
 
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-               return 0;
-
        if (quirks & MS_ERGONOMY) {
                int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
                if (ret)
@@ -134,14 +173,39 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field,
                struct hid_usage *usage, __s32 value)
 {
        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       struct input_dev *input;
 
        if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
                        !usage->type)
                return 0;
 
+       input = field->hidinput->input;
+
        /* Handling MS keyboards special buttons */
+       if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff00)) {
+               /* Special keypad keys */
+               input_report_key(input, KEY_KPEQUAL, value & 0x01);
+               input_report_key(input, KEY_KPLEFTPAREN, value & 0x02);
+               input_report_key(input, KEY_KPRIGHTPAREN, value & 0x04);
+               return 1;
+       }
+
+       if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff01)) {
+               /* Scroll wheel */
+               int step = ((value & 0x60) >> 5) + 1;
+
+               switch (value & 0x1f) {
+               case 0x01:
+                       input_report_rel(input, REL_WHEEL, step);
+                       break;
+               case 0x1f:
+                       input_report_rel(input, REL_WHEEL, -step);
+                       break;
+               }
+               return 1;
+       }
+
        if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
-               struct input_dev *input = field->hidinput->input;
                static unsigned int last_key = 0;
                unsigned int key = 0;
                switch (value) {
@@ -194,6 +258,8 @@ err_free:
 static const struct hid_device_id ms_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
                .driver_data = MS_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB),
+               .driver_data = MS_ERGONOMY },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
                .driver_data = MS_ERGONOMY },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP),
@@ -208,6 +274,10 @@ static const struct hid_device_id ms_devices[] = {
                .driver_data = MS_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500),
                .driver_data = MS_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2),
+               .driver_data = 0 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2),
+               .driver_data = 0 },
 
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
                .driver_data = MS_PRESENTER },
index 59742f49295cfe9f469be8eede5e4c5435b4cefb..35278e43c7a48d1999283c21f0f60cceccbc3b84 100644 (file)
@@ -1160,6 +1160,11 @@ static const struct hid_device_id mt_devices[] = {
                MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG,
                        USB_DEVICE_ID_MULTITOUCH_3200) },
 
+       /* FocalTech Panels */
+       { .driver_data = MT_CLS_SERIAL,
+               MT_USB_DEVICE(USB_VENDOR_ID_CYGNAL,
+                       USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH) },
+
        /* GeneralTouch panel */
        { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS,
                MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
index 46f4480035bca14642c43618eb17bfd6729dfb0f..5182031f7b520052f6a8f51588faf3bc839353e0 100644 (file)
@@ -56,9 +56,9 @@ struct sensor_hub_pending {
  * @dyn_callback_lock: spin lock to protect callback list
  * @hid_sensor_hub_client_devs:        Stores all MFD cells for a hub instance.
  * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached).
+ * @ref_cnt:           Number of MFD clients have opened this device
  */
 struct sensor_hub_data {
-       struct hid_sensor_hub_device *hsdev;
        struct mutex mutex;
        spinlock_t lock;
        struct sensor_hub_pending pending;
@@ -67,6 +67,7 @@ struct sensor_hub_data {
        struct mfd_cell *hid_sensor_hub_client_devs;
        int hid_sensor_client_cnt;
        unsigned long quirks;
+       int ref_cnt;
 };
 
 /**
@@ -79,6 +80,7 @@ struct sensor_hub_data {
 struct hid_sensor_hub_callbacks_list {
        struct list_head list;
        u32 usage_id;
+       struct hid_sensor_hub_device *hsdev;
        struct hid_sensor_hub_callbacks *usage_callback;
        void *priv;
 };
@@ -97,20 +99,18 @@ static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,
        return NULL;
 }
 
-static int sensor_hub_get_physical_device_count(
-                               struct hid_report_enum *report_enum)
+static int sensor_hub_get_physical_device_count(struct hid_device *hdev)
 {
-       struct hid_report *report;
-       struct hid_field *field;
-       int cnt = 0;
+       int i;
+       int count = 0;
 
-       list_for_each_entry(report, &report_enum->report_list, list) {
-               field = report->field[0];
-               if (report->maxfield && field && field->physical)
-                       cnt++;
+       for (i = 0; i < hdev->maxcollection; ++i) {
+               struct hid_collection *collection = &hdev->collection[i];
+               if (collection->type == HID_COLLECTION_PHYSICAL)
+                       ++count;
        }
 
-       return cnt;
+       return count;
 }
 
 static void sensor_hub_fill_attr_info(
@@ -128,15 +128,23 @@ static void sensor_hub_fill_attr_info(
 
 static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
                                        struct hid_device *hdev,
-                                       u32 usage_id, void **priv)
+                                       u32 usage_id,
+                                       int collection_index,
+                                       struct hid_sensor_hub_device **hsdev,
+                                       void **priv)
 {
        struct hid_sensor_hub_callbacks_list *callback;
        struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
 
        spin_lock(&pdata->dyn_callback_lock);
        list_for_each_entry(callback, &pdata->dyn_callback_list, list)
-               if (callback->usage_id == usage_id) {
+               if (callback->usage_id == usage_id &&
+                       (collection_index >=
+                               callback->hsdev->start_collection_index) &&
+                       (collection_index <
+                               callback->hsdev->end_collection_index)) {
                        *priv = callback->priv;
+                       *hsdev = callback->hsdev;
                        spin_unlock(&pdata->dyn_callback_lock);
                        return callback->usage_callback;
                }
@@ -154,7 +162,8 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
 
        spin_lock(&pdata->dyn_callback_lock);
        list_for_each_entry(callback, &pdata->dyn_callback_list, list)
-               if (callback->usage_id == usage_id) {
+               if (callback->usage_id == usage_id &&
+                                               callback->hsdev == hsdev) {
                        spin_unlock(&pdata->dyn_callback_lock);
                        return -EINVAL;
                }
@@ -163,6 +172,7 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
                spin_unlock(&pdata->dyn_callback_lock);
                return -ENOMEM;
        }
+       callback->hsdev = hsdev;
        callback->usage_callback = usage_callback;
        callback->usage_id = usage_id;
        callback->priv = NULL;
@@ -181,7 +191,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
 
        spin_lock(&pdata->dyn_callback_lock);
        list_for_each_entry(callback, &pdata->dyn_callback_list, list)
-               if (callback->usage_id == usage_id) {
+               if (callback->usage_id == usage_id &&
+                                               callback->hsdev == hsdev) {
                        list_del(&callback->list);
                        kfree(callback);
                        break;
@@ -260,13 +271,12 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
 
        spin_lock_irqsave(&data->lock, flags);
        data->pending.status = true;
+       spin_unlock_irqrestore(&data->lock, flags);
        report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
-       if (!report) {
-               spin_unlock_irqrestore(&data->lock, flags);
+       if (!report)
                goto err_free;
-       }
+
        hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
-       spin_unlock_irqrestore(&data->lock, flags);
        wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5);
        switch (data->pending.raw_size) {
        case 1:
@@ -291,6 +301,28 @@ err_free:
 }
 EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value);
 
+int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev,
+                               u32 report_id, int field_index, u32 usage_id)
+{
+       struct hid_report *report;
+       struct hid_field *field;
+       int i;
+
+       report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
+       if (!report || (field_index >= report->maxfield))
+               goto done_proc;
+
+       field = report->field[field_index];
+       for (i = 0; i < field->maxusage; ++i) {
+               if (field->usage[i].hid == usage_id)
+                       return field->usage[i].usage_index;
+       }
+
+done_proc:
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(hid_sensor_get_usage_index);
+
 int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
                                u8 type,
                                u32 usage_id,
@@ -298,8 +330,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
                                struct hid_sensor_hub_attribute_info *info)
 {
        int ret = -1;
-       int i, j;
-       int collection_index = -1;
+       int i;
        struct hid_report *report;
        struct hid_field *field;
        struct hid_report_enum *report_enum;
@@ -313,44 +344,31 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
        info->units = -1;
        info->unit_expo = -1;
 
-       for (i = 0; i < hdev->maxcollection; ++i) {
-               struct hid_collection *collection = &hdev->collection[i];
-               if (usage_id == collection->usage) {
-                       collection_index = i;
-                       break;
-               }
-       }
-       if (collection_index == -1)
-               goto err_ret;
-
        report_enum = &hdev->report_enum[type];
        list_for_each_entry(report, &report_enum->report_list, list) {
                for (i = 0; i < report->maxfield; ++i) {
                        field = report->field[i];
-                       if (field->physical == usage_id &&
-                               field->logical == attr_usage_id) {
-                               sensor_hub_fill_attr_info(info, i, report->id,
-                                                         field);
-                               ret = 0;
-                       } else {
-                               for (j = 0; j < field->maxusage; ++j) {
-                                       if (field->usage[j].hid ==
-                                       attr_usage_id &&
-                                       field->usage[j].collection_index ==
-                                       collection_index) {
-                                               sensor_hub_fill_attr_info(info,
-                                                         i, report->id, field);
-                                               ret = 0;
-                                               break;
-                                       }
+                       if (field->maxusage) {
+                               if (field->physical == usage_id &&
+                                       (field->logical == attr_usage_id ||
+                                       field->usage[0].hid ==
+                                                       attr_usage_id) &&
+                                       (field->usage[0].collection_index >=
+                                       hsdev->start_collection_index) &&
+                                       (field->usage[0].collection_index <
+                                       hsdev->end_collection_index)) {
+
+                                       sensor_hub_fill_attr_info(info, i,
+                                                               report->id,
+                                                               field);
+                                       ret = 0;
+                                       break;
                                }
                        }
-                       if (ret == 0)
-                               break;
                }
+
        }
 
-err_ret:
        return ret;
 }
 EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info);
@@ -366,7 +384,7 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)
        list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
                if (callback->usage_callback->suspend)
                        callback->usage_callback->suspend(
-                                       pdata->hsdev, callback->priv);
+                                       callback->hsdev, callback->priv);
        }
        spin_unlock(&pdata->dyn_callback_lock);
 
@@ -383,7 +401,7 @@ static int sensor_hub_resume(struct hid_device *hdev)
        list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
                if (callback->usage_callback->resume)
                        callback->usage_callback->resume(
-                                       pdata->hsdev, callback->priv);
+                                       callback->hsdev, callback->priv);
        }
        spin_unlock(&pdata->dyn_callback_lock);
 
@@ -410,6 +428,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
        struct hid_sensor_hub_callbacks *callback = NULL;
        struct hid_collection *collection = NULL;
        void *priv = NULL;
+       struct hid_sensor_hub_device *hsdev = NULL;
 
        hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
                         report->id, size, report->type);
@@ -444,23 +463,26 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
                                report->field[i]->usage->collection_index];
                hid_dbg(hdev, "collection->usage %x\n",
                                        collection->usage);
-               callback = sensor_hub_get_callback(pdata->hsdev->hdev,
-                                               report->field[i]->physical,
-                                                       &priv);
+
+               callback = sensor_hub_get_callback(hdev,
+                               report->field[i]->physical,
+                               report->field[i]->usage[0].collection_index,
+                               &hsdev, &priv);
+
                if (callback && callback->capture_sample) {
                        if (report->field[i]->logical)
-                               callback->capture_sample(pdata->hsdev,
+                               callback->capture_sample(hsdev,
                                        report->field[i]->logical, sz, ptr,
                                        callback->pdev);
                        else
-                               callback->capture_sample(pdata->hsdev,
+                               callback->capture_sample(hsdev,
                                        report->field[i]->usage->hid, sz, ptr,
                                        callback->pdev);
                }
                ptr += sz;
        }
        if (callback && collection && callback->send_event)
-               callback->send_event(pdata->hsdev, collection->usage,
+               callback->send_event(hsdev, collection->usage,
                                callback->pdev);
        spin_unlock_irqrestore(&pdata->lock, flags);
 
@@ -473,7 +495,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
        struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev);
 
        mutex_lock(&data->mutex);
-       if (!hsdev->ref_cnt) {
+       if (!data->ref_cnt) {
                ret = hid_hw_open(hsdev->hdev);
                if (ret) {
                        hid_err(hsdev->hdev, "failed to open hid device\n");
@@ -481,7 +503,7 @@ int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
                        return ret;
                }
        }
-       hsdev->ref_cnt++;
+       data->ref_cnt++;
        mutex_unlock(&data->mutex);
 
        return ret;
@@ -493,8 +515,8 @@ void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev)
        struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev);
 
        mutex_lock(&data->mutex);
-       hsdev->ref_cnt--;
-       if (!hsdev->ref_cnt)
+       data->ref_cnt--;
+       if (!data->ref_cnt)
                hid_hw_close(hsdev->hdev);
        mutex_unlock(&data->mutex);
 }
@@ -541,26 +563,19 @@ static int sensor_hub_probe(struct hid_device *hdev,
        struct sensor_hub_data *sd;
        int i;
        char *name;
-       struct hid_report *report;
-       struct hid_report_enum *report_enum;
-       struct hid_field *field;
        int dev_cnt;
+       struct hid_sensor_hub_device *hsdev;
+       struct hid_sensor_hub_device *last_hsdev = NULL;
 
        sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
        if (!sd) {
                hid_err(hdev, "cannot allocate Sensor data\n");
                return -ENOMEM;
        }
-       sd->hsdev = devm_kzalloc(&hdev->dev, sizeof(*sd->hsdev), GFP_KERNEL);
-       if (!sd->hsdev) {
-               hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
-               return -ENOMEM;
-       }
+
        hid_set_drvdata(hdev, sd);
        sd->quirks = id->driver_data;
-       sd->hsdev->hdev = hdev;
-       sd->hsdev->vendor_id = hdev->vendor;
-       sd->hsdev->product_id = hdev->product;
+
        spin_lock_init(&sd->lock);
        spin_lock_init(&sd->dyn_callback_lock);
        mutex_init(&sd->mutex);
@@ -578,9 +593,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
        }
        INIT_LIST_HEAD(&sd->dyn_callback_list);
        sd->hid_sensor_client_cnt = 0;
-       report_enum = &hdev->report_enum[HID_INPUT_REPORT];
 
-       dev_cnt = sensor_hub_get_physical_device_count(report_enum);
+       dev_cnt = sensor_hub_get_physical_device_count(hdev);
        if (dev_cnt > HID_MAX_PHY_DEVICES) {
                hid_err(hdev, "Invalid Physical device count\n");
                ret = -EINVAL;
@@ -594,42 +608,63 @@ static int sensor_hub_probe(struct hid_device *hdev,
                        ret = -ENOMEM;
                        goto err_stop_hw;
        }
-       list_for_each_entry(report, &report_enum->report_list, list) {
-               hid_dbg(hdev, "Report id:%x\n", report->id);
-               field = report->field[0];
-               if (report->maxfield && field &&
-                                       field->physical) {
+
+       for (i = 0; i < hdev->maxcollection; ++i) {
+               struct hid_collection *collection = &hdev->collection[i];
+
+               if (collection->type == HID_COLLECTION_PHYSICAL) {
+
+                       hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
+                       if (!hsdev) {
+                               hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
+                               ret = -ENOMEM;
+                               goto err_no_mem;
+                       }
+                       hsdev->hdev = hdev;
+                       hsdev->vendor_id = hdev->vendor;
+                       hsdev->product_id = hdev->product;
+                       hsdev->start_collection_index = i;
+                       if (last_hsdev)
+                               last_hsdev->end_collection_index = i;
+                       last_hsdev = hsdev;
                        name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x",
-                                               field->physical);
+                                       collection->usage);
                        if (name == NULL) {
                                hid_err(hdev, "Failed MFD device name\n");
                                        ret = -ENOMEM;
-                                       goto err_free_names;
+                                       goto err_no_mem;
                        }
                        sd->hid_sensor_hub_client_devs[
-                               sd->hid_sensor_client_cnt].id = PLATFORM_DEVID_AUTO;
+                               sd->hid_sensor_client_cnt].id =
+                                                       PLATFORM_DEVID_AUTO;
                        sd->hid_sensor_hub_client_devs[
                                sd->hid_sensor_client_cnt].name = name;
                        sd->hid_sensor_hub_client_devs[
                                sd->hid_sensor_client_cnt].platform_data =
-                                               sd->hsdev;
+                                                       hsdev;
                        sd->hid_sensor_hub_client_devs[
                                sd->hid_sensor_client_cnt].pdata_size =
-                                               sizeof(*sd->hsdev);
-                       hid_dbg(hdev, "Adding %s:%p\n", name, sd);
+                                                       sizeof(*hsdev);
+                       hid_dbg(hdev, "Adding %s:%d\n", name,
+                                       hsdev->start_collection_index);
                        sd->hid_sensor_client_cnt++;
                }
        }
+       if (last_hsdev)
+               last_hsdev->end_collection_index = i;
+
        ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs,
                sd->hid_sensor_client_cnt, NULL, 0, NULL);
        if (ret < 0)
-               goto err_free_names;
+               goto err_no_mem;
 
        return ret;
 
-err_free_names:
-       for (i = 0; i < sd->hid_sensor_client_cnt ; ++i)
+err_no_mem:
+       for (i = 0; i < sd->hid_sensor_client_cnt; ++i) {
                kfree(sd->hid_sensor_hub_client_devs[i].name);
+               kfree(sd->hid_sensor_hub_client_devs[i].platform_data);
+       }
        kfree(sd->hid_sensor_hub_client_devs);
 err_stop_hw:
        hid_hw_stop(hdev);
@@ -651,8 +686,10 @@ static void sensor_hub_remove(struct hid_device *hdev)
                complete(&data->pending.ready);
        spin_unlock_irqrestore(&data->lock, flags);
        mfd_remove_devices(&hdev->dev);
-       for (i = 0; i < data->hid_sensor_client_cnt ; ++i)
+       for (i = 0; i < data->hid_sensor_client_cnt; ++i) {
                kfree(data->hid_sensor_hub_client_devs[i].name);
+               kfree(data->hid_sensor_hub_client_devs[i].platform_data);
+       }
        kfree(data->hid_sensor_hub_client_devs);
        hid_set_drvdata(hdev, NULL);
        mutex_destroy(&data->mutex);
@@ -665,6 +702,9 @@ static const struct hid_device_id sensor_hub_devices[] = {
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1,
                        USB_DEVICE_ID_INTEL_HID_SENSOR),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
+                       USB_DEVICE_ID_STM_HID_SENSOR),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,
                     HID_ANY_ID) },
        { }
index e3e89b6a41c28d9b18702e8c53e21218cb292840..4d348c069981f21b51c12aabfea9f4fdaa342952 100644 (file)
@@ -17,7 +17,8 @@
  * any later version.
  */
 
-/* NOTE: in order for the Sony PS3 BD Remote Control to be found by
+/*
+ * NOTE: in order for the Sony PS3 BD Remote Control to be found by
  * a Bluetooth host, the key combination Start+Enter has to be kept pressed
  * for about 7 seconds with the Bluetooth Host Controller in discovering mode.
  *
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/usb.h>
 #include <linux/leds.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/input/mt.h>
 
 #include "hid-ids.h"
 
 #define DUALSHOCK4_CONTROLLER_USB BIT(5)
 #define DUALSHOCK4_CONTROLLER_BT  BIT(6)
 
-#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB)
+#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
+#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\
+                               DUALSHOCK4_CONTROLLER_BT)
+#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
+                               DUALSHOCK4_CONTROLLER)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
+#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
 
 #define MAX_LEDS 4
 
@@ -73,7 +83,8 @@ static const u8 sixaxis_rdesc_fixup2[] = {
        0xb1, 0x02, 0xc0, 0xc0,
 };
 
-/* The default descriptor doesn't provide mapping for the accelerometers
+/*
+ * The default descriptor doesn't provide mapping for the accelerometers
  * or orientation sensors.  This fixed descriptor maps the accelerometers
  * to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors
  * to usage values 0x43, 0x44 and 0x45.
@@ -332,6 +343,217 @@ static u8 dualshock4_usb_rdesc[] = {
        0xC0                /*  End Collection                      */
 };
 
+/*
+ * The default behavior of the Dualshock 4 is to send reports using report
+ * type 1 when running over Bluetooth. However, as soon as it receives a
+ * report of type 17 to set the LEDs or rumble it starts returning it's state
+ * in report 17 instead of 1.  Since report 17 is undefined in the default HID
+ * descriptor the button and axis definitions must be moved to report 17 or
+ * the HID layer won't process the received input once a report is sent.
+ */
+static u8 dualshock4_bt_rdesc[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x05,         /*  Usage (Gamepad),                    */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x01,         /*      Report ID (1),                  */
+       0x75, 0x08,         /*      Report Size (8),                */
+       0x95, 0x0A,         /*      Report Count (9),               */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x06, 0x04, 0xFF,   /*      Usage Page (FF04h),             */
+       0x85, 0x02,         /*      Report ID (2),                  */
+       0x09, 0x24,         /*      Usage (24h),                    */
+       0x95, 0x24,         /*      Report Count (36),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0xA3,         /*      Report ID (163),                */
+       0x09, 0x25,         /*      Usage (25h),                    */
+       0x95, 0x30,         /*      Report Count (48),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x05,         /*      Report ID (5),                  */
+       0x09, 0x26,         /*      Usage (26h),                    */
+       0x95, 0x28,         /*      Report Count (40),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x06,         /*      Report ID (6),                  */
+       0x09, 0x27,         /*      Usage (27h),                    */
+       0x95, 0x34,         /*      Report Count (52),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x07,         /*      Report ID (7),                  */
+       0x09, 0x28,         /*      Usage (28h),                    */
+       0x95, 0x30,         /*      Report Count (48),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x08,         /*      Report ID (8),                  */
+       0x09, 0x29,         /*      Usage (29h),                    */
+       0x95, 0x2F,         /*      Report Count (47),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x06, 0x03, 0xFF,   /*      Usage Page (FF03h),             */
+       0x85, 0x03,         /*      Report ID (3),                  */
+       0x09, 0x21,         /*      Usage (21h),                    */
+       0x95, 0x26,         /*      Report Count (38),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x04,         /*      Report ID (4),                  */
+       0x09, 0x22,         /*      Usage (22h),                    */
+       0x95, 0x2E,         /*      Report Count (46),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0xF0,         /*      Report ID (240),                */
+       0x09, 0x47,         /*      Usage (47h),                    */
+       0x95, 0x3F,         /*      Report Count (63),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0xF1,         /*      Report ID (241),                */
+       0x09, 0x48,         /*      Usage (48h),                    */
+       0x95, 0x3F,         /*      Report Count (63),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0xF2,         /*      Report ID (242),                */
+       0x09, 0x49,         /*      Usage (49h),                    */
+       0x95, 0x0F,         /*      Report Count (15),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x11,         /*      Report ID (17),                 */
+       0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+       0x09, 0x20,         /*      Usage (20h),                    */
+       0x95, 0x02,         /*      Report Count (2),               */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x05, 0x01,         /*      Usage Page (Desktop),           */
+       0x09, 0x30,         /*      Usage (X),                      */
+       0x09, 0x31,         /*      Usage (Y),                      */
+       0x09, 0x32,         /*      Usage (Z),                      */
+       0x09, 0x35,         /*      Usage (Rz),                     */
+       0x15, 0x00,         /*      Logical Minimum (0),            */
+       0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+       0x75, 0x08,         /*      Report Size (8),                */
+       0x95, 0x04,         /*      Report Count (4),               */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x39,         /*      Usage (Hat Switch),             */
+       0x15, 0x00,         /*      Logical Minimum (0),            */
+       0x25, 0x07,         /*      Logical Maximum (7),            */
+       0x75, 0x04,         /*      Report Size (4),                */
+       0x95, 0x01,         /*      Report Count (1),               */
+       0x81, 0x42,         /*      Input (Variable, Null State),   */
+       0x05, 0x09,         /*      Usage Page (Button),            */
+       0x19, 0x01,         /*      Usage Minimum (01h),            */
+       0x29, 0x0E,         /*      Usage Maximum (0Eh),            */
+       0x15, 0x00,         /*      Logical Minimum (0),            */
+       0x25, 0x01,         /*      Logical Maximum (1),            */
+       0x75, 0x01,         /*      Report Size (1),                */
+       0x95, 0x0E,         /*      Report Count (14),              */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x75, 0x06,         /*      Report Size (6),                */
+       0x95, 0x01,         /*      Report Count (1),               */
+       0x81, 0x01,         /*      Input (Constant),               */
+       0x05, 0x01,         /*      Usage Page (Desktop),           */
+       0x09, 0x33,         /*      Usage (Rx),                     */
+       0x09, 0x34,         /*      Usage (Ry),                     */
+       0x15, 0x00,         /*      Logical Minimum (0),            */
+       0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+       0x75, 0x08,         /*      Report Size (8),                */
+       0x95, 0x02,         /*      Report Count (2),               */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+       0x09, 0x20,         /*      Usage (20h),                    */
+       0x95, 0x03,         /*      Report Count (3),               */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x05, 0x01,         /*      Usage Page (Desktop),           */
+       0x19, 0x40,         /*      Usage Minimum (40h),            */
+       0x29, 0x42,         /*      Usage Maximum (42h),            */
+       0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */
+       0x26, 0x00, 0x7F,   /*      Logical Maximum (32767),        */
+       0x75, 0x10,         /*      Report Size (16),               */
+       0x95, 0x03,         /*      Report Count (3),               */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x19, 0x43,         /*      Usage Minimum (43h),            */
+       0x29, 0x45,         /*      Usage Maximum (45h),            */
+       0x16, 0xFF, 0xBF,   /*      Logical Minimum (-16385),       */
+       0x26, 0x00, 0x40,   /*      Logical Maximum (16384),        */
+       0x95, 0x03,         /*      Report Count (3),               */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
+       0x09, 0x20,         /*      Usage (20h),                    */
+       0x15, 0x00,         /*      Logical Minimum (0),            */
+       0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
+       0x75, 0x08,         /*      Report Size (8),                */
+       0x95, 0x31,         /*      Report Count (51),              */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x21,         /*      Usage (21h),                    */
+       0x75, 0x08,         /*      Report Size (8),                */
+       0x95, 0x4D,         /*      Report Count (77),              */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x12,         /*      Report ID (18),                 */
+       0x09, 0x22,         /*      Usage (22h),                    */
+       0x95, 0x8D,         /*      Report Count (141),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x23,         /*      Usage (23h),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x13,         /*      Report ID (19),                 */
+       0x09, 0x24,         /*      Usage (24h),                    */
+       0x95, 0xCD,         /*      Report Count (205),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x25,         /*      Usage (25h),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x14,         /*      Report ID (20),                 */
+       0x09, 0x26,         /*      Usage (26h),                    */
+       0x96, 0x0D, 0x01,   /*      Report Count (269),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x27,         /*      Usage (27h),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x15,         /*      Report ID (21),                 */
+       0x09, 0x28,         /*      Usage (28h),                    */
+       0x96, 0x4D, 0x01,   /*      Report Count (333),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x29,         /*      Usage (29h),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x16,         /*      Report ID (22),                 */
+       0x09, 0x2A,         /*      Usage (2Ah),                    */
+       0x96, 0x8D, 0x01,   /*      Report Count (397),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x2B,         /*      Usage (2Bh),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x17,         /*      Report ID (23),                 */
+       0x09, 0x2C,         /*      Usage (2Ch),                    */
+       0x96, 0xCD, 0x01,   /*      Report Count (461),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x2D,         /*      Usage (2Dh),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x18,         /*      Report ID (24),                 */
+       0x09, 0x2E,         /*      Usage (2Eh),                    */
+       0x96, 0x0D, 0x02,   /*      Report Count (525),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x2F,         /*      Usage (2Fh),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x85, 0x19,         /*      Report ID (25),                 */
+       0x09, 0x30,         /*      Usage (30h),                    */
+       0x96, 0x22, 0x02,   /*      Report Count (546),             */
+       0x81, 0x02,         /*      Input (Variable),               */
+       0x09, 0x31,         /*      Usage (31h),                    */
+       0x91, 0x02,         /*      Output (Variable),              */
+       0x06, 0x80, 0xFF,   /*      Usage Page (FF80h),             */
+       0x85, 0x82,         /*      Report ID (130),                */
+       0x09, 0x22,         /*      Usage (22h),                    */
+       0x95, 0x3F,         /*      Report Count (63),              */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x83,         /*      Report ID (131),                */
+       0x09, 0x23,         /*      Usage (23h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x84,         /*      Report ID (132),                */
+       0x09, 0x24,         /*      Usage (24h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x90,         /*      Report ID (144),                */
+       0x09, 0x30,         /*      Usage (30h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x91,         /*      Report ID (145),                */
+       0x09, 0x31,         /*      Usage (31h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x92,         /*      Report ID (146),                */
+       0x09, 0x32,         /*      Usage (32h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0x93,         /*      Report ID (147),                */
+       0x09, 0x33,         /*      Usage (33h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0xA0,         /*      Report ID (160),                */
+       0x09, 0x40,         /*      Usage (40h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0x85, 0xA4,         /*      Report ID (164),                */
+       0x09, 0x44,         /*      Usage (44h),                    */
+       0xB1, 0x02,         /*      Feature (Variable),             */
+       0xC0                /*  End Collection                      */
+};
+
 static __u8 ps3remote_rdesc[] = {
        0x05, 0x01,          /* GUsagePage Generic Desktop */
        0x09, 0x05,          /* LUsage 0x05 [Game Pad] */
@@ -449,7 +671,8 @@ static const unsigned int ps3remote_keymap_remote_buttons[] = {
 };
 
 static const unsigned int buzz_keymap[] = {
-       /* The controller has 4 remote buzzers, each with one LED and 5
+       /*
+        * The controller has 4 remote buzzers, each with one LED and 5
         * buttons.
         * 
         * We use the mapping chosen by the controller, which is:
@@ -487,18 +710,35 @@ static const unsigned int buzz_keymap[] = {
        [20] = BTN_TRIGGER_HAPPY20,
 };
 
+static enum power_supply_property sony_battery_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_SCOPE,
+       POWER_SUPPLY_PROP_STATUS,
+};
+
+static spinlock_t sony_dev_list_lock;
+static LIST_HEAD(sony_device_list);
+
 struct sony_sc {
+       spinlock_t lock;
+       struct list_head list_node;
        struct hid_device *hdev;
        struct led_classdev *leds[MAX_LEDS];
-       struct hid_report *output_report;
        unsigned long quirks;
        struct work_struct state_worker;
+       struct power_supply battery;
 
 #ifdef CONFIG_SONY_FF
        __u8 left;
        __u8 right;
 #endif
 
+       __u8 mac_address[6];
+       __u8 worker_initialized;
+       __u8 cable_state;
+       __u8 battery_charging;
+       __u8 battery_capacity;
        __u8 led_state[MAX_LEDS];
        __u8 led_count;
 };
@@ -576,6 +816,10 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n");
                rdesc = dualshock4_usb_rdesc;
                *rsize = sizeof(dualshock4_usb_rdesc);
+       } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && *rsize == 357) {
+               hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n");
+               rdesc = dualshock4_bt_rdesc;
+               *rsize = sizeof(dualshock4_bt_rdesc);
        }
 
        /* The HID descriptor exposed over BT has a trailing zero byte */
@@ -599,20 +843,127 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
        return rdesc;
 }
 
+static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+{
+       static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
+       unsigned long flags;
+       __u8 cable_state, battery_capacity, battery_charging;
+
+       /*
+        * The sixaxis is charging if the battery value is 0xee
+        * and it is fully charged if the value is 0xef.
+        * It does not report the actual level while charging so it
+        * is set to 100% while charging is in progress.
+        */
+       if (rd[30] >= 0xee) {
+               battery_capacity = 100;
+               battery_charging = !(rd[30] & 0x01);
+       } else {
+               __u8 index = rd[30] <= 5 ? rd[30] : 5;
+               battery_capacity = sixaxis_battery_capacity[index];
+               battery_charging = 0;
+       }
+       cable_state = !(rd[31] & 0x04);
+
+       spin_lock_irqsave(&sc->lock, flags);
+       sc->cable_state = cable_state;
+       sc->battery_capacity = battery_capacity;
+       sc->battery_charging = battery_charging;
+       spin_unlock_irqrestore(&sc->lock, flags);
+}
+
+static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+{
+       struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
+                                               struct hid_input, list);
+       struct input_dev *input_dev = hidinput->input;
+       unsigned long flags;
+       int n, offset;
+       __u8 cable_state, battery_capacity, battery_charging;
+
+       /*
+        * Battery and touchpad data starts at byte 30 in the USB report and
+        * 32 in Bluetooth report.
+        */
+       offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32;
+
+       /*
+        * The lower 4 bits of byte 30 contain the battery level
+        * and the 5th bit contains the USB cable state.
+        */
+       cable_state = (rd[offset] >> 4) & 0x01;
+       battery_capacity = rd[offset] & 0x0F;
+
+       /*
+        * When a USB power source is connected the battery level ranges from
+        * 0 to 10, and when running on battery power it ranges from 0 to 9.
+        * A battery level above 10 when plugged in means charge completed.
+        */
+       if (!cable_state || battery_capacity > 10)
+               battery_charging = 0;
+       else
+               battery_charging = 1;
+
+       if (!cable_state)
+               battery_capacity++;
+       if (battery_capacity > 10)
+               battery_capacity = 10;
+
+       battery_capacity *= 10;
+
+       spin_lock_irqsave(&sc->lock, flags);
+       sc->cable_state = cable_state;
+       sc->battery_capacity = battery_capacity;
+       sc->battery_charging = battery_charging;
+       spin_unlock_irqrestore(&sc->lock, flags);
+
+       offset += 5;
+
+       /*
+        * The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB
+        * and 37 on Bluetooth.
+        * The first 7 bits of the first byte is a counter and bit 8 is a touch
+        * indicator that is 0 when pressed and 1 when not pressed.
+        * The next 3 bytes are two 12 bit touch coordinates, X and Y.
+        * The data for the second touch is in the same format and immediatly
+        * follows the data for the first.
+        */
+       for (n = 0; n < 2; n++) {
+               __u16 x, y;
+
+               x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
+               y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
+
+               input_mt_slot(input_dev, n);
+               input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+                                       !(rd[offset] >> 7));
+               input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+               input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+
+               offset += 4;
+       }
+}
+
 static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
                __u8 *rd, int size)
 {
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
-       /* Sixaxis HID report has acclerometers/gyro with MSByte first, this
+       /*
+        * Sixaxis HID report has acclerometers/gyro with MSByte first, this
         * has to be BYTE_SWAPPED before passing up to joystick interface
         */
-       if ((sc->quirks & (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)) &&
-                       rd[0] == 0x01 && size == 49) {
+       if ((sc->quirks & SIXAXIS_CONTROLLER) && rd[0] == 0x01 && size == 49) {
                swap(rd[41], rd[42]);
                swap(rd[43], rd[44]);
                swap(rd[45], rd[46]);
                swap(rd[47], rd[48]);
+
+               sixaxis_parse_report(sc, rd, size);
+       } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
+                       size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
+                       && rd[0] == 0x11 && size == 78)) {
+               dualshock4_parse_report(sc, rd, size);
        }
 
        return 0;
@@ -654,45 +1005,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
        return 0;
 }
 
-/*
- * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
- * like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
- * so we need to override that forcing HID Output Reports on the Control EP.
- *
- * There is also another issue about HID Output Reports via USB, the Sixaxis
- * does not want the report_id as part of the data packet, so we have to
- * discard buf[0] when sending the actual control message, even for numbered
- * reports, humpf!
- */
-static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
-               size_t count, unsigned char report_type)
-{
-       struct usb_interface *intf = to_usb_interface(hid->dev.parent);
-       struct usb_device *dev = interface_to_usbdev(intf);
-       struct usb_host_interface *interface = intf->cur_altsetting;
-       int report_id = buf[0];
-       int ret;
-
-       if (report_type == HID_OUTPUT_REPORT) {
-               /* Don't send the Report ID */
-               buf++;
-               count--;
-       }
-
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-               HID_REQ_SET_REPORT,
-               USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-               ((report_type + 1) << 8) | report_id,
-               interface->desc.bInterfaceNumber, buf, count,
-               USB_CTRL_SET_TIMEOUT);
-
-       /* Count also the Report ID, in case of an Output report. */
-       if (ret > 0 && report_type == HID_OUTPUT_REPORT)
-               ret++;
-
-       return ret;
-}
-
 /*
  * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
  * to "operational".  Without this, the ps3 controller will not report any
@@ -724,6 +1036,18 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
                                  HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 }
 
+/*
+ * Requesting feature report 0x02 in Bluetooth mode changes the state of the
+ * controller so that it sends full input reports of type 0x11.
+ */
+static int dualshock4_set_operational_bt(struct hid_device *hdev)
+{
+       __u8 buf[37] = { 0 };
+
+       return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf),
+                               HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+}
+
 static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
 {
        struct list_head *report_list =
@@ -751,8 +1075,7 @@ static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
 
        if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
                buzz_set_leds(hdev, leds);
-       } else if ((drv_data->quirks & SIXAXIS_CONTROLLER_USB) ||
-                  (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB)) {
+       } else {
                for (n = 0; n < count; n++)
                        drv_data->led_state[n] = leds[n];
                schedule_work(&drv_data->state_worker);
@@ -792,7 +1115,6 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
        struct sony_sc *drv_data;
 
        int n;
-       int on = 0;
 
        drv_data = hid_get_drvdata(hdev);
        if (!drv_data) {
@@ -801,13 +1123,11 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
        }
 
        for (n = 0; n < drv_data->led_count; n++) {
-               if (led == drv_data->leds[n]) {
-                       on = !!(drv_data->led_state[n]);
-                       break;
-               }
+               if (led == drv_data->leds[n])
+                       return drv_data->led_state[n];
        }
 
-       return on ? LED_FULL : LED_OFF;
+       return LED_OFF;
 }
 
 static void sony_leds_remove(struct hid_device *hdev)
@@ -857,7 +1177,7 @@ static int sony_leds_init(struct hid_device *hdev)
                /* Validate expected report characteristics. */
                if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
                        return -ENODEV;
-       } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) {
+       } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
                drv_data->led_count = 3;
                max_brightness = 255;
                use_colors = 1;
@@ -871,9 +1191,11 @@ static int sony_leds_init(struct hid_device *hdev)
                name_fmt = "%s::sony%d";
        }
 
-       /* Clear LEDs as we have no way of reading their initial state. This is
+       /*
+        * Clear LEDs as we have no way of reading their initial state. This is
         * only relevant if the driver is loaded after somebody actively set the
-        * LEDs to on */
+        * LEDs to on
+        */
        sony_set_leds(hdev, initial_values, drv_data->led_count);
 
        name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
@@ -943,28 +1265,45 @@ static void sixaxis_state_worker(struct work_struct *work)
        buf[10] |= sc->led_state[2] << 3;
        buf[10] |= sc->led_state[3] << 4;
 
-       hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT);
+       hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
+                       HID_REQ_SET_REPORT);
 }
 
 static void dualshock4_state_worker(struct work_struct *work)
 {
        struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
        struct hid_device *hdev = sc->hdev;
-       struct hid_report *report = sc->output_report;
-       __s32 *value = report->field[0]->value;
+       int offset;
 
-       value[0] = 0x03;
+       __u8 buf[78] = { 0 };
+
+       if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+               buf[0] = 0x05;
+               buf[1] = 0x03;
+               offset = 4;
+       } else {
+               buf[0] = 0x11;
+               buf[1] = 0xB0;
+               buf[3] = 0x0F;
+               offset = 6;
+       }
 
 #ifdef CONFIG_SONY_FF
-       value[3] = sc->right;
-       value[4] = sc->left;
+       buf[offset++] = sc->right;
+       buf[offset++] = sc->left;
+#else
+       offset += 2;
 #endif
 
-       value[5] = sc->led_state[0];
-       value[6] = sc->led_state[1];
-       value[7] = sc->led_state[2];
+       buf[offset++] = sc->led_state[0];
+       buf[offset++] = sc->led_state[1];
+       buf[offset++] = sc->led_state[2];
 
-       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+       if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
+               hid_hw_output_report(hdev, buf, 32);
+       else
+               hid_hw_raw_request(hdev, 0x11, buf, 78,
+                               HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
 #ifdef CONFIG_SONY_FF
@@ -994,51 +1333,252 @@ static int sony_init_ff(struct hid_device *hdev)
        return input_ff_create_memless(input_dev, NULL, sony_play_effect);
 }
 
-static void sony_destroy_ff(struct hid_device *hdev)
+#else
+static int sony_init_ff(struct hid_device *hdev)
 {
-       struct sony_sc *sc = hid_get_drvdata(hdev);
+       return 0;
+}
+
+#endif
+
+static int sony_battery_get_property(struct power_supply *psy,
+                                    enum power_supply_property psp,
+                                    union power_supply_propval *val)
+{
+       struct sony_sc *sc = container_of(psy, struct sony_sc, battery);
+       unsigned long flags;
+       int ret = 0;
+       u8 battery_charging, battery_capacity, cable_state;
+
+       spin_lock_irqsave(&sc->lock, flags);
+       battery_charging = sc->battery_charging;
+       battery_capacity = sc->battery_capacity;
+       cable_state = sc->cable_state;
+       spin_unlock_irqrestore(&sc->lock, flags);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = battery_capacity;
+               break;
+       case POWER_SUPPLY_PROP_STATUS:
+               if (battery_charging)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       if (battery_capacity == 100 && cable_state)
+                               val->intval = POWER_SUPPLY_STATUS_FULL;
+                       else
+                               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int sony_battery_probe(struct sony_sc *sc)
+{
+       static atomic_t power_id_seq = ATOMIC_INIT(0);
+       unsigned long power_id;
+       struct hid_device *hdev = sc->hdev;
+       int ret;
+
+       /*
+        * Set the default battery level to 100% to avoid low battery warnings
+        * if the battery is polled before the first device report is received.
+        */
+       sc->battery_capacity = 100;
+
+       power_id = (unsigned long)atomic_inc_return(&power_id_seq);
+
+       sc->battery.properties = sony_battery_props;
+       sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
+       sc->battery.get_property = sony_battery_get_property;
+       sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+       sc->battery.use_for_apm = 0;
+       sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
+                                    power_id);
+       if (!sc->battery.name)
+               return -ENOMEM;
+
+       ret = power_supply_register(&hdev->dev, &sc->battery);
+       if (ret) {
+               hid_err(hdev, "Unable to register battery device\n");
+               goto err_free;
+       }
+
+       power_supply_powers(&sc->battery, &hdev->dev);
+       return 0;
 
-       cancel_work_sync(&sc->state_worker);
+err_free:
+       kfree(sc->battery.name);
+       sc->battery.name = NULL;
+       return ret;
 }
 
-#else
-static int sony_init_ff(struct hid_device *hdev)
+static void sony_battery_remove(struct sony_sc *sc)
+{
+       if (!sc->battery.name)
+               return;
+
+       power_supply_unregister(&sc->battery);
+       kfree(sc->battery.name);
+       sc->battery.name = NULL;
+}
+
+static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
+                                       int w, int h)
 {
+       struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
+                                               struct hid_input, list);
+       struct input_dev *input_dev = hidinput->input;
+       int ret;
+
+       ret = input_mt_init_slots(input_dev, touch_count, 0);
+       if (ret < 0) {
+               hid_err(sc->hdev, "Unable to initialize multi-touch slots\n");
+               return ret;
+       }
+
+       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0);
+
        return 0;
 }
 
-static void sony_destroy_ff(struct hid_device *hdev)
+/*
+ * If a controller is plugged in via USB while already connected via Bluetooth
+ * it will show up as two devices. A global list of connected controllers and
+ * their MAC addresses is maintained to ensure that a device is only connected
+ * once.
+ */
+static int sony_check_add_dev_list(struct sony_sc *sc)
 {
+       struct sony_sc *entry;
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&sony_dev_list_lock, flags);
+
+       list_for_each_entry(entry, &sony_device_list, list_node) {
+               ret = memcmp(sc->mac_address, entry->mac_address,
+                               sizeof(sc->mac_address));
+               if (!ret) {
+                       ret = -EEXIST;
+                       hid_info(sc->hdev, "controller with MAC address %pMR already connected\n",
+                               sc->mac_address);
+                       goto unlock;
+               }
+       }
+
+       ret = 0;
+       list_add(&(sc->list_node), &sony_device_list);
+
+unlock:
+       spin_unlock_irqrestore(&sony_dev_list_lock, flags);
+       return ret;
 }
-#endif
 
-static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
+static void sony_remove_dev_list(struct sony_sc *sc)
 {
-       struct list_head *head, *list;
-       struct hid_report *report;
-       struct hid_device *hdev = sc->hdev;
+       unsigned long flags;
 
-       list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+       if (sc->list_node.next) {
+               spin_lock_irqsave(&sony_dev_list_lock, flags);
+               list_del(&(sc->list_node));
+               spin_unlock_irqrestore(&sony_dev_list_lock, flags);
+       }
+}
+
+static int sony_get_bt_devaddr(struct sony_sc *sc)
+{
+       int ret;
 
-       list_for_each(head, list) {
-               report = list_entry(head, struct hid_report, list);
+       /* HIDP stores the device MAC address as a string in the uniq field. */
+       ret = strlen(sc->hdev->uniq);
+       if (ret != 17)
+               return -EINVAL;
 
-               if (report->id == req_id) {
-                       if (report->size < req_size) {
-                               hid_err(hdev, "Output report 0x%02x (%i bits) is smaller than requested size (%i bits)\n",
-                                       req_id, report->size, req_size);
-                               return -EINVAL;
-                       }
-                       sc->output_report = report;
+       ret = sscanf(sc->hdev->uniq,
+               "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+               &sc->mac_address[5], &sc->mac_address[4], &sc->mac_address[3],
+               &sc->mac_address[2], &sc->mac_address[1], &sc->mac_address[0]);
+
+       if (ret != 6)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sony_check_add(struct sony_sc *sc)
+{
+       int n, ret;
+
+       if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
+           (sc->quirks & SIXAXIS_CONTROLLER_BT)) {
+               /*
+                * sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
+                * address from the uniq string where HIDP stores it.
+                * As uniq cannot be guaranteed to be a MAC address in all cases
+                * a failure of this function should not prevent the connection.
+                */
+               if (sony_get_bt_devaddr(sc) < 0) {
+                       hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n");
                        return 0;
                }
-       }
+       } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
+               __u8 buf[7];
+
+               /*
+                * The MAC address of a DS4 controller connected via USB can be
+                * retrieved with feature report 0x81. The address begins at
+                * offset 1.
+                */
+               ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf),
+                               HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+
+               if (ret != 7) {
+                       hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
+                       return ret < 0 ? ret : -EINVAL;
+               }
 
-       hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id);
+               memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
+       } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+               __u8 buf[18];
+
+               /*
+                * The MAC address of a Sixaxis controller connected via USB can
+                * be retrieved with feature report 0xf2. The address begins at
+                * offset 4.
+                */
+               ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf),
+                               HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+
+               if (ret != 18) {
+                       hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n");
+                       return ret < 0 ? ret : -EINVAL;
+               }
 
-       return -EINVAL;
+               /*
+                * The Sixaxis device MAC in the report is big-endian and must
+                * be byte-swapped.
+                */
+               for (n = 0; n < 6; n++)
+                       sc->mac_address[5-n] = buf[4+n];
+       } else {
+               return 0;
+       }
+
+       return sony_check_add_dev_list(sc);
 }
 
+
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        int ret;
@@ -1076,23 +1616,61 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
        }
 
        if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
-               hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
+               /*
+                * The Sony Sixaxis does not handle HID Output Reports on the
+                * Interrupt EP like it could, so we need to force HID Output
+                * Reports to use HID_REQ_SET_REPORT on the Control EP.
+                *
+                * There is also another issue about HID Output Reports via USB,
+                * the Sixaxis does not want the report_id as part of the data
+                * packet, so we have to discard buf[0] when sending the actual
+                * control message, even for numbered reports, humpf!
+                */
+               hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
+               hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
                ret = sixaxis_set_operational_usb(hdev);
+               sc->worker_initialized = 1;
                INIT_WORK(&sc->state_worker, sixaxis_state_worker);
-       }
-       else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
+       } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+               /*
+                * The Sixaxis wants output reports sent on the ctrl endpoint
+                * when connected via Bluetooth.
+                */
+               hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
                ret = sixaxis_set_operational_bt(hdev);
-       else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
-               /* Report 5 (31 bytes) is used to send data to the controller via USB */
-               ret = sony_set_output_report(sc, 0x05, 248);
+               sc->worker_initialized = 1;
+               INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+       } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+               if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
+                       /*
+                        * The DualShock 4 wants output reports sent on the ctrl
+                        * endpoint when connected via Bluetooth.
+                        */
+                       hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
+                       ret = dualshock4_set_operational_bt(hdev);
+                       if (ret < 0) {
+                               hid_err(hdev, "failed to set the Dualshock 4 operational mode\n");
+                               goto err_stop;
+                       }
+               }
+               /*
+                * The Dualshock 4 touchpad supports 2 touches and has a
+                * resolution of 1920x940.
+                */
+               ret = sony_register_touchpad(sc, 2, 1920, 940);
                if (ret < 0)
                        goto err_stop;
 
+               sc->worker_initialized = 1;
                INIT_WORK(&sc->state_worker, dualshock4_state_worker);
        } else {
                ret = 0;
        }
 
+       if (ret < 0)
+               goto err_stop;
+
+       ret = sony_check_add(sc);
        if (ret < 0)
                goto err_stop;
 
@@ -1102,14 +1680,40 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                        goto err_stop;
        }
 
-       ret = sony_init_ff(hdev);
-       if (ret < 0)
-               goto err_stop;
+       if (sc->quirks & SONY_FF_SUPPORT) {
+               ret = sony_init_ff(hdev);
+               if (ret < 0)
+                       goto err_stop;
+       if (sc->quirks & SONY_BATTERY_SUPPORT) {
+               ret = sony_battery_probe(sc);
+               if (ret < 0)
+                       goto err_stop;
+
+               /* Open the device to receive reports with battery info */
+               ret = hid_hw_open(hdev);
+               if (ret < 0) {
+                       hid_err(hdev, "hw open failed\n");
+                       goto err_stop;
+               }
+       }
+
+       if (sc->quirks & SONY_FF_SUPPORT) {
+               ret = sony_init_ff(hdev);
+               if (ret < 0)
+                       goto err_close;
+       }
 
        return 0;
+err_close:
+       hid_hw_close(hdev);
 err_stop:
        if (sc->quirks & SONY_LED_SUPPORT)
                sony_leds_remove(hdev);
+       if (sc->quirks & SONY_BATTERY_SUPPORT)
+               sony_battery_remove(sc);
+       if (sc->worker_initialized)
+               cancel_work_sync(&sc->state_worker);
+       sony_remove_dev_list(sc);
        hid_hw_stop(hdev);
        return ret;
 }
@@ -1121,7 +1725,17 @@ static void sony_remove(struct hid_device *hdev)
        if (sc->quirks & SONY_LED_SUPPORT)
                sony_leds_remove(hdev);
 
-       sony_destroy_ff(hdev);
+       if (sc->worker_initialized)
+               cancel_work_sync(&sc->state_worker);
+       if (sc->quirks & SONY_BATTERY_SUPPORT) {
+               hid_hw_close(hdev);
+               sony_battery_remove(sc);
+       }
+
+       if (sc->worker_initialized)
+               cancel_work_sync(&sc->state_worker);
+
+       sony_remove_dev_list(sc);
 
        hid_hw_stop(hdev);
 }
index 2cc484c0017b51e00ad8f0b714c4c3d40600e518..9c2d7c23f2966f43953626b82d03cf62bca0c8ba 100644 (file)
@@ -149,7 +149,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
                goto out_free;
        }
 
-       if (report_type == HID_OUTPUT_REPORT) {
+       if ((report_type == HID_OUTPUT_REPORT) &&
+           !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
                ret = hid_hw_output_report(dev, buf, count);
                /*
                 * compatibility with old implementation of USB-HID and I2C-HID:
@@ -330,13 +331,13 @@ static void drop_ref(struct hidraw *hidraw, int exists_bit)
                        hid_hw_close(hidraw->hid);
                        wake_up_interruptible(&hidraw->wait);
                }
+               device_destroy(hidraw_class,
+                              MKDEV(hidraw_major, hidraw->minor));
        } else {
                --hidraw->open;
        }
        if (!hidraw->open) {
                if (!hidraw->exist) {
-                       device_destroy(hidraw_class,
-                                       MKDEV(hidraw_major, hidraw->minor));
                        hidraw_table[hidraw->minor] = NULL;
                        kfree(hidraw);
                } else {
index 1a955317d05fd058ef4feb7dc89c29ec2859e806..8c52a07d56526ed6b3c16c67640174d52fda8391 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/device.h>
 #include <linux/wait.h>
 #include <linux/err.h>
@@ -467,10 +468,18 @@ static void i2c_hid_init_reports(struct hid_device *hid)
                return;
        }
 
+       /*
+        * The device must be powered on while we fetch initial reports
+        * from it.
+        */
+       pm_runtime_get_sync(&client->dev);
+
        list_for_each_entry(report,
                &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
                i2c_hid_init_report(report, inbuf, ihid->bufsize);
 
+       pm_runtime_put(&client->dev);
+
        kfree(inbuf);
 }
 
@@ -587,19 +596,6 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
        return ret;
 }
 
-static int __i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
-               size_t count, unsigned char report_type)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       bool data = true; /* SET_REPORT */
-
-       if (report_type == HID_OUTPUT_REPORT)
-               data = le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0;
-
-       return i2c_hid_output_raw_report(hid, buf, count, report_type, data);
-}
-
 static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
                size_t count)
 {
@@ -722,8 +718,8 @@ static int i2c_hid_open(struct hid_device *hid)
 
        mutex_lock(&i2c_hid_open_mut);
        if (!hid->open++) {
-               ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
-               if (ret) {
+               ret = pm_runtime_get_sync(&client->dev);
+               if (ret < 0) {
                        hid->open--;
                        goto done;
                }
@@ -731,7 +727,7 @@ static int i2c_hid_open(struct hid_device *hid)
        }
 done:
        mutex_unlock(&i2c_hid_open_mut);
-       return ret;
+       return ret < 0 ? ret : 0;
 }
 
 static void i2c_hid_close(struct hid_device *hid)
@@ -748,7 +744,7 @@ static void i2c_hid_close(struct hid_device *hid)
                clear_bit(I2C_HID_STARTED, &ihid->flags);
 
                /* Save some power */
-               i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+               pm_runtime_put(&client->dev);
        }
        mutex_unlock(&i2c_hid_open_mut);
 }
@@ -757,19 +753,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)
 {
        struct i2c_client *client = hid->driver_data;
        struct i2c_hid *ihid = i2c_get_clientdata(client);
-       int ret = 0;
 
        i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
 
        switch (lvl) {
        case PM_HINT_FULLON:
-               ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+               pm_runtime_get_sync(&client->dev);
                break;
        case PM_HINT_NORMAL:
-               ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+               pm_runtime_put(&client->dev);
                break;
        }
-       return ret;
+       return 0;
 }
 
 static struct hid_ll_driver i2c_hid_ll_driver = {
@@ -1007,13 +1002,17 @@ static int i2c_hid_probe(struct i2c_client *client,
        if (ret < 0)
                goto err;
 
+       pm_runtime_get_noresume(&client->dev);
+       pm_runtime_set_active(&client->dev);
+       pm_runtime_enable(&client->dev);
+
        ret = i2c_hid_fetch_hid_descriptor(ihid);
        if (ret < 0)
-               goto err;
+               goto err_pm;
 
        ret = i2c_hid_init_irq(client);
        if (ret < 0)
-               goto err;
+               goto err_pm;
 
        hid = hid_allocate_device();
        if (IS_ERR(hid)) {
@@ -1025,7 +1024,6 @@ static int i2c_hid_probe(struct i2c_client *client,
 
        hid->driver_data = client;
        hid->ll_driver = &i2c_hid_ll_driver;
-       hid->hid_output_raw_report = __i2c_hid_output_raw_report;
        hid->dev.parent = &client->dev;
        ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
        hid->bus = BUS_I2C;
@@ -1043,6 +1041,7 @@ static int i2c_hid_probe(struct i2c_client *client,
                goto err_mem_free;
        }
 
+       pm_runtime_put(&client->dev);
        return 0;
 
 err_mem_free:
@@ -1051,6 +1050,10 @@ err_mem_free:
 err_irq:
        free_irq(client->irq, ihid);
 
+err_pm:
+       pm_runtime_put_noidle(&client->dev);
+       pm_runtime_disable(&client->dev);
+
 err:
        i2c_hid_free_buffers(ihid);
        kfree(ihid);
@@ -1062,6 +1065,11 @@ static int i2c_hid_remove(struct i2c_client *client)
        struct i2c_hid *ihid = i2c_get_clientdata(client);
        struct hid_device *hid;
 
+       pm_runtime_get_sync(&client->dev);
+       pm_runtime_disable(&client->dev);
+       pm_runtime_set_suspended(&client->dev);
+       pm_runtime_put_noidle(&client->dev);
+
        hid = ihid->hid;
        hid_destroy_device(hid);
 
@@ -1107,7 +1115,31 @@ static int i2c_hid_resume(struct device *dev)
 }
 #endif
 
-static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume);
+#ifdef CONFIG_PM_RUNTIME
+static int i2c_hid_runtime_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+       disable_irq(client->irq);
+       return 0;
+}
+
+static int i2c_hid_runtime_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       enable_irq(client->irq);
+       i2c_hid_set_power(client, I2C_HID_PWR_ON);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops i2c_hid_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
+       SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
+                          NULL)
+};
 
 static const struct i2c_device_id i2c_hid_id_table[] = {
        { "hid", 0 },
index 60acee422fdcb79862058468109b44b57b0a209c..0d078c32db4f20cce7c6bf57bfeb0b96e4ba2ec7 100644 (file)
@@ -400,7 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid,
        hid->uniq[63] = 0;
 
        hid->ll_driver = &uhid_hid_driver;
-       hid->hid_output_raw_report = uhid_hid_output_raw;
        hid->bus = ev->u.create.bus;
        hid->vendor = ev->u.create.vendor;
        hid->product = ev->u.create.product;
@@ -429,6 +428,67 @@ err_free:
        return ret;
 }
 
+static int uhid_dev_create2(struct uhid_device *uhid,
+                           const struct uhid_event *ev)
+{
+       struct hid_device *hid;
+       int ret;
+
+       if (uhid->running)
+               return -EALREADY;
+
+       uhid->rd_size = ev->u.create2.rd_size;
+       if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
+               return -EINVAL;
+
+       uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
+       if (!uhid->rd_data)
+               return -ENOMEM;
+
+       memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size);
+
+       hid = hid_allocate_device();
+       if (IS_ERR(hid)) {
+               ret = PTR_ERR(hid);
+               goto err_free;
+       }
+
+       strncpy(hid->name, ev->u.create2.name, 127);
+       hid->name[127] = 0;
+       strncpy(hid->phys, ev->u.create2.phys, 63);
+       hid->phys[63] = 0;
+       strncpy(hid->uniq, ev->u.create2.uniq, 63);
+       hid->uniq[63] = 0;
+
+       hid->ll_driver = &uhid_hid_driver;
+       hid->bus = ev->u.create2.bus;
+       hid->vendor = ev->u.create2.vendor;
+       hid->product = ev->u.create2.product;
+       hid->version = ev->u.create2.version;
+       hid->country = ev->u.create2.country;
+       hid->driver_data = uhid;
+       hid->dev.parent = uhid_misc.this_device;
+
+       uhid->hid = hid;
+       uhid->running = true;
+
+       ret = hid_add_device(hid);
+       if (ret) {
+               hid_err(hid, "Cannot register HID device\n");
+               goto err_hid;
+       }
+
+       return 0;
+
+err_hid:
+       hid_destroy_device(hid);
+       uhid->hid = NULL;
+       uhid->running = false;
+err_free:
+       kfree(uhid->rd_data);
+       return ret;
+}
+
 static int uhid_dev_destroy(struct uhid_device *uhid)
 {
        if (!uhid->running)
@@ -457,6 +517,17 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
        return 0;
 }
 
+static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
+{
+       if (!uhid->running)
+               return -EINVAL;
+
+       hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data,
+                        min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0);
+
+       return 0;
+}
+
 static int uhid_dev_feature_answer(struct uhid_device *uhid,
                                   struct uhid_event *ev)
 {
@@ -593,12 +664,18 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
        case UHID_CREATE:
                ret = uhid_dev_create(uhid, &uhid->input_buf);
                break;
+       case UHID_CREATE2:
+               ret = uhid_dev_create2(uhid, &uhid->input_buf);
+               break;
        case UHID_DESTROY:
                ret = uhid_dev_destroy(uhid);
                break;
        case UHID_INPUT:
                ret = uhid_dev_input(uhid, &uhid->input_buf);
                break;
+       case UHID_INPUT2:
+               ret = uhid_dev_input2(uhid, &uhid->input_buf);
+               break;
        case UHID_FEATURE_ANSWER:
                ret = uhid_dev_feature_answer(uhid, &uhid->input_buf);
                break;
index 0d1d87533f4851b3b94b2b19ccc860b445535554..7b88f4cb990218f0e02a9c1cb2638a872d691bcc 100644 (file)
@@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
        int ret, skipped_report_id = 0;
 
        /* Byte 0 is the report number. Report data starts at byte 1.*/
-       buf[0] = reportnum;
+       if ((rtype == HID_OUTPUT_REPORT) &&
+           (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID))
+               buf[0] = 0;
+       else
+               buf[0] = reportnum;
+
        if (buf[0] == 0x0) {
                /* Don't send the Report ID */
                buf++;
@@ -945,17 +950,6 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
        return ret;
 }
 
-static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf,
-               size_t count, unsigned char report_type)
-{
-       struct usbhid_device *usbhid = hid->driver_data;
-
-       if (usbhid->urbout && report_type != HID_FEATURE_REPORT)
-               return usbhid_output_report(hid, buf, count);
-
-       return usbhid_set_raw_report(hid, buf[0], buf, count, report_type);
-}
-
 static void usbhid_restart_queues(struct usbhid_device *usbhid)
 {
        if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
@@ -1289,7 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
 
        usb_set_intfdata(intf, hid);
        hid->ll_driver = &usb_hid_driver;
-       hid->hid_output_raw_report = usbhid_output_raw_report;
        hid->ff_init = hid_pidff_init;
 #ifdef CONFIG_USB_HIDDEV
        hid->hiddev_connect = hiddev_connect;
index 175ec0afb70cff7770fe4460e18e3d1480546cec..dbd83878ff99ec029a1cda07b265ddcd27418710 100644 (file)
@@ -74,6 +74,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
index 7dcf83998e6f7fc58de8c5c3a894207abcd06bab..dbefbdaf7cd10b4c25f31f33a63b7d6b37d58c03 100644 (file)
@@ -38,29 +38,40 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
        if (state) {
                if (sensor_hub_device_open(st->hsdev))
                        return -EIO;
-               state_val =
-               HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM;
-               report_val =
-               HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM;
-
+               state_val = hid_sensor_get_usage_index(st->hsdev,
+                       st->power_state.report_id,
+                       st->power_state.index,
+                       HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);
+               report_val = hid_sensor_get_usage_index(st->hsdev,
+                       st->report_state.report_id,
+                       st->report_state.index,
+                       HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
        } else {
                sensor_hub_device_close(st->hsdev);
-               state_val =
-               HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM;
-               report_val =
-               HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM;
+               state_val = hid_sensor_get_usage_index(st->hsdev,
+                       st->power_state.report_id,
+                       st->power_state.index,
+                       HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM);
+               report_val = hid_sensor_get_usage_index(st->hsdev,
+                       st->report_state.report_id,
+                       st->report_state.index,
+                       HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
        }
-
        st->data_ready = state;
-       state_val += st->power_state.logical_minimum;
-       report_val += st->report_state.logical_minimum;
-       sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
+
+       if (state_val >= 0) {
+               state_val += st->power_state.logical_minimum;
+               sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
                                        st->power_state.index,
                                        (s32)state_val);
+       }
 
-       sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
+       if (report_val >= 0) {
+               report_val += st->report_state.logical_minimum;
+               sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
                                        st->report_state.index,
                                        (s32)report_val);
+       }
 
        return 0;
 }
index b914ca3f57ba4469264034cd521bf2cdc43b0cfb..b70cfd7ff29cab61123e537b03583d927692e6e5 100644 (file)
@@ -51,13 +51,15 @@ struct hid_sensor_hub_attribute_info {
  * @hdev:              Stores the hid instance.
  * @vendor_id:         Vendor id of hub device.
  * @product_id:                Product id of hub device.
- * @ref_cnt:           Number of MFD clients have opened this device
+ * @start_collection_index: Starting index for a phy type collection
+ * @end_collection_index: Last index for a phy type collection
  */
 struct hid_sensor_hub_device {
        struct hid_device *hdev;
        u32 vendor_id;
        u32 product_id;
-       int ref_cnt;
+       int start_collection_index;
+       int end_collection_index;
 };
 
 /**
@@ -218,4 +220,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
 int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
                                        int *val1, int *val2);
 
+int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev,
+                               u32 report_id, int field_index, u32 usage_id);
+
 #endif
index beaf965621c164735bf070d7513de36d071405b1..f8607605a31c453a4c57827fafa566a7e708e22d 100644 (file)
 #define HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS               0x1000
 
 /* Power state enumerations */
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_UNDEFINED_ENUM               0x00
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM           0x01
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D1_LOW_POWER_ENUM            0x02
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D2_STANDBY_WITH_WAKE_ENUM    0x03
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D3_SLEEP_WITH_WAKE_ENUM      0x04
-#define HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM            0x05
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_UNDEFINED_ENUM       0x200850
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM   0x200851
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D1_LOW_POWER_ENUM    0x200852
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D2_STANDBY_WITH_WAKE_ENUM 0x200853
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D3_SLEEP_WITH_WAKE_ENUM 0x200854
+#define HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM    0x200855
 
 /* Report State enumerations */
-#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM           0x00
-#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM          0x01
+#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM   0x200840
+#define HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM  0x200841
 
 #endif
index e224516cc565be991280c40b842cf28ff24c4aa4..720e3a10608cec94c02c94a6a1053789a567bae5 100644 (file)
@@ -290,6 +290,8 @@ struct hid_item {
 #define HID_QUIRK_NO_EMPTY_INPUT               0x00000100
 #define HID_QUIRK_NO_INIT_INPUT_REPORTS                0x00000200
 #define HID_QUIRK_SKIP_OUTPUT_REPORTS          0x00010000
+#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID                0x00020000
+#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000
 #define HID_QUIRK_FULLSPEED_INTERVAL           0x10000000
 #define HID_QUIRK_NO_INIT_REPORTS              0x20000000
 #define HID_QUIRK_NO_IGNORE                    0x40000000
@@ -511,9 +513,6 @@ struct hid_device {                                                 /* device report descriptor */
                                  struct hid_usage *, __s32);
        void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
 
-       /* handler for raw output data, used by hidraw */
-       int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char);
-
        /* debugging support via debugfs */
        unsigned short debug;
        struct dentry *debug_dir;
@@ -1020,22 +1019,6 @@ static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf,
        return -ENOSYS;
 }
 
-/**
- * hid_output_raw_report - send an output or a feature report to the device
- *
- * @hdev: hid device
- * @buf: raw data to transfer
- * @len: length of buf
- * @report_type: HID_FEATURE_REPORT or HID_OUTPUT_REPORT
- *
- * @return: count of data transfered, negative if error
- */
-static inline int hid_output_raw_report(struct hid_device *hdev, __u8 *buf,
-                                       size_t len, unsigned char report_type)
-{
-       return hdev->hid_output_raw_report(hdev, buf, len, report_type);
-}
-
 /**
  * hid_hw_idle - send idle request to device
  *
index 414b74be4da1695bf677e599b8aaf5ec8500679c..1e3b09c191cd50648fd4bd31425a6adaa7c0bd7b 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <linux/input.h>
 #include <linux/types.h>
+#include <linux/hid.h>
 
 enum uhid_event_type {
        UHID_CREATE,
@@ -34,6 +35,8 @@ enum uhid_event_type {
        UHID_INPUT,
        UHID_FEATURE,
        UHID_FEATURE_ANSWER,
+       UHID_CREATE2,
+       UHID_INPUT2,
 };
 
 struct uhid_create_req {
@@ -50,6 +53,19 @@ struct uhid_create_req {
        __u32 country;
 } __attribute__((__packed__));
 
+struct uhid_create2_req {
+       __u8 name[128];
+       __u8 phys[64];
+       __u8 uniq[64];
+       __u16 rd_size;
+       __u16 bus;
+       __u32 vendor;
+       __u32 product;
+       __u32 version;
+       __u32 country;
+       __u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
 #define UHID_DATA_MAX 4096
 
 enum uhid_report_type {
@@ -63,6 +79,11 @@ struct uhid_input_req {
        __u16 size;
 } __attribute__((__packed__));
 
+struct uhid_input2_req {
+       __u16 size;
+       __u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
 struct uhid_output_req {
        __u8 data[UHID_DATA_MAX];
        __u16 size;
@@ -100,6 +121,8 @@ struct uhid_event {
                struct uhid_output_ev_req output_ev;
                struct uhid_feature_req feature;
                struct uhid_feature_answer_req feature_answer;
+               struct uhid_create2_req create2;
+               struct uhid_input2_req input2;
        } u;
 } __attribute__((__packed__));
 
index 77c4badb3e9d7bbc93a1d1ff35fce210ce23a200..8181ea4bc2f236f3755e515782bb7cff2b86ff6f 100644 (file)
@@ -382,18 +382,6 @@ static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
                                      data, count);
 }
 
-static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data,
-               size_t count, unsigned char report_type)
-{
-       if (report_type == HID_OUTPUT_REPORT) {
-               return hidp_output_report(hid, data, count);
-       } else if (report_type != HID_FEATURE_REPORT) {
-               return -EINVAL;
-       }
-
-       return hidp_set_raw_report(hid, data[0], data, count, report_type);
-}
-
 static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
                            __u8 *buf, size_t len, unsigned char rtype,
                            int reqtype)
@@ -427,6 +415,16 @@ static void hidp_del_timer(struct hidp_session *session)
                del_timer(&session->timer);
 }
 
+static void hidp_process_report(struct hidp_session *session,
+                               int type, const u8 *data, int len, int intr)
+{
+       if (len > HID_MAX_BUFFER_SIZE)
+               len = HID_MAX_BUFFER_SIZE;
+
+       memcpy(session->input_buf, data, len);
+       hid_input_report(session->hid, type, session->input_buf, len, intr);
+}
+
 static void hidp_process_handshake(struct hidp_session *session,
                                        unsigned char param)
 {
@@ -499,7 +497,8 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
                        hidp_input_report(session, skb);
 
                if (session->hid)
-                       hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
+                       hidp_process_report(session, HID_INPUT_REPORT,
+                                           skb->data, skb->len, 0);
                break;
 
        case HIDP_DATA_RTYPE_OTHER:
@@ -581,7 +580,8 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
                        hidp_input_report(session, skb);
 
                if (session->hid) {
-                       hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
+                       hidp_process_report(session, HID_INPUT_REPORT,
+                                           skb->data, skb->len, 1);
                        BT_DBG("report len %d", skb->len);
                }
        } else {
@@ -767,14 +767,15 @@ static int hidp_setup_hid(struct hidp_session *session,
        snprintf(hid->phys, sizeof(hid->phys), "%pMR",
                 &l2cap_pi(session->ctrl_sock->sk)->chan->src);
 
+       /* NOTE: Some device modules depend on the dst address being stored in
+        * uniq. Please be aware of this before making changes to this behavior.
+        */
        snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
                 &l2cap_pi(session->ctrl_sock->sk)->chan->dst);
 
        hid->dev.parent = &session->conn->hcon->dev;
        hid->ll_driver = &hidp_hid_driver;
 
-       hid->hid_output_raw_report = hidp_output_raw_report;
-
        /* True if device is blacklisted in drivers/hid/hid-core.c */
        if (hid_ignore(hid)) {
                hid_destroy_device(session->hid);
index ab5241400cf78a9d7371d7a866d58aeba0d8043a..8798492a6e9971fff198344b580fa5ec3f17b8be 100644 (file)
@@ -24,6 +24,7 @@
 #define __HIDP_H
 
 #include <linux/types.h>
+#include <linux/hid.h>
 #include <linux/kref.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/l2cap.h>
@@ -179,6 +180,9 @@ struct hidp_session {
 
        /* Used in hidp_output_raw_report() */
        int output_report_success; /* boolean */
+
+       /* temporary input buffer */
+       u8 input_buf[HID_MAX_BUFFER_SIZE];
 };
 
 /* HIDP init defines */