OSDN Git Service

Merge branch 'for-6.6/steelseries' into for-linus
authorBenjamin Tissoires <bentiss@kernel.org>
Thu, 31 Aug 2023 08:45:52 +0000 (10:45 +0200)
committerBenjamin Tissoires <bentiss@kernel.org>
Thu, 31 Aug 2023 08:45:52 +0000 (10:45 +0200)
Add support for the Steelseries Arctis 1 XBox headset by Bastien Nocera

33 files changed:
.mailmap
Documentation/devicetree/bindings/input/ilitek,ili9882t.yaml [new file with mode: 0644]
Documentation/hid/hidintro.rst [new file with mode: 0644]
Documentation/hid/hidreport-parsing.rst [new file with mode: 0644]
Documentation/hid/index.rst
MAINTAINERS
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-cp2112.c
drivers/hid/hid-google-stadiaff.c [new file with mode: 0644]
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-nvidia-shield.c
drivers/hid/hid-roccat-arvo.c
drivers/hid/hid-roccat-isku.c
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-roccat-koneplus.c
drivers/hid/hid-roccat-konepure.c
drivers/hid/hid-roccat-kovaplus.c
drivers/hid/hid-roccat-pyra.c
drivers/hid/hid-roccat-ryos.c
drivers/hid/hid-roccat-savu.c
drivers/hid/hid-roccat.c
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-uclogic-core.c
drivers/hid/hidraw.c
drivers/hid/i2c-hid/i2c-hid-of-elan.c
include/linux/hid-roccat.h
include/linux/hid.h
include/linux/string_choices.h

index 1bce47a..cea6991 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -119,6 +119,9 @@ Daniel Borkmann <daniel@iogearbox.net> <dborkmann@redhat.com>
 Daniel Borkmann <daniel@iogearbox.net> <dborkman@redhat.com>
 Daniel Borkmann <daniel@iogearbox.net> <dxchgb@gmail.com>
 David Brownell <david-b@pacbell.net>
+David Rheinsberg <david@readahead.eu> <dh.herrmann@gmail.com>
+David Rheinsberg <david@readahead.eu> <dh.herrmann@googlemail.com>
+David Rheinsberg <david@readahead.eu> <david.rheinsberg@gmail.com>
 David Woodhouse <dwmw2@shinybook.infradead.org>
 Dengcheng Zhu <dzhu@wavecomp.com> <dczhu@mips.com>
 Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@gmail.com>
