Browse Source

Merge branch 'for-3.15/hid-core-ll-transport-cleanup' into for-linus

Conflicts:
	drivers/hid/hid-ids.h
	drivers/hid/hid-sony.c
	drivers/hid/i2c-hid/i2c-hid.c
Jiri Kosina 11 years ago
parent
commit
ad295b6d57

+ 317 - 0
Documentation/hid/hid-transport.txt

@@ -0,0 +1,317 @@
+                          HID I/O Transport Drivers
+                         ===========================
+
+The HID subsystem is independent of the underlying transport driver. Initially,
+only USB was supported, but other specifications adopted the HID design and
+provided new transport drivers. The kernel includes at least support for USB,
+Bluetooth, I2C and user-space I/O drivers.
+
+1) HID Bus
+==========
+
+The HID subsystem is designed as a bus. Any I/O subsystem may provide HID
+devices and register them with the HID bus. HID core then loads generic device
+drivers on top of it. The transport drivers are responsible of raw data
+transport and device setup/management. HID core is responsible of
+report-parsing, report interpretation and the user-space API. Device specifics
+and quirks are handled by all layers depending on the quirk.
+
+ +-----------+  +-----------+            +-----------+  +-----------+
+ | Device #1 |  | Device #i |            | Device #j |  | Device #k |
+ +-----------+  +-----------+            +-----------+  +-----------+
+          \\      //                              \\      //
+        +------------+                          +------------+
+        | I/O Driver |                          | I/O Driver |
+        +------------+                          +------------+
+              ||                                      ||
+     +------------------+                    +------------------+
+     | Transport Driver |                    | Transport Driver |
+     +------------------+                    +------------------+
+                       \___                ___/
+                           \              /
+                          +----------------+
+                          |    HID Core    |
+                          +----------------+
+                           /  |        |  \
+                          /   |        |   \
+             ____________/    |        |    \_________________
+            /                 |        |                      \
+           /                  |        |                       \
+ +----------------+  +-----------+  +------------------+  +------------------+
+ | Generic Driver |  | MT Driver |  | Custom Driver #1 |  | Custom Driver #2 |
+ +----------------+  +-----------+  +------------------+  +------------------+
+
+Example Drivers:
+  I/O: USB, I2C, Bluetooth-l2cap
+  Transport: USB-HID, I2C-HID, BT-HIDP
+
+Everything below "HID Core" is simplified in this graph as it is only of
+interest to HID device drivers. Transport drivers do not need to know the
+specifics.
+
+1.1) Device Setup
+-----------------
+
+I/O drivers normally provide hotplug detection or device enumeration APIs to the
+transport drivers. Transport drivers use this to find any suitable HID device.
+They allocate HID device objects and register them with HID core. Transport
+drivers are not required to register themselves with HID core. HID core is never
+aware of which transport drivers are available and is not interested in it. It
+is only interested in devices.
+
+Transport drivers attach a constant "struct hid_ll_driver" object with each
+device. Once a device is registered with HID core, the callbacks provided via
+this struct are used by HID core to communicate with the device.
+
+Transport drivers are responsible of detecting device failures and unplugging.
+HID core will operate a device as long as it is registered regardless of any
+device failures. Once transport drivers detect unplug or failure events, they
+must unregister the device from HID core and HID core will stop using the
+provided callbacks.
+
+1.2) Transport Driver Requirements
+----------------------------------
+
+The terms "asynchronous" and "synchronous" in this document describe the
+transmission behavior regarding acknowledgements. An asynchronous channel must
+not perform any synchronous operations like waiting for acknowledgements or
+verifications. Generally, HID calls operating on asynchronous channels must be
+running in atomic-context just fine.
+On the other hand, synchronous channels can be implemented by the transport
+driver in whatever way they like. They might just be the same as asynchronous
+channels, but they can also provide acknowledgement reports, automatic
+retransmission on failure, etc. in a blocking manner. If such functionality is
+required on asynchronous channels, a transport-driver must implement that via
+its own worker threads.
+
+HID core requires transport drivers to follow a given design. A Transport
+driver must provide two bi-directional I/O channels to each HID device. These
+channels must not necessarily be bi-directional in the hardware itself. A
+transport driver might just provide 4 uni-directional channels. Or it might
+multiplex all four on a single physical channel. However, in this document we
+will describe them as two bi-directional channels as they have several
+properties in common.
+
+ - Interrupt Channel (intr): The intr channel is used for asynchronous data
+   reports. No management commands or data acknowledgements are sent on this
+   channel. Any unrequested incoming or outgoing data report must be sent on
+   this channel and is never acknowledged by the remote side. Devices usually
+   send their input events on this channel. Outgoing events are normally
+   not send via intr, except if high throughput is required.
+ - Control Channel (ctrl): The ctrl channel is used for synchronous requests and
+   device management. Unrequested data input events must not be sent on this
+   channel and are normally ignored. Instead, devices only send management
+   events or answers to host requests on this channel.
+   The control-channel is used for direct blocking queries to the device
+   independent of any events on the intr-channel.
+   Outgoing reports are usually sent on the ctrl channel via synchronous
+   SET_REPORT requests.
+
+Communication between devices and HID core is mostly done via HID reports. A
+report can be of one of three types:
+
+ - INPUT Report: Input reports provide data from device to host. This
+   data may include button events, axis events, battery status or more. This
+   data is generated by the device and sent to the host with or without
+   requiring explicit requests. Devices can choose to send data continuously or
+   only on change.
+ - OUTPUT Report: Output reports change device states. They are sent from host
+   to device and may include LED requests, rumble requests or more. Output
+   reports are never sent from device to host, but a host can retrieve their
+   current state.
+   Hosts may choose to send output reports either continuously or only on
+   change.
+ - FEATURE Report: Feature reports are used for specific static device features
+   and never reported spontaneously. A host can read and/or write them to access
+   data like battery-state or device-settings.
+   Feature reports are never sent without requests. A host must explicitly set
+   or retrieve a feature report. This also means, feature reports are never sent
+   on the intr channel as this channel is asynchronous.
+
+INPUT and OUTPUT reports can be sent as pure data reports on the intr channel.
+For INPUT reports this is the usual operational mode. But for OUTPUT reports,
+this is rarely done as OUTPUT reports are normally quite scarce. But devices are
+free to make excessive use of asynchronous OUTPUT reports (for instance, custom
+HID audio speakers make great use of it).
+
+Plain reports must not be sent on the ctrl channel, though. Instead, the ctrl
+channel provides synchronous GET/SET_REPORT requests. Plain reports are only
+allowed on the intr channel and are the only means of data there.
+
+ - GET_REPORT: A GET_REPORT request has a report ID as payload and is sent
+   from host to device. The device must answer with a data report for the
+   requested report ID on the ctrl channel as a synchronous acknowledgement.
+   Only one GET_REPORT request can be pending for each device. This restriction
+   is enforced by HID core as several transport drivers don't allow multiple
+   simultaneous GET_REPORT requests.
+   Note that data reports which are sent as answer to a GET_REPORT request are
+   not handled as generic device events. That is, if a device does not operate
+   in continuous data reporting mode, an answer to GET_REPORT does not replace
+   the raw data report on the intr channel on state change.
+   GET_REPORT is only used by custom HID device drivers to query device state.
+   Normally, HID core caches any device state so this request is not necessary
+   on devices that follow the HID specs except during device initialization to
+   retrieve the current state.
+   GET_REPORT requests can be sent for any of the 3 report types and shall
+   return the current report state of the device. However, OUTPUT reports as
+   payload may be blocked by the underlying transport driver if the
+   specification does not allow them.
+ - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is
+   sent from host to device and a device must update it's current report state
+   according to the given data. Any of the 3 report types can be used. However,
+   INPUT reports as payload might be blocked by the underlying transport driver
+   if the specification does not allow them.
+   A device must answer with a synchronous acknowledgement. However, HID core
+   does not require transport drivers to forward this acknowledgement to HID
+   core.
+   Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This
+   restriction is enforced by HID core as some transport drivers do not support
+   multiple synchronous SET_REPORT requests.
+
+Other ctrl-channel requests are supported by USB-HID but are not available
+(or deprecated) in most other transport level specifications:
+
+ - GET/SET_IDLE: Only used by USB-HID and I2C-HID.
+ - GET/SET_PROTOCOL: Not used by HID core.
+ - RESET: Used by I2C-HID, not hooked up in HID core.
+ - SET_POWER: Used by I2C-HID, not hooked up in HID core.
+
+2) HID API
+==========
+
+2.1) Initialization
+-------------------
+
+Transport drivers normally use the following procedure to register a new device
+with HID core:
+
+	struct hid_device *hid;
+	int ret;
+
+	hid = hid_allocate_device();
+	if (IS_ERR(hid)) {
+		ret = PTR_ERR(hid);
+		goto err_<...>;
+	}
+
+	strlcpy(hid->name, <device-name-src>, 127);
+	strlcpy(hid->phys, <device-phys-src>, 63);
+	strlcpy(hid->uniq, <device-uniq-src>, 63);
+
+	hid->ll_driver = &custom_ll_driver;
+	hid->bus = <device-bus>;
+	hid->vendor = <device-vendor>;
+	hid->product = <device-product>;
+	hid->version = <device-version>;
+	hid->country = <device-country>;
+	hid->dev.parent = <pointer-to-parent-device>;
+	hid->driver_data = <transport-driver-data-field>;
+
+	ret = hid_add_device(hid);
+	if (ret)
+		goto err_<...>;
+
+Once hid_add_device() is entered, HID core might use the callbacks provided in
+"custom_ll_driver". Note that fields like "country" can be ignored by underlying
+transport-drivers if not supported.
+
+To unregister a device, use:
+
+	hid_destroy_device(hid);
+
+Once hid_destroy_device() returns, HID core will no longer make use of any
+driver callbacks.
+
+2.2) hid_ll_driver operations
+-----------------------------
+
+The available HID callbacks are:
+ - int (*start) (struct hid_device *hdev)
+   Called from HID device drivers once they want to use the device. Transport
+   drivers can choose to setup their device in this callback. However, normally
+   devices are already set up before transport drivers register them to HID core
+   so this is mostly only used by USB-HID.
+
+ - void (*stop) (struct hid_device *hdev)
+   Called from HID device drivers once they are done with a device. Transport
+   drivers can free any buffers and deinitialize the device. But note that
+   ->start() might be called again if another HID device driver is loaded on the
+   device.
+   Transport drivers are free to ignore it and deinitialize devices after they
+   destroyed them via hid_destroy_device().
+
+ - int (*open) (struct hid_device *hdev)
+   Called from HID device drivers once they are interested in data reports.
+   Usually, while user-space didn't open any input API/etc., device drivers are
+   not interested in device data and transport drivers can put devices asleep.
+   However, once ->open() is called, transport drivers must be ready for I/O.
+   ->open() calls are nested for each client that opens the HID device.
+
+ - void (*close) (struct hid_device *hdev)
+   Called from HID device drivers after ->open() was called but they are no
+   longer interested in device reports. (Usually if user-space closed any input
+   devices of the driver).
+   Transport drivers can put devices asleep and terminate any I/O of all
+   ->open() calls have been followed by a ->close() call. However, ->start() may
+   be called again if the device driver is interested in input reports again.
+
+ - int (*parse) (struct hid_device *hdev)
+   Called once during device setup after ->start() has been called. Transport
+   drivers must read the HID report-descriptor from the device and tell HID core
+   about it via hid_parse_report().
+
+ - int (*power) (struct hid_device *hdev, int level)
+   Called by HID core to give PM hints to transport drivers. Usually this is
+   analogical to the ->open() and ->close() hints and redundant.
+
+ - void (*request) (struct hid_device *hdev, struct hid_report *report,
+                    int reqtype)
+   Send an HID request on the ctrl channel. "report" contains the report that
+   should be sent and "reqtype" the request type. Request-type can be
+   HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
+   This callback is optional. If not provided, HID core will assemble a raw
+   report following the HID specs and send it via the ->raw_request() callback.
+   The transport driver is free to implement this asynchronously.
+
+ - int (*wait) (struct hid_device *hdev)
+   Used by HID core before calling ->request() again. A transport driver can use
+   it to wait for any pending requests to complete if only one request is
+   allowed at a time.
+
+ - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+                       __u8 *buf, size_t count, unsigned char rtype,
+                       int reqtype)
+   Same as ->request() but provides the report as raw buffer. This request shall
+   be synchronous. A transport driver must not use ->wait() to complete such
+   requests. This request is mandatory and hid core will reject the device if
+   it is missing.
+
+ - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+   Send raw output report via intr channel. Used by some HID device drivers
+   which require high throughput for outgoing requests on the intr channel. This
+   must not cause SET_REPORT calls! This must be implemented as asynchronous
+   output report on the intr channel!
+
+ - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+   Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
+
+2.3) Data Path
+--------------
+
+Transport drivers are responsible of reading data from I/O devices. They must
+handle any I/O-related state-tracking themselves. HID core does not implement
+protocol handshakes or other management commands which can be required by the
+given HID transport specification.
+
+Every raw data packet read from a device must be fed into HID core via
+hid_input_report(). You must specify the channel-type (intr or ctrl) and report
+type (input/output/feature). Under normal conditions, only input reports are
+provided via this API.
+
+Responses to GET_REPORT requests via ->request() must also be provided via this
+API. Responses to ->raw_request() are synchronous and must be intercepted by the
+transport driver and not passed to hid_input_report().
+Acknowledgements to SET_REPORT requests are not of interest to HID core.
+
+----------------------------------------------------
+Written 2013, David Herrmann <dh.herrmann@gmail.com>

