for-linus-2023083101

-----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCAAxFiEEoEVH9lhNrxiMPSyI7MXwXhnZSjYFAmTworgTHGJlbnRpc3NA
 a2VybmVsLm9yZwAKCRDsxfBeGdlKNmIFD/wKBNvoGV8uEw/U+FeE++kpgsuc3Bnl
 Yr4O4gxE+MODOUCvNo9l24REsYXmQvBdMtYlvMbhPrQ0lPrfG73y3DRDagZdq5c7
 Mfolrun72A46LnqznNZLbMD5SVBZzRRRQGjXUZaTwJF5Ss7iNNK+v/C5cd0l2ZaB
 PBJ4DGTjMMnDKnhbPua5HL5IvZqLOjxlI1G30TU//6MAt1iLlOiWNmv8Wus/nl6U
 h5kufTbyr1Dl3reGlgMFsvD94f4NlJITs5q5+9DtAZ4iJ185bSIk5NXb/lGh7umJ
 hB1YUVqvGNobQ6wbXSTD7M+A983vqREIo47qnkuOP/2KducRLcpPL2AZ9Eq8JDQS
 reTJeuE9c559SKzKDw6NC2Igs+yC4MAV5GfHps+vtavJxAKNI3RISGuoiv5J39Ms
 MSl5DAGg9fYjP4E7ZMp6hMwVt0fuMOrtQhi/Zh+YHOg0HubIgGHDTVLSpMLTb8NM
 Aovf28szSbM7qpseTsk6auCqBXST/f4z6iSsLJ1y/XCE1eqEEehfaEFlXG5s0XEU
 U0D2Z0bKh1AIe23ptluZDAvKLMlFDxnssJuXXk8UO9AzfNmcQcsxDmvq/AYh9nBc
 /iEh/yc5pPRCltTq/nAjCMee5N9UpfF6I3EQfrlEW2PxKFgM+Md9z+WlKF0esDT3
 WUbYsEiAc+N7dg==
 =24SV
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-2023083101' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID updates from Benjamin Tissoires:

 - devm fixes for problems that caused use-after-free reports (Rahul
   Rameshbabu)

 - Some extensive HID docs (Marco Morandini)

 - Constification of struct class (Ivan Orlov and Greg Kroah-Hartman)

 - Google Stadia Force Feedback support (Fabio Baltieri)

 - Various fixes and new device ID support

* tag 'for-linus-2023083101' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (42 commits)
  HID: logitech-hidpp: rework one more time the retries attempts
  HID: nvidia-shield: Reference hid_device devm allocation of input_dev name
  HID: multitouch: Correct devm device reference for hidinput input_dev name
  HID: uclogic: Correct devm device reference for hidinput input_dev name
  HID: logitech-dj: Fix error handling in logi_dj_recv_switch_to_dj_mode()
  HID: i2c-hid: elan: Add ili9882t timing
  dt-bindings: input: i2c-hid: Introduce Ilitek ili9882t
  HID: apple: Add "Hailuck" to the list of non-apple keyboards
  HID: steelseries: arctis_1_battery_request[] should be static
  MAINTAINERS: update my email address
  HID: logitech-hidpp: Add support for Logitech MX Anywhere 3 mouse
  HID: wacom: struct name cleanup
  HID: wacom: remove unnecessary 'connected' variable from EKR
  HID: wacom: remove the battery when the EKR is off
  HID: nvidia-shield: Update Thunderstrike LED instance name to use id
  HID: nvidia-shield: Add battery support for Thunderstrike
  HID: nvidia-shield: Remove led_classdev_unregister in thunderstrike_create
  HID: hid-google-stadiaff: add support for Stadia force feedback
  HID: logitech-dj: Add support for a new lightspeed receiver iteration
  HID: logitech-hidpp: Add support for the Pro X Superlight
  ...
This commit is contained in:
Linus Torvalds 2023-09-01 12:31:44 -07:00
commit 29aa98d0fe
28 changed files with 1802 additions and 266 deletions

View File

@ -139,6 +139,9 @@ Daniel Borkmann <daniel@iogearbox.net> <dborkman@redhat.com>
Daniel Borkmann <daniel@iogearbox.net> <dxchgb@gmail.com>
David Brownell <david-b@pacbell.net>
David Collins <quic_collinsd@quicinc.com> <collinsd@codeaurora.org>
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>
Dedy Lansky <quic_dlansky@quicinc.com> <dlansky@codeaurora.org>
Deepak Kumar Singh <quic_deesin@quicinc.com> <deesin@codeaurora.org>