diff --git a/Documentation/devicetree/bindings/input/ilitek,ili9882t.yaml b/Documentation/devicetree/bindings/input/ilitek,ili9882t.yaml
new file mode 100644 (file)
index 0000000..c5d9e0e
--- /dev/null
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/ilitek,ili9882t.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ilitek ili9882t touchscreen controller
+
+maintainers:
+  - Cong Yang <yangcong5@huaqin.corp-partner.google.com>
+
+description:
+  Supports the Ilitek ili9882t touchscreen controller.
+  This touchscreen controller uses the i2c-hid protocol with a reset GPIO.
+
+allOf:
+  - $ref: /schemas/input/touchscreen/touchscreen.yaml#
+
+properties:
+  compatible:
+    const: ilitek,ili9882t
+
+  reg:
+    const: 0x41
+
+  interrupts:
+    maxItems: 1
+
+  panel: true
+
+  reset-gpios:
+    maxItems: 1
+    description: Reset GPIO.
+
+  vccio-supply:
+    description: The 1.8V supply to the touchscreen.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - panel
+  - vccio-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      touchscreen: touchscreen@41 {
+        compatible = "ilitek,ili9882t";
+        reg = <0x41>;
+
+        interrupt-parent = <&pio>;
+        interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
+
+        panel = <&panel>;
+        reset-gpios = <&pio 60 GPIO_ACTIVE_LOW>;
+        vccio-supply = <&mt6366_vio18_reg>;
+      };
+    };
diff --git a/Documentation/hid/hidintro.rst b/Documentation/hid/hidintro.rst
new file mode 100644 (file)
index 0000000..73523e3
--- /dev/null
@@ -0,0 +1,524 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Introduction to HID report descriptors
+======================================
+
+This chapter is meant to give a broad overview of what HID report
+descriptors are, and of how a casual (non-kernel) programmer can deal
+with HID devices that are not working well with Linux.
+
+.. contents::
+    :local:
+    :depth: 2
+
+.. toctree::
+   :maxdepth: 2
+
+   hidreport-parsing
+
+
+Introduction
+============
+
+HID stands for Human Interface Device, and can be whatever device you
+are using to interact with a computer, be it a mouse, a touchpad, a
+tablet, a microphone.
+
+Many HID devices work out the box, even if their hardware is different.
+For example, mice can have any number of buttons; they may have a
+wheel; movement sensitivity differs between different models, and so
+on. Nonetheless, most of the time everything just works, without the
+need to have specialized code in the kernel for every mouse model
+developed since 1970.
+
+This is because modern HID devices do advertise their capabilities
+through the *HID report descriptor*, a fixed set of bytes describing
+exactly what *HID reports* may be sent between the device and the host
+and the meaning of each individual bit in those reports. For example,
+a HID Report Descriptor may specify that "in a report with ID 3 the
+bits from 8 to 15 is the delta x coordinate of a mouse".
+
+The HID report itself then merely carries the actual data values
+without any extra meta information. Note that HID reports may be sent
+from the device ("Input Reports", i.e. input events), to the device
+("Output Reports" to e.g. change LEDs) or used for device configuration
+("Feature reports"). A device may support one or more HID reports.
+
+The HID subsystem is in charge of parsing the HID report descriptors,
+and converts HID events into normal input device interfaces (see
+Documentation/hid/hid-transport.rst). Devices may misbehave because the
+HID report descriptor provided by the device is wrong, or because it
+needs to be dealt with in a special way, or because some special
+device or interaction mode is not handled by the default code.
+
+The format of HID report descriptors is described by two documents,
+available from the `USB Implementers Forum <https://www.usb.org/>`_
+`HID web page <https://www.usb.org/hid>`_ address:
+
+ * the `HID USB Device Class Definition
+   <https://www.usb.org/document-library/device-class-definition-hid-111>`_ (HID Spec from now on)
+ * the `HID Usage Tables <https://usb.org/document-library/hid-usage-tables-14>`_ (HUT from now on)
+
+The HID subsystem can deal with different transport drivers
+(USB, I2C, Bluetooth, etc.). See Documentation/hid/hid-transport.rst.
+
+Parsing HID report descriptors
+==============================
+
+The current list of HID devices can be found at ``/sys/bus/hid/devices/``.
+For each device, say ``/sys/bus/hid/devices/0003\:093A\:2510.0002/``,
+one can read the corresponding report descriptor::
+
+  $ hexdump -C /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor
+  00000000  05 01 09 02 a1 01 09 01  a1 00 05 09 19 01 29 03  |..............).|
+  00000010  15 00 25 01 75 01 95 03  81 02 75 05 95 01 81 01  |..%.u.....u.....|
+  00000020  05 01 09 30 09 31 09 38  15 81 25 7f 75 08 95 03  |...0.1.8..%.u...|
+  00000030  81 06 c0 c0                                       |....|
+  00000034
+
+Optional: the HID report descriptor can be read also by
+directly accessing the hidraw driver [#hidraw]_.
+
+The basic structure of HID report descriptors is defined in the HID
+spec, while HUT "defines constants that can be interpreted by an
+application to identify the purpose and meaning of a data field in a
+HID report". Each entry is defined by at least two bytes, where the
+first one defines what type of value is following and is described in
+the HID spec, while the second one carries the actual value and is
+described in the HUT.
+
+HID report descriptors can, in principle, be painstakingly parsed by
+hand, byte by byte.
+
+A short introduction on how to do this is sketched in
+Documentation/hid/hidreport-parsing.rst; you only need to understand it
+if you need to patch HID report descriptors.
+
+In practice you should not parse HID report descriptors by hand; rather,
+you should use an existing parser. Among all the available ones
+
+  * the online `USB Descriptor and Request Parser
+    <http://eleccelerator.com/usbdescreqparser/>`_;
+  * `hidrdd <https://github.com/abend0c1/hidrdd>`_,
+    that provides very detailed and somewhat verbose descriptions
+    (verbosity can be useful if you are not familiar with HID report
+    descriptors);
+  * `hid-tools <https://gitlab.freedesktop.org/libevdev/hid-tools>`_,
+    a complete utility set that allows, among other things,
+    to record and replay the raw HID reports and to debug
+    and replay HID devices.
+    It is being actively developed by the Linux HID subsystem maintainers.
+
+Parsing the mouse HID report descriptor with `hid-tools
+<https://gitlab.freedesktop.org/libevdev/hid-tools>`_ leads to
+(explanations interposed)::
+
+    $ ./hid-decode /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor
+    # device 0:0
+    # 0x05, 0x01,                   // Usage Page (Generic Desktop)        0
+    # 0x09, 0x02,                   // Usage (Mouse)                       2
+    # 0xa1, 0x01,                   // Collection (Application)            4
+    # 0x09, 0x01,                   // Usage (Pointer)                     6
+    # 0xa1, 0x00,                   // Collection (Physical)               8
+    # 0x05, 0x09,                   // Usage Page (Button)                10
+
+what follows is a button ::
+
+    # 0x19, 0x01,                   // Usage Minimum (1)                  12
+    # 0x29, 0x03,                   // Usage Maximum (3)                  14
+
+first button is button number 1, last button is button number 3 ::
+
+    # 0x15, 0x00,                   // Logical Minimum (0)                16
+    # 0x25, 0x01,                   // Logical Maximum (1)                18
+
+each button can send values from 0 up to including 1
+(i.e. they are binary buttons) ::
+
+    # 0x75, 0x01,                   // Report Size (1)                    20
+
+each button is sent as exactly one bit ::
+
+    # 0x95, 0x03,                   // Report Count (3)                   22
+
+and there are three of those bits (matching the three buttons) ::
+
+    # 0x81, 0x02,                   // Input (Data,Var,Abs)               24
+
+it's actual Data (not constant padding), they represent
+a single variable (Var) and their values are Absolute (not relative);
+See HID spec Sec. 6.2.2.5 "Input, Output, and Feature Items" ::
+
+    # 0x75, 0x05,                   // Report Size (5)                    26
+
+five additional padding bits, needed to reach a byte ::
+
+    # 0x95, 0x01,                   // Report Count (1)                   28
+
+those five bits are repeated only once ::
+
+    # 0x81, 0x01,                   // Input (Cnst,Arr,Abs)               30
+
+and take Constant (Cnst) values i.e. they can be ignored. ::
+
+    # 0x05, 0x01,                   // Usage Page (Generic Desktop)       32
+    # 0x09, 0x30,                   // Usage (X)                          34
+    # 0x09, 0x31,                   // Usage (Y)                          36
+    # 0x09, 0x38,                   // Usage (Wheel)                      38
+
+The mouse has also two physical positions (Usage (X), Usage (Y))
+and a wheel (Usage (Wheel)) ::
+
+    # 0x15, 0x81,                   // Logical Minimum (-127)             40
+    # 0x25, 0x7f,                   // Logical Maximum (127)              42
+
+each of them can send values ranging from -127 up to including 127 ::
+
+    # 0x75, 0x08,                   // Report Size (8)                    44
+
+which is represented by eight bits ::
+
+    # 0x95, 0x03,                   // Report Count (3)                   46
+
+and there are three of those eight bits, matching X, Y and Wheel. ::
+
+    # 0x81, 0x06,                   // Input (Data,Var,Rel)               48
+
+This time the data values are Relative (Rel), i.e. they represent
+the change from the previously sent report (event) ::
+
+    # 0xc0,                         // End Collection                     50
+    # 0xc0,                         // End Collection                     51
+    #
+    R: 52 05 01 09 02 a1 01 09 01 a1 00 05 09 19 01 29 03 15 00 25 01 75 01 95 03 81 02 75 05 95 01 81 01 05 01 09 30 09 31 09 38 15 81 25 7f 75 08 95 03 81 06 c0 c0
+    N: device 0:0
+    I: 3 0001 0001
+
+
+This Report Descriptor tells us that the mouse input will be
+transmitted using four bytes: the first one for the buttons (three
+bits used, five for padding), the last three for the mouse X, Y and
+wheel changes, respectively.
+
+Indeed, for any event, the mouse will send a *report* of four bytes.
+We can check the values sent by resorting e.g. to the `hid-recorder`
+tool, from `hid-tools <https://gitlab.freedesktop.org/libevdev/hid-tools>`_:
+The sequence of bytes sent by clicking and releasing button 1, then button 2, then button 3 is::
+
+  $ sudo ./hid-recorder /dev/hidraw1
+
+  ....
+  output of hid-decode
+  ....
+
+  #  Button: 1  0  0 | # | X:   0 | Y:    0 | Wheel:    0
+  E: 000000.000000 4 01 00 00 00
+  #  Button: 0  0  0 | # | X:   0 | Y:    0 | Wheel:    0
+  E: 000000.183949 4 00 00 00 00
+  #  Button: 0  1  0 | # | X:   0 | Y:    0 | Wheel:    0
+  E: 000001.959698 4 02 00 00 00
+  #  Button: 0  0  0 | # | X:   0 | Y:    0 | Wheel:    0
+  E: 000002.103899 4 00 00 00 00
+  #  Button: 0  0  1 | # | X:   0 | Y:    0 | Wheel:    0
+  E: 000004.855799 4 04 00 00 00
+  #  Button: 0  0  0 | # | X:    0 | Y:    0 | Wheel:    0
+  E: 000005.103864 4 00 00 00 00
+
+This example shows that when button 2 is clicked,
+the bytes ``02 00 00 00`` are sent, and the immediately subsequent
+event (``00 00 00 00``) is the release of button 2 (no buttons are
+pressed, remember that the data values are *absolute*).
+
+If instead one clicks and holds button 1, then clicks and holds button
+2, releases button 1, and finally releases button 2, the reports are::
+
+  #  Button: 1  0  0 | # | X:    0 | Y:    0 | Wheel:    0
+  E: 000044.175830 4 01 00 00 00
+  #  Button: 1  1  0 | # | X:    0 | Y:    0 | Wheel:    0
+  E: 000045.975997 4 03 00 00 00
+  #  Button: 0  1  0 | # | X:    0 | Y:    0 | Wheel:    0
+  E: 000047.407930 4 02 00 00 00
+  #  Button: 0  0  0 | # | X:    0 | Y:    0 | Wheel:    0
+  E: 000049.199919 4 00 00 00 00
+
+where with ``03 00 00 00`` both buttons are pressed, and with the
+subsequent ``02 00 00 00`` button 1 is released while button 2 is still
+active.
+
+Output, Input and Feature Reports
+---------------------------------
+
+HID devices can have Input Reports, like in the mouse example, Output
+Reports, and Feature Reports. "Output" means that the information is
+sent to the device. For example, a joystick with force feedback will
+have some output; the led of a keyboard would need an output as well.
+"Input" means that data come from the device.
+
+"Feature"s are not meant to be consumed by the end user and define
+configuration options for the device. They can be queried from the host;
+when declared as *Volatile* they should be changed by the host.
+
+
+Collections, Report IDs and Evdev events
+========================================
+
+A single device can logically group data into different independent
+sets, called a *Collection*. Collections can be nested and there are
+different types of collections (see the HID spec 6.2.2.6
+"Collection, End Collection Items" for details).
+
+Different reports are identified by means of different *Report ID*
+fields, i.e. a number identifying the structure of the immediately
+following report.
+Whenever a Report ID is needed it is transmitted as the first byte of
+any report. A device with only one supported HID report (like the mouse
+example above) may omit the report ID.
+
+Consider the following HID report descriptor::
+
+  05 01 09 02 A1 01 85 01 05 09 19 01 29 05 15 00
+  25 01 95 05 75 01 81 02 95 01 75 03 81 01 05 01
+  09 30 09 31 16 00 F8 26 FF 07 75 0C 95 02 81 06
+  09 38 15 80 25 7F 75 08 95 01 81 06 05 0C 0A 38
+  02 15 80 25 7F 75 08 95 01 81 06 C0 05 01 09 02
+  A1 01 85 02 05 09 19 01 29 05 15 00 25 01 95 05
+  75 01 81 02 95 01 75 03 81 01 05 01 09 30 09 31
+  16 00 F8 26 FF 07 75 0C 95 02 81 06 09 38 15 80
+  25 7F 75 08 95 01 81 06 05 0C 0A 38 02 15 80 25
+  7F 75 08 95 01 81 06 C0 05 01 09 07 A1 01 85 05
+  05 07 15 00 25 01 09 29 09 3E 09 4B 09 4E 09 E3
+  09 E8 09 E8 09 E8 75 01 95 08 81 02 95 00 81 01
+  C0 05 0C 09 01 A1 01 85 06 15 00 25 01 75 01 95
+  01 09 3F 81 06 09 3F 81 06 09 3F 81 06 09 3F 81
+  06 09 3F 81 06 09 3F 81 06 09 3F 81 06 09 3F 81
+  06 C0 05 0C 09 01 A1 01 85 03 09 05 15 00 26 FF
+  00 75 08 95 02 B1 02 C0
+
+After parsing it (try to parse it on your own using the suggested
+tools!) one can see that the device presents two ``Mouse`` Application
+Collections (with reports identified by Reports IDs 1 and 2,
+respectively), a ``Keypad`` Application Collection (whose report is
+identified by the Report ID 5) and two ``Consumer Controls`` Application
+Collections, (with Report IDs 6 and 3, respectively). Note, however,
+that a device can have different Report IDs for the same Application
+Collection.
+
+The data sent will begin with the Report ID byte, and will be followed
+by the corresponding information. For example, the data transmitted for
+the last consumer control::
+
+  0x05, 0x0C,        // Usage Page (Consumer)
+  0x09, 0x01,        // Usage (Consumer Control)
+  0xA1, 0x01,        // Collection (Application)
+  0x85, 0x03,        //   Report ID (3)
+  0x09, 0x05,        //   Usage (Headphone)
+  0x15, 0x00,        //   Logical Minimum (0)
+  0x26, 0xFF, 0x00,  //   Logical Maximum (255)
+  0x75, 0x08,        //   Report Size (8)
+  0x95, 0x02,        //   Report Count (2)
+  0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+  0xC0,              // End Collection
+
+will be of three bytes: the first for the Report ID (3), the next two
+for the headphone, with two (``Report Count (2)``) bytes
+(``Report Size (8)``), each ranging from 0 (``Logical Minimum (0)``)
+to 255 (``Logical Maximum (255)``).
+
+All the Input data sent by the device should be translated into
+corresponding Evdev events, so that the remaining part of the stack can
+know what is going on, e.g. the bit for the first button translates into
+the ``EV_KEY/BTN_LEFT`` evdev event and relative X movement translates
+into the ``EV_REL/REL_X`` evdev event".
+
+Events
+======
+
+In Linux, one ``/dev/input/event*`` is created for each ``Application
+Collection``. Going back to the mouse example, and repeating the
+sequence where one clicks and holds button 1, then clicks and holds
+button 2, releases button 1, and finally releases button 2, one gets::
+
+  $ sudo libinput record /dev/input/event1
+  # libinput record
+  version: 1
+  ndevices: 1
+  libinput:
+    version: "1.23.0"
+    git: "unknown"
+  system:
+    os: "opensuse-tumbleweed:20230619"
+    kernel: "6.3.7-1-default"
+    dmi: "dmi:bvnHP:bvrU77Ver.01.05.00:bd03/24/2022:br5.0:efr20.29:svnHP:pnHPEliteBook64514inchG9NotebookPC:pvr:rvnHP:rn89D2:rvrKBCVersion14.1D.00:cvnHP:ct10:cvr:sku5Y3J1EA#ABZ:"
+  devices:
+  - node: /dev/input/event1
+    evdev:
+      # Name: PixArt HP USB Optical Mouse
+      # ID: bus 0x3 vendor 0x3f0 product 0x94a version 0x111
+      # Supported Events:
+      # Event type 0 (EV_SYN)
+      # Event type 1 (EV_KEY)
+      #   Event code 272 (BTN_LEFT)
+      #   Event code 273 (BTN_RIGHT)
+      #   Event code 274 (BTN_MIDDLE)
+      # Event type 2 (EV_REL)
+      #   Event code 0 (REL_X)
+      #   Event code 1 (REL_Y)
+      #   Event code 8 (REL_WHEEL)
+      #   Event code 11 (REL_WHEEL_HI_RES)
+      # Event type 4 (EV_MSC)
+      #   Event code 4 (MSC_SCAN)
+      # Properties:
+      name: "PixArt HP USB Optical Mouse"
+      id: [3, 1008, 2378, 273]
+      codes:
+       0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] # EV_SYN
+       1: [272, 273, 274] # EV_KEY
+       2: [0, 1, 8, 11] # EV_REL
+       4: [4] # EV_MSC
+      properties: []
+    hid: [
+      0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
+      0x15, 0x00, 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31,
+      0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, 0x81, 0x06, 0xc0, 0xc0
+    ]
+    udev:
+      properties:
+      - ID_INPUT=1
+      - ID_INPUT_MOUSE=1
+      - LIBINPUT_DEVICE_GROUP=3/3f0/94a:usb-0000:05:00.3-2
+    quirks:
+    events:
+    # Current time is 12:31:56
+    - evdev:
+      - [  0,     0,   4,   4,      30] # EV_MSC / MSC_SCAN                 30 (obfuscated)
+      - [  0,     0,   1, 272,       1] # EV_KEY / BTN_LEFT                  1
+      - [  0,     0,   0,   0,       0] # ------------ SYN_REPORT (0) ---------- +0ms
+    - evdev:
+      - [  1, 207892,  4,   4,      30] # EV_MSC / MSC_SCAN                 30 (obfuscated)
+      - [  1, 207892,  1, 273,       1] # EV_KEY / BTN_RIGHT                 1
+      - [  1, 207892,  0,   0,       0] # ------------ SYN_REPORT (0) ---------- +1207ms
+    - evdev:
+      - [  2, 367823,  4,   4,      30] # EV_MSC / MSC_SCAN                 30 (obfuscated)
+      - [  2, 367823,  1, 272,       0] # EV_KEY / BTN_LEFT                  0
+      - [  2, 367823,  0,   0,       0] # ------------ SYN_REPORT (0) ---------- +1160ms
+    # Current time is 12:32:00
+    - evdev:
+      - [  3, 247617,  4,   4,      30] # EV_MSC / MSC_SCAN                 30 (obfuscated)
+      - [  3, 247617,  1, 273,       0] # EV_KEY / BTN_RIGHT                 0
+      - [  3, 247617,   0,   0,       0] # ------------ SYN_REPORT (0) ---------- +880ms
+
+Note: if ``libinput record`` is not available on your system try using
+``evemu-record``.
+
+When something does not work
+============================
+
+There can be a number of reasons why a device does not behave
+correctly. For example
+
+* The HID report descriptor provided by the HID device may be wrong
+  because e.g.
+
+  * it does not follow the standard, so that the kernel
+    will not able to make sense of the HID report descriptor;
+  * the HID report descriptor *does not match* what is actually
+    sent by the device (this can be verified by reading the raw HID
+    data);
+* the HID report descriptor may need some "quirks" (see later on).
+
+As a consequence, a ``/dev/input/event*`` may not be created
+for each Application Collection, and/or the events
+there may not match what you would expect.
+
+
+Quirks
+------
+
+There are some known peculiarities of HID devices that the kernel
+knows how to fix - these are called the HID quirks and a list of those
+is available in `include/linux/hid.h`.
+
+Should this be the case, it should be enough to add the required quirk
+in the kernel, for the HID device at hand. This can be done in the file
+`drivers/hid/hid-quirks.c`. How to do it should be relatively
+straightforward after looking into the file.
+
+The list of currently defined quirks, from `include/linux/hid.h`, is
+
+.. kernel-doc:: include/linux/hid.h
+   :doc: HID quirks
+
+Quirks for USB devices can be specified while loading the usbhid module,
+see ``modinfo usbhid``, although the proper fix should go into
+hid-quirks.c and **be submitted upstream**.
+See Documentation/process/submitting-patches.rst for guidelines on how
+to submit a patch. Quirks for other busses need to go into hid-quirks.c.
+
+Fixing HID report descriptors
+-----------------------------
+
+Should you need to patch HID report descriptors the easiest way is to
+resort to eBPF, as described in Documentation/hid/hid-bpf.rst.
+
+Basically, you can change any byte of the original HID report
+descriptor. The examples in samples/hid should be a good starting point
+for your code, see e.g. `samples/hid/hid_mouse.bpf.c`::
+
+  SEC("fmod_ret/hid_bpf_rdesc_fixup")
+  int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hctx)
+  {
+    ....
+       data[39] = 0x31;
+       data[41] = 0x30;
+    return 0;
+  }
+
+Of course this can be also done within the kernel source code, see e.g.
+`drivers/hid/hid-aureal.c` or `drivers/hid/hid-samsung.c` for a slightly
+more complex file.
+
+Check Documentation/hid/hidreport-parsing.rst if you need any help
+navigating the HID manuals and understanding the exact meaning of
+the HID report descriptor hex numbers.
+
+Whatever solution you come up with, please remember to **submit the
+fix to the HID maintainers**, so that it can be directly integrated in
+the kernel and that particular HID device will start working for
+everyone else. See Documentation/process/submitting-patches.rst for
+guidelines on how to do this.
+
+
+Modifying the transmitted data on the fly
+-----------------------------------------
+
+Using eBPF it is also possible to modify the data exchanged with the
+device. See again the examples in `samples/hid`.
+
+Again, **please post your fix**, so that it can be integrated in the
+kernel!
+
+Writing a specialized driver
+----------------------------
+
+This should really be your last resort.
+
+
+.. rubric:: Footnotes
+
+.. [#hidraw] read hidraw: see Documentation/hid/hidraw.rst and
+  file `samples/hidraw/hid-example.c` for an example.
+  The output of ``hid-example`` would be, for the same mouse::
+
+    $ sudo ./hid-example
+    Report Descriptor Size: 52
+    Report Descriptor:
+    5 1 9 2 a1 1 9 1 a1 0 5 9 19 1 29 3 15 0 25 1 75 1 95 3 81 2 75 5 95 1 81 1 5 1 9 30 9 31 9 38 15 81 25 7f 75 8 95 3 81 6 c0 c0
+
+    Raw Name: PixArt USB Optical Mouse
+    Raw Phys: usb-0000:05:00.4-2.3/input0
+    Raw Info:
+            bustype: 3 (USB)
+            vendor: 0x093a
+            product: 0x2510
+    ...
diff --git a/Documentation/hid/hidreport-parsing.rst b/Documentation/hid/hidreport-parsing.rst
new file mode 100644 (file)
index 0000000..1d3c17f
--- /dev/null
@@ -0,0 +1,49 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================================
+Manual parsing of HID report descriptors
+========================================
+
+Consider again the mouse HID report descriptor
+introduced in Documentation/hid/hidintro.rst::
+
+  $ hexdump -C /sys/bus/hid/devices/0003\:093A\:2510.0002/report_descriptor
+  00000000  05 01 09 02 a1 01 09 01  a1 00 05 09 19 01 29 03  |..............).|
+  00000010  15 00 25 01 75 01 95 03  81 02 75 05 95 01 81 01  |..%.u.....u.....|
+  00000020  05 01 09 30 09 31 09 38  15 81 25 7f 75 08 95 03  |...0.1.8..%.u...|
+  00000030  81 06 c0 c0                                       |....|
+  00000034
+
+and try to parse it by hand.
+
+Start with the first number, 0x05: it carries 2 bits for the
+length of the item, 2 bits for the type of the item and 4 bits for the
+function::
+
+  +----------+
+  | 00000101 |
+  +----------+
+          ^^
+          ---- Length of data (see HID spec 6.2.2.2)
+        ^^
+        ------ Type of the item (see HID spec 6.2.2.2, then jump to 6.2.2.7)
+    ^^^^
+    --------- Function of the item (see HID spec 6.2.2.7, then HUT Sec 3)
+
+In our case, the length is 1 byte, the type is ``Global`` and the
+function is ``Usage Page``, thus for parsing the value 0x01 in the second byte
+we need to refer to HUT Sec 3.
+
+The second number is the actual data, and its meaning can be found in
+the HUT. We have a ``Usage Page``, thus we need to refer to HUT
+Sec. 3, "Usage Pages"; from there, one sees that ``0x01`` stands for
+``Generic Desktop Page``.
+
+Moving now to the second two bytes, and following the same scheme,
+``0x09`` (i.e. ``00001001``) will be followed by one byte (``01``)
+and is a ``Local`` item (``10``). Thus, the meaning of the remaining four bits
+(``0000``) is given in the HID spec Sec. 6.2.2.8 "Local Items", so that
+we have a ``Usage``. From HUT, Sec. 4, "Generic Desktop Page",  we see that
+0x02 stands for ``Mouse``.
+
+The following numbers can be parsed in the same way.
index b2028f3..af02cf7 100644 (file)
@@ -7,6 +7,7 @@ Human Interface Devices (HID)
 .. toctree::
    :maxdepth: 1
 