+ 15 - 4
drivers/hid/Kconfig

@@ -175,6 +175,15 @@ config HID_PRODIKEYS
 	  multimedia keyboard, but will lack support for the musical keyboard
 	  multimedia keyboard, but will lack support for the musical keyboard
 	  and some additional multimedia keys.
 	  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
 config HID_CYPRESS
 	tristate "Cypress mouse and barcode readers" if EXPERT
 	tristate "Cypress mouse and barcode readers" if EXPERT
 	depends on HID
 	depends on HID
@@ -608,25 +617,27 @@ config HID_SAMSUNG
 	Support for Samsung InfraRed remote control or keyboards.
 	Support for Samsung InfraRed remote control or keyboards.
 
 
 config HID_SONY
 config HID_SONY
-	tristate "Sony PS2/3 accessories"
+	tristate "Sony PS2/3/4 accessories"
 	depends on USB_HID
 	depends on USB_HID
 	depends on NEW_LEDS
 	depends on NEW_LEDS
 	depends on LEDS_CLASS
 	depends on LEDS_CLASS
+	select POWER_SUPPLY
 	---help---
 	---help---
 	Support for
 	Support for
 
 
 	  * Sony PS3 6-axis controllers
 	  * Sony PS3 6-axis controllers
+	  * Sony PS4 DualShock 4 controllers
 	  * Buzz controllers
 	  * Buzz controllers
 	  * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
 	  * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
 	  * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
 	  * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
 
 
 config SONY_FF
 config SONY_FF
-	bool "Sony PS2/3 accessories force feedback support"
+	bool "Sony PS2/3/4 accessories force feedback support" 
 	depends on HID_SONY
 	depends on HID_SONY
 	select INPUT_FF_MEMLESS
 	select INPUT_FF_MEMLESS
 	---help---
 	---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
 config HID_SPEEDLINK
 	tristate "Speedlink VAD Cezanne mouse support"
 	tristate "Speedlink VAD Cezanne mouse support"

+ 1 - 0
drivers/hid/Makefile

@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_AUREAL)        += hid-aureal.o
 obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o
 obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o
 obj-$(CONFIG_HID_CHERRY)	+= hid-cherry.o
 obj-$(CONFIG_HID_CHERRY)	+= hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)	+= hid-chicony.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_CYPRESS)	+= hid-cypress.o
 obj-$(CONFIG_HID_DRAGONRISE)	+= hid-dr.o
 obj-$(CONFIG_HID_DRAGONRISE)	+= hid-dr.o
 obj-$(CONFIG_HID_EMS_FF)	+= hid-emsff.o
 obj-$(CONFIG_HID_EMS_FF)	+= hid-emsff.o

+ 50 - 1
drivers/hid/hid-core.c

@@ -1248,6 +1248,11 @@ void hid_output_report(struct hid_report *report, __u8 *data)
 }
 }
 EXPORT_SYMBOL_GPL(hid_output_report);
 EXPORT_SYMBOL_GPL(hid_output_report);
 
 
+static int hid_report_len(struct hid_report *report)
+{
+	return ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7;
+}
+
 /*
 /*
  * Allocator for buffer that is going to be passed to hid_output_report()
  * Allocator for buffer that is going to be passed to hid_output_report()
  */
  */
@@ -1258,7 +1263,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)
 	 * of implement() working on 8 byte chunks
 	 * of implement() working on 8 byte chunks
 	 */
 	 */
 
 
-	int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7;
+	int len = hid_report_len(report);
 
 
 	return kmalloc(len, flags);
 	return kmalloc(len, flags);
 }
 }
@@ -1314,6 +1319,41 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
 	return report;
 	return report;
 }
 }
 
 
+/*
+ * Implement a generic .request() callback, using .raw_request()
+ * DO NOT USE in hid drivers directly, but through hid_hw_request instead.
+ */
+void __hid_request(struct hid_device *hid, struct hid_report *report,
+		int reqtype)
+{
+	char *buf;
+	int ret;
+	int len;
+
+	buf = hid_alloc_report_buf(report, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	len = hid_report_len(report);
+
+	if (reqtype == HID_REQ_SET_REPORT)
+		hid_output_report(report, buf);
+
+	ret = hid->ll_driver->raw_request(hid, report->id, buf, len,
+					  report->type, reqtype);
+	if (ret < 0) {
+		dbg_hid("unable to complete request: %d\n", ret);
+		goto out;
+	}
+
+	if (reqtype == HID_REQ_GET_REPORT)
+		hid_input_report(hid, report->type, buf, ret, 0);
+
+out:
+	kfree(buf);
+}
+EXPORT_SYMBOL_GPL(__hid_request);
+
 int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
 int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
 		int interrupt)
 		int interrupt)
 {
 {
@@ -1693,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_WIRELESS2) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },
 	{ 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_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_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_2) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