View File

@ -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>;
};
};

View File

@ -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
...

View File

@ -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.

View File

@ -7,6 +7,7 @@ Human Interface Devices (HID)
.. toctree::
:maxdepth: 1
hidintro
hiddev
hidraw
hid-sensor

View File

@ -21987,7 +21987,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
@ -23159,7 +23159,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*

View 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
@ -1066,9 +1073,11 @@ config STEAM_FF
Deck.
config HID_STEELSERIES
tristate "Steelseries SRW-S1 steering wheel support"
tristate "Steelseries devices support"
depends on USB_HID
help
Support for Steelseries SRW-S1 steering wheel
Support for Steelseries SRW-S1 steering wheel, and the Steelseries
Arctis 1 Wireless for XBox headset.
config HID_SUNPLUS
tristate "Sunplus wireless desktop"

View 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

View File

@ -343,7 +343,8 @@ static const struct apple_non_apple_keyboard non_apple_keyboards[] = {
{ "SONiX USB DEVICE" },
{ "Keychron" },
{ "AONE" },
{ "GANSS" }
{ "GANSS" },
{ "Hailuck" },
};
static bool apple_is_non_apple_keyboard(struct hid_device *hdev)

View File

@ -16,14 +16,14 @@
* 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 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
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

View File

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

View File

@ -531,6 +531,7 @@
#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
@ -866,6 +867,7 @@
#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

View File

@ -358,6 +358,9 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICTRACKPAD),
HID_BATTERY_QUIRK_IGNORE },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
USB_DEVICE_ID_ELECOM_BM084),
HID_BATTERY_QUIRK_IGNORE },
@ -988,6 +991,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 +1017,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 +1588,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;

View 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),

View 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);
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));
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);
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);
}
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 (!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 (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_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;
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) },
{}

View 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;
}

View 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);
}

