Merge branches 'for-3.18/always-poll-quirk', 'for-3.18/logitech', 'for-3.18/picolcd...
authorJiri Kosina <jkosina@suse.cz>
Mon, 6 Oct 2014 21:34:40 +0000 (23:34 +0200)
committerJiri Kosina <jkosina@suse.cz>
Mon, 6 Oct 2014 21:34:40 +0000 (23:34 +0200)
19 files changed:
Documentation/hid/uhid.txt
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-holtek-mouse.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-penmount.c [new file with mode: 0644]
drivers/hid/hid-picolcd_core.c
drivers/hid/hid-rmi.c
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-sony.c
drivers/hid/hid-thingm.c
drivers/hid/uhid.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
include/linux/hid.h
include/uapi/linux/uhid.h

index 54c8f9706a952691799ef65f7552e449eeb45bfb..c8656dd029a910251bc9f6ffec70781d3a093f90 100644 (file)
@@ -1,28 +1,13 @@
       UHID - User-space I/O driver support for HID subsystem
      ========================================================
 
-The HID subsystem needs two kinds of drivers. In this document we call them:
+UHID allows user-space to implement HID transport drivers. Please see
+hid-transport.txt for an introduction into HID transport drivers. This document
+relies heavily on the definitions declared there.
 
- 1. The "HID I/O Driver" is the driver that performs raw data I/O to the
-    low-level device. Internally, they register an hid_ll_driver structure with
-    the HID core. They perform device setup, read raw data from the device and
-    push it into the HID subsystem and they provide a callback so the HID
-    subsystem can send data to the device.
-
- 2. The "HID Device Driver" is the driver that parses HID reports and reacts on
-    them. There are generic drivers like "generic-usb" and "generic-bluetooth"
-    which adhere to the HID specification and provide the standardizes features.
-    But there may be special drivers and quirks for each non-standard device out
-    there. Internally, they use the hid_driver structure.
-
-Historically, the USB stack was the first subsystem to provide an HID I/O
-Driver. However, other standards like Bluetooth have adopted the HID specs and
-may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O
-Drivers in user-space and feed the data into the kernel HID-subsystem.
-
-This allows user-space to operate on the same level as USB-HID, Bluetooth-HID
-and similar. It does not provide a way to write HID Device Drivers, though. Use
-hidraw for this purpose.
+With UHID, a user-space transport driver can create kernel hid-devices for each
+device connected to the user-space controlled bus. The UHID API defines the I/O
+events provided from the kernel to user-space and vice versa.
 
 There is an example user-space application in ./samples/uhid/uhid-example.c
 
@@ -42,8 +27,9 @@ by setting O_NONBLOCK.
 struct uhid_event {
         __u32 type;
         union {
-                struct uhid_create_req create;
-                struct uhid_data_req data;
+                struct uhid_create2_req create2;
+                struct uhid_output_req output;
+                struct uhid_input2_req input2;
                 ...
         } u;
 };
@@ -54,8 +40,11 @@ multiple write()'s. A single event must always be sent as a whole. Furthermore,
 only a single event can be sent per read() or write(). Pending data is ignored.
 If you want to handle multiple events in a single syscall, then use vectored
 I/O with readv()/writev().
+The "type" field defines the payload. For each type, there is a
+payload-structure available in the union "u" (except for empty payloads). This
+payload contains management and/or device data.
 
-The first thing you should do is sending an UHID_CREATE event. This will
+The first thing you should do is sending an UHID_CREATE2 event. This will
 register the device. UHID will respond with an UHID_START event. You can now
 start sending data to and reading data from UHID. However, unless UHID sends the
 UHID_OPEN event, the internally attached HID Device Driver has no user attached.
@@ -69,12 +58,20 @@ ref-counting for you.
 You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even
 though the device may have no users.
 
-If you want to send data to the HID subsystem, you send an HID_INPUT event with
-your raw data payload. If the kernel wants to send data to the device, you will
-read an UHID_OUTPUT or UHID_OUTPUT_EV event.
+If you want to send data on the interrupt channel to the HID subsystem, you send
+an HID_INPUT2 event with your raw data payload. If the kernel wants to send data
+on the interrupt channel to the device, you will read an UHID_OUTPUT event.
+Data requests on the control channel are currently limited to GET_REPORT and
+SET_REPORT (no other data reports on the control channel are defined so far).
+Those requests are always synchronous. That means, the kernel sends
+UHID_GET_REPORT and UHID_SET_REPORT events and requires you to forward them to
+the device on the control channel. Once the device responds, you must forward
+the response via UHID_GET_REPORT_REPLY and UHID_SET_REPORT_REPLY to the kernel.
+The kernel blocks internal driver-execution during such round-trips (times out
+after a hard-coded period).
 
 If your device disconnects, you should send an UHID_DESTROY event. This will
-unregister the device. You can now send UHID_CREATE again to register a new
+unregister the device. You can now send UHID_CREATE2 again to register a new
 device.
 If you close() the fd, the device is automatically unregistered and destroyed
 internally.
@@ -82,73 +79,79 @@ internally.
 write()
 -------
 write() allows you to modify the state of the device and feed input data into
-the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and
-UHID_INPUT. The kernel will parse the event immediately and if the event ID is
+the kernel. The kernel will parse the event immediately and if the event ID is
 not supported, it will return -EOPNOTSUPP. If the payload is invalid, then
 -EINVAL is returned, otherwise, the amount of data that was read is returned and
-the request was handled successfully.
+the request was handled successfully. O_NONBLOCK does not affect write() as
+writes are always handled immediately in a non-blocking fashion. Future requests
+might make use of O_NONBLOCK, though.
 
-  UHID_CREATE:
+  UHID_CREATE2:
   This creates the internal HID device. No I/O is possible until you send this
-  event to the kernel. The payload is of type struct uhid_create_req and
+  event to the kernel. The payload is of type struct uhid_create2_req and
   contains information about your device. You can start I/O now.
 
-  UHID_CREATE2:
-  Same as UHID_CREATE, but the HID report descriptor data (rd_data) is an array
-  inside struct uhid_create2_req, instead of a pointer to a separate array.
-  Enables use from languages that don't support pointers, e.g. Python.
-
   UHID_DESTROY:
   This destroys the internal HID device. No further I/O will be accepted. There
   may still be pending messages that you can receive with read() but no further
   UHID_INPUT events can be sent to the kernel.
-  You can create a new device by sending UHID_CREATE again. There is no need to
+  You can create a new device by sending UHID_CREATE2 again. There is no need to
   reopen the character device.
 
-  UHID_INPUT:
-  You must send UHID_CREATE before sending input to the kernel! This event
-  contains a data-payload. This is the raw data that you read from your device.
-  The kernel will parse the HID reports and react on it.
-
   UHID_INPUT2:
-  Same as UHID_INPUT, but the data array is the last field of uhid_input2_req.
-  Enables userspace to write only the required bytes to kernel (ev.type +
-  ev.u.input2.size + the part of the data array that matters), instead of
-  the entire struct uhid_input2_req.
-
-  UHID_FEATURE_ANSWER:
-  If you receive a UHID_FEATURE request you must answer with this request. You
-  must copy the "id" field from the request into the answer. Set the "err" field
-  to 0 if no error occurred or to EIO if an I/O error occurred.
+  You must send UHID_CREATE2 before sending input to the kernel! This event
+  contains a data-payload. This is the raw data that you read from your device
+  on the interrupt channel. The kernel will parse the HID reports.
+
+  UHID_GET_REPORT_REPLY:
+  If you receive a UHID_GET_REPORT request you must answer with this request.
+  You  must copy the "id" field from the request into the answer. Set the "err"
+  field to 0 if no error occurred or to EIO if an I/O error occurred.
   If "err" is 0 then you should fill the buffer of the answer with the results