@@ -2431,6 +2472,14 @@ int hid_add_device(struct hid_device *hdev)
 	if (hid_ignore(hdev))
 	if (hid_ignore(hdev))
 		return -ENODEV;
 		return -ENODEV;
 
 
+	/*
+	 * Check for the mandatory transport channel.
+	 */
+	 if (!hdev->ll_driver->raw_request) {
+		hid_err(hdev, "transport driver missing .raw_request()\n");
+		return -EINVAL;
+	 }
+
 	/*
 	/*
 	 * Read the device report descriptor once and use as template
 	 * Read the device report descriptor once and use as template
 	 * for the driver-specific modifications.
 	 * for the driver-specific modifications.

+ 1073 - 0
drivers/hid/hid-cp2112.c

@@ -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");
+

+ 10 - 0
drivers/hid/hid-hyperv.c

@@ -460,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 = {
 static struct hid_ll_driver mousevsc_ll_driver = {
 	.parse = mousevsc_hid_parse,
 	.parse = mousevsc_hid_parse,
 	.open = mousevsc_hid_open,
 	.open = mousevsc_hid_open,
 	.close = mousevsc_hid_close,
 	.close = mousevsc_hid_close,
 	.start = mousevsc_hid_start,
 	.start = mousevsc_hid_start,
 	.stop = mousevsc_hid_stop,
 	.stop = mousevsc_hid_stop,
+	.raw_request = mousevsc_hid_raw_request,
 };
 };
 
 
 static struct hid_driver mousevsc_hid_driver;
 static struct hid_driver mousevsc_hid_driver;

+ 1 - 0
drivers/hid/hid-ids.h

@@ -242,6 +242,7 @@
 #define USB_VENDOR_ID_CYGNAL		0x10c4
 #define USB_VENDOR_ID_CYGNAL		0x10c4
 #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X	0x818a
 #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X	0x818a
 #define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH	0x81b9
 #define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH	0x81b9
+#define USB_DEVICE_ID_CYGNAL_CP2112	0xea90
 
 
 #define USB_VENDOR_ID_CYPRESS		0x04b4
 #define USB_VENDOR_ID_CYPRESS		0x04b4
 #define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001
 #define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001

+ 9 - 9
drivers/hid/hid-input.c

@@ -350,9 +350,9 @@ static int hidinput_get_battery_property(struct power_supply *psy,
 			ret = -ENOMEM;
 			ret = -ENOMEM;
 			break;
 			break;
 		}
 		}
-		ret = dev->hid_get_raw_report(dev, dev->battery_report_id,
-					      buf, 2,
-					      dev->battery_report_type);
+		ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
+					 dev->battery_report_type,
+					 HID_REQ_GET_REPORT);
 
 
 		if (ret != 2) {
 		if (ret != 2) {
 			ret = -ENODATA;
 			ret = -ENODATA;
@@ -1150,7 +1150,7 @@ static void hidinput_led_worker(struct work_struct *work)
 					      led_work);
 					      led_work);
 	struct hid_field *field;
 	struct hid_field *field;
 	struct hid_report *report;
 	struct hid_report *report;
-	int len;
+	int len, ret;
 	__u8 *buf;
 	__u8 *buf;
 
 
 	field = hidinput_get_led_field(hid);
 	field = hidinput_get_led_field(hid);
@@ -1184,7 +1184,10 @@ static void hidinput_led_worker(struct work_struct *work)
 
 
 	hid_output_report(report, buf);
 	hid_output_report(report, buf);
 	/* synchronous output report */
 	/* synchronous output report */
-	hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT);
+	ret = hid_hw_output_report(hid, buf, len);
+	if (ret == -ENOSYS)
+		hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT,
+				HID_REQ_SET_REPORT);
 	kfree(buf);
 	kfree(buf);
 }
 }
 
 
@@ -1263,10 +1266,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
 	}
 	}
 
 
 	input_set_drvdata(input_dev, hid);
 	input_set_drvdata(input_dev, hid);
-	if (hid->ll_driver->hidinput_input_event)
-		input_dev->event = hid->ll_driver->hidinput_input_event;
-	else if (hid->ll_driver->request || hid->hid_output_raw_report)
-		input_dev->event = hidinput_input_event;
+	input_dev->event = hidinput_input_event;
 	input_dev->open = hidinput_open;
 	input_dev->open = hidinput_open;
 	input_dev->close = hidinput_close;
 	input_dev->close = hidinput_close;
 	input_dev->setkeycode = hidinput_setkeycode;
 	input_dev->setkeycode = hidinput_setkeycode;

+ 4 - 2
drivers/hid/hid-lg.c

@@ -692,7 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
 	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
 		unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 		unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
 
-		ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
+					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 
 
 		if (ret >= 0) {
 		if (ret >= 0) {
 			/* insert a little delay of 10 jiffies ~ 40ms */
 			/* insert a little delay of 10 jiffies ~ 40ms */
@@ -704,7 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
 			buf[1] = 0xB2;
 			buf[1] = 0xB2;
 			get_random_bytes(&buf[2], 2);
 			get_random_bytes(&buf[2], 2);
 
 
-			ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
+					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 		}
 		}
 	}
 	}
 
 

+ 40 - 71
drivers/hid/hid-logitech-dj.c

@@ -44,14 +44,6 @@ static const char kbd_descriptor[] = {
 	0x19, 0xE0,		/*   USAGE_MINIMUM (Left Control)   */
 	0x19, 0xE0,		/*   USAGE_MINIMUM (Left Control)   */
 	0x29, 0xE7,		/*   USAGE_MAXIMUM (Right GUI)      */
 	0x29, 0xE7,		/*   USAGE_MAXIMUM (Right GUI)      */
 	0x81, 0x02,		/*   INPUT (Data,Var,Abs)       */
 	0x81, 0x02,		/*   INPUT (Data,Var,Abs)       */
-	0x95, 0x05,		/*   REPORT COUNT (5)           */
-	0x05, 0x08,		/*   USAGE PAGE (LED page)      */
-	0x19, 0x01,		/*   USAGE MINIMUM (1)          */
-	0x29, 0x05,		/*   USAGE MAXIMUM (5)          */
-	0x91, 0x02,		/*   OUTPUT (Data, Variable, Absolute)  */
-	0x95, 0x01,		/*   REPORT COUNT (1)           */
-	0x75, 0x03,		/*   REPORT SIZE (3)            */
-	0x91, 0x01,		/*   OUTPUT (Constant)          */
 	0x95, 0x06,		/*   REPORT_COUNT (6)           */
 	0x95, 0x06,		/*   REPORT_COUNT (6)           */
 	0x75, 0x08,		/*   REPORT_SIZE (8)            */
 	0x75, 0x08,		/*   REPORT_SIZE (8)            */
 	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */
 	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */
@@ -60,6 +52,18 @@ static const char kbd_descriptor[] = {
 	0x19, 0x00,		/*   USAGE_MINIMUM (no event)       */
 	0x19, 0x00,		/*   USAGE_MINIMUM (no event)       */
 	0x2A, 0xFF, 0x00,	/*   USAGE_MAXIMUM (reserved)       */
 	0x2A, 0xFF, 0x00,	/*   USAGE_MAXIMUM (reserved)       */
 	0x81, 0x00,		/*   INPUT (Data,Ary,Abs)       */
 	0x81, 0x00,		/*   INPUT (Data,Ary,Abs)       */
+	0x85, 0x0e,		/* REPORT_ID (14)               */
+	0x05, 0x08,		/*   USAGE PAGE (LED page)      */
+	0x95, 0x05,		/*   REPORT COUNT (5)           */
+	0x75, 0x01,		/*   REPORT SIZE (1)            */
+	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */
+	0x25, 0x01,		/*   LOGICAL_MAXIMUM (1)        */
+	0x19, 0x01,		/*   USAGE MINIMUM (1)          */
+	0x29, 0x05,		/*   USAGE MAXIMUM (5)          */
+	0x91, 0x02,		/*   OUTPUT (Data, Variable, Absolute)  */
+	0x95, 0x01,		/*   REPORT COUNT (1)           */
+	0x75, 0x03,		/*   REPORT SIZE (3)            */
+	0x91, 0x01,		/*   OUTPUT (Constant)          */
 	0xC0
 	0xC0
 };
 };
 
 
@@ -189,9 +193,6 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
 
 
 static struct hid_ll_driver logi_dj_ll_driver;
 static struct hid_ll_driver logi_dj_ll_driver;
 
 
-static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
-					size_t count,
-					unsigned char report_type);
 static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
 static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
 
 
 static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
 static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
@@ -258,7 +259,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
 	}
 	}
 
 
 	dj_hiddev->ll_driver = &logi_dj_ll_driver;
 	dj_hiddev->ll_driver = &logi_dj_ll_driver;