+   hidintro
    hiddev
    hidraw
    hid-sensor
index 3be1bdf..23f3456 100644 (file)
@@ -21778,7 +21778,7 @@ F:      Documentation/admin-guide/ufs.rst
 F:     fs/ufs/
 
 UHID USERSPACE HID IO DRIVER
-M:     David Rheinsberg <david.rheinsberg@gmail.com>
+M:     David Rheinsberg <david@readahead.eu>
 L:     linux-input@vger.kernel.org
 S:     Maintained
 F:     drivers/hid/uhid.c
@@ -22902,7 +22902,7 @@ S:      Maintained
 F:     drivers/rtc/rtc-sd3078.c
 
 WIIMOTE HID DRIVER
-M:     David Rheinsberg <david.rheinsberg@gmail.com>
+M:     David Rheinsberg <david@readahead.eu>
 L:     linux-input@vger.kernel.org
 S:     Maintained
 F:     drivers/hid/hid-wiimote*
index 5afdeff..0cea301 100644 (file)
@@ -412,6 +412,13 @@ config HID_GOOGLE_HAMMER
        help
        Say Y here if you have a Google Hammer device.
 
+config HID_GOOGLE_STADIA_FF
+       tristate "Google Stadia force feedback"
+       select INPUT_FF_MEMLESS
+       help
+       Say Y here if you want to enable force feedback support for the Google
+       Stadia controller.
+
 config HID_VIVALDI
        tristate "Vivaldi Keyboard"
        select HID_VIVALDI_COMMON
index 7a9e160..8a06d0f 100644 (file)
@@ -55,6 +55,7 @@ obj-$(CONFIG_HID_GFRM)                += hid-gfrm.o
 obj-$(CONFIG_HID_GLORIOUS)  += hid-glorious.o
 obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o
 obj-$(CONFIG_HID_GOOGLE_HAMMER)        += hid-google-hammer.o
+obj-$(CONFIG_HID_GOOGLE_STADIA_FF)     += hid-google-stadiaff.o
 obj-$(CONFIG_HID_VIVALDI)      += hid-vivaldi.o
 obj-$(CONFIG_HID_GT683R)       += hid-gt683r.o
 obj-$(CONFIG_HID_GYRATION)     += hid-gyration.o
index 27cadad..54c33a2 100644 (file)
  *   https://www.silabs.com/documents/public/application-notes/an495-cp2112-interface-specification.pdf
  */
 
-#include <linux/gpio/consumer.h>
-#include <linux/gpio/machine.h>
+#include <linux/bitops.h>
 #include <linux/gpio/driver.h>
 #include <linux/hid.h>
 #include <linux/hidraw.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/nls.h>
+#include <linux/string_choices.h>
 #include <linux/usb/ch9.h>
 #include "hid-ids.h"
 
@@ -31,6 +31,8 @@
 #define CP2112_GPIO_CONFIG_LENGTH              5
 #define CP2112_GPIO_GET_LENGTH                 2
 #define CP2112_GPIO_SET_LENGTH                 3
+#define CP2112_GPIO_MAX_GPIO                   8
+#define CP2112_GPIO_ALL_GPIO_MASK              GENMASK(7, 0)
 
 enum {
        CP2112_GPIO_CONFIG              = 0x02,
@@ -163,19 +165,17 @@ struct cp2112_device {
        atomic_t read_avail;
        atomic_t xfer_avail;
        struct gpio_chip gc;
-       struct irq_chip irq;
        u8 *in_out_buffer;
        struct mutex lock;
 
-       struct gpio_desc *desc[8];
        bool gpio_poll;
        struct delayed_work gpio_poll_worker;
        unsigned long irq_mask;
        u8 gpio_prev_state;
 };
 
-static int gpio_push_pull = 0xFF;
-module_param(gpio_push_pull, int, S_IRUGO | S_IWUSR);
+static int gpio_push_pull = CP2112_GPIO_ALL_GPIO_MASK;
+module_param(gpio_push_pull, int, 0644);
 MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask");
 
 static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -197,7 +197,7 @@ static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
                goto exit;
        }
 
-       buf[1] &= ~(1 << offset);
+       buf[1] &= ~BIT(offset);
        buf[2] = gpio_push_pull;
 
        ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
@@ -227,8 +227,8 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
        mutex_lock(&dev->lock);
 
        buf[0] = CP2112_GPIO_SET;
-       buf[1] = value ? 0xff : 0;
-       buf[2] = 1 << offset;
+       buf[1] = value ? CP2112_GPIO_ALL_GPIO_MASK : 0;
+       buf[2] = BIT(offset);
 
        ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf,
                                 CP2112_GPIO_SET_LENGTH, HID_FEATURE_REPORT,