-  of the feature request and set "size" correspondingly.
+  of the GET_REPORT request and set "size" correspondingly.
+
+  UHID_SET_REPORT_REPLY:
+  This is the SET_REPORT equivalent of UHID_GET_REPORT_REPLY. Unlike GET_REPORT,
+  SET_REPORT never returns a data buffer, therefore, it's sufficient to set the
+  "id" and "err" fields correctly.
 
 read()
 ------
-read() will return a queued output report. These output reports can be of type
-UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No
-reaction is required to any of them but you should handle them according to your
-needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
+read() will return a queued output report. No reaction is required to any of
+them but you should handle them according to your needs.
 
   UHID_START:
   This is sent when the HID device is started. Consider this as an answer to
-  UHID_CREATE. This is always the first event that is sent.
+  UHID_CREATE2. This is always the first event that is sent. Note that this
+  event might not be available immediately after write(UHID_CREATE2) returns.
+  Device drivers might required delayed setups.
+  This event contains a payload of type uhid_start_req. The "dev_flags" field
+  describes special behaviors of a device. The following flags are defined:
+      UHID_DEV_NUMBERED_FEATURE_REPORTS:
+      UHID_DEV_NUMBERED_OUTPUT_REPORTS:
+      UHID_DEV_NUMBERED_INPUT_REPORTS:
+          Each of these flags defines whether a given report-type uses numbered
+          reports. If numbered reports are used for a type, all messages from
+          the kernel already have the report-number as prefix. Otherwise, no
+          prefix is added by the kernel.
+          For messages sent by user-space to the kernel, you must adjust the
+          prefixes according to these flags.
 
   UHID_STOP:
   This is sent when the HID device is stopped. Consider this as an answer to
   UHID_DESTROY.
-  If the kernel HID device driver closes the device manually (that is, you
-  didn't send UHID_DESTROY) then you should consider this device closed and send
-  an UHID_DESTROY event. You may want to reregister your device, though. This is
-  always the last message that is sent to you unless you reopen the device with
-  UHID_CREATE.
+  If you didn't destroy your device via UHID_DESTROY, but the kernel sends an
+  UHID_STOP event, this should usually be ignored. It means that the kernel
+  reloaded/changed the device driver loaded on your HID device (or some other
+  maintenance actions happened).
+  You can usually ignored any UHID_STOP events safely.
 
   UHID_OPEN:
   This is sent when the HID device is opened. That is, the data that the HID
   device provides is read by some other process. You may ignore this event but
   it is useful for power-management. As long as you haven't received this event
   there is actually no other process that reads your data so there is no need to
-  send UHID_INPUT events to the kernel.
+  send UHID_INPUT2 events to the kernel.
 
   UHID_CLOSE:
   This is sent when there are no more processes which read the HID data. It is
@@ -156,27 +159,29 @@ needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
 
   UHID_OUTPUT:
   This is sent if the HID device driver wants to send raw data to the I/O
-  device. You should read the payload and forward it to the device. The payload
-  is of type "struct uhid_data_req".
+  device on the interrupt channel. You should read the payload and forward it to
+  the device. The payload is of type "struct uhid_data_req".
   This may be received even though you haven't received UHID_OPEN, yet.
 
-  UHID_OUTPUT_EV (obsolete):
-  Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This
-  is called for force-feedback, LED or similar events which are received through
-  an input device by the HID subsystem. You should convert this into raw reports
-  and send them to your device similar to events of type UHID_OUTPUT.
-  This is no longer sent by newer kernels. Instead, HID core converts it into a
-  raw output report and sends it via UHID_OUTPUT.
-
-  UHID_FEATURE:
-  This event is sent if the kernel driver wants to perform a feature request as
-  described in the HID specs. The report-type and report-number are available in
-  the payload.
-  The kernel serializes feature requests so there will never be two in parallel.
-  However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5
-  seconds, then the requests will be dropped and a new one might be sent.
-  Therefore, the payload also contains an "id" field that identifies every
-  request.
-
-Document by:
-  David Herrmann <dh.herrmann@googlemail.com>
+  UHID_GET_REPORT:
+  This event is sent if the kernel driver wants to perform a GET_REPORT request
+  on the control channeld as described in the HID specs. The report-type and
+  report-number are available in the payload.
+  The kernel serializes GET_REPORT requests so there will never be two in
+  parallel. However, if you fail to respond with a UHID_GET_REPORT_REPLY, the
+  request might silently time out.
+  Once you read a GET_REPORT request, you shall forward it to the hid device and
+  remember the "id" field in the payload. Once your hid device responds to the
+  GET_REPORT (or if it fails), you must send a UHID_GET_REPORT_REPLY to the
+  kernel with the exact same "id" as in the request. If the request already
+  timed out, the kernel will ignore the response silently. The "id" field is
+  never re-used, so conflicts cannot happen.
+
+  UHID_SET_REPORT:
+  This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shall
+  send a SET_REPORT request to your hid device. Once it replies, you must tell
+  the kernel about it via UHID_SET_REPORT_REPLY.
+  The same restrictions as for UHID_GET_REPORT apply.
+
+----------------------------------------------------
+Written 2012, David Herrmann <dh.herrmann@gmail.com>
index c18d5d71062d48828148e128ac3019d06a090e99..f42df4dd58d285dff4eac05ba9ff7e8688bf4de9 100644 (file)
@@ -530,6 +530,17 @@ config PANTHERLORD_FF
          Say Y here if you have a PantherLord/GreenAsia based game controller
          or adapter and want to enable force feedback support for it.
 
+config HID_PENMOUNT
+       tristate "Penmount touch device"
+       depends on USB_HID
+       ---help---
+         This selects a driver for the PenMount 6000 touch controller.
+
+         The driver works around a problem in the report descript allowing
+         the userspace to touch events instead of mouse events.
+
+         Say Y here if you have a Penmount based touch controller.
+
 config HID_PETALYNX
        tristate "Petalynx Maxter remote control"
        depends on HID
index 4dbac7f8530c3b072ba9b5f43f9059f2cfbb962c..e2850d8af9ca353a3b95ad6706667266f5a804b8 100644 (file)
@@ -71,6 +71,7 @@ obj-$(CONFIG_HID_NTRIG)               += hid-ntrig.o
 obj-$(CONFIG_HID_ORTEK)                += hid-ortek.o
 obj-$(CONFIG_HID_PRODIKEYS)    += hid-prodikeys.o
 obj-$(CONFIG_HID_PANTHERLORD)  += hid-pl.o
+obj-$(CONFIG_HID_PENMOUNT)     += hid-penmount.o
 obj-$(CONFIG_HID_PETALYNX)     += hid-petalynx.o
 obj-$(CONFIG_HID_PICOLCD)      += hid-picolcd.o
 hid-picolcd-y                  += hid-picolcd_core.o
index 583344dba3c614b927915472f9045d832dbe1506..73bd9e2e42bc3c7dfbd249c0a1d3d4e1c78a19f9 100644 (file)
@@ -52,7 +52,7 @@ EXPORT_SYMBOL_GPL(hid_debug);
 
 static int hid_ignore_special_drivers = 0;
 module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600);