-	dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report;
 
 
 	dj_hiddev->dev.parent = &djrcv_hdev->dev;
 	dj_hiddev->dev.parent = &djrcv_hdev->dev;
 	dj_hiddev->bus = BUS_USB;
 	dj_hiddev->bus = BUS_USB;
@@ -540,14 +540,35 @@ static void logi_dj_ll_close(struct hid_device *hid)
 	dbg_hid("%s:%s\n", __func__, hid->phys);
 	dbg_hid("%s:%s\n", __func__, hid->phys);
 }
 }
 
 
-static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
-					size_t count,
-					unsigned char report_type)
+static int logi_dj_ll_raw_request(struct hid_device *hid,
+				  unsigned char reportnum, __u8 *buf,
+				  size_t count, unsigned char report_type,
+				  int reqtype)
 {
 {
-	/* Called by hid raw to send data */
-	dbg_hid("%s\n", __func__);
+	struct dj_device *djdev = hid->driver_data;
+	struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev;
+	u8 *out_buf;
+	int ret;
 
 
-	return 0;
+	if (buf[0] != REPORT_TYPE_LEDS)
+		return -EINVAL;
+
+	out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC);
+	if (!out_buf)
+		return -ENOMEM;
+
+	if (count < DJREPORT_SHORT_LENGTH - 2)
+		count = DJREPORT_SHORT_LENGTH - 2;
+
+	out_buf[0] = REPORT_ID_DJ_SHORT;
+	out_buf[1] = djdev->device_index;
+	memcpy(out_buf + 2, buf, count);
+
+	ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf,
+		DJREPORT_SHORT_LENGTH, report_type, reqtype);
+
+	kfree(out_buf);
+	return ret;
 }
 }
 
 
 static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size)
 static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size)
@@ -613,58 +634,6 @@ static int logi_dj_ll_parse(struct hid_device *hid)
 	return retval;
 	return retval;
 }
 }
 
 