@@ -532,15 +532,13 @@ static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
        hid_dbg(hdev, "I2C %d messages\n", num);
 
        if (num == 1) {
+               hid_dbg(hdev, "I2C %s %#04x len %d\n",
+                       str_read_write(msgs->flags & I2C_M_RD), msgs->addr, msgs->len);
                if (msgs->flags & I2C_M_RD) {
-                       hid_dbg(hdev, "I2C read %#04x len %d\n",
-                               msgs->addr, msgs->len);
                        read_length = msgs->len;
                        read_buf = msgs->buf;
                        count = cp2112_read_req(buf, msgs->addr, msgs->len);
                } else {
-                       hid_dbg(hdev, "I2C write %#04x len %d\n",
-                               msgs->addr, msgs->len);
                        count = cp2112_i2c_write_req(buf, msgs->addr,
                                                     msgs->buf, msgs->len);
                }
@@ -648,7 +646,7 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
        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",
+               str_write_read(read_write == I2C_SMBUS_WRITE),
                addr, flags, command, size);
 
        switch (size) {
@@ -895,7 +893,7 @@ static ssize_t name##_show(struct device *kdev, \
        int ret = cp2112_get_usb_config(hdev, &cfg); \
        if (ret) \
                return ret; \
-       return scnprintf(buf, PAGE_SIZE, format, ##__VA_ARGS__); \
+       return sysfs_emit(buf, format, ##__VA_ARGS__); \
 } \
 static DEVICE_ATTR_RW(name);
 
@@ -946,18 +944,10 @@ CP2112_CONFIG_ATTR(release_version, ({
 
 #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)
+static ssize_t pstr_store(struct device *kdev, struct device_attribute *kattr,
+                         const char *buf, size_t count, int number)
 {
        struct hid_device *hdev = to_hid_device(kdev);
-       struct cp2112_pstring_attribute *attr =
-               container_of(kattr, struct cp2112_pstring_attribute, attr);
        struct cp2112_string_report report;
        int ret;
 
@@ -965,7 +955,7 @@ static ssize_t pstr_store(struct device *kdev,
 
        ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN,
                              report.string, ARRAY_SIZE(report.string));
-       report.report = attr->report;
+       report.report = number;
        report.length = ret * sizeof(report.string[0]) + 2;
        report.type = USB_DT_STRING;
 
@@ -983,17 +973,15 @@ static ssize_t pstr_store(struct device *kdev,
        return count;
 }
 
-static ssize_t pstr_show(struct device *kdev,
-                        struct device_attribute *kattr, char *buf)
+static ssize_t pstr_show(struct device *kdev, struct device_attribute *kattr,
+                        char *buf, int number)
 {
        struct hid_device *hdev = to_hid_device(kdev);
-       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, (u8 *)&report.contents,
+       ret = cp2112_hid_get(hdev, number, (u8 *)&report.contents,
                             sizeof(report.contents), HID_FEATURE_REPORT);
        if (ret < 3) {
                hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name,
@@ -1018,10 +1006,16 @@ static ssize_t pstr_show(struct device *kdev,
 }
 
 #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, \
-};
+static ssize_t name##_store(struct device *kdev, struct device_attribute *kattr, \
+                           const char *buf, size_t count) \
+{ \
+       return pstr_store(kdev, kattr, buf, count, _report); \
+} \
+static ssize_t name##_show(struct device *kdev, struct device_attribute *kattr, char *buf) \
+{ \
+       return pstr_show(kdev, kattr, buf, _report); \
+} \
+static DEVICE_ATTR_RW(name);
 
 CP2112_PSTR_ATTR(manufacturer, CP2112_MANUFACTURER_STRING);
 CP2112_PSTR_ATTR(product,      CP2112_PRODUCT_STRING);
@@ -1036,9 +1030,9 @@ static const struct attribute_group cp2112_attr_group = {
                &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,
+               &dev_attr_manufacturer.attr,
+               &dev_attr_product.attr,
+               &dev_attr_serial.attr,
                NULL
        }
 };
@@ -1063,7 +1057,7 @@ static void chmod_sysfs_attrs(struct hid_device *hdev)
        }
 
        for (attr = cp2112_attr_group.attrs; *attr; ++attr) {
-               umode_t mode = (buf[1] & 1) ? S_IWUSR | S_IRUGO : S_IRUGO;
+               umode_t mode = (buf[1] & 1) ? 0644 : 0444;
                ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode);
                if (ret < 0)
                        hid_err(hdev, "error chmoding sysfs file %s\n",
@@ -1080,16 +1074,20 @@ static void cp2112_gpio_irq_mask(struct irq_data *d)
 {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct cp2112_device *dev = gpiochip_get_data(gc);
+       irq_hw_number_t hwirq = irqd_to_hwirq(d);
 
-       __clear_bit(d->hwirq, &dev->irq_mask);
+       __clear_bit(hwirq, &dev->irq_mask);
+       gpiochip_disable_irq(gc, hwirq);
 }
 
 static void cp2112_gpio_irq_unmask(struct irq_data *d)
 {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct cp2112_device *dev = gpiochip_get_data(gc);
+       irq_hw_number_t hwirq = irqd_to_hwirq(d);
 
-       __set_bit(d->hwirq, &dev->irq_mask);
+       gpiochip_enable_irq(gc, hwirq);
+       __set_bit(hwirq, &dev->irq_mask);
 }
 
 static void cp2112_gpio_poll_callback(struct work_struct *work)
@@ -1098,7 +1096,6 @@ static void cp2112_gpio_poll_callback(struct work_struct *work)
                                                 gpio_poll_worker.work);
        struct irq_data *d;
        u8 gpio_mask;
-       u8 virqs = (u8)dev->irq_mask;
        u32 irq_type;
        int irq, virq, ret;
 
@@ -1109,15 +1106,10 @@ static void cp2112_gpio_poll_callback(struct work_struct *work)
                goto exit;
 
        gpio_mask = ret;
-
-       while (virqs) {
-               virq = ffs(virqs) - 1;
-               virqs &= ~BIT(virq);
-
-               if (!dev->gc.to_irq)
-                       break;
-
-               irq = dev->gc.to_irq(&dev->gc, virq);
+       for_each_set_bit(virq, &dev->irq_mask, CP2112_GPIO_MAX_GPIO) {
+               irq = irq_find_mapping(dev->gc.irq.domain, virq);
+               if (!irq)
+                       continue;
 
                d = irq_get_irq_data(irq);
                if (!d)
@@ -1175,6 +1167,7 @@ static void cp2112_gpio_irq_shutdown(struct irq_data *d)
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct cp2112_device *dev = gpiochip_get_data(gc);
 
+       cp2112_gpio_irq_mask(d);
        cancel_delayed_work_sync(&dev->gpio_poll_worker);
 }
 
@@ -1183,50 +1176,17 @@ static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type)
        return 0;
 }
 
-static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev,
-                                             int pin)
-{
-       int ret;
-
-       if (dev->desc[pin])
-               return -EINVAL;
-
-       dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin,
-                                                  "HID/I2C:Event",
-                                                  GPIO_ACTIVE_HIGH,
-                                                  GPIOD_IN);
-       if (IS_ERR(dev->desc[pin])) {
-               dev_err(dev->gc.parent, "Failed to request GPIO\n");
-               return PTR_ERR(dev->desc[pin]);
-       }
-
-       ret = cp2112_gpio_direction_input(&dev->gc, pin);
-       if (ret < 0) {
-               dev_err(dev->gc.parent, "Failed to set GPIO to input dir\n");
-               goto err_desc;
-       }
-
-       ret = gpiochip_lock_as_irq(&dev->gc, pin);
-       if (ret) {
-               dev_err(dev->gc.parent, "Failed to lock GPIO as interrupt\n");
-               goto err_desc;
-       }
-
-       ret = gpiod_to_irq(dev->desc[pin]);
-       if (ret < 0) {
-               dev_err(dev->gc.parent, "Failed to translate GPIO to IRQ\n");
-               goto err_lock;
-       }
-
-       return ret;
-
-err_lock:
-       gpiochip_unlock_as_irq(&dev->gc, pin);
-err_desc:
-       gpiochip_free_own_desc(dev->desc[pin]);
-       dev->desc[pin] = NULL;
-       return ret;
-}
+static const struct irq_chip cp2112_gpio_irqchip = {
+       .name = "cp2112-gpio",
+       .irq_startup = cp2112_gpio_irq_startup,
+       .irq_shutdown = cp2112_gpio_irq_shutdown,
+       .irq_ack = cp2112_gpio_irq_ack,
+       .irq_mask = cp2112_gpio_irq_mask,
+       .irq_unmask = cp2112_gpio_irq_unmask,
+       .irq_set_type = cp2112_gpio_irq_type,
+       .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
+       GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
 
 static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
@@ -1333,21 +1293,12 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
        dev->gc.set                     = cp2112_gpio_set;
        dev->gc.get                     = cp2112_gpio_get;
        dev->gc.base                    = -1;
-       dev->gc.ngpio                   = 8;
+       dev->gc.ngpio                   = CP2112_GPIO_MAX_GPIO;
        dev->gc.can_sleep               = 1;
        dev->gc.parent                  = &hdev->dev;
 
-       dev->irq.name = "cp2112-gpio";
-       dev->irq.irq_startup = cp2112_gpio_irq_startup;
-       dev->irq.irq_shutdown = cp2112_gpio_irq_shutdown;
-       dev->irq.irq_ack = cp2112_gpio_irq_ack;
-       dev->irq.irq_mask = cp2112_gpio_irq_mask;
-       dev->irq.irq_unmask = cp2112_gpio_irq_unmask;
-       dev->irq.irq_set_type = cp2112_gpio_irq_type;
-       dev->irq.flags = IRQCHIP_MASK_ON_SUSPEND;
-
        girq = &dev->gc.irq;
-       girq->chip = &dev->irq;
+       gpio_irq_chip_set_chip(girq, &cp2112_gpio_irqchip);
        /* The event comes from the outside so no parent handler */
        girq->parent_handler = NULL;
        girq->num_parents = 0;
@@ -1389,7 +1340,6 @@ err_hid_stop:
 static void cp2112_remove(struct hid_device *hdev)
 {
        struct cp2112_device *dev = hid_get_drvdata(hdev);
-       int i;
 
        sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
        i2c_del_adapter(&dev->adap);
@@ -1399,11 +1349,6 @@ static void cp2112_remove(struct hid_device *hdev)
                cancel_delayed_work_sync(&dev->gpio_poll_worker);
        }
 
-       for (i = 0; i < ARRAY_SIZE(dev->desc); i++) {
-               gpiochip_unlock_as_irq(&dev->gc, i);
-               gpiochip_free_own_desc(dev->desc[i]);
-       }
-
        gpiochip_remove(&dev->gc);
        /* i2c_del_adapter has finished removing all i2c devices from our
         * adapter. Well behaved devices should no longer call our cp2112_xfer
diff --git a/drivers/hid/hid-google-stadiaff.c b/drivers/hid/hid-google-stadiaff.c
new file mode 100644 (file)
index 0000000..3731575
--- /dev/null
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Stadia controller rumble support.
+ *
+ * Copyright 2023 Google LLC
+ */
+
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define STADIA_FF_REPORT_ID 5
+
+struct stadiaff_device {
+       struct hid_device *hid;
+       struct hid_report *report;
+       spinlock_t lock;
+       bool removed;
+       uint16_t strong_magnitude;
+       uint16_t weak_magnitude;
+       struct work_struct work;
+};
+
+static void stadiaff_work(struct work_struct *work)
+{
+       struct stadiaff_device *stadiaff =
+               container_of(work, struct stadiaff_device, work);
+       struct hid_field *rumble_field = stadiaff->report->field[0];
+       unsigned long flags;
+
+       spin_lock_irqsave(&stadiaff->lock, flags);
+       rumble_field->value[0] = stadiaff->strong_magnitude;
+       rumble_field->value[1] = stadiaff->weak_magnitude;
+       spin_unlock_irqrestore(&stadiaff->lock, flags);
+
+       hid_hw_request(stadiaff->hid, stadiaff->report, HID_REQ_SET_REPORT);
+}
+
+static int stadiaff_play(struct input_dev *dev, void *data,
+                        struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
+       unsigned long flags;
+
+       spin_lock_irqsave(&stadiaff->lock, flags);
+       if (!stadiaff->removed) {
+               stadiaff->strong_magnitude = effect->u.rumble.strong_magnitude;
+               stadiaff->weak_magnitude = effect->u.rumble.weak_magnitude;
+               schedule_work(&stadiaff->work);
+       }
+       spin_unlock_irqrestore(&stadiaff->lock, flags);
+
+       return 0;
+}
+
+static int stadiaff_init(struct hid_device *hid)
+{
+       struct stadiaff_device *stadiaff;
+       struct hid_report *report;
+       struct hid_input *hidinput;
+       struct input_dev *dev;
+       int error;
+
+       if (list_empty(&hid->inputs)) {
+               hid_err(hid, "no inputs found\n");
+               return -ENODEV;
+       }
+       hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       dev = hidinput->input;
+
+       report = hid_validate_values(hid, HID_OUTPUT_REPORT,
+                                    STADIA_FF_REPORT_ID, 0, 2);
+       if (!report)
+               return -ENODEV;
+
+       stadiaff = devm_kzalloc(&hid->dev, sizeof(struct stadiaff_device),
+                               GFP_KERNEL);
+       if (!stadiaff)
+               return -ENOMEM;
+
+       hid_set_drvdata(hid, stadiaff);
+
+       input_set_capability(dev, EV_FF, FF_RUMBLE);
+
+       error = input_ff_create_memless(dev, NULL, stadiaff_play);
+       if (error)
+               return error;
+
+       stadiaff->removed = false;
+       stadiaff->hid = hid;
+       stadiaff->report = report;
+       INIT_WORK(&stadiaff->work, stadiaff_work);
+       spin_lock_init(&stadiaff->lock);
+
+       hid_info(hid, "Force Feedback for Google Stadia controller\n");
+
+       return 0;
+}
+
+static int stadia_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       ret = stadiaff_init(hdev);
+       if (ret) {
+               hid_err(hdev, "force feedback init failed\n");
+               hid_hw_stop(hdev);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void stadia_remove(struct hid_device *hid)
+{
+       struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
+       unsigned long flags;
+
+       spin_lock_irqsave(&stadiaff->lock, flags);
+       stadiaff->removed = true;
+       spin_unlock_irqrestore(&stadiaff->lock, flags);
+
+       cancel_work_sync(&stadiaff->work);
+       hid_hw_stop(hid);
+}
+
+static const struct hid_device_id stadia_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, stadia_devices);
+
+static struct hid_driver stadia_driver = {
+       .name = "stadia",
+       .id_table = stadia_devices,
+       .probe = stadia_probe,
+       .remove = stadia_remove,
+};
+module_hid_driver(stadia_driver);
+
+MODULE_LICENSE("GPL");
index 8a310f8..7e49999 100644 (file)
 #define USB_DEVICE_ID_GOOGLE_DON       0x5050
 #define USB_DEVICE_ID_GOOGLE_EEL       0x5057
 #define USB_DEVICE_ID_GOOGLE_JEWEL     0x5061
+#define USB_DEVICE_ID_GOOGLE_STADIA    0x9400
 
 #define USB_VENDOR_ID_GOTOP            0x08f2
 #define USB_DEVICE_ID_SUPER_Q2         0x007f
 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2         0xc534
 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1      0xc539
 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1    0xc53f
+#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2    0xc547
 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a
 #define USB_DEVICE_ID_SPACETRAVELLER   0xc623
 #define USB_DEVICE_ID_SPACENAVIGATOR   0xc626
index 851ee86..40a5645 100644 (file)
@@ -988,6 +988,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        return;
 
                case 0x3c: /* Invert */
+                       device->quirks &= ~HID_QUIRK_NOINVERT;
                        map_key_clear(BTN_TOOL_RUBBER);
                        break;
 
@@ -1013,9 +1014,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                case 0x45: /* ERASER */
                        /*
                         * This event is reported when eraser tip touches the surface.
-                        * Actual eraser (BTN_TOOL_RUBBER) is set by Invert usage when
-                        * tool gets in proximity.
+                        * Actual eraser (BTN_TOOL_RUBBER) is set and released either
+                        * by Invert if tool reports proximity or by Eraser directly.
                         */
+                       if (!test_bit(BTN_TOOL_RUBBER, input->keybit)) {
+                               device->quirks |= HID_QUIRK_NOINVERT;
+                               set_bit(BTN_TOOL_RUBBER, input->keybit);
+                       }
                        map_key_clear(BTN_TOUCH);
                        break;
 
@@ -1580,6 +1585,15 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
                else if (report->tool != BTN_TOOL_RUBBER)
                        /* value is off, tool is not rubber, ignore */
                        return;
+               else if (*quirks & HID_QUIRK_NOINVERT &&
+                        !test_bit(BTN_TOUCH, input->key)) {
+                       /*
+                        * There is no invert to release the tool, let hid_input
+                        * send BTN_TOUCH with scancode and release the tool after.
+                        */
+                       hid_report_release_tool(report, input, BTN_TOOL_RUBBER);
+                       return;
+               }
 
                /* let hid-input set BTN_TOUCH */
                break;
index 6218041..8afe3be 100644 (file)
@@ -1285,6 +1285,9 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
                 * 50 msec should gives enough time to the receiver to be ready.
                 */
                msleep(50);
+
+               if (retval)
+                       return retval;
        }
 
        /*
@@ -1306,7 +1309,7 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
        buf[5] = 0x09;
        buf[6] = 0x00;
 
-       hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf,
+       retval = hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf,
                        HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT,
                        HID_REQ_SET_REPORT);
 
@@ -1692,11 +1695,12 @@ static int logi_dj_raw_event(struct hid_device *hdev,
                }
                /*
                 * Mouse-only receivers send unnumbered mouse data. The 27 MHz
-                * receiver uses 6 byte packets, the nano receiver 8 bytes.
+                * receiver uses 6 byte packets, the nano receiver 8 bytes,
+                * the lightspeed receiver (Pro X Superlight) 13 bytes.
                 */
                if (djrcv_dev->unnumbered_application == HID_GD_MOUSE &&
-                   size <= 8) {
-                       u8 mouse_report[9];
+                   size <= 13){
+                       u8 mouse_report[14];
 
                        /* Prepend report id */
                        mouse_report[0] = REPORT_TYPE_MOUSE;
@@ -1980,6 +1984,10 @@ static const struct hid_device_id logi_dj_receivers[] = {
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1),
         .driver_data = recvr_type_gaming_hidpp},
+       { /* Logitech lightspeed receiver (0xc547) */
+         HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+               USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2),
+        .driver_data = recvr_type_gaming_hidpp},
 
        { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
index 129b01b..05f5b5f 100644 (file)
@@ -228,7 +228,7 @@ struct hidpp_device {
 #define HIDPP20_ERROR_INVALID_ARGS             0x02
 #define HIDPP20_ERROR_OUT_OF_RANGE             0x03
 #define HIDPP20_ERROR_HW_ERROR                 0x04
-#define HIDPP20_ERROR_LOGITECH_INTERNAL                0x05
+#define HIDPP20_ERROR_NOT_ALLOWED              0x05
 #define HIDPP20_ERROR_INVALID_FEATURE_INDEX    0x06
 #define HIDPP20_ERROR_INVALID_FUNCTION_ID      0x07
 #define HIDPP20_ERROR_BUSY                     0x08
@@ -275,21 +275,22 @@ static int __hidpp_send_report(struct hid_device *hdev,
 }
 
 /*
- * hidpp_send_message_sync() returns 0 in case of success, and something else
- * in case of a failure.
- * - If ' something else' is positive, that means that an error has been raised
- *   by the protocol itself.
- * - If ' something else' is negative, that means that we had a classic error
- *   (-ENOMEM, -EPIPE, etc...)
+ * Effectively send the message to the device, waiting for its answer.
+ *
+ * Must be called with hidpp->send_mutex locked
+ *
+ * Same return protocol than hidpp_send_message_sync():
+ * - success on 0
+ * - negative error means transport error
+ * - positive value means protocol error
  */
-static int hidpp_send_message_sync(struct hidpp_device *hidpp,
+static int __do_hidpp_send_message_sync(struct hidpp_device *hidpp,
        struct hidpp_report *message,
        struct hidpp_report *response)
 {
-       int ret = -1;
-       int max_retries = 3;
+       int ret;
 
-       mutex_lock(&hidpp->send_mutex);
+       __must_hold(&hidpp->send_mutex);
 
        hidpp->send_receive_buf = response;
        hidpp->answer_available = false;
@@ -300,47 +301,74 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
         */
        *response = *message;
 
-       for (; max_retries != 0 && ret; max_retries--) {
-               ret = __hidpp_send_report(hidpp->hid_dev, message);
+       ret = __hidpp_send_report(hidpp->hid_dev, message);
+       if (ret) {
+               dbg_hid("__hidpp_send_report returned err: %d\n", ret);
+               memset(response, 0, sizeof(struct hidpp_report));
+               return ret;
+       }
 
-               if (ret) {
-                       dbg_hid("__hidpp_send_report returned err: %d\n", ret);
-                       memset(response, 0, sizeof(struct hidpp_report));
-                       break;
-               }
+       if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
+                               5*HZ)) {
+               dbg_hid("%s:timeout waiting for response\n", __func__);
+               memset(response, 0, sizeof(struct hidpp_report));
+               return -ETIMEDOUT;
+       }
 
-               if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
-                                       5*HZ)) {
-                       dbg_hid("%s:timeout waiting for response\n", __func__);
-                       memset(response, 0, sizeof(struct hidpp_report));
-                       ret = -ETIMEDOUT;
-                       break;
-               }
+       if (response->report_id == REPORT_ID_HIDPP_SHORT &&
+           response->rap.sub_id == HIDPP_ERROR) {
+               ret = response->rap.params[1];
+               dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
+               return ret;
+       }
 
-               if (response->report_id == REPORT_ID_HIDPP_SHORT &&
-                   response->rap.sub_id == HIDPP_ERROR) {
-                       ret = response->rap.params[1];
-                       dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
+       if ((response->report_id == REPORT_ID_HIDPP_LONG ||
+            response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
+           response->fap.feature_index == HIDPP20_ERROR) {
+               ret = response->fap.params[1];
+               dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * hidpp_send_message_sync() returns 0 in case of success, and something else
+ * in case of a failure.
+ *
+ * See __do_hidpp_send_message_sync() for a detailed explanation of the returned
+ * value.
+ */
+static int hidpp_send_message_sync(struct hidpp_device *hidpp,
+       struct hidpp_report *message,
+       struct hidpp_report *response)
+{
+       int ret;
+       int max_retries = 3;
+
+       mutex_lock(&hidpp->send_mutex);
+
+       do {
+               ret = __do_hidpp_send_message_sync(hidpp, message, response);
+               if (ret != HIDPP20_ERROR_BUSY)
                        break;
-               }
 
-               if ((response->report_id == REPORT_ID_HIDPP_LONG ||
-                    response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
-                   response->fap.feature_index == HIDPP20_ERROR) {
-                       ret = response->fap.params[1];
-                       if (ret != HIDPP20_ERROR_BUSY) {
-                               dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
-                               break;
-                       }
-                       dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret);
-               }
-       }
+               dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret);
+       } while (--max_retries);
 
        mutex_unlock(&hidpp->send_mutex);
        return ret;
 
 }
 
+/*
+ * hidpp_send_fap_command_sync() returns 0 in case of success, and something else
+ * in case of a failure.
+ *
+ * See __do_hidpp_send_message_sync() for a detailed explanation of the returned
+ * value.
+ */
 static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
        u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count,
        struct hidpp_report *response)