-MODULE_PARM_DESC(debug, "Ignore any special drivers and handle all devices by generic driver");
+MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver");
 
 /*
  * Register a new report for a device.
@@ -1796,6 +1796,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
        { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },
@@ -1883,6 +1884,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
 #if IS_ENABLED(CONFIG_HID_ROCCAT)
index d60fbd0adc0c96a3bd5534f6d5fc6aeea00967f7..78b3a0c767751534c16167846cfb3571641ce6af 100644 (file)
@@ -29,6 +29,7 @@
  *   and Zalman ZM-GM1
  * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse
  * - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse
+ * - USB ID 04d9:a0c2, sold as ETEKCITY Scroll T-140 Gaming Mouse
  */
 
 static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
@@ -42,6 +43,7 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                switch (hdev->product) {
                case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067:
                case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072:
+               case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2:
                        if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f
                                        && rdesc[120] == 0xff && rdesc[121] == 0x7f) {
                                hid_info(hdev, "Fixing up report descriptor\n");
@@ -74,6 +76,8 @@ static const struct hid_device_id holtek_mouse_devices[] = {
                        USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
                        USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,
+                       USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, holtek_mouse_devices);
index 25cd674d6064887887a795c46518ebec68a90e46..cd9c9e96cf0ef838618a95438afa0794f82b1eb5 100644 (file)
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7      0x73f7
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001      0xa001
 
+#define USB_VENDOR_ID_ELAN             0x04f3
+#define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089
+
 #define USB_VENDOR_ID_ELECOM           0x056e
 #define USB_DEVICE_ID_ELECOM_BM084     0x0061
 
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070    0xa070
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072    0xa072
 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081    0xa081
+#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2    0xa0c2
 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096 0xa096
 
 #define USB_VENDOR_ID_IMATION          0x0718
 #define USB_DEVICE_ID_PENMOUNT_PCI     0x3500
 #define USB_DEVICE_ID_PENMOUNT_1610    0x1610
 #define USB_DEVICE_ID_PENMOUNT_1640    0x1640
+#define USB_DEVICE_ID_PENMOUNT_6000    0x6000
 
 #define USB_VENDOR_ID_PETALYNX         0x18b1
 #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE   0x0037
 #define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff
 
 #define USB_VENDOR_ID_PIXART                           0x093a
+#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2     0x0137
+#define USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE         0x2510
 #define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN      0x8001
 #define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1     0x8002
 #define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2     0x8003
index 2619f7f4517a13f1ecf525b6c483d9607c2615f9..2df7fddbd119bc0cccb5f9cc2a6c4a01bbc9f7e8 100644 (file)
@@ -599,6 +599,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                /* These usage IDs map directly to the usage codes. */
                case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
                case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
+                       if (field->flags & HID_MAIN_ITEM_RELATIVE)
+                               map_rel(usage->hid & 0xf);
+                       else
+                               map_abs_clear(usage->hid & 0xf);
+                       break;
+
                case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
                        if (field->flags & HID_MAIN_ITEM_RELATIVE)
                                map_rel(usage->hid & 0xf);
index 9bf8637747a57f1b362680eeb36faf649b35f2fd..71f569292cab24d35a43ca82197422406891bc10 100644 (file)
@@ -385,18 +385,6 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
 
        djdev = djrcv_dev->paired_dj_devices[dj_report->device_index];
 
-       if (!djdev) {
-               dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
-                       " is NULL, index %d\n", dj_report->device_index);
-               kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
-
-               if (schedule_work(&djrcv_dev->work) == 0) {
-                       dbg_hid("%s: did not schedule the work item, was already "
-                       "queued\n", __func__);
-               }
-               return;
-       }
-
        memset(reportbuffer, 0, sizeof(reportbuffer));
 
        for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
@@ -421,18 +409,6 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
 
        dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index];
 
-       if (dj_device == NULL) {
-               dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
-                       " is NULL, index %d\n", dj_report->device_index);
-               kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
-
-               if (schedule_work(&djrcv_dev->work) == 0) {
-                       dbg_hid("%s: did not schedule the work item, was already "
-                       "queued\n", __func__);
-               }
-               return;
-       }
-
        if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) ||
            (hid_reportid_size_map[dj_report->report_type] == 0)) {
                dbg_hid("invalid report type:%x\n", dj_report->report_type);
@@ -701,8 +677,17 @@ static int logi_dj_raw_event(struct hid_device *hdev,
        }
 
        spin_lock_irqsave(&djrcv_dev->lock, flags);
+
+       if (!djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+               /* received an event for an unknown device, bail out */
+               logi_dj_recv_queue_notification(djrcv_dev, dj_report);
+               goto out;
+       }
+
        switch (dj_report->report_type) {
        case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
+               /* pairing notifications are handled above the switch */
+               break;
        case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
                logi_dj_recv_queue_notification(djrcv_dev, dj_report);
                break;
@@ -715,6 +700,8 @@ static int logi_dj_raw_event(struct hid_device *hdev,
        default:
                logi_dj_recv_forward_report(djrcv_dev, dj_report);
        }
+
+out:
        spin_unlock_irqrestore(&djrcv_dev->lock, flags);
 
        return true;
diff --git a/drivers/hid/hid-penmount.c b/drivers/hid/hid-penmount.c
new file mode 100644 (file)
index 0000000..c11dce8
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  HID driver for PenMount touchscreens
+ *
+ *  Copyright (c) 2014 Christian Gmeiner <christian.gmeiner <at> gmail.com>
+ *
+ *  based on hid-penmount copyrighted by
+ *    PenMount Touch Solutions <penmount <at> seed.net.tw>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/hid.h>
+#include "hid-ids.h"
+
+static int penmount_input_mapping(struct hid_device *hdev,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
+               hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+               return 1;
+       }
+
+       return 0;
+}
+
+static const struct hid_device_id penmount_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_6000) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, penmount_devices);
+
+static struct hid_driver penmount_driver = {
+       .name = "hid-penmount",
+       .id_table = penmount_devices,
+       .input_mapping = penmount_input_mapping,
+};
+
+module_hid_driver(penmount_driver);
+
+MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
+MODULE_DESCRIPTION("PenMount HID TouchScreen driver");
+MODULE_LICENSE("GPL");
index 020df3c2e8b42717c62bbe0470aa47845535e4a5..c1b29a9eb41ab824fd643e920b9a16f30af29186 100644 (file)
@@ -351,8 +351,8 @@ static int picolcd_raw_event(struct hid_device *hdev,
                return 1;
 
        if (size > 64) {
-               hid_warn(hdev, "invalid size value (%d) for picolcd raw event\n",
-                               size);
+               hid_warn(hdev, "invalid size value (%d) for picolcd raw event (%d)\n",
+                               size, report->id);
                return 0;
        }
 
index 8389e8109218c7013567b727cdb3ae300c3a51b9..3cccff73b9b9283e01aacf81a60dc264815e9d2b 100644 (file)
@@ -320,10 +320,7 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
        int offset;
        int i;
 
-       if (size < hdata->f11.report_size)
-               return 0;
-
-       if (!(irq & hdata->f11.irq_mask))
+       if (!(irq & hdata->f11.irq_mask) || size <= 0)
                return 0;
 
        offset = (hdata->max_fingers >> 2) + 1;
@@ -332,9 +329,19 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
                int fs_bit_position = (i & 0x3) << 1;
                int finger_state = (data[fs_byte_position] >> fs_bit_position) &
                                        0x03;
+               int position = offset + 5 * i;
+
+               if (position + 5 > size) {
+                       /* partial report, go on with what we received */
+                       printk_once(KERN_WARNING
+                               "%s %s: Detected incomplete finger report. Finger reports may occasionally get dropped on this platform.\n",
+                                dev_driver_string(&hdev->dev),
+                                dev_name(&hdev->dev));
+                       hid_dbg(hdev, "Incomplete finger report\n");
+                       break;
+               }
 
-               rmi_f11_process_touch(hdata, i, finger_state,
-                               &data[offset + 5 * i]);
+               rmi_f11_process_touch(hdata, i, finger_state, &data[position]);
        }
        input_mt_sync_frame(hdata->input);
        input_sync(hdata->input);