-static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,
-				  unsigned int code, int value)
-{
-	/* Sent by the input layer to handle leds and Force Feedback */
-	struct hid_device *dj_hiddev = input_get_drvdata(dev);
-	struct dj_device *dj_dev = dj_hiddev->driver_data;
-
-	struct dj_receiver_dev *djrcv_dev =
-	    dev_get_drvdata(dj_hiddev->dev.parent);
-	struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev;
-	struct hid_report_enum *output_report_enum;
-
-	struct hid_field *field;
-	struct hid_report *report;
-	unsigned char *data;
-	int offset;
-
-	dbg_hid("%s: %s, type:%d | code:%d | value:%d\n",
-		__func__, dev->phys, type, code, value);
-
-	if (type != EV_LED)
-		return -1;
-
-	offset = hidinput_find_field(dj_hiddev, type, code, &field);
-
-	if (offset == -1) {
-		dev_warn(&dev->dev, "event field not found\n");
-		return -1;
-	}
-	hid_set_field(field, offset, value);
-
-	data = hid_alloc_report_buf(field->report, GFP_ATOMIC);
-	if (!data) {
-		dev_warn(&dev->dev, "failed to allocate report buf memory\n");
-		return -1;
-	}
-
-	hid_output_report(field->report, &data[0]);
-
-	output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT];
-	report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
-	hid_set_field(report->field[0], 0, dj_dev->device_index);
-	hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS);
-	hid_set_field(report->field[0], 2, data[1]);
-
-	hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT);
-
-	kfree(data);
-
-	return 0;
-}
-
 static int logi_dj_ll_start(struct hid_device *hid)
 static int logi_dj_ll_start(struct hid_device *hid)
 {
 {
 	dbg_hid("%s\n", __func__);
 	dbg_hid("%s\n", __func__);
@@ -683,7 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {
 	.stop = logi_dj_ll_stop,
 	.stop = logi_dj_ll_stop,
 	.open = logi_dj_ll_open,
 	.open = logi_dj_ll_open,
 	.close = logi_dj_ll_close,
 	.close = logi_dj_ll_close,
-	.hidinput_input_event = logi_dj_ll_input_event,
+	.raw_request = logi_dj_ll_raw_request,
 };
 };
 
 
 
 

+ 2 - 2
drivers/hid/hid-magicmouse.c

@@ -538,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev,
 	 * but there seems to be no other way of switching the mode.
 	 * but there seems to be no other way of switching the mode.
 	 * Thus the super-ugly hacky success check below.
 	 * Thus the super-ugly hacky success check below.
 	 */
 	 */
-	ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature),
-			HID_FEATURE_REPORT);
+	ret = hid_hw_raw_request(hdev, feature[0], feature, sizeof(feature),
+				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 	if (ret != -EIO && ret != sizeof(feature)) {
 	if (ret != -EIO && ret != sizeof(feature)) {
 		hid_err(hdev, "unable to request touch data (%d)\n", ret);
 		hid_err(hdev, "unable to request touch data (%d)\n", ret);
 		goto err_stop_hw;
 		goto err_stop_hw;

+ 704 - 96
drivers/hid/hid-sony.c

@@ -17,7 +17,8 @@
  * any later version.
  * 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
  * 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.
  * for about 7 seconds with the Bluetooth Host Controller in discovering mode.
  *
  *
@@ -28,8 +29,11 @@
 #include <linux/hid.h>
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
-#include <linux/usb.h>
 #include <linux/leds.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"
 #include "hid-ids.h"
 
 
@@ -41,8 +45,13 @@
 #define DUALSHOCK4_CONTROLLER_USB BIT(5)
 #define DUALSHOCK4_CONTROLLER_USB BIT(5)
 #define DUALSHOCK4_CONTROLLER_BT  BIT(6)
 #define DUALSHOCK4_CONTROLLER_BT  BIT(6)
 
 
-#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB)
-#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER_USB | 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
 #define MAX_LEDS 4
 
 
@@ -74,7 +83,8 @@ static const u8 sixaxis_rdesc_fixup2[] = {
 	0xb1, 0x02, 0xc0, 0xc0,
 	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
  * or orientation sensors.  This fixed descriptor maps the accelerometers
  * to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors
  * to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors
  * to usage values 0x43, 0x44 and 0x45.
  * to usage values 0x43, 0x44 and 0x45.
@@ -333,6 +343,217 @@ static u8 dualshock4_usb_rdesc[] = {
 	0xC0                /*  End Collection                      */
 	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[] = {
 static __u8 ps3remote_rdesc[] = {
 	0x05, 0x01,          /* GUsagePage Generic Desktop */
 	0x05, 0x01,          /* GUsagePage Generic Desktop */
 	0x09, 0x05,          /* LUsage 0x05 [Game Pad] */
 	0x09, 0x05,          /* LUsage 0x05 [Game Pad] */
@@ -450,7 +671,8 @@ static const unsigned int ps3remote_keymap_remote_buttons[] = {
 };
 };
 
 
 static const unsigned int buzz_keymap[] = {
 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.
 	 * buttons.
 	 * 
 	 * 
 	 * We use the mapping chosen by the controller, which is:
 	 * We use the mapping chosen by the controller, which is:
@@ -488,19 +710,35 @@ static const unsigned int buzz_keymap[] = {
 	[20] = BTN_TRIGGER_HAPPY20,
 	[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 {
 struct sony_sc {
+	spinlock_t lock;
+	struct list_head list_node;
 	struct hid_device *hdev;
 	struct hid_device *hdev;
 	struct led_classdev *leds[MAX_LEDS];
 	struct led_classdev *leds[MAX_LEDS];
-	struct hid_report *output_report;
 	unsigned long quirks;
 	unsigned long quirks;
 	struct work_struct state_worker;
 	struct work_struct state_worker;
+	struct power_supply battery;
 
 
 #ifdef CONFIG_SONY_FF
 #ifdef CONFIG_SONY_FF
 	__u8 left;
 	__u8 left;
 	__u8 right;
 	__u8 right;
 #endif
 #endif
 
 
+	__u8 mac_address[6];
 	__u8 worker_initialized;
 	__u8 worker_initialized;
+	__u8 cable_state;
+	__u8 battery_charging;
+	__u8 battery_capacity;
 	__u8 led_state[MAX_LEDS];
 	__u8 led_state[MAX_LEDS];
 	__u8 led_count;
 	__u8 led_count;
 };
 };
@@ -578,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");
 		hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n");
 		rdesc = dualshock4_usb_rdesc;
 		rdesc = dualshock4_usb_rdesc;
 		*rsize = sizeof(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 */
 	/* The HID descriptor exposed over BT has a trailing zero byte */
@@ -601,20 +843,127 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 	return 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] >> 4) & 0x01);
+
+	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,
 static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		__u8 *rd, int size)
 		__u8 *rd, int size)
 {
 {
 	struct sony_sc *sc = hid_get_drvdata(hdev);
 	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
 	 * 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[41], rd[42]);
 		swap(rd[43], rd[44]);
 		swap(rd[43], rd[44]);
 		swap(rd[45], rd[46]);
 		swap(rd[45], rd[46]);
 		swap(rd[47], rd[48]);
 		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;
 	return 0;
@@ -656,45 +1005,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
 	return 0;
 	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
  * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
  * to "operational".  Without this, the ps3 controller will not report any
  * to "operational".  Without this, the ps3 controller will not report any
@@ -708,7 +1018,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 	if (!buf)
 	if (!buf)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	ret = hdev->hid_get_raw_report(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT);
+	ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT,
+				 HID_REQ_GET_REPORT);
 
 
 	if (ret < 0)
 	if (ret < 0)
 		hid_err(hdev, "can't set operational mode\n");
 		hid_err(hdev, "can't set operational mode\n");
@@ -721,7 +1032,20 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 static int sixaxis_set_operational_bt(struct hid_device *hdev)
 static int sixaxis_set_operational_bt(struct hid_device *hdev)
 {
 {
 	unsigned char buf[] = { 0xf4,  0x42, 0x03, 0x00, 0x00 };
 	unsigned char buf[] = { 0xf4,  0x42, 0x03, 0x00, 0x00 };
-	return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+	return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
+				  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)
 static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
@@ -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) {
 	if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
 		buzz_set_leds(hdev, leds);
 		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++)
 		for (n = 0; n < count; n++)
 			drv_data->led_state[n] = leds[n];
 			drv_data->led_state[n] = leds[n];
 		schedule_work(&drv_data->state_worker);
 		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;
 	struct sony_sc *drv_data;
 
 
 	int n;
 	int n;
-	int on = 0;
 
 
 	drv_data = hid_get_drvdata(hdev);
 	drv_data = hid_get_drvdata(hdev);
 	if (!drv_data) {
 	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++) {
 	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)
 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. */
 		/* Validate expected report characteristics. */
 		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
 		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
 			return -ENODEV;
 			return -ENODEV;
-	} else if (drv_data->quirks & DUALSHOCK4_CONTROLLER_USB) {
+	} else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
 		drv_data->led_count = 3;
 		drv_data->led_count = 3;
 		max_brightness = 255;
 		max_brightness = 255;
 		use_colors = 1;
 		use_colors = 1;
@@ -871,9 +1191,11 @@ static int sony_leds_init(struct hid_device *hdev)
 		name_fmt = "%s::sony%d";
 		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
 	 * 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);
 	sony_set_leds(hdev, initial_values, drv_data->led_count);
 
 
 	name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
 	name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
@@ -943,29 +1265,45 @@ static void sixaxis_state_worker(struct work_struct *work)
 	buf[10] |= sc->led_state[2] << 3;
 	buf[10] |= sc->led_state[2] << 3;
 	buf[10] |= sc->led_state[3] << 4;
 	buf[10] |= sc->led_state[3] << 4;
 
 
-	sc->hdev->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)
 static void dualshock4_state_worker(struct work_struct *work)
 {
 {
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
 	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
 	struct hid_device *hdev = sc->hdev;
 	struct hid_device *hdev = sc->hdev;
-	struct hid_report *report = sc->output_report;
-	__s32 *value = report->field[0]->value;
+	int offset;
+
+	__u8 buf[78] = { 0 };
 
 
-	value[0] = 0x03;
+	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
 #ifdef CONFIG_SONY_FF
-	value[3] = sc->right;
-	value[4] = sc->left;
+	buf[offset++] = sc->right;
+	buf[offset++] = sc->left;
+#else
+	offset += 2;
 #endif
 #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
 #ifdef CONFIG_SONY_FF
@@ -1000,35 +1338,247 @@ static int sony_init_ff(struct hid_device *hdev)
 {
 {
 	return 0;
 	return 0;
 }
 }
+
 #endif
 #endif
 
 
-static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size)
+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)
 {
 {
-	struct list_head *head, *list;
-	struct hid_report *report;
+	static atomic_t power_id_seq = ATOMIC_INIT(0);
+	unsigned long power_id;
 	struct hid_device *hdev = sc->hdev;
 	struct hid_device *hdev = sc->hdev;
+	int ret;
 
 
-	list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+	/*
+	 * 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;
 
 
-	list_for_each(head, list) {
-		report = list_entry(head, struct hid_report, list);
+	ret = power_supply_register(&hdev->dev, &sc->battery);
+	if (ret) {
+		hid_err(hdev, "Unable to register battery device\n");
+		goto err_free;
+	}
 
 
-		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;
-			return 0;
+	power_supply_powers(&sc->battery, &hdev->dev);
+	return 0;
+
+err_free:
+	kfree(sc->battery.name);
+	sc->battery.name = NULL;
+	return ret;
+}
+
+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;
+}
+
+/*
+ * 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;
 		}
 		}
 	}
 	}
 
 
-	hid_err(hdev, "Unable to locate output report 0x%02x\n", req_id);
+	ret = 0;
+	list_add(&(sc->list_node), &sony_device_list);
+
+unlock:
+	spin_unlock_irqrestore(&sony_dev_list_lock, flags);
+	return ret;
+}
+
+static void sony_remove_dev_list(struct sony_sc *sc)
+{
+	unsigned long flags;
 
 
-	return -EINVAL;
+	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;
+
+	/* HIDP stores the device MAC address as a string in the uniq field. */
+	ret = strlen(sc->hdev->uniq);
+	if (ret != 17)
+		return -EINVAL;
+
+	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;
+		}
+
+		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;
+		}
+
+		/*
+		 * 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)
 static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 {
 	int ret;
 	int ret;
@@ -1066,17 +1616,38 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 	}
 
 
 	if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
 	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);
 		ret = sixaxis_set_operational_usb(hdev);
-
 		sc->worker_initialized = 1;
 		sc->worker_initialized = 1;
 		INIT_WORK(&sc->state_worker, sixaxis_state_worker);
 		INIT_WORK(&sc->state_worker, sixaxis_state_worker);
-	}
-	else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
+	} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
 		ret = sixaxis_set_operational_bt(hdev);
 		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) {
+			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)
 		if (ret < 0)
 			goto err_stop;
 			goto err_stop;
 
 
@@ -1086,6 +1657,10 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		ret = 0;
 		ret = 0;
 	}
 	}
 
 
+	if (ret < 0)
+		goto err_stop;
+
+	ret = sony_check_add(sc);
 	if (ret < 0)
 	if (ret < 0)
 		goto err_stop;
 		goto err_stop;
 
 
@@ -1099,12 +1674,36 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		ret = sony_init_ff(hdev);
 		ret = sony_init_ff(hdev);
 		if (ret < 0)
 		if (ret < 0)
 			goto err_stop;
 			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;
 	return 0;
+err_close:
+	hid_hw_close(hdev);
 err_stop:
 err_stop:
 	if (sc->quirks & SONY_LED_SUPPORT)
 	if (sc->quirks & SONY_LED_SUPPORT)
 		sony_leds_remove(hdev);
 		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);
 	hid_hw_stop(hdev);
 	return ret;
 	return ret;
 }
 }
@@ -1118,6 +1717,15 @@ static void sony_remove(struct hid_device *hdev)
 
 
 	if (sc->worker_initialized)
 	if (sc->worker_initialized)
 		cancel_work_sync(&sc->state_worker);
 		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);
 	hid_hw_stop(hdev);
 }
 }

+ 2 - 2
drivers/hid/hid-thingm.c

@@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data,
 			buf[0], buf[1], buf[2], buf[3], buf[4],
 			buf[0], buf[1], buf[2], buf[3], buf[4],
 			buf[5], buf[6], buf[7], buf[8]);
 			buf[5], buf[6], buf[7], buf[8]);
 
 
-	ret = data->hdev->hid_output_raw_report(data->hdev, buf,
-			BLINK1_CMD_SIZE, HID_FEATURE_REPORT);
+	ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE,
+				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 
 
 	return ret < 0 ? ret : 0;
 	return ret < 0 ? ret : 0;
 }
 }

+ 15 - 13
drivers/hid/hid-wacom.c

@@ -128,8 +128,8 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,
 
 
 	rep_data[0] = WAC_CMD_ICON_START_STOP;
 	rep_data[0] = WAC_CMD_ICON_START_STOP;
 	rep_data[1] = 0;
 	rep_data[1] = 0;
-	ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
-				HID_FEATURE_REPORT);
+	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 	if (ret < 0)
 	if (ret < 0)
 		goto err;
 		goto err;
 
 
@@ -143,15 +143,15 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,
 			rep_data[j + 3] = p[(i << 6) + j];
 			rep_data[j + 3] = p[(i << 6) + j];
 
 
 		rep_data[2] = i;
 		rep_data[2] = i;
-		ret = hdev->hid_output_raw_report(hdev, rep_data, 67,
-					HID_FEATURE_REPORT);
+		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67,
+					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 	}
 	}
 
 
 	rep_data[0] = WAC_CMD_ICON_START_STOP;
 	rep_data[0] = WAC_CMD_ICON_START_STOP;
 	rep_data[1] = 0;
 	rep_data[1] = 0;
 
 
-	ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
-				HID_FEATURE_REPORT);
+	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 
 
 err:
 err:
 	return;
 	return;
@@ -183,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
 		buf[3] = value;
 		buf[3] = value;
 		/* use fixed brightness for OLEDs */
 		/* use fixed brightness for OLEDs */
 		buf[4] = 0x08;
 		buf[4] = 0x08;
-		hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT);
+		hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT,
+				   HID_REQ_SET_REPORT);
 		kfree(buf);
 		kfree(buf);
 	}
 	}
 
 