@@ -373,6 +401,13 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
        return ret;
 }
 
+/*
+ * hidpp_send_rap_command_sync() returns 0 in case of success, and something else
+ * in case of a failure.
+ *
+ * See __do_hidpp_send_message_sync() for a detailed explanation of the returned
+ * value.
+ */
 static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
        u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count,
        struct hidpp_report *response)
@@ -4620,6 +4655,8 @@ static const struct hid_device_id hidpp_devices[] = {
                .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS },
        { /* Logitech G Pro Gaming Mouse over USB */
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
+       { /* Logitech G Pro X Superlight Gaming Mouse over USB */
+         HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC094) },
 
        { /* G935 Gaming Headset */
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0x0a87),
@@ -4647,6 +4684,8 @@ static const struct hid_device_id hidpp_devices[] = {
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) },
        { /* MX Master 3 mouse over Bluetooth */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) },
+       { /* MX Anywhere 3 mouse over Bluetooth */
+         HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb025) },
        { /* MX Master 3S mouse over Bluetooth */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb034) },
        {}
index e31be0c..521b2ff 100644 (file)
@@ -1594,7 +1594,6 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app)
 static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
 {
        struct mt_device *td = hid_get_drvdata(hdev);
-       char *name;
        const char *suffix = NULL;
        struct mt_report_data *rdata;
        struct mt_application *mt_application = NULL;
@@ -1645,15 +1644,9 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
                break;
        }
 
-       if (suffix) {
-               name = devm_kzalloc(&hi->input->dev,
-                                   strlen(hdev->name) + strlen(suffix) + 2,
-                                   GFP_KERNEL);
-               if (name) {
-                       sprintf(name, "%s %s", hdev->name, suffix);
-                       hi->input->name = name;
-               }
-       }
+       if (suffix)
+               hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+                                                "%s %s", hdev->name, suffix);
 
        return 0;
 }
index a928ad2..9a3576d 100644 (file)
@@ -6,11 +6,15 @@
  */
 
 #include <linux/hid.h>
+#include <linux/idr.h>
 #include <linux/input-event-codes.h>
 #include <linux/input.h>
+#include <linux/jiffies.h>
 #include <linux/leds.h>
 #include <linux/module.h>
+#include <linux/power_supply.h>
 #include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <linux/workqueue.h>
 
 #include "hid-ids.h"