@@ -352,6 +359,11 @@ static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
        if (!(irq & hdata->f30.irq_mask))
                return 0;
 
+       if (size < (int)hdata->f30.report_size) {
+               hid_warn(hdev, "Click Button pressed, but the click data is missing\n");
+               return 0;
+       }
+
        for (i = 0; i < hdata->gpio_led_count; i++) {
                if (test_bit(i, &hdata->button_mask)) {
                        value = (data[i / 8] >> (i & 0x07)) & BIT(0);
@@ -412,9 +424,29 @@ static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
        return 1;
 }
 
+static int rmi_check_sanity(struct hid_device *hdev, u8 *data, int size)
+{
+       int valid_size = size;
+       /*
+        * On the Dell XPS 13 9333, the bus sometimes get confused and fills
+        * the report with a sentinel value "ff". Synaptics told us that such
+        * behavior does not comes from the touchpad itself, so we filter out
+        * such reports here.
+        */
+
+       while ((data[valid_size - 1] == 0xff) && valid_size > 0)
+               valid_size--;
+
+       return valid_size;
+}
+
 static int rmi_raw_event(struct hid_device *hdev,
                struct hid_report *report, u8 *data, int size)
 {
+       size = rmi_check_sanity(hdev, data, size);
+       if (size < 2)
+               return 0;
+
        switch (data[0]) {
        case RMI_READ_DATA_REPORT_ID:
                return rmi_read_data_event(hdev, data, size);
index 2ac25760a9a9da02004299ab702c481d7e712cc5..e6d8e18dae97e487cba5a1ff3e711425652aa7e1 100644 (file)
@@ -708,6 +708,9 @@ static const struct hid_device_id sensor_hub_devices[] = {
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
                        USB_DEVICE_ID_MS_TYPE_COVER_2),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
+                       USB_DEVICE_ID_STM_HID_SENSOR),
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
                        USB_DEVICE_ID_STM_HID_SENSOR_1),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
index c372368e438c7d486fa54b64d2f29ea49dcb218d..bc4269e559f119bde1a957ffd1b249c619395048 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  HID driver for Sony / PS2 / PS3 BD devices.
+ *  HID driver for Sony / PS2 / PS3 / PS4 BD devices.
  *
  *  Copyright (c) 1999 Andreas Gal
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
@@ -8,6 +8,7 @@
  *  Copyright (c) 2012 David Dillow <dave@thedillows.org>
  *  Copyright (c) 2006-2013 Jiri Kosina
  *  Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
+ *  Copyright (c) 2014 Frank Praznik <frank.praznik@gmail.com>
  */
 
 /*
@@ -176,7 +177,7 @@ static u8 dualshock4_usb_rdesc[] = {
        0x75, 0x06,         /*      Report Size (6),                */
        0x95, 0x01,         /*      Report Count (1),               */
        0x15, 0x00,         /*      Logical Minimum (0),            */
-       0x25, 0x7F,         /*      Logical Maximum (127),          */
+       0x25, 0x3F,         /*      Logical Maximum (63),           */
        0x81, 0x02,         /*      Input (Variable),               */
        0x05, 0x01,         /*      Usage Page (Desktop),           */
        0x09, 0x33,         /*      Usage (Rx),                     */
@@ -200,14 +201,14 @@ static u8 dualshock4_usb_rdesc[] = {
        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),        */
+       0x16, 0x00, 0xE0,   /*      Logical Minimum (-8192),        */
+       0x26, 0xFF, 0x1F,   /*      Logical Maximum (8191),         */
        0x95, 0x03,         /*      Report Count (3),               */
        0x81, 0x02,         /*      Input (Variable),               */
        0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
        0x09, 0x21,         /*      Usage (21h),                    */
        0x15, 0x00,         /*      Logical Minimum (0),            */
-       0x25, 0xFF,         /*      Logical Maximum (255),          */
+       0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
        0x75, 0x08,         /*      Report Size (8),                */
        0x95, 0x27,         /*      Report Count (39),              */
        0x81, 0x02,         /*      Input (Variable),               */
@@ -395,11 +396,11 @@ static u8 dualshock4_usb_rdesc[] = {
 
 /*
  * 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
+ * type 1 when running over Bluetooth. However, when feature report 2 is
+ * requested during the controller initialization it starts sending input
+ * reports in report 17.  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.
+ * the HID layer won't process the received input.
  */
 static u8 dualshock4_bt_rdesc[] = {
        0x05, 0x01,         /*  Usage Page (Desktop),               */
@@ -509,8 +510,8 @@ static u8 dualshock4_bt_rdesc[] = {
        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),        */
+       0x16, 0x00, 0xE0,   /*      Logical Minimum (-8192),        */
+       0x26, 0xFF, 0x1F,   /*      Logical Maximum (8191),         */
        0x95, 0x03,         /*      Report Count (3),               */
        0x81, 0x02,         /*      Input (Variable),               */
        0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
@@ -935,12 +936,13 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
        if (rd[30] >= 0xee) {
                battery_capacity = 100;
                battery_charging = !(rd[30] & 0x01);
+               cable_state = 1;
        } else {
                __u8 index = rd[30] <= 5 ? rd[30] : 5;
                battery_capacity = sixaxis_battery_capacity[index];
                battery_charging = 0;
+               cable_state = 0;
        }
-       cable_state = !(rd[31] & 0x04);
 
        spin_lock_irqsave(&sc->lock, flags);
        sc->cable_state = cable_state;
@@ -1082,6 +1084,38 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
        return 0;
 }
 
+static int sony_register_touchpad(struct hid_input *hi, int touch_count,
+                                       int w, int h)
+{
+       struct input_dev *input_dev = hi->input;
+       int ret;
+
+       ret = input_mt_init_slots(input_dev, touch_count, 0);
+       if (ret < 0)
+               return ret;
+
+       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0);
+
+       return 0;
+}
+
+static void sony_input_configured(struct hid_device *hdev,
+                                       struct hid_input *hidinput)
+{
+       struct sony_sc *sc = hid_get_drvdata(hdev);
+
+       /*
+        * The Dualshock 4 touchpad supports 2 touches and has a
+        * resolution of 1920x942 (44.86 dots/mm).
+        */
+       if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+               if (sony_register_touchpad(hidinput, 2, 1920, 942) != 0)
+                       hid_err(sc->hdev,
+                               "Unable to initialize multi-touch slots\n");
+       }
+}
+
 /*
  * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
  * to "operational".  Without this, the ps3 controller will not report any
@@ -1654,26 +1688,6 @@ static void sony_battery_remove(struct sony_sc *sc)
        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
@@ -1923,13 +1937,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                                goto err_stop;
                        }
                }
-               /*
-                * The Dualshock 4 touchpad supports 2 touches and has a
-                * resolution of 1920x940.
-                */
-               ret = sony_register_touchpad(sc, 2, 1920, 940);
-               if (ret < 0)
-                       goto err_stop;
 
                sony_init_work(sc, dualshock4_state_worker);
        } else {
@@ -2037,13 +2044,14 @@ static const struct hid_device_id sony_devices[] = {
 MODULE_DEVICE_TABLE(hid, sony_devices);
 
 static struct hid_driver sony_driver = {
-       .name          = "sony",
-       .id_table      = sony_devices,
-       .input_mapping = sony_mapping,
-       .probe         = sony_probe,
-       .remove        = sony_remove,
-       .report_fixup  = sony_report_fixup,
-       .raw_event     = sony_raw_event
+       .name             = "sony",
+       .id_table         = sony_devices,
+       .input_mapping    = sony_mapping,
+       .input_configured = sony_input_configured,
+       .probe            = sony_probe,
+       .remove           = sony_remove,
+       .report_fixup     = sony_report_fixup,
+       .raw_event        = sony_raw_event
 };
 
 static int __init sony_init(void)
index 134be89b15eadd3af92f81ac55e8d4d77790529e..b95d3978c2723ffc9a49a82bf07b2c2a06da0dbf 100644 (file)
@@ -208,10 +208,10 @@ unregister_red:
 
 static void thingm_remove_rgb(struct thingm_rgb *rgb)
 {
-       flush_work(&rgb->work);
        led_classdev_unregister(&rgb->red.ldev);
        led_classdev_unregister(&rgb->green.ldev);
        led_classdev_unregister(&rgb->blue.ldev);
+       flush_work(&rgb->work);
 }
 
 static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
@@ -250,6 +250,7 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
        if (!tdev->fwinfo) {
                hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
+               err = -ENODEV;
                goto stop;
        }
 
@@ -286,10 +287,10 @@ static void thingm_remove(struct hid_device *hdev)
        struct thingm_device *tdev = hid_get_drvdata(hdev);
        int i;
 
+       hid_hw_stop(hdev);
+
        for (i = 0; i < tdev->fwinfo->numrgb; ++i)
                thingm_remove_rgb(tdev->rgb + i);
-
-       hid_hw_stop(hdev);
 }
 
 static const struct hid_device_id thingm_table[] = {
index 0cb92e3472589d7a6260fbdd0a9d4e308d165d74..e094c572b86e61ff5b4108a29ef78340114d018e 100644 (file)
@@ -44,10 +44,12 @@ struct uhid_device {
        __u8 tail;
        struct uhid_event *outq[UHID_BUFSIZE];
 
+       /* blocking GET_REPORT support; state changes protected by qlock */
        struct mutex report_lock;
        wait_queue_head_t report_wait;
-       atomic_t report_done;
-       atomic_t report_id;
+       bool report_running;
+       u32 report_id;
+       u32 report_type;
        struct uhid_event report_buf;
 };
 
@@ -90,8 +92,27 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event)
 static int uhid_hid_start(struct hid_device *hid)
 {
        struct uhid_device *uhid = hid->driver_data;
+       struct uhid_event *ev;
+       unsigned long flags;
+
+       ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+       if (!ev)
+               return -ENOMEM;
+
+       ev->type = UHID_START;
 
-       return uhid_queue_event(uhid, UHID_START);
+       if (hid->report_enum[HID_FEATURE_REPORT].numbered)
+               ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS;
+       if (hid->report_enum[HID_OUTPUT_REPORT].numbered)
+               ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS;
+       if (hid->report_enum[HID_INPUT_REPORT].numbered)
+               ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS;
+
+       spin_lock_irqsave(&uhid->qlock, flags);
+       uhid_queue(uhid, ev);
+       spin_unlock_irqrestore(&uhid->qlock, flags);
+
+       return 0;
 }
 
 static void uhid_hid_stop(struct hid_device *hid)
@@ -123,87 +144,169 @@ static int uhid_hid_parse(struct hid_device *hid)
        return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
 }
 