@@ -339,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
 		rep_data[0] = 0x03 ; rep_data[1] = 0x00;
 		rep_data[0] = 0x03 ; rep_data[1] = 0x00;
 		limit = 3;
 		limit = 3;
 		do {
 		do {
-			ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
-					HID_FEATURE_REPORT);
+			ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 		} while (ret < 0 && limit-- > 0);
 		} while (ret < 0 && limit-- > 0);
 
 
 		if (ret >= 0) {
 		if (ret >= 0) {
@@ -352,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
 			rep_data[1] = 0x00;
 			rep_data[1] = 0x00;
 			limit = 3;
 			limit = 3;
 			do {
 			do {
-				ret = hdev->hid_output_raw_report(hdev,
-					rep_data, 2, HID_FEATURE_REPORT);
+				ret = hid_hw_raw_request(hdev, rep_data[0],
+					rep_data, 2, HID_FEATURE_REPORT,
+					HID_REQ_SET_REPORT);
 			} while (ret < 0 && limit-- > 0);
 			} while (ret < 0 && limit-- > 0);
 
 
 			if (ret >= 0) {
 			if (ret >= 0) {
@@ -378,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)
 		rep_data[0] = 0x03;
 		rep_data[0] = 0x03;
 		rep_data[1] = wdata->features;
 		rep_data[1] = wdata->features;
 
 
-		ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
-					HID_FEATURE_REPORT);
+		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
+				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 		if (ret >= 0)
 		if (ret >= 0)
 			wdata->high_speed = speed;
 			wdata->high_speed = speed;
 		break;
 		break;

+ 2 - 2
drivers/hid/hid-wiimote-core.c

@@ -28,14 +28,14 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
 	__u8 *buf;
 	__u8 *buf;
 	int ret;
 	int ret;
 
 
-	if (!hdev->hid_output_raw_report)
+	if (!hdev->ll_driver->output_report)
 		return -ENODEV;
 		return -ENODEV;
 
 
 	buf = kmemdup(buffer, count, GFP_KERNEL);
 	buf = kmemdup(buffer, count, GFP_KERNEL);
 	if (!buf)
 	if (!buf)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT);
+	ret = hid_hw_output_report(hdev, buf, count);
 
 
 	kfree(buf);
 	kfree(buf);
 	return ret;
 	return ret;

+ 19 - 8
drivers/hid/hidraw.c

@@ -123,10 +123,6 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
 
 
 	dev = hidraw_table[minor]->hid;
 	dev = hidraw_table[minor]->hid;
 
 
-	if (!dev->hid_output_raw_report) {
-		ret = -ENODEV;
-		goto out;
-	}
 
 
 	if (count > HID_MAX_BUFFER_SIZE) {
 	if (count > HID_MAX_BUFFER_SIZE) {
 		hid_warn(dev, "pid %d passed too large report\n",
 		hid_warn(dev, "pid %d passed too large report\n",
@@ -153,7 +149,21 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
 		goto out_free;
 		goto out_free;
 	}
 	}
 
 
-	ret = dev->hid_output_raw_report(dev, buf, count, report_type);
+	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:
+		 * if the device does not support receiving output reports,
+		 * on an interrupt endpoint, fallback to SET_REPORT HID command.
+		 */
+		if (ret != -ENOSYS)
+			goto out_free;
+	}
+
+	ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type,
+				HID_REQ_SET_REPORT);
+
 out_free:
 out_free:
 	kfree(buf);
 	kfree(buf);
 out:
 out:
@@ -189,7 +199,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
 
 
 	dev = hidraw_table[minor]->hid;
 	dev = hidraw_table[minor]->hid;
 
 
-	if (!dev->hid_get_raw_report) {
+	if (!dev->ll_driver->raw_request) {
 		ret = -ENODEV;
 		ret = -ENODEV;
 		goto out;
 		goto out;
 	}
 	}
@@ -216,14 +226,15 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
 
 
 	/*
 	/*
 	 * Read the first byte from the user. This is the report number,
 	 * Read the first byte from the user. This is the report number,
-	 * which is passed to dev->hid_get_raw_report().
+	 * which is passed to hid_hw_raw_request().
 	 */
 	 */
 	if (copy_from_user(&report_number, buffer, 1)) {
 	if (copy_from_user(&report_number, buffer, 1)) {
 		ret = -EFAULT;
 		ret = -EFAULT;
 		goto out_free;
 		goto out_free;
 	}
 	}
 
 
-	ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type);
+	ret = hid_hw_raw_request(dev, report_number, buf, count, report_type,
+				 HID_REQ_GET_REPORT);
 
 
 	if (ret < 0)
 	if (ret < 0)
 		goto out_free;
 		goto out_free;

+ 38 - 33
drivers/hid/i2c-hid/i2c-hid.c

@@ -257,18 +257,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
 	return 0;
 	return 0;
 }
 }
 
 
-static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
-		u8 reportID, unsigned char *buf, size_t data_len)
+/**
+ * i2c_hid_set_or_send_report: forward an incoming report to the device
+ * @client: the i2c_client of the device
+ * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
+ * @reportID: the report ID
+ * @buf: the actual data to transfer, without the report ID
+ * @len: size of buf
+ * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
+ */
+static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
+		u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
 {
 {
 	struct i2c_hid *ihid = i2c_get_clientdata(client);
 	struct i2c_hid *ihid = i2c_get_clientdata(client);
 	u8 *args = ihid->argsbuf;
 	u8 *args = ihid->argsbuf;
-	const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd;
+	const struct i2c_hid_cmd *hidcmd;
 	int ret;
 	int ret;
 	u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
 	u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
 	u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
 	u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
 	u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
 	u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
 
 
-	/* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */
+	/* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */
 	u16 size =	2			/* size */ +
 	u16 size =	2			/* size */ +
 			(reportID ? 1 : 0)	/* reportID */ +
 			(reportID ? 1 : 0)	/* reportID */ +
 			data_len		/* buf */;
 			data_len		/* buf */;
@@ -279,6 +288,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
 
 
 	i2c_hid_dbg(ihid, "%s\n", __func__);
 	i2c_hid_dbg(ihid, "%s\n", __func__);
 
 
+	if (!use_data && maxOutputLength == 0)
+		return -ENOSYS;
+
 	if (reportID >= 0x0F) {
 	if (reportID >= 0x0F) {
 		args[index++] = reportID;
 		args[index++] = reportID;
 		reportID = 0x0F;
 		reportID = 0x0F;
@@ -288,9 +300,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
 	 * use the data register for feature reports or if the device does not
 	 * use the data register for feature reports or if the device does not
 	 * support the output register
 	 * support the output register
 	 */
 	 */
-	if (reportType == 0x03 || maxOutputLength == 0) {
+	if (use_data) {
 		args[index++] = dataRegister & 0xFF;
 		args[index++] = dataRegister & 0xFF;
 		args[index++] = dataRegister >> 8;
 		args[index++] = dataRegister >> 8;
+		hidcmd = &hid_set_report_cmd;
 	} else {
 	} else {
 		args[index++] = outputRegister & 0xFF;
 		args[index++] = outputRegister & 0xFF;
 		args[index++] = outputRegister >> 8;
 		args[index++] = outputRegister >> 8;
@@ -559,7 +572,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
 }
 }
 
 
 static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
 static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
-		size_t count, unsigned char report_type)
+		size_t count, unsigned char report_type, bool use_data)
 {
 {
 	struct i2c_client *client = hid->driver_data;
 	struct i2c_client *client = hid->driver_data;
 	int report_id = buf[0];
 	int report_id = buf[0];
@@ -573,9 +586,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
 		count--;
 		count--;
 	}
 	}
 
 
-	ret = i2c_hid_set_report(client,
+	ret = i2c_hid_set_or_send_report(client,
 				report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
 				report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
-				report_id, buf, count);
+				report_id, buf, count, use_data);
 
 
 	if (report_id && ret >= 0)
 	if (report_id && ret >= 0)
 		ret++; /* add report_id to the number of transfered bytes */
 		ret++; /* add report_id to the number of transfered bytes */
@@ -583,34 +596,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
 	return ret;
 	return ret;
 }
 }
 
 
-static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep,
-		int reqtype)
+static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
+		size_t count)
 {
 {
-	struct i2c_client *client = hid->driver_data;
-	char *buf;
-	int ret;
-	int len = i2c_hid_get_report_length(rep) - 2;
-
-	buf = hid_alloc_report_buf(rep, GFP_KERNEL);
-	if (!buf)
-		return;
+	return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
+			false);
+}
 
 
+static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
+			       __u8 *buf, size_t len, unsigned char rtype,
+			       int reqtype)
+{
 	switch (reqtype) {
 	switch (reqtype) {
 	case HID_REQ_GET_REPORT:
 	case HID_REQ_GET_REPORT:
-		ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type);
-		if (ret < 0)
-			dev_err(&client->dev, "%s: unable to get report: %d\n",
-				__func__, ret);
-		else
-			hid_input_report(hid, rep->type, buf, ret, 0);
-		break;
+		return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
 	case HID_REQ_SET_REPORT:
 	case HID_REQ_SET_REPORT:
-		hid_output_report(rep, buf);
-		i2c_hid_output_raw_report(hid, buf, len, rep->type);
-		break;
+		if (buf[0] != reportnum)
+			return -EINVAL;
+		return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
+	default:
+		return -EIO;
 	}
 	}