View 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;

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HID driver for Steelseries SRW-S1
* HID driver for Steelseries devices
*
* Copyright (c) 2013 Simon Wood
* Copyright (c) 2023 Bastien Nocera
*/
/*
@ -11,10 +12,28 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/leds.h>
#include "hid-ids.h"
#define STEELSERIES_SRWS1 BIT(0)
#define STEELSERIES_ARCTIS_1 BIT(1)
struct steelseries_device {
struct hid_device *hdev;
unsigned long quirks;
struct delayed_work battery_work;
spinlock_t lock;
bool removed;
struct power_supply_desc battery_desc;
struct power_supply *battery;
uint8_t battery_capacity;
bool headset_connected;
};
#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
(IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
#define SRWS1_NUMBER_LEDS 15
@ -353,9 +372,211 @@ static void steelseries_srws1_remove(struct hid_device *hdev)
}
#endif
#define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000
#define ARCTIS_1_BATTERY_RESPONSE_LEN 8
static const char arctis_1_battery_request[] = { 0x06, 0x12 };
static int steelseries_headset_arctis_1_fetch_battery(struct hid_device *hdev)
{
u8 *write_buf;
int ret;
/* Request battery information */
write_buf = kmemdup(arctis_1_battery_request, sizeof(arctis_1_battery_request), GFP_KERNEL);
if (!write_buf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, arctis_1_battery_request[0],
write_buf, sizeof(arctis_1_battery_request),
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
if (ret < sizeof(arctis_1_battery_request)) {
hid_err(hdev, "hid_hw_raw_request() failed with %d\n", ret);
ret = -ENODATA;
}
kfree(write_buf);
return ret;
}
static void steelseries_headset_fetch_battery(struct hid_device *hdev)
{
struct steelseries_device *sd = hid_get_drvdata(hdev);
int ret = 0;
if (sd->quirks & STEELSERIES_ARCTIS_1)
ret = steelseries_headset_arctis_1_fetch_battery(hdev);
if (ret < 0)
hid_dbg(hdev,
"Battery query failed (err: %d)\n", ret);
}
static void steelseries_headset_battery_timer_tick(struct work_struct *work)
{
struct steelseries_device *sd = container_of(work,
struct steelseries_device, battery_work.work);
struct hid_device *hdev = sd->hdev;
steelseries_headset_fetch_battery(hdev);
}
static int steelseries_headset_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct steelseries_device *sd = power_supply_get_drvdata(psy);
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = sd->headset_connected ?
POWER_SUPPLY_STATUS_DISCHARGING :
POWER_SUPPLY_STATUS_UNKNOWN;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = sd->battery_capacity;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static void
steelseries_headset_set_wireless_status(struct hid_device *hdev,
bool connected)
{
struct usb_interface *intf;
if (!hid_is_usb(hdev))
return;
intf = to_usb_interface(hdev->dev.parent);
usb_set_wireless_status(intf, connected ?
USB_WIRELESS_STATUS_CONNECTED :
USB_WIRELESS_STATUS_DISCONNECTED);
}
static enum power_supply_property steelseries_headset_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_CAPACITY,
};
static int steelseries_headset_battery_register(struct steelseries_device *sd)
{
static atomic_t battery_no = ATOMIC_INIT(0);
struct power_supply_config battery_cfg = { .drv_data = sd, };
unsigned long n;
int ret;
sd->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
sd->battery_desc.properties = steelseries_headset_battery_props;
sd->battery_desc.num_properties = ARRAY_SIZE(steelseries_headset_battery_props);
sd->battery_desc.get_property = steelseries_headset_battery_get_property;
sd->battery_desc.use_for_apm = 0;
n = atomic_inc_return(&battery_no) - 1;
sd->battery_desc.name = devm_kasprintf(&sd->hdev->dev, GFP_KERNEL,
"steelseries_headset_battery_%ld", n);
if (!sd->battery_desc.name)
return -ENOMEM;
/* avoid the warning of 0% battery while waiting for the first info */
steelseries_headset_set_wireless_status(sd->hdev, false);
sd->battery_capacity = 100;
sd->battery = devm_power_supply_register(&sd->hdev->dev,
&sd->battery_desc, &battery_cfg);
if (IS_ERR(sd->battery)) {
ret = PTR_ERR(sd->battery);
hid_err(sd->hdev,
"%s:power_supply_register failed with error %d\n",
__func__, ret);
return ret;
}
power_supply_powers(sd->battery, &sd->hdev->dev);
INIT_DELAYED_WORK(&sd->battery_work, steelseries_headset_battery_timer_tick);
steelseries_headset_fetch_battery(sd->hdev);
return 0;
}
static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct steelseries_device *sd;
int ret;
sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
if (!sd)
return -ENOMEM;
hid_set_drvdata(hdev, sd);
sd->hdev = hdev;
sd->quirks = id->driver_data;
if (sd->quirks & STEELSERIES_SRWS1) {
#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
(IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
return steelseries_srws1_probe(hdev, id);
#else
return -ENODEV;
#endif
}
ret = hid_parse(hdev);
if (ret)
return ret;
spin_lock_init(&sd->lock);
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
return ret;
if (steelseries_headset_battery_register(sd) < 0)
hid_err(sd->hdev,
"Failed to register battery for headset\n");
return ret;
}
static void steelseries_remove(struct hid_device *hdev)
{
struct steelseries_device *sd = hid_get_drvdata(hdev);
unsigned long flags;
if (sd->quirks & STEELSERIES_SRWS1) {
#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
(IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
steelseries_srws1_remove(hdev);
#endif
return;
}
spin_lock_irqsave(&sd->lock, flags);
sd->removed = true;
spin_unlock_irqrestore(&sd->lock, flags);
cancel_delayed_work_sync(&sd->battery_work);
hid_hw_stop(hdev);
}
static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
if (hdev->vendor != USB_VENDOR_ID_STEELSERIES ||
hdev->product != USB_DEVICE_ID_STEELSERIES_SRWS1)
return rdesc;
if (*rsize >= 115 && rdesc[11] == 0x02 && rdesc[13] == 0xc8
&& rdesc[29] == 0xbb && rdesc[40] == 0xc5) {
hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n");
@ -365,22 +586,82 @@ static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc
return rdesc;
}
static const struct hid_device_id steelseries_srws1_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
static int steelseries_headset_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *read_buf,
int size)
{
struct steelseries_device *sd = hid_get_drvdata(hdev);
int capacity = sd->battery_capacity;
bool connected = sd->headset_connected;
unsigned long flags;
/* Not a headset */
if (sd->quirks & STEELSERIES_SRWS1)
return 0;
if (sd->quirks & STEELSERIES_ARCTIS_1) {
hid_dbg(sd->hdev,
"Parsing raw event for Arctis 1 headset (%*ph)\n", size, read_buf);
if (size < ARCTIS_1_BATTERY_RESPONSE_LEN ||
memcmp (read_buf, arctis_1_battery_request, sizeof(arctis_1_battery_request)))
return 0;
if (read_buf[2] == 0x01) {
connected = false;
capacity = 100;
} else {
connected = true;
capacity = read_buf[3];
}
}
if (connected != sd->headset_connected) {
hid_dbg(sd->hdev,
"Connected status changed from %sconnected to %sconnected\n",
sd->headset_connected ? "" : "not ",
connected ? "" : "not ");
sd->headset_connected = connected;
steelseries_headset_set_wireless_status(hdev, connected);
}
if (capacity != sd->battery_capacity) {
hid_dbg(sd->hdev,
"Battery capacity changed from %d%% to %d%%\n",
sd->battery_capacity, capacity);
sd->battery_capacity = capacity;
power_supply_changed(sd->battery);
}
spin_lock_irqsave(&sd->lock, flags);
if (!sd->removed)
schedule_delayed_work(&sd->battery_work,
msecs_to_jiffies(STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS));
spin_unlock_irqrestore(&sd->lock, flags);
return 0;
}
static const struct hid_device_id steelseries_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1),
.driver_data = STEELSERIES_SRWS1 },
{ /* SteelSeries Arctis 1 Wireless for XBox */
HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 0x12b6),
.driver_data = STEELSERIES_ARCTIS_1 },
{ }
};
MODULE_DEVICE_TABLE(hid, steelseries_srws1_devices);
MODULE_DEVICE_TABLE(hid, steelseries_devices);
static struct hid_driver steelseries_srws1_driver = {
.name = "steelseries_srws1",
.id_table = steelseries_srws1_devices,
#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
(IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES))
.probe = steelseries_srws1_probe,
.remove = steelseries_srws1_remove,
#endif
.report_fixup = steelseries_srws1_report_fixup
static struct hid_driver steelseries_driver = {
.name = "steelseries",
.id_table = steelseries_devices,
.probe = steelseries_probe,
.remove = steelseries_remove,
.report_fixup = steelseries_srws1_report_fixup,
.raw_event = steelseries_headset_raw_event,
};
module_hid_driver(steelseries_srws1_driver);
module_hid_driver(steelseries_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
MODULE_AUTHOR("Simon Wood <simon@mungewell.org>");

View 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;
}