-static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum,
-                           __u8 *buf, size_t count, unsigned char rtype)
+/* must be called with report_lock held */
+static int __uhid_report_queue_and_wait(struct uhid_device *uhid,
+                                       struct uhid_event *ev,
+                                       __u32 *report_id)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&uhid->qlock, flags);
+       *report_id = ++uhid->report_id;
+       uhid->report_type = ev->type + 1;
+       uhid->report_running = true;
+       uhid_queue(uhid, ev);
+       spin_unlock_irqrestore(&uhid->qlock, flags);
+
+       ret = wait_event_interruptible_timeout(uhid->report_wait,
+                               !uhid->report_running || !uhid->running,
+                               5 * HZ);
+       if (!ret || !uhid->running || uhid->report_running)
+               ret = -EIO;
+       else if (ret < 0)
+               ret = -ERESTARTSYS;
+       else
+               ret = 0;
+
+       uhid->report_running = false;
+
+       return ret;
+}
+
+static void uhid_report_wake_up(struct uhid_device *uhid, u32 id,
+                               const struct uhid_event *ev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhid->qlock, flags);
+
+       /* id for old report; drop it silently */
+       if (uhid->report_type != ev->type || uhid->report_id != id)
+               goto unlock;
+       if (!uhid->report_running)
+               goto unlock;
+
+       memcpy(&uhid->report_buf, ev, sizeof(*ev));
+       uhid->report_running = false;
+       wake_up_interruptible(&uhid->report_wait);
+
+unlock:
+       spin_unlock_irqrestore(&uhid->qlock, flags);
+}
+
+static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
+                              u8 *buf, size_t count, u8 rtype)
 {
        struct uhid_device *uhid = hid->driver_data;
-       __u8 report_type;
+       struct uhid_get_report_reply_req *req;
        struct uhid_event *ev;
-       unsigned long flags;
        int ret;
-       size_t uninitialized_var(len);
-       struct uhid_feature_answer_req *req;
 
        if (!uhid->running)
                return -EIO;
 
-       switch (rtype) {
-       case HID_FEATURE_REPORT:
-               report_type = UHID_FEATURE_REPORT;
-               break;
-       case HID_OUTPUT_REPORT:
-               report_type = UHID_OUTPUT_REPORT;
-               break;
-       case HID_INPUT_REPORT:
-               report_type = UHID_INPUT_REPORT;
-               break;
-       default:
-               return -EINVAL;
-       }
+       ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+       if (!ev)
+               return -ENOMEM;
+
+       ev->type = UHID_GET_REPORT;
+       ev->u.get_report.rnum = rnum;
+       ev->u.get_report.rtype = rtype;
 
        ret = mutex_lock_interruptible(&uhid->report_lock);
-       if (ret)
+       if (ret) {
+               kfree(ev);
                return ret;
+       }
 
-       ev = kzalloc(sizeof(*ev), GFP_KERNEL);
-       if (!ev) {
-               ret = -ENOMEM;
+       /* this _always_ takes ownership of @ev */
+       ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id);
+       if (ret)
                goto unlock;
+
+       req = &uhid->report_buf.u.get_report_reply;
+       if (req->err) {
+               ret = -EIO;
+       } else {
+               ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX);
+               memcpy(buf, req->data, ret);
        }
 
-       spin_lock_irqsave(&uhid->qlock, flags);
-       ev->type = UHID_FEATURE;
-       ev->u.feature.id = atomic_inc_return(&uhid->report_id);
-       ev->u.feature.rnum = rnum;
-       ev->u.feature.rtype = report_type;
+unlock:
+       mutex_unlock(&uhid->report_lock);
+       return ret;
+}
 