-
-	kfree(buf);
 }
 }
 
 
 static int i2c_hid_parse(struct hid_device *hid)
 static int i2c_hid_parse(struct hid_device *hid)
@@ -768,7 +774,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
 	.open = i2c_hid_open,
 	.open = i2c_hid_open,
 	.close = i2c_hid_close,
 	.close = i2c_hid_close,
 	.power = i2c_hid_power,
 	.power = i2c_hid_power,
-	.request = i2c_hid_request,
+	.output_report = i2c_hid_output_report,
+	.raw_request = i2c_hid_raw_request,
 };
 };
 
 
 static int i2c_hid_init_irq(struct i2c_client *client)
 static int i2c_hid_init_irq(struct i2c_client *client)
@@ -1017,8 +1024,6 @@ static int i2c_hid_probe(struct i2c_client *client,
 
 
 	hid->driver_data = client;
 	hid->driver_data = client;
 	hid->ll_driver = &i2c_hid_ll_driver;
 	hid->ll_driver = &i2c_hid_ll_driver;
-	hid->hid_get_raw_report = i2c_hid_get_raw_report;
-	hid->hid_output_raw_report = i2c_hid_output_raw_report;
 	hid->dev.parent = &client->dev;
 	hid->dev.parent = &client->dev;
 	ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
 	ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
 	hid->bus = BUS_I2C;
 	hid->bus = BUS_I2C;

+ 16 - 22
drivers/hid/uhid.c

@@ -247,27 +247,22 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
 static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
 static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
 				  size_t count)
 				  size_t count)
 {
 {
-	struct uhid_device *uhid = hid->driver_data;
-	unsigned long flags;
-	struct uhid_event *ev;
-
-	if (count < 1 || count > UHID_DATA_MAX)
-		return -EINVAL;
-
-	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
-	if (!ev)
-		return -ENOMEM;
-
-	ev->type = UHID_OUTPUT;
-	ev->u.output.size = count;
-	ev->u.output.rtype = UHID_OUTPUT_REPORT;
-	memcpy(ev->u.output.data, buf, count);
-
-	spin_lock_irqsave(&uhid->qlock, flags);
-	uhid_queue(uhid, ev);
-	spin_unlock_irqrestore(&uhid->qlock, flags);
+	return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
+}
 
 
-	return count;
+static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum,
+			    __u8 *buf, size_t len, unsigned char rtype,
+			    int reqtype)
+{
+	switch (reqtype) {
+	case HID_REQ_GET_REPORT:
+		return uhid_hid_get_raw(hid, reportnum, buf, len, rtype);
+	case HID_REQ_SET_REPORT:
+		/* TODO: implement proper SET_REPORT functionality */
+		return -ENOSYS;
+	default:
+		return -EIO;
+	}
 }
 }
 
 
 static struct hid_ll_driver uhid_hid_driver = {
 static struct hid_ll_driver uhid_hid_driver = {
@@ -277,6 +272,7 @@ static struct hid_ll_driver uhid_hid_driver = {
 	.close = uhid_hid_close,
 	.close = uhid_hid_close,
 	.parse = uhid_hid_parse,
 	.parse = uhid_hid_parse,
 	.output_report = uhid_hid_output_report,
 	.output_report = uhid_hid_output_report,
+	.raw_request = uhid_raw_request,
 };
 };
 
 
 #ifdef CONFIG_COMPAT
 #ifdef CONFIG_COMPAT
@@ -404,8 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid,
 	hid->uniq[63] = 0;
 	hid->uniq[63] = 0;
 
 
 	hid->ll_driver = &uhid_hid_driver;
 	hid->ll_driver = &uhid_hid_driver;
-	hid->hid_get_raw_report = uhid_hid_get_raw;
-	hid->hid_output_raw_report = uhid_hid_output_raw;
 	hid->bus = ev->u.create.bus;
 	hid->bus = ev->u.create.bus;
 	hid->vendor = ev->u.create.vendor;
 	hid->vendor = ev->u.create.vendor;
 	hid->product = ev->u.create.product;
 	hid->product = ev->u.create.product;

+ 7 - 57
drivers/hid/usbhid/hid-core.c

@@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
 	int ret, skipped_report_id = 0;
 	int ret, skipped_report_id = 0;
 
 
 	/* Byte 0 is the report number. Report data starts at byte 1.*/
 	/* 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) {
 	if (buf[0] == 0x0) {
 		/* Don't send the Report ID */
 		/* Don't send the Report ID */
 		buf++;
 		buf++;
@@ -915,59 +920,6 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
 	return ret;
 	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;
-	struct usb_device *dev = hid_to_usb_dev(hid);
-	struct usb_interface *intf = usbhid->intf;
-	struct usb_host_interface *interface = intf->cur_altsetting;
-	int ret;
-
-	if (usbhid->urbout && report_type != HID_FEATURE_REPORT) {
-		int actual_length;
-		int skipped_report_id = 0;
-
-		if (buf[0] == 0x0) {
-			/* Don't send the Report ID */
-			buf++;
-			count--;
-			skipped_report_id = 1;
-		}
-		ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
-			buf, count, &actual_length,
-			USB_CTRL_SET_TIMEOUT);
-		/* return the number of bytes transferred */
-		if (ret == 0) {
-			ret = actual_length;
-			/* count also the report id */
-			if (skipped_report_id)
-				ret++;
-		}
-	} else {
-		int skipped_report_id = 0;
-		int report_id = buf[0];
-		if (buf[0] == 0x0) {
-			/* Don't send the Report ID */
-			buf++;
-			count--;
-			skipped_report_id = 1;
-		}
-		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, if this was a numbered report. */
-		if (ret > 0 && skipped_report_id)
-			ret++;
-	}
-
-	return ret;
-}
-
 static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
 static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
 {
 {
 	struct usbhid_device *usbhid = hid->driver_data;
 	struct usbhid_device *usbhid = hid->driver_data;
@@ -975,7 +927,7 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count)
 	int actual_length, skipped_report_id = 0, ret;
 	int actual_length, skipped_report_id = 0, ret;
 
 
 	if (!usbhid->urbout)
 	if (!usbhid->urbout)
-		return -EIO;
+		return -ENOSYS;
 
 
 	if (buf[0] == 0x0) {
 	if (buf[0] == 0x0) {
 		/* Don't send the Report ID */
 		/* Don't send the Report ID */
@@ -1331,8 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
 
 
 	usb_set_intfdata(intf, hid);
 	usb_set_intfdata(intf, hid);
 	hid->ll_driver = &usb_hid_driver;
 	hid->ll_driver = &usb_hid_driver;
-	hid->hid_get_raw_report = usbhid_get_raw_report;
-	hid->hid_output_raw_report = usbhid_output_raw_report;
 	hid->ff_init = hid_pidff_init;
 	hid->ff_init = hid_pidff_init;
 #ifdef CONFIG_USB_HIDDEV
 #ifdef CONFIG_USB_HIDDEV
 	hid->hiddev_connect = hiddev_connect;
 	hid->hiddev_connect = hiddev_connect;

+ 54 - 11
include/linux/hid.h

@@ -287,6 +287,8 @@ struct hid_item {
 #define HID_QUIRK_NO_EMPTY_INPUT		0x00000100
 #define HID_QUIRK_NO_EMPTY_INPUT		0x00000100
 #define HID_QUIRK_NO_INIT_INPUT_REPORTS		0x00000200
 #define HID_QUIRK_NO_INIT_INPUT_REPORTS		0x00000200
 #define HID_QUIRK_SKIP_OUTPUT_REPORTS		0x00010000
 #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_FULLSPEED_INTERVAL		0x10000000
 #define HID_QUIRK_NO_INIT_REPORTS		0x20000000
 #define HID_QUIRK_NO_INIT_REPORTS		0x20000000
 #define HID_QUIRK_NO_IGNORE			0x40000000
 #define HID_QUIRK_NO_IGNORE			0x40000000
@@ -508,12 +510,6 @@ struct hid_device {							/* device report descriptor */
 				  struct hid_usage *, __s32);
 				  struct hid_usage *, __s32);
 	void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
 	void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
 
 
-	/* handler for raw input (Get_Report) data, used by hidraw */
-	int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char);
-
-	/* 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 */
 	/* debugging support via debugfs */
 	unsigned short debug;
 	unsigned short debug;
 	struct dentry *debug_dir;
 	struct dentry *debug_dir;
@@ -675,11 +671,12 @@ struct hid_driver {
  * @stop: called on remove
  * @stop: called on remove
  * @open: called by input layer on open
  * @open: called by input layer on open
  * @close: called by input layer on close
  * @close: called by input layer on close
- * @hidinput_input_event: event input event (e.g. ff or leds)
  * @parse: this method is called only once to parse the device data,
  * @parse: this method is called only once to parse the device data,
  *	   shouldn't allocate anything to not leak memory
  *	   shouldn't allocate anything to not leak memory
  * @request: send report request to device (e.g. feature report)
  * @request: send report request to device (e.g. feature report)
  * @wait: wait for buffered io to complete (send/recv reports)
  * @wait: wait for buffered io to complete (send/recv reports)
+ * @raw_request: send raw report request to device (e.g. feature report)
+ * @output_report: send output report to device
  * @idle: send idle request to device
  * @idle: send idle request to device
  */
  */
 struct hid_ll_driver {
 struct hid_ll_driver {
@@ -691,9 +688,6 @@ struct hid_ll_driver {
 
 
 	int (*power)(struct hid_device *hdev, int level);
 	int (*power)(struct hid_device *hdev, int level);
 
 
-	int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
-			unsigned int code, int value);
-
 	int (*parse)(struct hid_device *hdev);
 	int (*parse)(struct hid_device *hdev);
 
 
 	void (*request)(struct hid_device *hdev,
 	void (*request)(struct hid_device *hdev,
@@ -758,6 +752,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid);
 unsigned int hidinput_count_leds(struct hid_device *hid);
 unsigned int hidinput_count_leds(struct hid_device *hid);
 __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
 __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
 void hid_output_report(struct hid_report *report, __u8 *data);
 void hid_output_report(struct hid_report *report, __u8 *data);
+void __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype);
 u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
 u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
 struct hid_device *hid_allocate_device(void);
 struct hid_device *hid_allocate_device(void);
 struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
 struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
@@ -970,7 +965,55 @@ static inline void hid_hw_request(struct hid_device *hdev,
 				  struct hid_report *report, int reqtype)
 				  struct hid_report *report, int reqtype)
 {
 {
 	if (hdev->ll_driver->request)
 	if (hdev->ll_driver->request)
-		hdev->ll_driver->request(hdev, report, reqtype);
+		return hdev->ll_driver->request(hdev, report, reqtype);
+
+	__hid_request(hdev, report, reqtype);
+}
+
+/**
+ * hid_hw_raw_request - send report request to device
+ *
+ * @hdev: hid device
+ * @reportnum: report ID
+ * @buf: in/out data to transfer
+ * @len: length of buf
+ * @rtype: HID report type
+ * @reqtype: HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
+ *
+ * @return: count of data transfered, negative if error
+ *
+ * Same behavior as hid_hw_request, but with raw buffers instead.
+ */
+static inline int hid_hw_raw_request(struct hid_device *hdev,
+				  unsigned char reportnum, __u8 *buf,
+				  size_t len, unsigned char rtype, int reqtype)
+{
+	if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
+		return -EINVAL;
+
+	return hdev->ll_driver->raw_request(hdev, reportnum, buf, len,
+						    rtype, reqtype);
+}
+
+/**
+ * hid_hw_output_report - send output report to device
+ *
+ * @hdev: hid device
+ * @buf: raw data to transfer
+ * @len: length of buf
+ *
+ * @return: count of data transfered, negative if error
+ */
+static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf,
+					size_t len)
+{
+	if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
+		return -EINVAL;
+
+	if (hdev->ll_driver->output_report)
+		return hdev->ll_driver->output_report(hdev, buf, len);
+
+	return -ENOSYS;
 }
 }
 
 
 /**
 /**

+ 7 - 111
net/bluetooth/hidp/core.c

@@ -223,51 +223,6 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
 	input_sync(dev);
 	input_sync(dev);
 }
 }
 
 
-static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
-{
-	unsigned char hdr;
-	u8 *buf;
-	int rsize, ret;
-
-	buf = hid_alloc_report_buf(report, GFP_ATOMIC);
-	if (!buf)
-		return -EIO;
-
-	hid_output_report(report, buf);
-	hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
-
-	rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-	ret = hidp_send_intr_message(session, hdr, buf, rsize);
-
-	kfree(buf);
-	return ret;
-}
-
-static int hidp_hidinput_event(struct input_dev *dev, unsigned int type,
-			       unsigned int code, int value)
-{
-	struct hid_device *hid = input_get_drvdata(dev);
-	struct hidp_session *session = hid->driver_data;
-	struct hid_field *field;
-	int offset;
-
-	BT_DBG("session %p type %d code %d value %d",
-	       session, type, code, value);
-
-	if (type != EV_LED)
-		return -1;
-
-	offset = hidinput_find_field(hid, type, code, &field);
-	if (offset == -1) {
-		hid_warn(dev, "event field not found\n");
-		return -1;
-	}
-
-	hid_set_field(field, offset, value);
-
-	return hidp_send_report(session, field->report);
-}
-
 static int hidp_get_raw_report(struct hid_device *hid,
 static int hidp_get_raw_report(struct hid_device *hid,
 		unsigned char report_number,
 		unsigned char report_number,
 		unsigned char *data, size_t count,
 		unsigned char *data, size_t count,
@@ -418,62 +373,13 @@ err:
 	return ret;
 	return ret;
 }
 }
 
 
-static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
-		unsigned char report_type)
+static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
 {
 {
 	struct hidp_session *session = hid->driver_data;
 	struct hidp_session *session = hid->driver_data;
-	int ret;
-
-	if (report_type == HID_OUTPUT_REPORT) {
-		report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
-		return hidp_send_intr_message(session, report_type,
-					      data, count);
-	} else if (report_type != HID_FEATURE_REPORT) {
-		return -EINVAL;
-	}
-
-	if (mutex_lock_interruptible(&session->report_mutex))
-		return -ERESTARTSYS;
 
 
-	/* Set up our wait, and send the report request to the device. */
-	set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
-	report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
-	ret = hidp_send_ctrl_message(session, report_type, data, count);
-	if (ret)
-		goto err;
-
-	/* Wait for the ACK from the device. */
-	while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) &&
-	       !atomic_read(&session->terminate)) {
-		int res;
-
-		res = wait_event_interruptible_timeout(session->report_queue,
-			!test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)
-				|| atomic_read(&session->terminate),
-			10*HZ);
-		if (res == 0) {
-			/* timeout */
-			ret = -EIO;
-			goto err;
-		}
-		if (res < 0) {
-			/* signal */
-			ret = -ERESTARTSYS;
-			goto err;
-		}
-	}
-
-	if (!session->output_report_success) {
-		ret = -EIO;
-		goto err;
-	}
-
-	ret = count;
-
-err:
-	clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
-	mutex_unlock(&session->report_mutex);
-	return ret;
+	return hidp_send_intr_message(session,
+				      HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT,
+				      data, count);
 }
 }
 
 
 static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
 static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