@@ -30,6 +34,8 @@ enum {
 enum {
        SHIELD_FW_VERSION_INITIALIZED = 0,
        SHIELD_BOARD_INFO_INITIALIZED,
+       SHIELD_BATTERY_STATS_INITIALIZED,
+       SHIELD_CHARGER_STATE_INITIALIZED,
 };
 
 enum {
@@ -37,6 +43,7 @@ enum {
        THUNDERSTRIKE_BOARD_INFO_UPDATE,
        THUNDERSTRIKE_HAPTICS_UPDATE,
        THUNDERSTRIKE_LED_UPDATE,
+       THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE,
 };
 
 enum {
@@ -48,10 +55,46 @@ enum {
 enum {
        THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1,
        THUNDERSTRIKE_HOSTCMD_ID_LED = 6,
+       THUNDERSTRIKE_HOSTCMD_ID_BATTERY,
        THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16,
        THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53,
        THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57,
-       THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT = 58,
+       THUNDERSTRIKE_HOSTCMD_ID_CHARGER,
+};
+
+struct power_supply_dev {
+       struct power_supply *psy;
+       struct power_supply_desc desc;
+};
+
+struct thunderstrike_psy_prop_values {
+       int voltage_min;
+       int voltage_now;
+       int voltage_avg;
+       int voltage_boot;
+       int capacity;
+       int status;
+       int charge_type;
+       int temp;
+};
+
+static const enum power_supply_property thunderstrike_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_VOLTAGE_BOOT,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_SCOPE,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TEMP_MIN,
+       POWER_SUPPLY_PROP_TEMP_MAX,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
 };
 
 enum thunderstrike_led_state {
@@ -60,6 +103,38 @@ enum thunderstrike_led_state {
 } __packed;
 static_assert(sizeof(enum thunderstrike_led_state) == 1);
 
+struct thunderstrike_hostcmd_battery {
+       __le16 voltage_avg;
+       u8 reserved_at_10;
+       __le16 thermistor;
+       __le16 voltage_min;
+       __le16 voltage_boot;
+       __le16 voltage_now;
+       u8 capacity;
+} __packed;
+
+enum thunderstrike_charger_type {
+       THUNDERSTRIKE_CHARGER_TYPE_NONE = 0,
+       THUNDERSTRIKE_CHARGER_TYPE_TRICKLE,
+       THUNDERSTRIKE_CHARGER_TYPE_NORMAL,
+} __packed;
+static_assert(sizeof(enum thunderstrike_charger_type) == 1);
+
+enum thunderstrike_charger_state {
+       THUNDERSTRIKE_CHARGER_STATE_UNKNOWN = 0,
+       THUNDERSTRIKE_CHARGER_STATE_DISABLED,
+       THUNDERSTRIKE_CHARGER_STATE_CHARGING,
+       THUNDERSTRIKE_CHARGER_STATE_FULL,
+       THUNDERSTRIKE_CHARGER_STATE_FAILED = 8,
+} __packed;
+static_assert(sizeof(enum thunderstrike_charger_state) == 1);
+
+struct thunderstrike_hostcmd_charger {
+       u8 connected;
+       enum thunderstrike_charger_type type;
+       enum thunderstrike_charger_state state;
+} __packed;
+
 struct thunderstrike_hostcmd_board_info {
        __le16 revision;
        __le16 serial[7];
@@ -80,6 +155,8 @@ struct thunderstrike_hostcmd_resp_report {
                struct thunderstrike_hostcmd_haptics motors;
                __le16 fw_version;
                enum thunderstrike_led_state led_state;
+               struct thunderstrike_hostcmd_battery battery;
+               struct thunderstrike_hostcmd_charger charger;
                u8 payload[30];
        } __packed;
 } __packed;
@@ -109,6 +186,7 @@ static_assert(sizeof(struct thunderstrike_hostcmd_req_report) ==
 /* Common struct for shield accessories. */
 struct shield_device {
        struct hid_device *hdev;
+       struct power_supply_dev battery_dev;
 
        unsigned long initialized_flags;
        const char *codename;
@@ -119,9 +197,17 @@ struct shield_device {
        } board_info;
 };
 
+/*
+ * Non-trivial to uniquely identify Thunderstrike controllers at initialization
+ * time. Use an ID allocator to help with this.
+ */
+static DEFINE_IDA(thunderstrike_ida);
+
 struct thunderstrike {
        struct shield_device base;
 
+       int id;
+
        /* Sub-devices */
        struct input_dev *haptics_dev;
        struct led_classdev led_dev;
@@ -133,6 +219,9 @@ struct thunderstrike {
        spinlock_t haptics_update_lock;
        u8 led_state : 1;
        enum thunderstrike_led_state led_value;
+       struct thunderstrike_psy_prop_values psy_stats;
+       spinlock_t psy_stats_lock;
+       struct timer_list psy_stats_timer;
        struct work_struct hostcmd_req_work;
 };
 
@@ -164,7 +253,7 @@ static struct input_dev *shield_allocate_input_dev(struct hid_device *hdev,
        idev->id.product = hdev->product;
        idev->id.version = hdev->version;
        idev->uniq = hdev->uniq;
-       idev->name = devm_kasprintf(&idev->dev, GFP_KERNEL, "%s %s", hdev->name,
+       idev->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s %s", hdev->name,
                                    name_suffix);
        if (!idev->name)
                goto err_name;
@@ -247,6 +336,16 @@ static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work)
                thunderstrike_send_hostcmd_request(ts);
        }
 
+       if (test_and_clear_bit(THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, &ts->update_flags)) {
+               thunderstrike_hostcmd_req_report_init(
+                       report, THUNDERSTRIKE_HOSTCMD_ID_BATTERY);
+               thunderstrike_send_hostcmd_request(ts);
+
+               thunderstrike_hostcmd_req_report_init(
+                       report, THUNDERSTRIKE_HOSTCMD_ID_CHARGER);
+               thunderstrike_send_hostcmd_request(ts);
+       }
+
        if (test_and_clear_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags)) {
                thunderstrike_hostcmd_req_report_init(
                        report, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO);
@@ -352,6 +451,93 @@ static void thunderstrike_led_set_brightness(struct led_classdev *led,
        schedule_work(&ts->hostcmd_req_work);
 }
 
+static int thunderstrike_battery_get_property(struct power_supply *psy,
+                                             enum power_supply_property psp,
+                                             union power_supply_propval *val)
+{
+       struct shield_device *shield_dev = power_supply_get_drvdata(psy);
+       struct thunderstrike_psy_prop_values prop_values;
+       struct thunderstrike *ts;
+       int ret = 0;
+
+       ts = container_of(shield_dev, struct thunderstrike, base);
+       spin_lock(&ts->psy_stats_lock);
+       prop_values = ts->psy_stats;
+       spin_unlock(&ts->psy_stats_lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = prop_values.status;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               val->intval = prop_values.charge_type;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = 1;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+               val->intval = prop_values.voltage_min;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+               val->intval = 2900000; /* 2.9 V */
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = 2200000; /* 2.2 V */
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = prop_values.voltage_now;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               val->intval = prop_values.voltage_avg;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
+               val->intval = prop_values.voltage_boot;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               val->intval = prop_values.capacity;
+               break;
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = prop_values.temp;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_MIN:
+               val->intval = 0; /* 0 C */
+               break;
+       case POWER_SUPPLY_PROP_TEMP_MAX:
+               val->intval = 400; /* 40 C */
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+               val->intval = 15; /* 1.5 C */
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               val->intval = 380; /* 38 C */
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static inline void thunderstrike_request_psy_stats(struct thunderstrike *ts)
+{
+       set_bit(THUNDERSTRIKE_POWER_SUPPLY_STATS_UPDATE, &ts->update_flags);
+       schedule_work(&ts->hostcmd_req_work);
+}
+
+static void thunderstrike_psy_stats_timer_handler(struct timer_list *timer)
+{
+       struct thunderstrike *ts =
+               container_of(timer, struct thunderstrike, psy_stats_timer);
+
+       thunderstrike_request_psy_stats(ts);
+       /* Query battery statistics from device every five minutes */
+       mod_timer(timer, jiffies + 300 * HZ);
+}
+
 static void
 thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev,
                                       __le16 fw_version)
@@ -416,13 +602,138 @@ thunderstrike_parse_led_payload(struct shield_device *shield_dev,
        hid_dbg(shield_dev->hdev, "Thunderstrike led HOSTCMD response, 0x%02X\n", led_state);
 }
 
+static void thunderstrike_parse_battery_payload(
+       struct shield_device *shield_dev,
+       struct thunderstrike_hostcmd_battery *battery)
+{
+       struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+       u16 hostcmd_voltage_boot = le16_to_cpu(battery->voltage_boot);
+       u16 hostcmd_voltage_avg = le16_to_cpu(battery->voltage_avg);
+       u16 hostcmd_voltage_min = le16_to_cpu(battery->voltage_min);
+       u16 hostcmd_voltage_now = le16_to_cpu(battery->voltage_now);
+       u16 hostcmd_thermistor = le16_to_cpu(battery->thermistor);
+       int voltage_boot, voltage_avg, voltage_min, voltage_now;
+       struct hid_device *hdev = shield_dev->hdev;
+       u8 capacity = battery->capacity;
+       int temp;
+
+       /* Convert thunderstrike device values to ÂµV and tenths of degree Celsius */
+       voltage_boot = hostcmd_voltage_boot * 1000;
+       voltage_avg = hostcmd_voltage_avg * 1000;
+       voltage_min = hostcmd_voltage_min * 1000;
+       voltage_now = hostcmd_voltage_now * 1000;
+       temp = (1378 - (int)hostcmd_thermistor) * 10 / 19;
+
+       /* Copy converted values */
+       spin_lock(&ts->psy_stats_lock);
+       ts->psy_stats.voltage_boot = voltage_boot;
+       ts->psy_stats.voltage_avg = voltage_avg;
+       ts->psy_stats.voltage_min = voltage_min;
+       ts->psy_stats.voltage_now = voltage_now;
+       ts->psy_stats.capacity = capacity;
+       ts->psy_stats.temp = temp;
+       spin_unlock(&ts->psy_stats_lock);
+
+       set_bit(SHIELD_BATTERY_STATS_INITIALIZED, &shield_dev->initialized_flags);
+
+       hid_dbg(hdev,
+               "Thunderstrike battery HOSTCMD response, voltage_avg: %u voltage_now: %u\n",
+               hostcmd_voltage_avg, hostcmd_voltage_now);
+       hid_dbg(hdev,
+               "Thunderstrike battery HOSTCMD response, voltage_boot: %u voltage_min: %u\n",
+               hostcmd_voltage_boot, hostcmd_voltage_min);
+       hid_dbg(hdev,
+               "Thunderstrike battery HOSTCMD response, thermistor: %u\n",
+               hostcmd_thermistor);
+       hid_dbg(hdev,
+               "Thunderstrike battery HOSTCMD response, capacity: %u%%\n",
+               capacity);
+}
+
+static void thunderstrike_parse_charger_payload(
+       struct shield_device *shield_dev,
+       struct thunderstrike_hostcmd_charger *charger)
+{
+       struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+       int charge_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+       struct hid_device *hdev = shield_dev->hdev;
+       int status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       switch (charger->type) {
+       case THUNDERSTRIKE_CHARGER_TYPE_NONE:
+               charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               break;
+       case THUNDERSTRIKE_CHARGER_TYPE_TRICKLE:
+               charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               break;
+       case THUNDERSTRIKE_CHARGER_TYPE_NORMAL:
+               charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+               break;
+       default:
+               hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD type, %u\n",
+                        charger->type);
+               break;
+       }
+
+       switch (charger->state) {
+       case THUNDERSTRIKE_CHARGER_STATE_UNKNOWN:
+               status = POWER_SUPPLY_STATUS_UNKNOWN;
+               break;
+       case THUNDERSTRIKE_CHARGER_STATE_DISABLED:
+               /* Indicates charger is disconnected */
+               break;
+       case THUNDERSTRIKE_CHARGER_STATE_CHARGING:
+               status = POWER_SUPPLY_STATUS_CHARGING;
+               break;
+       case THUNDERSTRIKE_CHARGER_STATE_FULL:
+               status = POWER_SUPPLY_STATUS_FULL;
+               break;
+       case THUNDERSTRIKE_CHARGER_STATE_FAILED:
+               status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               hid_err(hdev, "Thunderstrike device failed to charge\n");
+               break;
+       default:
+               hid_warn(hdev, "Unhandled Thunderstrike charger HOSTCMD state, %u\n",
+                        charger->state);
+               break;
+       }
+
+       if (!charger->connected)
+               status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+       spin_lock(&ts->psy_stats_lock);
+       ts->psy_stats.charge_type = charge_type;
+       ts->psy_stats.status = status;
+       spin_unlock(&ts->psy_stats_lock);
+
+       set_bit(SHIELD_CHARGER_STATE_INITIALIZED, &shield_dev->initialized_flags);
+
+       hid_dbg(hdev,
+               "Thunderstrike charger HOSTCMD response, connected: %u, type: %u, state: %u\n",
+               charger->connected, charger->type, charger->state);
+}
+
+static inline void thunderstrike_device_init_info(struct shield_device *shield_dev)
+{
+       struct thunderstrike *ts =
+               container_of(shield_dev, struct thunderstrike, base);
+
+       if (!test_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags))
+               thunderstrike_request_firmware_version(ts);
+
+       if (!test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags))
+               thunderstrike_request_board_info(ts);
+
+       if (!test_bit(SHIELD_BATTERY_STATS_INITIALIZED, &shield_dev->initialized_flags) ||
+           !test_bit(SHIELD_CHARGER_STATE_INITIALIZED, &shield_dev->initialized_flags))
+               thunderstrike_psy_stats_timer_handler(&ts->psy_stats_timer);
+}
+
 static int thunderstrike_parse_report(struct shield_device *shield_dev,
                                      struct hid_report *report, u8 *data,
                                      int size)
 {
        struct thunderstrike_hostcmd_resp_report *hostcmd_resp_report;
-       struct thunderstrike *ts =
-               container_of(shield_dev, struct thunderstrike, base);
        struct hid_device *hdev = shield_dev->hdev;
 
        switch (report->id) {
@@ -445,6 +756,10 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
                case THUNDERSTRIKE_HOSTCMD_ID_LED:
                        thunderstrike_parse_led_payload(shield_dev, hostcmd_resp_report->led_state);
                        break;
+               case THUNDERSTRIKE_HOSTCMD_ID_BATTERY:
+                       thunderstrike_parse_battery_payload(shield_dev,
+                                                           &hostcmd_resp_report->battery);
+                       break;
                case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO:
                        thunderstrike_parse_board_info_payload(
                                shield_dev, &hostcmd_resp_report->board_info);
@@ -453,14 +768,17 @@ static int thunderstrike_parse_report(struct shield_device *shield_dev,
                        thunderstrike_parse_haptics_payload(
                                shield_dev, &hostcmd_resp_report->motors);
                        break;
-
                case THUNDERSTRIKE_HOSTCMD_ID_USB_INIT:
-               case THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT:
                        /* May block HOSTCMD requests till received initially */
-                       thunderstrike_request_firmware_version(ts);
-                       thunderstrike_request_board_info(ts);
-                       /* Only HOSTCMD that can be triggered without a request */
-                       return 0;
+                       thunderstrike_device_init_info(shield_dev);
+                       break;
+               case THUNDERSTRIKE_HOSTCMD_ID_CHARGER:
+                       /* May block HOSTCMD requests till received initially */
+                       thunderstrike_device_init_info(shield_dev);
+
+                       thunderstrike_parse_charger_payload(
+                               shield_dev, &hostcmd_resp_report->charger);
+                       break;
                default:
                        hid_warn(hdev,
                                 "Unhandled Thunderstrike HOSTCMD id %d\n",
@@ -480,7 +798,8 @@ static inline int thunderstrike_led_create(struct thunderstrike *ts)
 {
        struct led_classdev *led = &ts->led_dev;
 
-       led->name = "thunderstrike:blue:led";
+       led->name = devm_kasprintf(&ts->base.hdev->dev, GFP_KERNEL,
+                                  "thunderstrike%d:blue:led", ts->id);
        led->max_brightness = 1;
        led->flags = LED_CORE_SUSPENDRESUME;
        led->brightness_get = &thunderstrike_led_get_brightness;
@@ -489,6 +808,50 @@ static inline int thunderstrike_led_create(struct thunderstrike *ts)
        return led_classdev_register(&ts->base.hdev->dev, led);
 }
 
+static inline int thunderstrike_psy_create(struct shield_device *shield_dev)
+{
+       struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
+       struct power_supply_config psy_cfg = { .drv_data = shield_dev, };
+       struct hid_device *hdev = shield_dev->hdev;
+       int ret;
+
+       /*
+        * Set an initial capacity and temperature value to avoid prematurely
+        * triggering alerts. Will be replaced by values queried from initial
+        * HOSTCMD requests.
+        */
+       ts->psy_stats.capacity = 100;
+       ts->psy_stats.temp = 182;
+
+       shield_dev->battery_dev.desc.properties = thunderstrike_battery_props;
+       shield_dev->battery_dev.desc.num_properties =
+               ARRAY_SIZE(thunderstrike_battery_props);
+       shield_dev->battery_dev.desc.get_property = thunderstrike_battery_get_property;
+       shield_dev->battery_dev.desc.type = POWER_SUPPLY_TYPE_BATTERY;
+       shield_dev->battery_dev.desc.name =
+               devm_kasprintf(&ts->base.hdev->dev, GFP_KERNEL,
+                              "thunderstrike_%d", ts->id);
+
+       shield_dev->battery_dev.psy = power_supply_register(
+               &hdev->dev, &shield_dev->battery_dev.desc, &psy_cfg);
+       if (IS_ERR(shield_dev->battery_dev.psy)) {
+               hid_err(hdev, "Failed to register Thunderstrike battery device\n");
+               return PTR_ERR(shield_dev->battery_dev.psy);
+       }
+
+       ret = power_supply_powers(shield_dev->battery_dev.psy, &hdev->dev);
+       if (ret) {
+               hid_err(hdev, "Failed to associate battery device to Thunderstrike\n");
+               goto err;
+       }
+
+       return 0;
+
+err:
+       power_supply_unregister(shield_dev->battery_dev.psy);
+       return ret;
+}
+
 static struct shield_device *thunderstrike_create(struct hid_device *hdev)
 {
        struct shield_device *shield_dev;
@@ -509,26 +872,47 @@ static struct shield_device *thunderstrike_create(struct hid_device *hdev)
        shield_dev->codename = "Thunderstrike";
 
        spin_lock_init(&ts->haptics_update_lock);
+       spin_lock_init(&ts->psy_stats_lock);
        INIT_WORK(&ts->hostcmd_req_work, thunderstrike_hostcmd_req_work_handler);
 
        hid_set_drvdata(hdev, shield_dev);
 
+       ts->id = ida_alloc(&thunderstrike_ida, GFP_KERNEL);
+       if (ts->id < 0)
+               return ERR_PTR(ts->id);
+
+       ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
+       if (IS_ERR(ts->haptics_dev)) {
+               hid_err(hdev, "Failed to create Thunderstrike haptics instance\n");
+               ret = PTR_ERR(ts->haptics_dev);
+               goto err_id;
+       }
+
+       ret = thunderstrike_psy_create(shield_dev);
+       if (ret) {
+               hid_err(hdev, "Failed to create Thunderstrike power supply instance\n");
+               goto err_haptics;
+       }
+
        ret = thunderstrike_led_create(ts);
        if (ret) {
                hid_err(hdev, "Failed to create Thunderstrike LED instance\n");
-               return ERR_PTR(ret);
+               goto err_psy;
        }
 
-       ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
-       if (IS_ERR(ts->haptics_dev))
-               goto err;
+       timer_setup(&ts->psy_stats_timer, thunderstrike_psy_stats_timer_handler, 0);
 
        hid_info(hdev, "Registered Thunderstrike controller\n");
        return shield_dev;
 
-err:
-       led_classdev_unregister(&ts->led_dev);
-       return ERR_CAST(ts->haptics_dev);
+err_psy:
+       power_supply_unregister(shield_dev->battery_dev.psy);
+err_haptics:
+       if (ts->haptics_dev)
+               input_unregister_device(ts->haptics_dev);
+err_id:
+       ida_free(&thunderstrike_ida, ts->id);
+       return ERR_PTR(ret);
 }
 
 static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -683,8 +1067,7 @@ static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err_stop;
        }
 
-       thunderstrike_request_firmware_version(ts);
-       thunderstrike_request_board_info(ts);
+       thunderstrike_device_init_info(shield_dev);
 
        return ret;
 
@@ -704,9 +1087,12 @@ static void shield_remove(struct hid_device *hdev)
        ts = container_of(dev, struct thunderstrike, base);
 
        hid_hw_close(hdev);
-       led_classdev_unregister(&ts->led_dev);
+       power_supply_unregister(dev->battery_dev.psy);
        if (ts->haptics_dev)
                input_unregister_device(ts->haptics_dev);
+       led_classdev_unregister(&ts->led_dev);
+       ida_free(&thunderstrike_ida, ts->id);
+       del_timer_sync(&ts->psy_stats_timer);
        cancel_work_sync(&ts->hostcmd_req_work);
        hid_hw_stop(hdev);
 }
index ea6b79b..d55aaab 100644 (file)
@@ -23,8 +23,6 @@
 #include "hid-roccat-common.h"
 #include "hid-roccat-arvo.h"
 
-static struct class *arvo_class;
-
 static ssize_t arvo_sysfs_show_mode_key(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -268,6 +266,11 @@ static const struct attribute_group *arvo_groups[] = {
        NULL,
 };
 
+static const struct class arvo_class = {
+       .name = "arvo",
+       .dev_groups = arvo_groups,
+};
+
 static int arvo_init_arvo_device_struct(struct usb_device *usb_dev,
                struct arvo_device *arvo)
 {
@@ -309,7 +312,7 @@ static int arvo_init_specials(struct hid_device *hdev)
                goto exit_free;
        }
 
-       retval = roccat_connect(arvo_class, hdev,
+       retval = roccat_connect(&arvo_class, hdev,
                        sizeof(struct arvo_roccat_report));
        if (retval < 0) {
                hid_err(hdev, "couldn't init char dev\n");
@@ -433,21 +436,20 @@ static int __init arvo_init(void)
 {
        int retval;
 
-       arvo_class = class_create("arvo");
-       if (IS_ERR(arvo_class))
-               return PTR_ERR(arvo_class);
-       arvo_class->dev_groups = arvo_groups;
+       retval = class_register(&arvo_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&arvo_driver);
        if (retval)
-               class_destroy(arvo_class);
+               class_unregister(&arvo_class);
        return retval;
 }
 
 static void __exit arvo_exit(void)
 {
        hid_unregister_driver(&arvo_driver);
-       class_destroy(arvo_class);
+       class_unregister(&arvo_class);
 }
 
 module_init(arvo_init);
index 3903a2c..4580604 100644 (file)
@@ -23,8 +23,6 @@
 #include "hid-roccat-common.h"
 #include "hid-roccat-isku.h"
 
-static struct class *isku_class;
-
 static void isku_profile_activated(struct isku_device *isku, uint new_profile)
 {
        isku->actual_profile = new_profile;
@@ -248,6 +246,11 @@ static const struct attribute_group *isku_groups[] = {
        NULL,
 };
 
+static const struct class isku_class = {
+       .name = "isku",
+       .dev_groups = isku_groups,
+};
+
 static int isku_init_isku_device_struct(struct usb_device *usb_dev,
                struct isku_device *isku)
 {
@@ -289,7 +292,7 @@ static int isku_init_specials(struct hid_device *hdev)
                goto exit_free;
        }
 
-       retval = roccat_connect(isku_class, hdev,
+       retval = roccat_connect(&isku_class, hdev,
                        sizeof(struct isku_roccat_report));
        if (retval < 0) {
                hid_err(hdev, "couldn't init char dev\n");
@@ -435,21 +438,21 @@ static struct hid_driver isku_driver = {
 static int __init isku_init(void)
 {
        int retval;
-       isku_class = class_create("isku");
-       if (IS_ERR(isku_class))
-               return PTR_ERR(isku_class);
-       isku_class->dev_groups = isku_groups;
+
+       retval = class_register(&isku_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&isku_driver);
        if (retval)
-               class_destroy(isku_class);
+               class_unregister(&isku_class);
        return retval;
 }
 
 static void __exit isku_exit(void)
 {
        hid_unregister_driver(&isku_driver);
-       class_destroy(isku_class);
+       class_unregister(&isku_class);
 }
 
 module_init(isku_init);
index 945ae23..00a1abc 100644 (file)
@@ -89,9 +89,6 @@ static int kone_send(struct usb_device *usb_dev, uint usb_command,
        return ((len < 0) ? len : ((len != size) ? -EIO : 0));
 }
 
-/* kone_class is used for creating sysfs attributes via roccat char device */
-static struct class *kone_class;
-
 static void kone_set_settings_checksum(struct kone_settings *settings)
 {
        uint16_t checksum = 0;
@@ -657,6 +654,12 @@ static const struct attribute_group *kone_groups[] = {
        NULL,
 };
 
+/* kone_class is used for creating sysfs attributes via roccat char device */
+static const struct class kone_class = {
+       .name = "kone",
+       .dev_groups = kone_groups,
+};
+
 static int kone_init_kone_device_struct(struct usb_device *usb_dev,
                struct kone_device *kone)
 {
@@ -712,8 +715,8 @@ static int kone_init_specials(struct hid_device *hdev)
                        goto exit_free;
                }
 
-               retval = roccat_connect(kone_class, hdev,
-                               sizeof(struct kone_roccat_report));
+               retval = roccat_connect(&kone_class, hdev,
+                                       sizeof(struct kone_roccat_report));
                if (retval < 0) {
                        hid_err(hdev, "couldn't init char dev\n");
                        /* be tolerant about not getting chrdev */
@@ -890,21 +893,20 @@ static int __init kone_init(void)
        int retval;
 
        /* class name has to be same as driver name */
-       kone_class = class_create("kone");
-       if (IS_ERR(kone_class))
-               return PTR_ERR(kone_class);
-       kone_class->dev_groups = kone_groups;
+       retval = class_register(&kone_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&kone_driver);
        if (retval)
-               class_destroy(kone_class);
+               class_unregister(&kone_class);
        return retval;
 }
 
 static void __exit kone_exit(void)
 {
        hid_unregister_driver(&kone_driver);
-       class_destroy(kone_class);
+       class_unregister(&kone_class);
 }
 
 module_init(kone_init);
index 97b83b6..22b8954 100644 (file)
@@ -26,8 +26,6 @@
 
 static uint profile_numbers[5] = {0, 1, 2, 3, 4};
 
-static struct class *koneplus_class;
-
 static void koneplus_profile_activated(struct koneplus_device *koneplus,
                uint new_profile)
 {
@@ -356,6 +354,11 @@ static const struct attribute_group *koneplus_groups[] = {
        NULL,
 };
 
+static const struct class koneplus_class = {
+       .name = "koneplus",
+       .dev_groups = koneplus_groups,
+};
+
 static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev,
                struct koneplus_device *koneplus)
 {
@@ -394,8 +397,8 @@ static int koneplus_init_specials(struct hid_device *hdev)
                        goto exit_free;
                }
 
-               retval = roccat_connect(koneplus_class, hdev,
-                               sizeof(struct koneplus_roccat_report));
+               retval = roccat_connect(&koneplus_class, hdev,
+                                       sizeof(struct koneplus_roccat_report));
                if (retval < 0) {
                        hid_err(hdev, "couldn't init char dev\n");
                } else {
@@ -549,21 +552,20 @@ static int __init koneplus_init(void)
        int retval;
 
        /* class name has to be same as driver name */
-       koneplus_class = class_create("koneplus");
-       if (IS_ERR(koneplus_class))
-               return PTR_ERR(koneplus_class);
-       koneplus_class->dev_groups = koneplus_groups;
+       retval = class_register(&koneplus_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&koneplus_driver);
        if (retval)
-               class_destroy(koneplus_class);
+               class_unregister(&koneplus_class);
        return retval;
 }
 
 static void __exit koneplus_exit(void)
 {
        hid_unregister_driver(&koneplus_driver);
-       class_destroy(koneplus_class);
+       class_unregister(&koneplus_class);
 }
 
 module_init(koneplus_init);
index a297756..beca8ae 100644 (file)
@@ -36,8 +36,6 @@ struct konepure_mouse_report_button {
        uint8_t unknown[2];
 } __packed;
 
-static struct class *konepure_class;
-
 ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03);
 ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(actual_profile, 0x05, 0x03);
 ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_settings, 0x06, 0x1f);
@@ -72,6 +70,11 @@ static const struct attribute_group *konepure_groups[] = {
        NULL,
 };
 
+static const struct class konepure_class = {
+       .name = "konepure",
+       .dev_groups = konepure_groups,
+};
+
 static int konepure_init_specials(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
@@ -98,8 +101,8 @@ static int konepure_init_specials(struct hid_device *hdev)
                goto exit_free;
        }
 
-       retval = roccat_connect(konepure_class, hdev,
-                       sizeof(struct konepure_mouse_report_button));
+       retval = roccat_connect(&konepure_class, hdev,
+                               sizeof(struct konepure_mouse_report_button));
        if (retval < 0) {
                hid_err(hdev, "couldn't init char dev\n");
        } else {
@@ -207,21 +210,20 @@ static int __init konepure_init(void)
 {
        int retval;
 
-       konepure_class = class_create("konepure");
-       if (IS_ERR(konepure_class))
-               return PTR_ERR(konepure_class);
-       konepure_class->dev_groups = konepure_groups;
+       retval = class_register(&konepure_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&konepure_driver);
        if (retval)
-               class_destroy(konepure_class);
+               class_unregister(&konepure_class);
        return retval;
 }
 
 static void __exit konepure_exit(void)
 {
        hid_unregister_driver(&konepure_driver);
-       class_destroy(konepure_class);
+       class_unregister(&konepure_class);
 }
 
 module_init(konepure_init);
index 1a1d96e..86af538 100644 (file)
@@ -24,8 +24,6 @@
 
 static uint profile_numbers[5] = {0, 1, 2, 3, 4};
 
-static struct class *kovaplus_class;
-
 static uint kovaplus_convert_event_cpi(uint value)
 {
        return (value == 7 ? 4 : (value == 4 ? 3 : value));
@@ -409,6 +407,11 @@ static const struct attribute_group *kovaplus_groups[] = {
        NULL,
 };
 
+static const struct class kovaplus_class = {
+       .name = "kovaplus",
+       .dev_groups = kovaplus_groups,
+};
+
 static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
                struct kovaplus_device *kovaplus)
 {
@@ -463,8 +466,8 @@ static int kovaplus_init_specials(struct hid_device *hdev)
                        goto exit_free;
                }
 
-               retval = roccat_connect(kovaplus_class, hdev,
-                               sizeof(struct kovaplus_roccat_report));
+               retval = roccat_connect(&kovaplus_class, hdev,
+                                       sizeof(struct kovaplus_roccat_report));
                if (retval < 0) {
                        hid_err(hdev, "couldn't init char dev\n");
                } else {
@@ -638,21 +641,20 @@ static int __init kovaplus_init(void)
 {
        int retval;
 
-       kovaplus_class = class_create("kovaplus");
-       if (IS_ERR(kovaplus_class))
-               return PTR_ERR(kovaplus_class);
-       kovaplus_class->dev_groups = kovaplus_groups;
+       retval = class_register(&kovaplus_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&kovaplus_driver);
        if (retval)
-               class_destroy(kovaplus_class);
+               class_unregister(&kovaplus_class);
        return retval;
 }
 
 static void __exit kovaplus_exit(void)
 {
        hid_unregister_driver(&kovaplus_driver);
-       class_destroy(kovaplus_class);
+       class_unregister(&kovaplus_class);
 }
 
 module_init(kovaplus_init);
index 15528c3..5663b9c 100644 (file)
@@ -26,9 +26,6 @@
 
 static uint profile_numbers[5] = {0, 1, 2, 3, 4};
 
-/* pyra_class is used for creating sysfs attributes via roccat char device */
-static struct class *pyra_class;
-
 static void profile_activated(struct pyra_device *pyra,
                unsigned int new_profile)
 {
@@ -366,6 +363,12 @@ static const struct attribute_group *pyra_groups[] = {
        NULL,
 };
 
+/* pyra_class is used for creating sysfs attributes via roccat char device */
+static const struct class pyra_class = {
+       .name = "pyra",
+       .dev_groups = pyra_groups,
+};
+
 static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
                struct pyra_device *pyra)
 {
@@ -413,7 +416,7 @@ static int pyra_init_specials(struct hid_device *hdev)
                        goto exit_free;
                }
 
-               retval = roccat_connect(pyra_class, hdev,
+               retval = roccat_connect(&pyra_class, hdev,
                                sizeof(struct pyra_roccat_report));
                if (retval < 0) {
                        hid_err(hdev, "couldn't init char dev\n");
@@ -585,21 +588,20 @@ static int __init pyra_init(void)
        int retval;
 
        /* class name has to be same as driver name */
-       pyra_class = class_create("pyra");
-       if (IS_ERR(pyra_class))
-               return PTR_ERR(pyra_class);
-       pyra_class->dev_groups = pyra_groups;
+       retval = class_register(&pyra_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&pyra_driver);
        if (retval)
-               class_destroy(pyra_class);
+               class_unregister(&pyra_class);
        return retval;
 }
 
 static void __exit pyra_exit(void)
 {
        hid_unregister_driver(&pyra_driver);
-       class_destroy(pyra_class);
+       class_unregister(&pyra_class);
 }
 
 module_init(pyra_init);
index 0eb17a3..57714a4 100644 (file)
@@ -28,8 +28,6 @@ struct ryos_report_special {
        uint8_t data[4];
 } __packed;
 
-static struct class *ryos_class;
-
 ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03);
 ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x05, 0x03);
 ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_primary, 0x06, 0x7d);
@@ -80,6 +78,11 @@ static const struct attribute_group *ryos_groups[] = {
        NULL,
 };
 
+static const struct class ryos_class = {
+       .name = "ryos",
+       .dev_groups = ryos_groups,
+};
+
 static int ryos_init_specials(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
@@ -106,7 +109,7 @@ static int ryos_init_specials(struct hid_device *hdev)
                goto exit_free;
        }
 
-       retval = roccat_connect(ryos_class, hdev,
+       retval = roccat_connect(&ryos_class, hdev,
                        sizeof(struct ryos_report_special));
        if (retval < 0) {
                hid_err(hdev, "couldn't init char dev\n");
@@ -216,21 +219,20 @@ static int __init ryos_init(void)
 {
        int retval;
 
-       ryos_class = class_create("ryos");
-       if (IS_ERR(ryos_class))
-               return PTR_ERR(ryos_class);
-       ryos_class->dev_groups = ryos_groups;
+       retval = class_register(&ryos_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&ryos_driver);
        if (retval)
-               class_destroy(ryos_class);
+               class_unregister(&ryos_class);
        return retval;
 }
 
 static void __exit ryos_exit(void)
 {
        hid_unregister_driver(&ryos_driver);
-       class_destroy(ryos_class);
+       class_unregister(&ryos_class);
 }
 
 module_init(ryos_init);
index 93be7ac..2baa47a 100644 (file)
@@ -22,8 +22,6 @@
 #include "hid-roccat-common.h"
 #include "hid-roccat-savu.h"
 
-static struct class *savu_class;
-
 ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x4, 0x03);
 ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x5, 0x03);
 ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(general, 0x6, 0x10);
@@ -52,6 +50,11 @@ static const struct attribute_group *savu_groups[] = {
        NULL,
 };
 
+static const struct class savu_class = {
+       .name = "savu",
+       .dev_groups = savu_groups,
+};
+
 static int savu_init_specials(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
@@ -78,7 +81,7 @@ static int savu_init_specials(struct hid_device *hdev)
                goto exit_free;
        }
 
-       retval = roccat_connect(savu_class, hdev,
+       retval = roccat_connect(&savu_class, hdev,
                        sizeof(struct savu_roccat_report));
        if (retval < 0) {
                hid_err(hdev, "couldn't init char dev\n");
@@ -204,21 +207,20 @@ static int __init savu_init(void)
 {
        int retval;
 
-       savu_class = class_create("savu");
-       if (IS_ERR(savu_class))
-               return PTR_ERR(savu_class);
-       savu_class->dev_groups = savu_groups;
+       retval = class_register(&savu_class);
+       if (retval)
+               return retval;
 
        retval = hid_register_driver(&savu_driver);
        if (retval)
-               class_destroy(savu_class);
+               class_unregister(&savu_class);
        return retval;
 }
 
 static void __exit savu_exit(void)
 {
        hid_unregister_driver(&savu_driver);
-       class_destroy(savu_class);
+       class_unregister(&savu_class);
 }
 
 module_init(savu_init);
index 6da80e4..c7f7562 100644 (file)
@@ -295,7 +295,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event);
  * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on
  * success, a negative error code on failure.
  */
-int roccat_connect(struct class *klass, struct hid_device *hid, int report_size)
+int roccat_connect(const struct class *klass, struct hid_device *hid, int report_size)
 {
        unsigned int minor;
        struct roccat_device *device;
index 83237b8..2eba152 100644 (file)
@@ -632,7 +632,7 @@ static int sensor_hub_probe(struct hid_device *hdev,
        }
        INIT_LIST_HEAD(&hdev->inputs);
 
-       ret = hid_hw_start(hdev, 0);
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
        if (ret) {
                hid_err(hdev, "hw start failed\n");
                return ret;
index f67835f..ad74cbc 100644 (file)
@@ -85,10 +85,8 @@ static int uclogic_input_configured(struct hid_device *hdev,
 {
        struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
        struct uclogic_params *params = &drvdata->params;
-       char *name;
        const char *suffix = NULL;
        struct hid_field *field;
-       size_t len;
        size_t i;
        const struct uclogic_params_frame *frame;
 
@@ -146,14 +144,9 @@ static int uclogic_input_configured(struct hid_device *hdev,
                }
        }
 
-       if (suffix) {
-               len = strlen(hdev->name) + 2 + strlen(suffix);
-               name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL);
-               if (name) {
-                       snprintf(name, len, "%s %s", hdev->name, suffix);
-                       hi->input->name = name;
-               }
-       }
+       if (suffix)
+               hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+                                                "%s %s", hdev->name, suffix);
 
        return 0;
 }
index e63c56a..13c8dd8 100644 (file)
@@ -32,7 +32,9 @@
 
 static int hidraw_major;
 static struct cdev hidraw_cdev;
-static struct class *hidraw_class;
+static const struct class hidraw_class = {
+       .name = "hidraw",
+};
 static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
 static DECLARE_RWSEM(minors_rwsem);
 
@@ -329,7 +331,7 @@ static void drop_ref(struct hidraw *hidraw, int exists_bit)
                        hid_hw_close(hidraw->hid);
                        wake_up_interruptible(&hidraw->wait);
                }
-               device_destroy(hidraw_class,
+               device_destroy(&hidraw_class,
                               MKDEV(hidraw_major, hidraw->minor));
        } else {
                --hidraw->open;
@@ -569,7 +571,7 @@ int hidraw_connect(struct hid_device *hid)
                goto out;
        }
 
-       dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
+       dev->dev = device_create(&hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
                                 NULL, "%s%d", "hidraw", minor);
 
        if (IS_ERR(dev->dev)) {
@@ -623,11 +625,9 @@ int __init hidraw_init(void)
 
        hidraw_major = MAJOR(dev_id);
 
-       hidraw_class = class_create("hidraw");
-       if (IS_ERR(hidraw_class)) {
-               result = PTR_ERR(hidraw_class);
+       result = class_register(&hidraw_class);
+       if (result)
                goto error_cdev;
-       }
 
         cdev_init(&hidraw_cdev, &hidraw_ops);
        result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
@@ -639,7 +639,7 @@ out:
        return result;
 
 error_class:
-       class_destroy(hidraw_class);
+       class_unregister(&hidraw_class);
 error_cdev:
        unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
        goto out;
@@ -650,7 +650,7 @@ void hidraw_exit(void)
        dev_t dev_id = MKDEV(hidraw_major, 0);
 
        cdev_del(&hidraw_cdev);
-       class_destroy(hidraw_class);
+       class_unregister(&hidraw_class);
        unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
 
 }
index 029045d..31abab5 100644 (file)
 #include "i2c-hid.h"
 
 struct elan_i2c_hid_chip_data {
-       unsigned int post_gpio_reset_delay_ms;
+       unsigned int post_gpio_reset_on_delay_ms;
+       unsigned int post_gpio_reset_off_delay_ms;
        unsigned int post_power_delay_ms;
        u16 hid_descriptor_address;
+       const char *main_supply_name;
 };
 
 struct i2c_hid_of_elan {
@@ -38,9 +40,11 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops)
                container_of(ops, struct i2c_hid_of_elan, ops);
        int ret;
 
-       ret = regulator_enable(ihid_elan->vcc33);
-       if (ret)
-               return ret;
+       if (ihid_elan->vcc33) {
+               ret = regulator_enable(ihid_elan->vcc33);
+               if (ret)
+                       return ret;
+       }
 
        ret = regulator_enable(ihid_elan->vccio);
        if (ret) {
@@ -52,8 +56,8 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops)
                msleep(ihid_elan->chip_data->post_power_delay_ms);
 
        gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0);
-       if (ihid_elan->chip_data->post_gpio_reset_delay_ms)
-               msleep(ihid_elan->chip_data->post_gpio_reset_delay_ms);
+       if (ihid_elan->chip_data->post_gpio_reset_on_delay_ms)
+               msleep(ihid_elan->chip_data->post_gpio_reset_on_delay_ms);
 
        return 0;
 }
@@ -64,8 +68,12 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops)
                container_of(ops, struct i2c_hid_of_elan, ops);
 
        gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1);
+       if (ihid_elan->chip_data->post_gpio_reset_off_delay_ms)
+               msleep(ihid_elan->chip_data->post_gpio_reset_off_delay_ms);
+
        regulator_disable(ihid_elan->vccio);
-       regulator_disable(ihid_elan->vcc33);
+       if (ihid_elan->vcc33)
+               regulator_disable(ihid_elan->vcc33);
 }
 
 static int i2c_hid_of_elan_probe(struct i2c_client *client)
@@ -89,24 +97,42 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client)
        if (IS_ERR(ihid_elan->vccio))
                return PTR_ERR(ihid_elan->vccio);
 
-       ihid_elan->vcc33 = devm_regulator_get(&client->dev, "vcc33");
-       if (IS_ERR(ihid_elan->vcc33))
-               return PTR_ERR(ihid_elan->vcc33);
-
        ihid_elan->chip_data = device_get_match_data(&client->dev);
 
+       if (ihid_elan->chip_data->main_supply_name) {
+               ihid_elan->vcc33 = devm_regulator_get(&client->dev,
+                                                     ihid_elan->chip_data->main_supply_name);
+               if (IS_ERR(ihid_elan->vcc33))
+                       return PTR_ERR(ihid_elan->vcc33);
+       }
+
        return i2c_hid_core_probe(client, &ihid_elan->ops,
                                  ihid_elan->chip_data->hid_descriptor_address, 0);
 }
 
 static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = {
        .post_power_delay_ms = 1,
-       .post_gpio_reset_delay_ms = 300,
+       .post_gpio_reset_on_delay_ms = 300,
+       .hid_descriptor_address = 0x0001,
+       .main_supply_name = "vcc33",
+};
+
+static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = {
+       .post_power_delay_ms = 1,
+       .post_gpio_reset_on_delay_ms = 200,
+       .post_gpio_reset_off_delay_ms = 65,
        .hid_descriptor_address = 0x0001,
+       /*
+        * this touchscreen is tightly integrated with the panel and assumes
+        * that the relevant power rails (other than the IO rail) have already
+        * been turned on by the panel driver because we're a panel follower.
+        */
+       .main_supply_name = NULL,
 };
 
 static const struct of_device_id elan_i2c_hid_of_match[] = {
        { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data },
+       { .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data },
        { }
 };
 MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match);
index 3214fb0..753654f 100644 (file)
@@ -16,7 +16,7 @@
 
 #ifdef __KERNEL__
 
-int roccat_connect(struct class *klass, struct hid_device *hid,
+int roccat_connect(const struct class *klass, struct hid_device *hid,
                int report_size);
 void roccat_disconnect(int minor);
 int roccat_report_event(int minor, u8 const *data);
index 39e21e3..964ca1f 100644 (file)
@@ -341,6 +341,29 @@ struct hid_item {
  */
 #define MAX_USBHID_BOOT_QUIRKS 4
 
+/**
+ * DOC: HID quirks
+ * | @HID_QUIRK_NOTOUCH:
+ * | @HID_QUIRK_IGNORE: ignore this device
+ * | @HID_QUIRK_NOGET:
+ * | @HID_QUIRK_HIDDEV_FORCE:
+ * | @HID_QUIRK_BADPAD:
+ * | @HID_QUIRK_MULTI_INPUT:
+ * | @HID_QUIRK_HIDINPUT_FORCE:
+ * | @HID_QUIRK_ALWAYS_POLL:
+ * | @HID_QUIRK_INPUT_PER_APP:
+ * | @HID_QUIRK_X_INVERT:
+ * | @HID_QUIRK_Y_INVERT:
+ * | @HID_QUIRK_SKIP_OUTPUT_REPORTS:
+ * | @HID_QUIRK_SKIP_OUTPUT_REPORT_ID:
+ * | @HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP:
+ * | @HID_QUIRK_HAVE_SPECIAL_DRIVER:
+ * | @HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE:
+ * | @HID_QUIRK_FULLSPEED_INTERVAL:
+ * | @HID_QUIRK_NO_INIT_REPORTS:
+ * | @HID_QUIRK_NO_IGNORE:
+ * | @HID_QUIRK_NO_INPUT_SYNC:
+ */
 /* BIT(0) reserved for backward compatibility, was HID_QUIRK_INVERT */
 #define HID_QUIRK_NOTOUCH                      BIT(1)
 #define HID_QUIRK_IGNORE                       BIT(2)
@@ -360,6 +383,7 @@ struct hid_item {
 #define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP BIT(18)
 #define HID_QUIRK_HAVE_SPECIAL_DRIVER          BIT(19)
 #define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE BIT(20)
+#define HID_QUIRK_NOINVERT                     BIT(21)
 #define HID_QUIRK_FULLSPEED_INTERVAL           BIT(28)
 #define HID_QUIRK_NO_INIT_REPORTS              BIT(29)
 #define HID_QUIRK_NO_IGNORE                    BIT(30)
@@ -555,9 +579,9 @@ struct hid_input {
        struct hid_report *report;
        struct input_dev *input;
        const char *name;
-       bool registered;
        struct list_head reports;       /* the list of reports */
        unsigned int application;       /* application usage for this input */
+       bool registered;
 };
 
 enum hid_type {
index 4812022..3c10919 100644 (file)
@@ -30,6 +30,7 @@ static inline const char *str_read_write(bool v)
 {
        return v ? "read" : "write";
 }
+#define str_write_read(v)              str_read_write(!(v))
 
 static inline const char *str_on_off(bool v)
 {