-       atomic_set(&uhid->report_done, 0);
-       uhid_queue(uhid, ev);
-       spin_unlock_irqrestore(&uhid->qlock, flags);
+static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum,
+                              const u8 *buf, size_t count, u8 rtype)
+{
+       struct uhid_device *uhid = hid->driver_data;
+       struct uhid_event *ev;
+       int ret;
 
-       ret = wait_event_interruptible_timeout(uhid->report_wait,
-                               atomic_read(&uhid->report_done), 5 * HZ);
-
-       /*
-        * Make sure "uhid->running" is cleared on shutdown before
-        * "uhid->report_done" is set.
-        */
-       smp_rmb();
-       if (!ret || !uhid->running) {
-               ret = -EIO;
-       } else if (ret < 0) {
-               ret = -ERESTARTSYS;
-       } else {
-               spin_lock_irqsave(&uhid->qlock, flags);
-               req = &uhid->report_buf.u.feature_answer;
+       if (!uhid->running || count > UHID_DATA_MAX)
+               return -EIO;
 
-               if (req->err) {
-                       ret = -EIO;
-               } else {
-                       ret = 0;
-                       len = min(count,
-                               min_t(size_t, req->size, UHID_DATA_MAX));
-                       memcpy(buf, req->data, len);
-               }
+       ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+       if (!ev)
+               return -ENOMEM;
+
+       ev->type = UHID_SET_REPORT;
+       ev->u.set_report.rnum = rnum;
+       ev->u.set_report.rtype = rtype;
+       ev->u.set_report.size = count;
+       memcpy(ev->u.set_report.data, buf, count);
 
-               spin_unlock_irqrestore(&uhid->qlock, flags);
+       ret = mutex_lock_interruptible(&uhid->report_lock);
+       if (ret) {
+               kfree(ev);
+               return ret;
        }
 
-       atomic_set(&uhid->report_done, 1);
+       /* this _always_ takes ownership of @ev */
+       ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id);
+       if (ret)
+               goto unlock;
+
+       if (uhid->report_buf.u.set_report_reply.err)
+               ret = -EIO;
+       else
+               ret = count;
 
 unlock:
        mutex_unlock(&uhid->report_lock);
-       return ret ? ret : len;
+       return ret;
+}
+
+static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
+                               __u8 *buf, size_t len, unsigned char rtype,
+                               int reqtype)
+{
+       u8 u_rtype;
+
+       switch (rtype) {
+       case HID_FEATURE_REPORT:
+               u_rtype = UHID_FEATURE_REPORT;
+               break;
+       case HID_OUTPUT_REPORT:
+               u_rtype = UHID_OUTPUT_REPORT;
+               break;
+       case HID_INPUT_REPORT:
+               u_rtype = UHID_INPUT_REPORT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (reqtype) {
+       case HID_REQ_GET_REPORT:
+               return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype);
+       case HID_REQ_SET_REPORT:
+               return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype);
+       default:
+               return -EIO;
+       }
 }
 
 static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
@@ -250,29 +353,14 @@ static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
        return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
 }
 
-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 = {
        .start = uhid_hid_start,
        .stop = uhid_hid_stop,
        .open = uhid_hid_open,
        .close = uhid_hid_close,
        .parse = uhid_hid_parse,
+       .raw_request = uhid_hid_raw_request,
        .output_report = uhid_hid_output_report,
-       .raw_request = uhid_raw_request,
 };
 
 #ifdef CONFIG_COMPAT
@@ -363,28 +451,27 @@ static int uhid_event_from_user(const char __user *buffer, size_t len,
 }
 #endif
 
-static int uhid_dev_create(struct uhid_device *uhid,
-                          const struct uhid_event *ev)
+static int uhid_dev_create2(struct uhid_device *uhid,
+                           const struct uhid_event *ev)
 {
        struct hid_device *hid;
+       size_t rd_size, len;
+       void *rd_data;
        int ret;
 
        if (uhid->running)
                return -EALREADY;
 
-       uhid->rd_size = ev->u.create.rd_size;
-       if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
+       rd_size = ev->u.create2.rd_size;
+       if (rd_size <= 0 || rd_size > HID_MAX_DESCRIPTOR_SIZE)
                return -EINVAL;
 
-       uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL);
-       if (!uhid->rd_data)
+       rd_data = kmemdup(ev->u.create2.rd_data, rd_size, GFP_KERNEL);
+       if (!rd_data)
                return -ENOMEM;
 
-       if (copy_from_user(uhid->rd_data, ev->u.create.rd_data,
-                          uhid->rd_size)) {
-               ret = -EFAULT;
-               goto err_free;
-       }
+       uhid->rd_size = rd_size;
+       uhid->rd_data = rd_data;
 
        hid = hid_allocate_device();
        if (IS_ERR(hid)) {
@@ -392,19 +479,19 @@ static int uhid_dev_create(struct uhid_device *uhid,
                goto err_free;
        }
 
-       strncpy(hid->name, ev->u.create.name, 127);
-       hid->name[127] = 0;
-       strncpy(hid->phys, ev->u.create.phys, 63);
-       hid->phys[63] = 0;
-       strncpy(hid->uniq, ev->u.create.uniq, 63);
-       hid->uniq[63] = 0;
+       len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1;
+       strncpy(hid->name, ev->u.create2.name, len);
+       len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1;
+       strncpy(hid->phys, ev->u.create2.phys, len);
+       len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1;
+       strncpy(hid->uniq, ev->u.create2.uniq, len);
 
        hid->ll_driver = &uhid_hid_driver;
-       hid->bus = ev->u.create.bus;
-       hid->vendor = ev->u.create.vendor;
-       hid->product = ev->u.create.product;
-       hid->version = ev->u.create.version;
-       hid->country = ev->u.create.country;
+       hid->bus = ev->u.create2.bus;
+       hid->vendor = ev->u.create2.vendor;
+       hid->product = ev->u.create2.product;
+       hid->version = ev->u.create2.version;
+       hid->country = ev->u.create2.country;
        hid->driver_data = uhid;
        hid->dev.parent = uhid_misc.this_device;
 
@@ -425,67 +512,34 @@ err_hid:
        uhid->running = false;
 err_free:
        kfree(uhid->rd_data);
+       uhid->rd_data = NULL;
+       uhid->rd_size = 0;
        return ret;
 }
 
-static int uhid_dev_create2(struct uhid_device *uhid,
-                           const struct uhid_event *ev)
+static int uhid_dev_create(struct uhid_device *uhid,
+                          struct uhid_event *ev)
 {
-       struct hid_device *hid;
-       int ret;
+       struct uhid_create_req orig;
 
-       if (uhid->running)
-               return -EALREADY;
+       orig = ev->u.create;
 
-       uhid->rd_size = ev->u.create2.rd_size;
-       if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)
+       if (orig.rd_size <= 0 || orig.rd_size > HID_MAX_DESCRIPTOR_SIZE)
                return -EINVAL;
+       if (copy_from_user(&ev->u.create2.rd_data, orig.rd_data, orig.rd_size))
+               return -EFAULT;
 