@@ -490,15 +396,6 @@ static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum,
 	}
 	}
 }
 }
 
 
-static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count)
-{
-	struct hidp_session *session = hid->driver_data;
-
-	return hidp_send_intr_message(session,
-				      HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT,
-				      data, count);
-}
-
 static void hidp_idle_timeout(unsigned long arg)
 static void hidp_idle_timeout(unsigned long arg)
 {
 {
 	struct hidp_session *session = (struct hidp_session *) arg;
 	struct hidp_session *session = (struct hidp_session *) arg;
@@ -829,7 +726,6 @@ static struct hid_ll_driver hidp_hid_driver = {
 	.close = hidp_close,
 	.close = hidp_close,
 	.raw_request = hidp_raw_request,
 	.raw_request = hidp_raw_request,
 	.output_report = hidp_output_report,
 	.output_report = hidp_output_report,
-	.hidinput_input_event = hidp_hidinput_event,
 };
 };
 
 
 /* This function sets up the hid device. It does not add it
 /* This function sets up the hid device. It does not add it
@@ -871,15 +767,15 @@ static int hidp_setup_hid(struct hidp_session *session,
 	snprintf(hid->phys, sizeof(hid->phys), "%pMR",
 	snprintf(hid->phys, sizeof(hid->phys), "%pMR",
 		 &l2cap_pi(session->ctrl_sock->sk)->chan->src);
 		 &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",
 	snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
 		 &l2cap_pi(session->ctrl_sock->sk)->chan->dst);
 		 &l2cap_pi(session->ctrl_sock->sk)->chan->dst);
 
 
 	hid->dev.parent = &session->conn->hcon->dev;
 	hid->dev.parent = &session->conn->hcon->dev;
 	hid->ll_driver = &hidp_hid_driver;
 	hid->ll_driver = &hidp_hid_driver;
 
 
-	hid->hid_get_raw_report = hidp_get_raw_report;
-	hid->hid_output_raw_report = hidp_output_raw_report;
-
 	/* True if device is blacklisted in drivers/hid/hid-core.c */
 	/* True if device is blacklisted in drivers/hid/hid-core.c */
 	if (hid_ignore(hid)) {
 	if (hid_ignore(hid)) {
 		hid_destroy_device(session->hid);
 		hid_destroy_device(session->hid);