View File

@ -173,7 +173,6 @@ int wiidebug_init(struct wiimote_data *wdata)
{
struct wiimote_debug *dbg;
unsigned long flags;
int ret = -ENOMEM;
dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
if (!dbg)
@ -183,13 +182,9 @@ int wiidebug_init(struct wiimote_data *wdata)
dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR,
dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops);
if (!dbg->eeprom)
goto err;
dbg->drm = debugfs_create_file("drm", S_IRUSR,
dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops);
if (!dbg->drm)
goto err_drm;
spin_lock_irqsave(&wdata->state.lock, flags);
wdata->debug = dbg;
@ -197,11 +192,6 @@ int wiidebug_init(struct wiimote_data *wdata)
return 0;
err_drm:
debugfs_remove(dbg->eeprom);
err:
kfree(dbg);
return ret;
}
void wiidebug_deinit(struct wiimote_data *wdata)

View File

@ -18,9 +18,11 @@
#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);

View File

@ -150,6 +150,7 @@ struct wacom_remote {
struct input_dev *input;
bool registered;
struct wacom_battery battery;
ktime_t active_time;
} remotes[WACOM_MAX_REMOTES];
};

View File

@ -1997,7 +1997,7 @@ static int wacom_initialize_remotes(struct wacom *wacom)
spin_lock_init(&remote->remote_lock);
error = kfifo_alloc(&remote->remote_fifo,
5 * sizeof(struct wacom_remote_data),
5 * sizeof(struct wacom_remote_work_data),
GFP_KERNEL);
if (error) {
hid_err(wacom->hdev, "failed allocating remote_fifo\n");
@ -2523,6 +2523,18 @@ static void wacom_wireless_work(struct work_struct *work)
return;
}
static void wacom_remote_destroy_battery(struct wacom *wacom, int index)
{
struct wacom_remote *remote = wacom->remote;
if (remote->remotes[index].battery.battery) {
devres_release_group(&wacom->hdev->dev,
&remote->remotes[index].battery.bat_desc);
remote->remotes[index].battery.battery = NULL;
remote->remotes[index].active_time = 0;
}
}
static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index)
{
struct wacom_remote *remote = wacom->remote;
@ -2537,9 +2549,7 @@ static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index)
remote->remotes[i].registered = false;
spin_unlock_irqrestore(&remote->remote_lock, flags);
if (remote->remotes[i].battery.battery)
devres_release_group(&wacom->hdev->dev,
&remote->remotes[i].battery.bat_desc);
wacom_remote_destroy_battery(wacom, i);
if (remote->remotes[i].group.name)
devres_release_group(&wacom->hdev->dev,
@ -2547,7 +2557,6 @@ static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index)
remote->remotes[i].serial = 0;
remote->remotes[i].group.name = NULL;
remote->remotes[i].battery.battery = NULL;
wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN;
}
}
@ -2632,6 +2641,9 @@ static int wacom_remote_attach_battery(struct wacom *wacom, int index)
if (remote->remotes[index].battery.battery)
return 0;
if (!remote->remotes[index].active_time)
return 0;
if (wacom->led.groups[index].select == WACOM_STATUS_UNKNOWN)
return 0;
@ -2647,17 +2659,19 @@ static void wacom_remote_work(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, remote_work);
struct wacom_remote *remote = wacom->remote;
struct wacom_remote_data data;
ktime_t kt = ktime_get();
struct wacom_remote_work_data remote_work_data;
unsigned long flags;
unsigned int count;
u32 serial;
u32 work_serial;
int i;
spin_lock_irqsave(&remote->remote_lock, flags);
count = kfifo_out(&remote->remote_fifo, &data, sizeof(data));
count = kfifo_out(&remote->remote_fifo, &remote_work_data,
sizeof(remote_work_data));
if (count != sizeof(data)) {
if (count != sizeof(remote_work_data)) {
hid_err(wacom->hdev,
"workitem triggered without status available\n");
spin_unlock_irqrestore(&remote->remote_lock, flags);
@ -2670,10 +2684,14 @@ static void wacom_remote_work(struct work_struct *work)
spin_unlock_irqrestore(&remote->remote_lock, flags);
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
serial = data.remote[i].serial;
if (data.remote[i].connected) {
work_serial = remote_work_data.remote[i].serial;
if (work_serial) {
if (remote->remotes[i].serial == serial) {
if (kt - remote->remotes[i].active_time > WACOM_REMOTE_BATTERY_TIMEOUT
&& remote->remotes[i].active_time != 0)
wacom_remote_destroy_battery(wacom, i);
if (remote->remotes[i].serial == work_serial) {
wacom_remote_attach_battery(wacom, i);
continue;
}
@ -2681,7 +2699,7 @@ static void wacom_remote_work(struct work_struct *work)
if (remote->remotes[i].serial)
wacom_remote_destroy_one(wacom, i);
wacom_remote_create_one(wacom, serial, i);
wacom_remote_create_one(wacom, work_serial, i);
} else if (remote->remotes[i].serial) {
wacom_remote_destroy_one(wacom, i);

View File

@ -1134,6 +1134,7 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
if (index < 0 || !remote->remotes[index].registered)
goto out;
remote->remotes[i].active_time = ktime_get();
input = remote->remotes[index].input;
input_report_key(input, BTN_0, (data[9] & 0x01));
@ -1196,22 +1197,20 @@ static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
unsigned char *data = wacom_wac->data;
struct wacom_remote *remote = wacom->remote;
struct wacom_remote_data remote_data;
struct wacom_remote_work_data remote_data;
unsigned long flags;
int i, ret;
if (data[0] != WACOM_REPORT_DEVICE_LIST)
return;
memset(&remote_data, 0, sizeof(struct wacom_remote_data));
memset(&remote_data, 0, sizeof(struct wacom_remote_work_data));
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
int j = i * 6;
int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4];
bool connected = data[j+2];
remote_data.remote[i].serial = serial;
remote_data.remote[i].connected = connected;
}
spin_lock_irqsave(&remote->remote_lock, flags);

View File

@ -13,6 +13,7 @@
#define WACOM_NAME_MAX 64
#define WACOM_MAX_REMOTES 5
#define WACOM_STATUS_UNKNOWN 255
#define WACOM_REMOTE_BATTERY_TIMEOUT 21000000000ll
/* packet length for individual models */
#define WACOM_PKGLEN_BBFUN 9
@ -327,10 +328,9 @@ struct hid_data {
ktime_t time_delayed;
};
struct wacom_remote_data {
struct wacom_remote_work_data {
struct {
u32 serial;
bool connected;
} remote[WACOM_MAX_REMOTES];
};

View 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 {

View 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)
{