-       uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size,
-                               GFP_KERNEL);
-       if (!uhid->rd_data)
-               return -ENOMEM;
-
-       hid = hid_allocate_device();
-       if (IS_ERR(hid)) {
-               ret = PTR_ERR(hid);
-               goto err_free;
-       }
-
-       strncpy(hid->name, ev->u.create2.name, 127);
-       hid->name[127] = 0;
-       strncpy(hid->phys, ev->u.create2.phys, 63);
-       hid->phys[63] = 0;
-       strncpy(hid->uniq, ev->u.create2.uniq, 63);
-       hid->uniq[63] = 0;
-
-       hid->ll_driver = &uhid_hid_driver;
-       hid->bus = ev->u.create2.bus;
-       hid->vendor = ev->u.create2.vendor;
-       hid->product = ev->u.create2.product;
-       hid->version = ev->u.create2.version;
-       hid->country = ev->u.create2.country;
-       hid->driver_data = uhid;
-       hid->dev.parent = uhid_misc.this_device;
-
-       uhid->hid = hid;
-       uhid->running = true;
-
-       ret = hid_add_device(hid);
-       if (ret) {
-               hid_err(hid, "Cannot register HID device\n");
-               goto err_hid;
-       }
-
-       return 0;
-
-err_hid:
-       hid_destroy_device(hid);
-       uhid->hid = NULL;
-       uhid->running = false;
-err_free:
-       kfree(uhid->rd_data);
-       return ret;
+       memcpy(ev->u.create2.name, orig.name, sizeof(orig.name));
+       memcpy(ev->u.create2.phys, orig.phys, sizeof(orig.phys));
+       memcpy(ev->u.create2.uniq, orig.uniq, sizeof(orig.uniq));
+       ev->u.create2.rd_size = orig.rd_size;
+       ev->u.create2.bus = orig.bus;
+       ev->u.create2.vendor = orig.vendor;
+       ev->u.create2.product = orig.product;
+       ev->u.create2.version = orig.version;
+       ev->u.create2.country = orig.country;
+
+       return uhid_dev_create2(uhid, ev);
 }
 
 static int uhid_dev_destroy(struct uhid_device *uhid)
@@ -493,10 +547,7 @@ static int uhid_dev_destroy(struct uhid_device *uhid)
        if (!uhid->running)
                return -EINVAL;
 
-       /* clear "running" before setting "report_done" */
        uhid->running = false;
-       smp_wmb();
-       atomic_set(&uhid->report_done, 1);
        wake_up_interruptible(&uhid->report_wait);
 
        hid_destroy_device(uhid->hid);
@@ -527,28 +578,23 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
        return 0;
 }
 
-static int uhid_dev_feature_answer(struct uhid_device *uhid,
-                                  struct uhid_event *ev)
+static int uhid_dev_get_report_reply(struct uhid_device *uhid,
+                                    struct uhid_event *ev)
 {
-       unsigned long flags;
-
        if (!uhid->running)
                return -EINVAL;
 
-       spin_lock_irqsave(&uhid->qlock, flags);
-
-       /* id for old report; drop it silently */
-       if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id)
-               goto unlock;
-       if (atomic_read(&uhid->report_done))
-               goto unlock;
+       uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
+       return 0;
+}
 
-       memcpy(&uhid->report_buf, ev, sizeof(*ev));
-       atomic_set(&uhid->report_done, 1);
-       wake_up_interruptible(&uhid->report_wait);
+static int uhid_dev_set_report_reply(struct uhid_device *uhid,
+                                    struct uhid_event *ev)
+{
+       if (!uhid->running)
+               return -EINVAL;
 
-unlock:
-       spin_unlock_irqrestore(&uhid->qlock, flags);
+       uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);
        return 0;
 }
 
@@ -566,7 +612,6 @@ static int uhid_char_open(struct inode *inode, struct file *file)
        init_waitqueue_head(&uhid->waitq);
        init_waitqueue_head(&uhid->report_wait);
        uhid->running = false;
-       atomic_set(&uhid->report_done, 1);
 
        file->private_data = uhid;
        nonseekable_open(inode, file);
@@ -675,8 +720,11 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
        case UHID_INPUT2:
                ret = uhid_dev_input2(uhid, &uhid->input_buf);
                break;
-       case UHID_FEATURE_ANSWER:
-               ret = uhid_dev_feature_answer(uhid, &uhid->input_buf);
+       case UHID_GET_REPORT_REPLY:
+               ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf);
+               break;
+       case UHID_SET_REPORT_REPLY:
+               ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf);
                break;
        default:
                ret = -EOPNOTSUPP;
index 79cf503e37bf14eadd227f3e9e54c1a9f488ec25..ca6849a0121e7bee62b23740f52c0a66bddde5e8 100644 (file)
@@ -82,7 +82,7 @@ static int hid_start_in(struct hid_device *hid)
        struct usbhid_device *usbhid = hid->driver_data;
 
        spin_lock_irqsave(&usbhid->lock, flags);
-       if (hid->open > 0 &&
+       if ((hid->open > 0 || hid->quirks & HID_QUIRK_ALWAYS_POLL) &&
                        !test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
                        !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
                        !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
@@ -116,40 +116,24 @@ static void hid_reset(struct work_struct *work)
        struct usbhid_device *usbhid =
                container_of(work, struct usbhid_device, reset_work);
        struct hid_device *hid = usbhid->hid;
-       int rc = 0;
+       int rc;
 
        if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
                dev_dbg(&usbhid->intf->dev, "clear halt\n");
                rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
                clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
-               hid_start_in(hid);
-       }
-
-       else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
-               dev_dbg(&usbhid->intf->dev, "resetting device\n");
-               rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
                if (rc == 0) {
-                       rc = usb_reset_device(hid_to_usb_dev(hid));
-                       usb_unlock_device(hid_to_usb_dev(hid));
+                       hid_start_in(hid);
+               } else {
+                       dev_dbg(&usbhid->intf->dev,
+                                       "clear-halt failed: %d\n", rc);
+                       set_bit(HID_RESET_PENDING, &usbhid->iofl);
                }
-               clear_bit(HID_RESET_PENDING, &usbhid->iofl);
        }
 
-       switch (rc) {
-       case 0:
-               if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
-                       hid_io_error(hid);
-               break;
-       default:
-               hid_err(hid, "can't reset device, %s-%s/input%d, status %d\n",
-                       hid_to_usb_dev(hid)->bus->bus_name,
-                       hid_to_usb_dev(hid)->devpath,
-                       usbhid->ifnum, rc);
-               /* FALLTHROUGH */
-       case -EHOSTUNREACH:
-       case -ENODEV:
-       case -EINTR:
-               break;
+       if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
+               dev_dbg(&usbhid->intf->dev, "resetting device\n");
+               usb_queue_reset_device(usbhid->intf);
        }
 }
 
@@ -292,6 +276,8 @@ static void hid_irq_in(struct urb *urb)
        case 0:                 /* success */
                usbhid_mark_busy(usbhid);
                usbhid->retry_delay = 0;
+               if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open)
+                       break;
                hid_input_report(urb->context, HID_INPUT_REPORT,
                                 urb->transfer_buffer,
                                 urb->actual_length, 1);
@@ -735,8 +721,10 @@ void usbhid_close(struct hid_device *hid)
        if (!--hid->open) {
                spin_unlock_irq(&usbhid->lock);
                hid_cancel_delayed_stuff(usbhid);
-               usb_kill_urb(usbhid->urbin);
-               usbhid->intf->needs_remote_wakeup = 0;
+               if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) {
+                       usb_kill_urb(usbhid->urbin);
+                       usbhid->intf->needs_remote_wakeup = 0;
+               }
        } else {
                spin_unlock_irq(&usbhid->lock);
        }
@@ -1134,6 +1122,19 @@ static int usbhid_start(struct hid_device *hid)
 
        set_bit(HID_STARTED, &usbhid->iofl);
 
+       if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
+               ret = usb_autopm_get_interface(usbhid->intf);
+               if (ret)
+                       goto fail;
+               usbhid->intf->needs_remote_wakeup = 1;
+               ret = hid_start_in(hid);
+               if (ret) {
+                       dev_err(&hid->dev,
+                               "failed to start in urb: %d\n", ret);
+               }
+               usb_autopm_put_interface(usbhid->intf);
+       }
+
        /* Some keyboards don't work until their LEDs have been set.
         * Since BIOSes do set the LEDs, it must be safe for any device
         * that supports the keyboard boot protocol.
@@ -1166,6 +1167,9 @@ static void usbhid_stop(struct hid_device *hid)
        if (WARN_ON(!usbhid))
                return;
 
+       if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
+               usbhid->intf->needs_remote_wakeup = 0;
+
        clear_bit(HID_STARTED, &usbhid->iofl);
        spin_lock_irq(&usbhid->lock);   /* Sync with error and led handlers */
        set_bit(HID_DISCONNECTED, &usbhid->iofl);
index 15225f3eaed1b841974308568a02b905be952d99..f3cb5b0a43454e7b27b2d8c0e3ddf817aa539e6f 100644 (file)
@@ -70,6 +70,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
@@ -79,6 +80,8 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1610, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1640, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
+       { USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
index 3dcd00496064f4356154503e0e2943e5d66dc523..78ea9bf941cd3352def7eaff6f26304beb1e2f7e 100644 (file)
@@ -288,6 +288,7 @@ struct hid_item {
 #define HID_QUIRK_HIDINPUT_FORCE               0x00000080
 #define HID_QUIRK_NO_EMPTY_INPUT               0x00000100
 #define HID_QUIRK_NO_INIT_INPUT_REPORTS                0x00000200
+#define HID_QUIRK_ALWAYS_POLL                  0x00000400
 #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
index 1e3b09c191cd50648fd4bd31425a6adaa7c0bd7b..aaa86d6bd1dd921ab0637f232ced8678c60e8849 100644 (file)
 #include <linux/hid.h>
 
 enum uhid_event_type {
-       UHID_CREATE,
+       __UHID_LEGACY_CREATE,
        UHID_DESTROY,
        UHID_START,
        UHID_STOP,
        UHID_OPEN,
        UHID_CLOSE,
        UHID_OUTPUT,
-       UHID_OUTPUT_EV,                 /* obsolete! */
-       UHID_INPUT,
-       UHID_FEATURE,
-       UHID_FEATURE_ANSWER,
+       __UHID_LEGACY_OUTPUT_EV,
+       __UHID_LEGACY_INPUT,
+       UHID_GET_REPORT,
+       UHID_GET_REPORT_REPLY,
        UHID_CREATE2,
        UHID_INPUT2,
+       UHID_SET_REPORT,
+       UHID_SET_REPORT_REPLY,
 };
 
-struct uhid_create_req {
-       __u8 name[128];
-       __u8 phys[64];
-       __u8 uniq[64];
-       __u8 __user *rd_data;
-       __u16 rd_size;
-
-       __u16 bus;
-       __u32 vendor;
-       __u32 product;
-       __u32 version;
-       __u32 country;
-} __attribute__((__packed__));
-
 struct uhid_create2_req {
        __u8 name[128];
        __u8 phys[64];
@@ -66,6 +54,16 @@ struct uhid_create2_req {
        __u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
 } __attribute__((__packed__));
 
+enum uhid_dev_flag {
+       UHID_DEV_NUMBERED_FEATURE_REPORTS                       = (1ULL << 0),
+       UHID_DEV_NUMBERED_OUTPUT_REPORTS                        = (1ULL << 1),
+       UHID_DEV_NUMBERED_INPUT_REPORTS                         = (1ULL << 2),
+};
+
+struct uhid_start_req {
+       __u64 dev_flags;
+};
+
 #define UHID_DATA_MAX 4096
 
 enum uhid_report_type {
@@ -74,36 +72,94 @@ enum uhid_report_type {
        UHID_INPUT_REPORT,
 };
 
-struct uhid_input_req {
+struct uhid_input2_req {
+       __u16 size;
+       __u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
+struct uhid_output_req {
        __u8 data[UHID_DATA_MAX];
        __u16 size;
+       __u8 rtype;
 } __attribute__((__packed__));
 
-struct uhid_input2_req {
+struct uhid_get_report_req {
+       __u32 id;
+       __u8 rnum;
+       __u8 rtype;
+} __attribute__((__packed__));
+
+struct uhid_get_report_reply_req {
+       __u32 id;
+       __u16 err;
        __u16 size;
        __u8 data[UHID_DATA_MAX];
 } __attribute__((__packed__));
 
-struct uhid_output_req {
+struct uhid_set_report_req {
+       __u32 id;
+       __u8 rnum;
+       __u8 rtype;
+       __u16 size;
+       __u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
+struct uhid_set_report_reply_req {
+       __u32 id;
+       __u16 err;
+} __attribute__((__packed__));
+
+/*
+ * Compat Layer
+ * All these commands and requests are obsolete. You should avoid using them in
+ * new code. We support them for backwards-compatibility, but you might not get
+ * access to new feature in case you use them.
+ */
+
+enum uhid_legacy_event_type {
+       UHID_CREATE                     = __UHID_LEGACY_CREATE,
+       UHID_OUTPUT_EV                  = __UHID_LEGACY_OUTPUT_EV,
+       UHID_INPUT                      = __UHID_LEGACY_INPUT,
+       UHID_FEATURE                    = UHID_GET_REPORT,
+       UHID_FEATURE_ANSWER             = UHID_GET_REPORT_REPLY,
+};
+
+/* Obsolete! Use UHID_CREATE2. */
+struct uhid_create_req {
+       __u8 name[128];
+       __u8 phys[64];
+       __u8 uniq[64];
+       __u8 __user *rd_data;
+       __u16 rd_size;
+
+       __u16 bus;
+       __u32 vendor;
+       __u32 product;
+       __u32 version;
+       __u32 country;
+} __attribute__((__packed__));
+
+/* Obsolete! Use UHID_INPUT2. */
+struct uhid_input_req {
        __u8 data[UHID_DATA_MAX];
        __u16 size;
-       __u8 rtype;
 } __attribute__((__packed__));
 
-/* Obsolete! Newer kernels will no longer send these events but instead convert
- * it into raw output reports via UHID_OUTPUT. */
+/* Obsolete! Kernel uses UHID_OUTPUT exclusively now. */
 struct uhid_output_ev_req {
        __u16 type;
        __u16 code;
        __s32 value;
 } __attribute__((__packed__));
 
+/* Obsolete! Kernel uses ABI compatible UHID_GET_REPORT. */
 struct uhid_feature_req {
        __u32 id;
        __u8 rnum;
        __u8 rtype;
 } __attribute__((__packed__));
 
+/* Obsolete! Use ABI compatible UHID_GET_REPORT_REPLY. */
 struct uhid_feature_answer_req {
        __u32 id;
        __u16 err;
@@ -111,6 +167,15 @@ struct uhid_feature_answer_req {
        __u8 data[UHID_DATA_MAX];
 } __attribute__((__packed__));
 
+/*
+ * UHID Events
+ * All UHID events from and to the kernel are encoded as "struct uhid_event".
+ * The "type" field contains a UHID_* type identifier. All payload depends on
+ * that type and can be accessed via ev->u.XYZ accordingly.
+ * If user-space writes short events, they're extended with 0s by the kernel. If
+ * the kernel writes short events, user-space shall extend them with 0s.
+ */
+
 struct uhid_event {
        __u32 type;
 
@@ -120,9 +185,14 @@ struct uhid_event {
                struct uhid_output_req output;
                struct uhid_output_ev_req output_ev;
                struct uhid_feature_req feature;
+               struct uhid_get_report_req get_report;
                struct uhid_feature_answer_req feature_answer;
+               struct uhid_get_report_reply_req get_report_reply;
                struct uhid_create2_req create2;
                struct uhid_input2_req input2;
+               struct uhid_set_report_req set_report;
+               struct uhid_set_report_reply_req set_report_reply;
+               struct uhid_start_req start;
        } u;
 } __attribute__((__packed__));