mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 20:32:22 +00:00
hid-for-linus-2024091602
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIVAwUAZuqd7aZi849r7WBJAQLI0g//TIM5bR5iJ6FivvTHoYZ6xP4na/43g9fM LqLYtfuR6iEogCawqJjC8bETnry3URyph8C6EmqND0TAS7LGQSYg46yu1pdPAar1 rG+txtJcNqtLq34SkKmZzA8AD3Zyf3X8e9d5XnFTNyqBA/hT1a1B4uivSPaXiEkt hwSxVCJt7OQJ7GRkd6LOWvs/tvQTOkW1FgUrIyXj0weI7zMPuNx4vAgAQaKoUP0O 5DsZwKMRod6/GC4UmXxl5U2eQRcdF/2VvgGbSFIJM559k0uvtwo0saVM6M/5CBNp BEvsaEwBnDlBAqnLOdPUyPdKpSPLd8gt2GbtvKhwr/vycyCRX/oZbG2Ldf4s5W/k gHJ5JCoYyCX+AQf+N5EAA5C8OU5IypbnkyD4ynDm5wyYcqaIYESO4LJzfV2Y54XQ gijLQKqq1GbbVwt2zFyrvOE1IH7ZSSelfNAKQKFSYR1i+HpenqRvTommTR72jvcV jCTe4yEfxBUzVA3Cbb7hpR8HXVGnszk80ynCWTS+nqi6t+Uca6yqCwOV6lGeBucL UgCbfJ9t2liM6U3rN6X6f+c0i2E7+5ZE6xaZ6k7xHnA1JHtO30N74awIXbIssDOE uwngPRZn8wBouKabiTsmdZXr3BjZBDuT8YC2NOXiCwZEtP7dlD7C/N7D4Cp1Xvi6 VLMrn83Ides= =FMSD -----END PGP SIGNATURE----- Merge tag 'hid-for-linus-2024091602' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid Pull HID updates from Jiri Kosina: - New HID over SPI driver for Goodix devices that don't follow Microsoft's HID-over-SPI specification, so a separate driver is needed. Currently supported device is GT7986U touchscreen (Charles Wang) - support for new hardware features in Wacom driver (high-res wheel scrolling, touchstrings with relative motions, support for two touchrings) (Jason Gerecke) - support for customized vendor firmware loading in intel-ish driver (Zhang Lixu) - fix for theoretical race condition in i2c-hid (Dmitry Torokhov) - support for HIDIOCREVOKE -- evdev's EVIOCREVOKE equivalent in hidraw (Peter Hutterer) - initial hidraw selftest implementation (Benjamin Tissoires) - constification of device-specific report descriptors (Thomas Weißschuh) - other small assorted fixes and device ID / quirk additions * tag 'hid-for-linus-2024091602' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (54 commits) hid: cp2112: Use irq_get_trigger_type() helper HID: i2c-hid: ensure various commands do not interfere with each other HID: multitouch: Add support for Thinkpad X12 Gen 2 Kbd Portfolio HID: wacom: Do not warn about dropped packets for first packet HID: wacom: Support sequence numbers smaller than 16-bit HID: lg: constify fixed up report descriptor HID: uclogic: constify fixed up report descriptor HID: waltop: constify fixed up report descriptor HID: sony: constify fixed up report descriptor HID: pxrc: constify fixed up report descriptor HID: steelseries: constify fixed up report descriptor HID: viewsonic: constify fixed up report descriptor HID: vrc2: constify fixed up report descriptor HID: xiaomi: constify fixed up report descriptor HID: maltron: constify fixed up report descriptor HID: keytouch: constify fixed up report descriptor HID: holtek-kbd: constify fixed up report descriptor HID: dr: constify fixed up report descriptor HID: bigbenff: constify fixed up report descriptor HID: picoLCD: Use backlight power constants ...
This commit is contained in:
commit
a65b3c3ed4
@ -23,7 +23,9 @@ properties:
|
||||
- enum:
|
||||
- elan,ekth5015m
|
||||
- const: elan,ekth6915
|
||||
- const: elan,ekth6915
|
||||
- enum:
|
||||
- elan,ekth6915
|
||||
- elan,ekth6a12nay
|
||||
|
||||
reg:
|
||||
const: 0x10
|
||||
|
71
Documentation/devicetree/bindings/input/goodix,gt7986u.yaml
Normal file
71
Documentation/devicetree/bindings/input/goodix,gt7986u.yaml
Normal file
@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/goodix,gt7986u.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: GOODIX GT7986U SPI HID Touchscreen
|
||||
|
||||
maintainers:
|
||||
- Charles Wang <charles.goodix@gmail.com>
|
||||
|
||||
description: Supports the Goodix GT7986U touchscreen.
|
||||
This touch controller reports data packaged according to the HID protocol,
|
||||
but is incompatible with Microsoft's HID-over-SPI protocol.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- goodix,gt7986u
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
goodix,hid-report-addr:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The register address for retrieving HID report data.
|
||||
This address is related to the device firmware and may
|
||||
change after a firmware update.
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- reset-gpios
|
||||
- goodix,hid-report-addr
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@0 {
|
||||
compatible = "goodix,gt7986u";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
|
||||
reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
|
||||
spi-max-frequency = <10000000>;
|
||||
goodix,hid-report-addr = <0x22c8c>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -404,6 +404,35 @@ For more detailed information, please refer to the flow descriptions provided be
|
||||
| ISHTP Driver | | ISH Bootloader |
|
||||
+---------------+ +-----------------+
|
||||
|
||||
Vendor Custom Firmware Loading
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The firmware running inside ISH can be provided by Intel or developed by vendors using the Firmware Development Kit (FDK) provided by Intel.
|
||||
Intel will upstream the Intel-built firmware to the ``linux-firmware.git`` repository, located under the path ``intel/ish/``. For the Lunar Lake platform, the Intel-built ISH firmware will be named ``ish_lnlm.bin``.
|
||||
Vendors who wish to upstream their custom firmware should follow these guidelines for naming their firmware files:
|
||||
|
||||
- The firmware filename should use one of the following patterns:
|
||||
|
||||
- ``ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}_${PRODUCT_SKU_CRC32}.bin``
|
||||
- ``ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_SKU_CRC32}.bin``
|
||||
- ``ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}.bin``
|
||||
- ``ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}.bin``
|
||||
|
||||
- ``${intel_plat_gen}`` indicates the Intel platform generation (e.g., ``lnlm`` for Lunar Lake) and must not exceed 8 characters in length.
|
||||
- ``${SYS_VENDOR_CRC32}`` is the CRC32 checksum of the ``sys_vendor`` value from the DMI field ``DMI_SYS_VENDOR``.
|
||||
- ``${PRODUCT_NAME_CRC32}`` is the CRC32 checksum of the ``product_name`` value from the DMI field ``DMI_PRODUCT_NAME``.
|
||||
- ``${PRODUCT_SKU_CRC32}`` is the CRC32 checksum of the ``product_sku`` value from the DMI field ``DMI_PRODUCT_SKU``.
|
||||
|
||||
During system boot, the ISH Linux driver will attempt to load the firmware in the following order, prioritizing custom firmware with more precise matching patterns:
|
||||
|
||||
1. ``intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}_${PRODUCT_SKU_CRC32}.bin``
|
||||
2. ``intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_SKU_CRC32}.bin``
|
||||
3. ``intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}_${PRODUCT_NAME_CRC32}.bin``
|
||||
4. ``intel/ish/ish_${intel_plat_gen}_${SYS_VENDOR_CRC32}.bin``
|
||||
5. ``intel/ish/ish_${intel_plat_gen}.bin``
|
||||
|
||||
The driver will load the first matching firmware and skip the rest. If no matching firmware is found, it will proceed to the next pattern in the specified order. If all searches fail, the default Intel firmware, listed last in the order above, will be loaded.
|
||||
|
||||
ISH Debugging
|
||||
-------------
|
||||
|
||||
|
@ -404,6 +404,12 @@ config HID_VIVALDI_COMMON
|
||||
option so that drivers can use common code to parse the HID
|
||||
descriptors for vivaldi function row keymap.
|
||||
|
||||
config HID_GOODIX_SPI
|
||||
tristate "Goodix GT7986U SPI HID touchscreen"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Support for Goodix GT7986U SPI HID touchscreen device.
|
||||
|
||||
config HID_GOOGLE_HAMMER
|
||||
tristate "Google Hammer Keyboard"
|
||||
select HID_VIVALDI_COMMON
|
||||
|
@ -54,6 +54,7 @@ obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
|
||||
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_GOODIX_SPI) += hid-goodix-spi.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
|
||||
|
@ -73,8 +73,6 @@ struct amdtp_hid_data {
|
||||
};
|
||||
|
||||
/* Interface functions between HID LL driver and AMD SFH client */
|
||||
void hid_amdtp_set_feature(struct hid_device *hid, char *buf, u32 len, int report_id);
|
||||
void hid_amdtp_get_report(struct hid_device *hid, int report_id, int report_type);
|
||||
int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data);
|
||||
void amdtp_hid_remove(struct amdtp_cl_data *cli_data);
|
||||
int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type);
|
||||
|
@ -299,8 +299,8 @@ static void amd_sfh_set_ops(struct amd_mp2_dev *mp2)
|
||||
|
||||
sfh_interface_init(mp2);
|
||||
mp2_ops = mp2->mp2_ops;
|
||||
mp2_ops->clear_intr = amd_sfh_clear_intr_v2,
|
||||
mp2_ops->init_intr = amd_sfh_irq_init_v2,
|
||||
mp2_ops->clear_intr = amd_sfh_clear_intr_v2;
|
||||
mp2_ops->init_intr = amd_sfh_irq_init_v2;
|
||||
mp2_ops->suspend = amd_sfh_suspend;
|
||||
mp2_ops->resume = amd_sfh_resume;
|
||||
mp2_ops->remove = amd_mp2_pci_remove;
|
||||
|
@ -148,7 +148,7 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report);
|
||||
|
||||
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size)
|
||||
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size)
|
||||
{
|
||||
int ret;
|
||||
struct hid_bpf_ctx_kern ctx_kern = {
|
||||
@ -179,9 +179,7 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s
|
||||
*size = ret;
|
||||
}
|
||||
|
||||
rdesc = krealloc(ctx_kern.data, *size, GFP_KERNEL);
|
||||
|
||||
return rdesc;
|
||||
return krealloc(ctx_kern.data, *size, GFP_KERNEL);
|
||||
|
||||
ignore_bpf:
|
||||
kfree(ctx_kern.data);
|
||||
|
@ -620,7 +620,7 @@ static void apple_battery_timer_tick(struct timer_list *t)
|
||||
* MacBook JIS keyboard has wrong logical maximum
|
||||
* Magic Keyboard JIS has wrong logical maximum
|
||||
*/
|
||||
static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct apple_sc *asc = hid_get_drvdata(hdev);
|
||||
|
@ -1126,7 +1126,7 @@ static const __u8 asus_g752_fixed_rdesc[] = {
|
||||
0x2A, 0xFF, 0x00, /* Usage Maximum (0xFF) */
|
||||
};
|
||||
|
||||
static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) {
|
||||
|
@ -99,7 +99,7 @@
|
||||
* - map previously unused analog trigger data to Z/RZ
|
||||
* - simplify feature and output descriptor
|
||||
*/
|
||||
static __u8 pid0902_rdesc_fixed[] = {
|
||||
static const __u8 pid0902_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
|
||||
0x09, 0x05, /* Usage (Game Pad) */
|
||||
0xA1, 0x01, /* Collection (Application) */
|
||||
@ -464,12 +464,12 @@ error_hw_stop:
|
||||
return error;
|
||||
}
|
||||
|
||||
static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,
|
||||
static const __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == PID0902_RDESC_ORIG_SIZE) {
|
||||
rdesc = pid0902_rdesc_fixed;
|
||||
*rsize = sizeof(pid0902_rdesc_fixed);
|
||||
return pid0902_rdesc_fixed;
|
||||
} else
|
||||
hid_warn(hid, "unexpected rdesc, please submit for review\n");
|
||||
return rdesc;
|
||||
|
@ -22,7 +22,7 @@
|
||||
* Cherry Cymotion keyboard have an invalid HID report descriptor,
|
||||
* that needs fixing before we can parse it.
|
||||
*/
|
||||
static __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 18 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
|
||||
|
@ -88,8 +88,8 @@ static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *ch_switch12_report_fixup(struct hid_device *hdev,
|
||||
__u8 *rdesc, unsigned int *rsize)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
|
||||
|
@ -26,7 +26,7 @@ MODULE_LICENSE("GPL");
|
||||
/* Fixed report descriptor of HS-100B audio chip
|
||||
* Bit 4 is an abolute Microphone mute usage instead of being unassigned.
|
||||
*/
|
||||
static __u8 hs100b_rdesc_fixed[] = {
|
||||
static const __u8 hs100b_rdesc_fixed[] = {
|
||||
0x05, 0x0C, /* Usage Page (Consumer), */
|
||||
0x09, 0x01, /* Usage (Consumer Control), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -199,13 +199,13 @@ static struct hid_driver cmhid_driver = {
|
||||
.input_mapping = cmhid_input_mapping,
|
||||
};
|
||||
|
||||
static __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc,
|
||||
static const __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == HS100B_RDESC_ORIG_SIZE) {
|
||||
hid_info(hid, "Fixing CMedia HS-100B report descriptor\n");
|
||||
rdesc = hs100b_rdesc_fixed;
|
||||
*rsize = sizeof(hs100b_rdesc_fixed);
|
||||
return hs100b_rdesc_fixed;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
@ -723,7 +723,7 @@ static void hid_device_release(struct device *dev)
|
||||
* items, though they are not used yet.
|
||||
*/
|
||||
|
||||
static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
|
||||
static const u8 *fetch_item(const __u8 *start, const __u8 *end, struct hid_item *item)
|
||||
{
|
||||
u8 b;
|
||||
|
||||
@ -880,8 +880,8 @@ static int hid_scan_report(struct hid_device *hid)
|
||||
{
|
||||
struct hid_parser *parser;
|
||||
struct hid_item item;
|
||||
__u8 *start = hid->dev_rdesc;
|
||||
__u8 *end = start + hid->dev_rsize;
|
||||
const __u8 *start = hid->dev_rdesc;
|
||||
const __u8 *end = start + hid->dev_rsize;
|
||||
static int (*dispatch_type[])(struct hid_parser *parser,
|
||||
struct hid_item *item) = {
|
||||
hid_scan_main,
|
||||
@ -946,7 +946,7 @@ static int hid_scan_report(struct hid_device *hid)
|
||||
* Allocate the device report as read by the bus driver. This function should
|
||||
* only be called from parse() in ll drivers.
|
||||
*/
|
||||
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size)
|
||||
int hid_parse_report(struct hid_device *hid, const __u8 *start, unsigned size)
|
||||
{
|
||||
hid->dev_rdesc = kmemdup(start, size, GFP_KERNEL);
|
||||
if (!hid->dev_rdesc)
|
||||
@ -1204,10 +1204,10 @@ int hid_open_report(struct hid_device *device)
|
||||
struct hid_parser *parser;
|
||||
struct hid_item item;
|
||||
unsigned int size;
|
||||
__u8 *start;
|
||||
const __u8 *start;
|
||||
__u8 *buf;
|
||||
__u8 *end;
|
||||
__u8 *next;
|
||||
const __u8 *end;
|
||||
const __u8 *next;
|
||||
int ret;
|
||||
int i;
|
||||
static int (*dispatch_type[])(struct hid_parser *parser,
|
||||
@ -1912,6 +1912,31 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_set_field);
|
||||
|
||||
struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type,
|
||||
unsigned int application, unsigned int usage)
|
||||
{
|
||||
struct list_head *report_list = &hdev->report_enum[report_type].report_list;
|
||||
struct hid_report *report;
|
||||
int i, j;
|
||||
|
||||
list_for_each_entry(report, report_list, list) {
|
||||
if (report->application != application)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
struct hid_field *field = report->field[i];
|
||||
|
||||
for (j = 0; j < field->maxusage; j++) {
|
||||
if (field->usage[j].hid == usage)
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_find_field);
|
||||
|
||||
static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
|
||||
const u8 *data)
|
||||
{
|
||||
|
@ -690,8 +690,8 @@ static int corsair_input_mapping(struct hid_device *dev,
|
||||
* - USB ID 1b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse
|
||||
*/
|
||||
|
||||
static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *corsair_mouse_report_fixup(struct hid_device *hdev,
|
||||
__u8 *rdesc, unsigned int *rsize)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
|
||||
|
@ -103,8 +103,8 @@ static void cougar_fix_g6_mapping(void)
|
||||
/*
|
||||
* Constant-friendly rdesc fixup for mouse interface
|
||||
*/
|
||||
static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 117 && rdesc[2] == 0x09 && rdesc[3] == 0x02 &&
|
||||
(rdesc[115] | rdesc[116] << 8) >= HID_MAX_USAGES) {
|
||||
|
@ -1094,7 +1094,6 @@ static void cp2112_gpio_poll_callback(struct work_struct *work)
|
||||
{
|
||||
struct cp2112_device *dev = container_of(work, struct cp2112_device,
|
||||
gpio_poll_worker.work);
|
||||
struct irq_data *d;
|
||||
u8 gpio_mask;
|
||||
u32 irq_type;
|
||||
int irq, virq, ret;
|
||||
@ -1111,12 +1110,10 @@ static void cp2112_gpio_poll_callback(struct work_struct *work)
|
||||
if (!irq)
|
||||
continue;
|
||||
|
||||
d = irq_get_irq_data(irq);
|
||||
if (!d)
|
||||
irq_type = irq_get_trigger_type(irq);
|
||||
if (!irq_type)
|
||||
continue;
|
||||
|
||||
irq_type = irqd_get_trigger_type(d);
|
||||
|
||||
if (gpio_mask & BIT(virq)) {
|
||||
/* Level High */
|
||||
|
||||
|
@ -67,7 +67,7 @@ static __u8 *va_logical_boundary_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
|
@ -199,7 +199,7 @@ static inline int drff_init(struct hid_device *hid)
|
||||
#define PID0011_RDESC_ORIG_SIZE 101
|
||||
|
||||
/* Fixed report descriptor for PID 0x011 joystick */
|
||||
static __u8 pid0011_rdesc_fixed[] = {
|
||||
static const __u8 pid0011_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -228,14 +228,14 @@ static __u8 pid0011_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case 0x0011:
|
||||
if (*rsize == PID0011_RDESC_ORIG_SIZE) {
|
||||
rdesc = pid0011_rdesc_fixed;
|
||||
*rsize = sizeof(pid0011_rdesc_fixed);
|
||||
return pid0011_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ static void mouse_button_fixup(struct hid_device *hdev,
|
||||
rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons;
|
||||
}
|
||||
|
||||
static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
|
@ -57,7 +57,7 @@ static const __u8 gembird_jpd_fixed_rdesc[] = {
|
||||
0x81, 0x02, /* Input (Data,Var,Abs) */
|
||||
};
|
||||
|
||||
static __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
__u8 *new_rdesc;
|
||||
|
@ -26,7 +26,7 @@ MODULE_DESCRIPTION("HID driver for Glorious PC Gaming Race mice");
|
||||
* keyboard HID report, causing keycodes to be misinterpreted.
|
||||
* Fix this by setting Usage Minimum to 0 in that report.
|
||||
*/
|
||||
static __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == 213 &&
|
||||
|
818
drivers/hid/hid-goodix-spi.c
Normal file
818
drivers/hid/hid-goodix-spi.c
Normal file
@ -0,0 +1,818 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Goodix GT7986U SPI Driver Code for HID.
|
||||
*
|
||||
* Copyright (C) 2024 Godix, Inc.
|
||||
*/
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define GOODIX_DEV_CONFIRM_ADDR 0x10000
|
||||
#define GOODIX_HID_DESC_ADDR 0x1058C
|
||||
#define GOODIX_HID_REPORT_DESC_ADDR 0x105AA
|
||||
#define GOODIX_HID_SIGN_ADDR 0x10D32
|
||||
|
||||
#define GOODIX_HID_GET_REPORT_CMD 0x02
|
||||
#define GOODIX_HID_SET_REPORT_CMD 0x03
|
||||
|
||||
#define GOODIX_HID_MAX_INBUF_SIZE 128
|
||||
#define GOODIX_HID_ACK_READY_FLAG 0x01
|
||||
#define GOODIX_HID_REPORT_READY_FLAG 0x80
|
||||
|
||||
#define GOODIX_DEV_CONFIRM_VAL 0xAA
|
||||
|
||||
#define GOODIX_SPI_WRITE_FLAG 0xF0
|
||||
#define GOODIX_SPI_READ_FLAG 0xF1
|
||||
#define GOODIX_SPI_TRANS_PREFIX_LEN 1
|
||||
#define GOODIX_REGISTER_WIDTH 4
|
||||
#define GOODIX_SPI_READ_DUMMY_LEN 3
|
||||
#define GOODIX_SPI_READ_PREFIX_LEN (GOODIX_SPI_TRANS_PREFIX_LEN + \
|
||||
GOODIX_REGISTER_WIDTH + \
|
||||
GOODIX_SPI_READ_DUMMY_LEN)
|
||||
#define GOODIX_SPI_WRITE_PREFIX_LEN (GOODIX_SPI_TRANS_PREFIX_LEN + \
|
||||
GOODIX_REGISTER_WIDTH)
|
||||
|
||||
#define GOODIX_CHECKSUM_SIZE sizeof(u16)
|
||||
#define GOODIX_NORMAL_RESET_DELAY_MS 150
|
||||
|
||||
struct goodix_hid_report_header {
|
||||
u8 flag;
|
||||
__le16 size;
|
||||
} __packed;
|
||||
#define GOODIX_HID_ACK_HEADER_SIZE sizeof(struct goodix_hid_report_header)
|
||||
|
||||
struct goodix_hid_report_package {
|
||||
__le16 size;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
#define GOODIX_HID_PKG_LEN_SIZE sizeof(u16)
|
||||
#define GOODIX_HID_COOR_DATA_LEN 82
|
||||
#define GOODIX_HID_COOR_PKG_LEN (GOODIX_HID_PKG_LEN_SIZE + \
|
||||
GOODIX_HID_COOR_DATA_LEN)
|
||||
|
||||
/* power state */
|
||||
#define GOODIX_SPI_POWER_ON 0x00
|
||||
#define GOODIX_SPI_POWER_SLEEP 0x01
|
||||
|
||||
/* flags used to record the current device operating state */
|
||||
#define GOODIX_HID_STARTED 0
|
||||
|
||||
struct goodix_hid_report_event {
|
||||
struct goodix_hid_report_header hdr;
|
||||
u8 data[GOODIX_HID_COOR_PKG_LEN];
|
||||
} __packed;
|
||||
|
||||
struct goodix_hid_desc {
|
||||
__le16 desc_length;
|
||||
__le16 bcd_version;
|
||||
__le16 report_desc_length;
|
||||
__le16 report_desc_register;
|
||||
__le16 input_register;
|
||||
__le16 max_input_length;
|
||||
__le16 output_register;
|
||||
__le16 max_output_length;
|
||||
__le16 cmd_register;
|
||||
__le16 data_register;
|
||||
__le16 vendor_id;
|
||||
__le16 product_id;
|
||||
__le16 version_id;
|
||||
__le32 reserved;
|
||||
} __packed;
|
||||
|
||||
struct goodix_ts_data {
|
||||
struct device *dev;
|
||||
struct spi_device *spi;
|
||||
struct hid_device *hid;
|
||||
struct goodix_hid_desc hid_desc;
|
||||
|
||||
struct gpio_desc *reset_gpio;
|
||||
u32 hid_report_addr;
|
||||
|
||||
unsigned long flags;
|
||||
/* lock for hid raw request operation */
|
||||
struct mutex hid_request_lock;
|
||||
/* buffer used to store hid report event */
|
||||
u8 *event_buf;
|
||||
u32 hid_max_event_sz;
|
||||
/* buffer used to do spi data transfer */
|
||||
u8 xfer_buf[SZ_2K] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static void *goodix_get_event_report(struct goodix_ts_data *ts, u32 addr,
|
||||
u8 *data, size_t len)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(&ts->spi->dev);
|
||||
struct spi_transfer xfers;
|
||||
struct spi_message spi_msg;
|
||||
int error;
|
||||
|
||||
/* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */
|
||||
data[0] = GOODIX_SPI_READ_FLAG;
|
||||
put_unaligned_be32(addr, data + GOODIX_SPI_TRANS_PREFIX_LEN);
|
||||
|
||||
spi_message_init(&spi_msg);
|
||||
memset(&xfers, 0, sizeof(xfers));
|
||||
xfers.tx_buf = data;
|
||||
xfers.rx_buf = data;
|
||||
xfers.len = GOODIX_SPI_READ_PREFIX_LEN + len;
|
||||
spi_message_add_tail(&xfers, &spi_msg);
|
||||
|
||||
error = spi_sync(spi, &spi_msg);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "spi transfer error: %d", error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data + GOODIX_SPI_READ_PREFIX_LEN;
|
||||
}
|
||||
|
||||
static int goodix_spi_read(struct goodix_ts_data *ts, u32 addr,
|
||||
void *data, size_t len)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(&ts->spi->dev);
|
||||
struct spi_transfer xfers;
|
||||
struct spi_message spi_msg;
|
||||
int error;
|
||||
|
||||
if (GOODIX_SPI_READ_PREFIX_LEN + len > sizeof(ts->xfer_buf)) {
|
||||
dev_err(ts->dev, "read data len exceed limit %zu",
|
||||
sizeof(ts->xfer_buf) - GOODIX_SPI_READ_PREFIX_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */
|
||||
ts->xfer_buf[0] = GOODIX_SPI_READ_FLAG;
|
||||
put_unaligned_be32(addr, ts->xfer_buf + GOODIX_SPI_TRANS_PREFIX_LEN);
|
||||
|
||||
spi_message_init(&spi_msg);
|
||||
memset(&xfers, 0, sizeof(xfers));
|
||||
xfers.tx_buf = ts->xfer_buf;
|
||||
xfers.rx_buf = ts->xfer_buf;
|
||||
xfers.len = GOODIX_SPI_READ_PREFIX_LEN + len;
|
||||
spi_message_add_tail(&xfers, &spi_msg);
|
||||
|
||||
error = spi_sync(spi, &spi_msg);
|
||||
if (error)
|
||||
dev_err(ts->dev, "spi transfer error: %d", error);
|
||||
else
|
||||
memcpy(data, ts->xfer_buf + GOODIX_SPI_READ_PREFIX_LEN, len);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int goodix_spi_write(struct goodix_ts_data *ts, u32 addr,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(&ts->spi->dev);
|
||||
struct spi_transfer xfers;
|
||||
struct spi_message spi_msg;
|
||||
int error;
|
||||
|
||||
if (GOODIX_SPI_WRITE_PREFIX_LEN + len > sizeof(ts->xfer_buf)) {
|
||||
dev_err(ts->dev, "write data len exceed limit %zu",
|
||||
sizeof(ts->xfer_buf) - GOODIX_SPI_WRITE_PREFIX_LEN);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* buffer format: 0xF0 + addr(4bytes) + data */
|
||||
ts->xfer_buf[0] = GOODIX_SPI_WRITE_FLAG;
|
||||
put_unaligned_be32(addr, ts->xfer_buf + GOODIX_SPI_TRANS_PREFIX_LEN);
|
||||
memcpy(ts->xfer_buf + GOODIX_SPI_WRITE_PREFIX_LEN, data, len);
|
||||
|
||||
spi_message_init(&spi_msg);
|
||||
memset(&xfers, 0, sizeof(xfers));
|
||||
xfers.tx_buf = ts->xfer_buf;
|
||||
xfers.len = GOODIX_SPI_WRITE_PREFIX_LEN + len;
|
||||
spi_message_add_tail(&xfers, &spi_msg);
|
||||
|
||||
error = spi_sync(spi, &spi_msg);
|
||||
if (error)
|
||||
dev_err(ts->dev, "spi transfer error: %d", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int goodix_dev_confirm(struct goodix_ts_data *ts)
|
||||
{
|
||||
u8 tx_buf[8], rx_buf[8];
|
||||
int retry = 3;
|
||||
int error;
|
||||
|
||||
gpiod_set_value_cansleep(ts->reset_gpio, 0);
|
||||
usleep_range(4000, 4100);
|
||||
|
||||
memset(tx_buf, GOODIX_DEV_CONFIRM_VAL, sizeof(tx_buf));
|
||||
while (retry--) {
|
||||
error = goodix_spi_write(ts, GOODIX_DEV_CONFIRM_ADDR,
|
||||
tx_buf, sizeof(tx_buf));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_spi_read(ts, GOODIX_DEV_CONFIRM_ADDR,
|
||||
rx_buf, sizeof(rx_buf));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf)))
|
||||
return 0;
|
||||
|
||||
usleep_range(5000, 5100);
|
||||
}
|
||||
|
||||
dev_err(ts->dev, "device confirm failed, rx_buf: %*ph", 8, rx_buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_hid_parse() - hid-core .parse() callback
|
||||
* @hid: hid device instance
|
||||
*
|
||||
* This function gets called during call to hid_add_device
|
||||
*
|
||||
* Return: 0 on success and non zero on error
|
||||
*/
|
||||
static int goodix_hid_parse(struct hid_device *hid)
|
||||
{
|
||||
struct goodix_ts_data *ts = hid->driver_data;
|
||||
u16 rsize;
|
||||
int error;
|
||||
|
||||
rsize = le16_to_cpu(ts->hid_desc.report_desc_length);
|
||||
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
|
||||
dev_err(ts->dev, "invalid report desc size, %d", rsize);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
u8 *rdesc __free(kfree) = kzalloc(rsize, GFP_KERNEL);
|
||||
if (!rdesc)
|
||||
return -ENOMEM;
|
||||
|
||||
error = goodix_spi_read(ts, GOODIX_HID_REPORT_DESC_ADDR, rdesc, rsize);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed get report desc, %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = hid_parse_report(hid, rdesc, rsize);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed parse report, %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_hid_get_report_length(struct hid_report *report)
|
||||
{
|
||||
return ((report->size - 1) >> 3) + 1 +
|
||||
report->device->report_enum[report->type].numbered + 2;
|
||||
}
|
||||
|
||||
static void goodix_hid_find_max_report(struct hid_device *hid, unsigned int type,
|
||||
unsigned int *max)
|
||||
{
|
||||
struct hid_report *report;
|
||||
unsigned int size;
|
||||
|
||||
list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
|
||||
size = goodix_hid_get_report_length(report);
|
||||
if (*max < size)
|
||||
*max = size;
|
||||
}
|
||||
}
|
||||
|
||||
static int goodix_hid_start(struct hid_device *hid)
|
||||
{
|
||||
struct goodix_ts_data *ts = hid->driver_data;
|
||||
unsigned int bufsize = GOODIX_HID_COOR_PKG_LEN;
|
||||
u32 report_size;
|
||||
|
||||
goodix_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize);
|
||||
goodix_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize);
|
||||
goodix_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize);
|
||||
|
||||
report_size = GOODIX_SPI_READ_PREFIX_LEN +
|
||||
GOODIX_HID_ACK_HEADER_SIZE + bufsize;
|
||||
if (report_size <= ts->hid_max_event_sz)
|
||||
return 0;
|
||||
|
||||
ts->event_buf = devm_krealloc(ts->dev, ts->event_buf,
|
||||
report_size, GFP_KERNEL);
|
||||
if (!ts->event_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ts->hid_max_event_sz = report_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void goodix_hid_stop(struct hid_device *hid)
|
||||
{
|
||||
hid->claimed = 0;
|
||||
}
|
||||
|
||||
static int goodix_hid_open(struct hid_device *hid)
|
||||
{
|
||||
struct goodix_ts_data *ts = hid->driver_data;
|
||||
|
||||
set_bit(GOODIX_HID_STARTED, &ts->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void goodix_hid_close(struct hid_device *hid)
|
||||
{
|
||||
struct goodix_ts_data *ts = hid->driver_data;
|
||||
|
||||
clear_bit(GOODIX_HID_STARTED, &ts->flags);
|
||||
}
|
||||
|
||||
/* Return date length of response data */
|
||||
static int goodix_hid_check_ack_status(struct goodix_ts_data *ts, u32 *resp_len)
|
||||
{
|
||||
struct goodix_hid_report_header hdr;
|
||||
int retry = 20;
|
||||
int error;
|
||||
int len;
|
||||
|
||||
while (retry--) {
|
||||
/*
|
||||
* 3 bytes of hid request response data
|
||||
* - byte 0: Ack flag, value of 1 for data ready
|
||||
* - bytes 1-2: Response data length
|
||||
*/
|
||||
error = goodix_spi_read(ts, ts->hid_report_addr,
|
||||
&hdr, sizeof(hdr));
|
||||
if (!error && (hdr.flag & GOODIX_HID_ACK_READY_FLAG)) {
|
||||
len = le16_to_cpu(hdr.size);
|
||||
if (len < GOODIX_HID_PKG_LEN_SIZE) {
|
||||
dev_err(ts->dev, "hrd.size too short: %d", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
*resp_len = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait 10ms for another try */
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_hid_get_raw_report() - Process hidraw GET REPORT operation
|
||||
* @hid: hid device instance
|
||||
* @reportnum: Report ID
|
||||
* @buf: Buffer for store the report date
|
||||
* @len: Length fo report data
|
||||
* @report_type: Report type
|
||||
*
|
||||
* The function for hid_ll_driver.get_raw_report to handle the HIDRAW ioctl
|
||||
* get report request. The transmitted data follows the standard i2c-hid
|
||||
* protocol with a specified header.
|
||||
*
|
||||
* Return: The length of the data in the buf on success, negative error code
|
||||
*/
|
||||
static int goodix_hid_get_raw_report(struct hid_device *hid,
|
||||
unsigned char reportnum,
|
||||
u8 *buf, size_t len,
|
||||
unsigned char report_type)
|
||||
{
|
||||
struct goodix_ts_data *ts = hid->driver_data;
|
||||
u16 data_register = le16_to_cpu(ts->hid_desc.data_register);
|
||||
u16 cmd_register = le16_to_cpu(ts->hid_desc.cmd_register);
|
||||
u8 tmp_buf[GOODIX_HID_MAX_INBUF_SIZE];
|
||||
int tx_len = 0, args_len = 0;
|
||||
u32 response_data_len;
|
||||
u8 args[3];
|
||||
int error;
|
||||
|
||||
if (report_type == HID_OUTPUT_REPORT)
|
||||
return -EINVAL;
|
||||
|
||||
if (reportnum == 3) {
|
||||
/* Get win8 signature data */
|
||||
error = goodix_spi_read(ts, GOODIX_HID_SIGN_ADDR, buf, len);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed get win8 sign: %d", error);
|
||||
return -EINVAL;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
if (reportnum >= 0x0F)
|
||||
args[args_len++] = reportnum;
|
||||
|
||||
put_unaligned_le16(data_register, args + args_len);
|
||||
args_len += sizeof(data_register);
|
||||
|
||||
/* Clean 3 bytes of hid ack header data */
|
||||
memset(tmp_buf, 0, GOODIX_HID_ACK_HEADER_SIZE);
|
||||
tx_len += GOODIX_HID_ACK_HEADER_SIZE;
|
||||
|
||||
put_unaligned_le16(cmd_register, tmp_buf + tx_len);
|
||||
tx_len += sizeof(cmd_register);
|
||||
|
||||
tmp_buf[tx_len] = (report_type == HID_FEATURE_REPORT ? 0x03 : 0x01) << 4;
|
||||
tmp_buf[tx_len] |= reportnum >= 0x0F ? 0x0F : reportnum;
|
||||
tx_len++;
|
||||
|
||||
tmp_buf[tx_len++] = GOODIX_HID_GET_REPORT_CMD;
|
||||
|
||||
memcpy(tmp_buf + tx_len, args, args_len);
|
||||
tx_len += args_len;
|
||||
|
||||
/* Step1: write report request info */
|
||||
error = goodix_spi_write(ts, ts->hid_report_addr, tmp_buf, tx_len);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed send read feature cmd, %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* No need read response data */
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/* Step2: check response data status */
|
||||
error = goodix_hid_check_ack_status(ts, &response_data_len);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
len = min(len, response_data_len - GOODIX_HID_PKG_LEN_SIZE);
|
||||
/* Step3: read response data(skip 2bytes of hid pkg length) */
|
||||
error = goodix_spi_read(ts, ts->hid_report_addr +
|
||||
GOODIX_HID_ACK_HEADER_SIZE +
|
||||
GOODIX_HID_PKG_LEN_SIZE, buf, len);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed read hid response data, %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (buf[0] != reportnum) {
|
||||
dev_err(ts->dev, "incorrect report (%d vs %d expected)",
|
||||
buf[0], reportnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_hid_set_raw_report() - process hidraw SET REPORT operation
|
||||
* @hid: HID device
|
||||
* @reportnum: Report ID
|
||||
* @buf: Buffer for communication
|
||||
* @len: Length of data in the buffer
|
||||
* @report_type: Report type
|
||||
*
|
||||
* The function for hid_ll_driver.get_raw_report to handle the HIDRAW ioctl
|
||||
* set report request. The transmitted data follows the standard i2c-hid
|
||||
* protocol with a specified header.
|
||||
*
|
||||
* Return: The length of the data sent, negative error code on failure
|
||||
*/
|
||||
static int goodix_hid_set_raw_report(struct hid_device *hid,
|
||||
unsigned char reportnum,
|
||||
__u8 *buf, size_t len,
|
||||
unsigned char report_type)
|
||||
{
|
||||
struct goodix_ts_data *ts = hid->driver_data;
|
||||
u16 data_register = le16_to_cpu(ts->hid_desc.data_register);
|
||||
u16 cmd_register = le16_to_cpu(ts->hid_desc.cmd_register);
|
||||
int tx_len = 0, args_len = 0;
|
||||
u8 tmp_buf[GOODIX_HID_MAX_INBUF_SIZE];
|
||||
u8 args[5];
|
||||
int error;
|
||||
|
||||
if (reportnum >= 0x0F) {
|
||||
args[args_len++] = reportnum;
|
||||
reportnum = 0x0F;
|
||||
}
|
||||
|
||||
put_unaligned_le16(data_register, args + args_len);
|
||||
args_len += sizeof(data_register);
|
||||
|
||||
put_unaligned_le16(GOODIX_HID_PKG_LEN_SIZE + len, args + args_len);
|
||||
args_len += GOODIX_HID_PKG_LEN_SIZE;
|
||||
|
||||
/* Clean 3 bytes of hid ack header data */
|
||||
memset(tmp_buf, 0, GOODIX_HID_ACK_HEADER_SIZE);
|
||||
tx_len += GOODIX_HID_ACK_HEADER_SIZE;
|
||||
|
||||
put_unaligned_le16(cmd_register, tmp_buf + tx_len);
|
||||
tx_len += sizeof(cmd_register);
|
||||
|
||||
tmp_buf[tx_len++] = ((report_type == HID_FEATURE_REPORT ? 0x03 : 0x02) << 4) | reportnum;
|
||||
tmp_buf[tx_len++] = GOODIX_HID_SET_REPORT_CMD;
|
||||
|
||||
memcpy(tmp_buf + tx_len, args, args_len);
|
||||
tx_len += args_len;
|
||||
|
||||
memcpy(tmp_buf + tx_len, buf, len);
|
||||
tx_len += len;
|
||||
|
||||
error = goodix_spi_write(ts, ts->hid_report_addr, tmp_buf, tx_len);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed send report: %*ph", tx_len, tmp_buf);
|
||||
return error;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int goodix_hid_raw_request(struct hid_device *hid,
|
||||
unsigned char reportnum,
|
||||
__u8 *buf, size_t len,
|
||||
unsigned char rtype, int reqtype)
|
||||
{
|
||||
struct goodix_ts_data *ts = hid->driver_data;
|
||||
int error = -EINVAL;
|
||||
|
||||
guard(mutex)(&ts->hid_request_lock);
|
||||
switch (reqtype) {
|
||||
case HID_REQ_GET_REPORT:
|
||||
error = goodix_hid_get_raw_report(hid, reportnum, buf,
|
||||
len, rtype);
|
||||
break;
|
||||
case HID_REQ_SET_REPORT:
|
||||
if (buf[0] == reportnum)
|
||||
error = goodix_hid_set_raw_report(hid, reportnum,
|
||||
buf, len, rtype);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct hid_ll_driver goodix_hid_ll_driver = {
|
||||
.parse = goodix_hid_parse,
|
||||
.start = goodix_hid_start,
|
||||
.stop = goodix_hid_stop,
|
||||
.open = goodix_hid_open,
|
||||
.close = goodix_hid_close,
|
||||
.raw_request = goodix_hid_raw_request
|
||||
};
|
||||
|
||||
static irqreturn_t goodix_hid_irq(int irq, void *data)
|
||||
{
|
||||
struct goodix_ts_data *ts = data;
|
||||
struct goodix_hid_report_event *event;
|
||||
struct goodix_hid_report_package *pkg;
|
||||
u16 report_size;
|
||||
|
||||
if (!test_bit(GOODIX_HID_STARTED, &ts->flags))
|
||||
return IRQ_HANDLED;
|
||||
/*
|
||||
* First, read buffer with space for header and coordinate package:
|
||||
* - event header = 3 bytes
|
||||
* - coordinate event = GOODIX_HID_COOR_PKG_LEN bytes
|
||||
*
|
||||
* If the data size info in the event header exceeds
|
||||
* GOODIX_HID_COOR_PKG_LEN, it means that there are other packages
|
||||
* besides the coordinate package.
|
||||
*/
|
||||
event = goodix_get_event_report(ts, ts->hid_report_addr, ts->event_buf,
|
||||
GOODIX_HID_ACK_HEADER_SIZE +
|
||||
GOODIX_HID_COOR_PKG_LEN);
|
||||
if (!event) {
|
||||
dev_err(ts->dev, "failed get coordinate data");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Check coordinate data valid falg */
|
||||
if (event->hdr.flag != GOODIX_HID_REPORT_READY_FLAG)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
pkg = (struct goodix_hid_report_package *)event->data;
|
||||
if (le16_to_cpu(pkg->size) < GOODIX_HID_PKG_LEN_SIZE) {
|
||||
dev_err(ts->dev, "invalid coordinate event package size, %d",
|
||||
le16_to_cpu(pkg->size));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
hid_input_report(ts->hid, HID_INPUT_REPORT, pkg->data,
|
||||
le16_to_cpu(pkg->size) - GOODIX_HID_PKG_LEN_SIZE, 1);
|
||||
|
||||
report_size = le16_to_cpu(event->hdr.size);
|
||||
/* Check if there are other packages */
|
||||
if (report_size <= GOODIX_HID_COOR_PKG_LEN)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (report_size >= ts->hid_max_event_sz) {
|
||||
dev_err(ts->dev, "package size exceed limit %d vs %d",
|
||||
report_size, ts->hid_max_event_sz);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Read the package behind the coordinate data */
|
||||
pkg = goodix_get_event_report(ts, ts->hid_report_addr + sizeof(*event),
|
||||
ts->event_buf,
|
||||
report_size - GOODIX_HID_COOR_PKG_LEN);
|
||||
if (!pkg) {
|
||||
dev_err(ts->dev, "failed read attachment data content");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
hid_input_report(ts->hid, HID_INPUT_REPORT, pkg->data,
|
||||
le16_to_cpu(pkg->size) - GOODIX_HID_PKG_LEN_SIZE, 1);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int goodix_hid_init(struct goodix_ts_data *ts)
|
||||
{
|
||||
struct hid_device *hid;
|
||||
int error;
|
||||
|
||||
/* Get hid descriptor */
|
||||
error = goodix_spi_read(ts, GOODIX_HID_DESC_ADDR, &ts->hid_desc,
|
||||
sizeof(ts->hid_desc));
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed get hid desc, %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
hid = hid_allocate_device();
|
||||
if (IS_ERR(hid))
|
||||
return PTR_ERR(hid);
|
||||
|
||||
hid->driver_data = ts;
|
||||
hid->ll_driver = &goodix_hid_ll_driver;
|
||||
hid->bus = BUS_SPI;
|
||||
hid->dev.parent = &ts->spi->dev;
|
||||
|
||||
hid->version = le16_to_cpu(ts->hid_desc.bcd_version);
|
||||
hid->vendor = le16_to_cpu(ts->hid_desc.vendor_id);
|
||||
hid->product = le16_to_cpu(ts->hid_desc.product_id);
|
||||
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-gdix",
|
||||
hid->vendor, hid->product);
|
||||
|
||||
error = hid_add_device(hid);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed add hid device, %d", error);
|
||||
hid_destroy_device(hid);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->hid = hid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct goodix_ts_data *ts;
|
||||
int error;
|
||||
|
||||
/* init spi_device */
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi->bits_per_word = 8;
|
||||
error = spi_setup(spi);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ts->hid_request_lock);
|
||||
spi_set_drvdata(spi, ts);
|
||||
ts->spi = spi;
|
||||
ts->dev = dev;
|
||||
ts->hid_max_event_sz = GOODIX_SPI_READ_PREFIX_LEN +
|
||||
GOODIX_HID_ACK_HEADER_SIZE + GOODIX_HID_COOR_PKG_LEN;
|
||||
ts->event_buf = devm_kmalloc(dev, ts->hid_max_event_sz, GFP_KERNEL);
|
||||
if (!ts->event_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->reset_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(ts->reset_gpio),
|
||||
"failed to request reset gpio\n");
|
||||
|
||||
error = device_property_read_u32(dev, "goodix,hid-report-addr",
|
||||
&ts->hid_report_addr);
|
||||
if (error)
|
||||
return dev_err_probe(dev, error,
|
||||
"failed get hid report addr\n");
|
||||
|
||||
error = goodix_dev_confirm(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Waits 150ms for firmware to fully boot */
|
||||
msleep(GOODIX_NORMAL_RESET_DELAY_MS);
|
||||
|
||||
error = goodix_hid_init(ts);
|
||||
if (error) {
|
||||
dev_err(dev, "failed init hid device");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&ts->spi->dev, ts->spi->irq,
|
||||
NULL, goodix_hid_irq, IRQF_ONESHOT,
|
||||
"goodix_spi_hid", ts);
|
||||
if (error) {
|
||||
dev_err(ts->dev, "could not register interrupt, irq = %d, %d",
|
||||
ts->spi->irq, error);
|
||||
goto err_destroy_hid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_destroy_hid:
|
||||
hid_destroy_device(ts->hid);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void goodix_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct goodix_ts_data *ts = spi_get_drvdata(spi);
|
||||
|
||||
disable_irq(spi->irq);
|
||||
hid_destroy_device(ts->hid);
|
||||
}
|
||||
|
||||
static int goodix_spi_set_power(struct goodix_ts_data *ts, int power_state)
|
||||
{
|
||||
u8 power_control_cmd[] = {0x00, 0x00, 0x00, 0x87, 0x02, 0x00, 0x08};
|
||||
int error;
|
||||
|
||||
/* value 0 for power on, 1 for power sleep */
|
||||
power_control_cmd[5] = power_state;
|
||||
|
||||
guard(mutex)(&ts->hid_request_lock);
|
||||
error = goodix_spi_write(ts, ts->hid_report_addr, power_control_cmd,
|
||||
sizeof(power_control_cmd));
|
||||
if (error) {
|
||||
dev_err(ts->dev, "failed set power mode: %s",
|
||||
power_state == GOODIX_SPI_POWER_ON ? "on" : "sleep");
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct goodix_ts_data *ts = dev_get_drvdata(dev);
|
||||
|
||||
disable_irq(ts->spi->irq);
|
||||
return goodix_spi_set_power(ts, GOODIX_SPI_POWER_SLEEP);
|
||||
}
|
||||
|
||||
static int goodix_spi_resume(struct device *dev)
|
||||
{
|
||||
struct goodix_ts_data *ts = dev_get_drvdata(dev);
|
||||
|
||||
enable_irq(ts->spi->irq);
|
||||
return goodix_spi_set_power(ts, GOODIX_SPI_POWER_ON);
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(goodix_spi_pm_ops,
|
||||
goodix_spi_suspend, goodix_spi_resume);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id goodix_spi_acpi_match[] = {
|
||||
{ "GXTS7986" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, goodix_spi_acpi_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id goodix_spi_of_match[] = {
|
||||
{ .compatible = "goodix,gt7986u", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, goodix_spi_of_match);
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id goodix_spi_ids[] = {
|
||||
{ "gt7986u" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, goodix_spi_ids);
|
||||
|
||||
static struct spi_driver goodix_spi_driver = {
|
||||
.driver = {
|
||||
.name = "goodix-spi-hid",
|
||||
.acpi_match_table = ACPI_PTR(goodix_spi_acpi_match),
|
||||
.of_match_table = of_match_ptr(goodix_spi_of_match),
|
||||
.pm = pm_sleep_ptr(&goodix_spi_pm_ops),
|
||||
},
|
||||
.probe = goodix_spi_probe,
|
||||
.remove = goodix_spi_remove,
|
||||
.id_table = goodix_spi_ids,
|
||||
};
|
||||
module_spi_driver(goodix_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Goodix SPI driver for HID touchscreen");
|
||||
MODULE_AUTHOR("Goodix, Inc.");
|
||||
MODULE_LICENSE("GPL");
|
@ -418,38 +418,15 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type,
|
||||
unsigned application, unsigned usage)
|
||||
{
|
||||
struct hid_report_enum *re = &hdev->report_enum[report_type];
|
||||
struct hid_report *report;
|
||||
int i, j;
|
||||
|
||||
list_for_each_entry(report, &re->report_list, list) {
|
||||
if (report->application != application)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
struct hid_field *field = report->field[i];
|
||||
|
||||
for (j = 0; j < field->maxusage; j++)
|
||||
if (field->usage[j].hid == usage)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hammer_has_folded_event(struct hid_device *hdev)
|
||||
{
|
||||
return hammer_has_usage(hdev, HID_INPUT_REPORT,
|
||||
return !!hid_find_field(hdev, HID_INPUT_REPORT,
|
||||
HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED);
|
||||
}
|
||||
|
||||
static bool hammer_has_backlight_control(struct hid_device *hdev)
|
||||
{
|
||||
return hammer_has_usage(hdev, HID_OUTPUT_REPORT,
|
||||
return !!hid_find_field(hdev, HID_OUTPUT_REPORT,
|
||||
HID_GD_KEYBOARD, HID_AD_BRIGHTNESS);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
* to the boot interface.
|
||||
*/
|
||||
|
||||
static __u8 holtek_kbd_rdesc_fixed[] = {
|
||||
static const __u8 holtek_kbd_rdesc_fixed[] = {
|
||||
/* Original report descriptor, with reduced number of consumer usages */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x80, /* Usage (Sys Control), */
|
||||
@ -102,14 +102,14 @@ static __u8 holtek_kbd_rdesc_fixed[] = {
|
||||
0xC0, /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *holtek_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *holtek_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
|
||||
rdesc = holtek_kbd_rdesc_fixed;
|
||||
*rsize = sizeof(holtek_kbd_rdesc_fixed);
|
||||
return holtek_kbd_rdesc_fixed;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
@ -29,8 +29,8 @@
|
||||
* - USB ID 04d9:a0c2, sold as ETEKCITY Scroll T-140 Gaming Mouse
|
||||
*/
|
||||
|
||||
static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *holtek_mouse_report_fixup(struct hid_device *hdev,
|
||||
__u8 *rdesc, unsigned int *rsize)
|
||||
{
|
||||
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
|
||||
|
||||
|
@ -417,24 +417,8 @@
|
||||
#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401
|
||||
#define USB_DEVICE_ID_HP_X2 0x074d
|
||||
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
|
||||
#define I2C_DEVICE_ID_HP_ENVY_X360_15 0x2d05
|
||||
#define I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100 0x29CF
|
||||
#define I2C_DEVICE_ID_HP_ENVY_X360_EU0009NV 0x2CF9
|
||||
#define I2C_DEVICE_ID_HP_SPECTRE_X360_15 0x2817
|
||||
#define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF
|
||||
#define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8
|
||||
#define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82
|
||||
#define I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN 0x2F2C
|
||||
#define I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN 0x4116
|
||||
#define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544
|
||||
#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706
|
||||
#define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A
|
||||
#define I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN 0x2A1C
|
||||
#define I2C_DEVICE_ID_LENOVO_YOGA_C630_TOUCHSCREEN 0x279F
|
||||
#define I2C_DEVICE_ID_HP_SPECTRE_X360_13T_AW100 0x29F5
|
||||
#define I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V1 0x2BED
|
||||
#define I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V2 0x2BEE
|
||||
#define I2C_DEVICE_ID_HP_ENVY_X360_15_EU0556NG 0x2D02
|
||||
#define I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM 0x2F81
|
||||
|
||||
#define USB_VENDOR_ID_ELECOM 0x056e
|
||||
@ -521,6 +505,7 @@
|
||||
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
|
||||
|
||||
#define I2C_VENDOR_ID_GOODIX 0x27c6
|
||||
#define I2C_DEVICE_ID_GOODIX_01E0 0x01e0
|
||||
#define I2C_DEVICE_ID_GOODIX_01E8 0x01e8
|
||||
#define I2C_DEVICE_ID_GOODIX_01E9 0x01e9
|
||||
#define I2C_DEVICE_ID_GOODIX_01F0 0x01f0
|
||||
@ -810,6 +795,7 @@
|
||||
#define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3
|
||||
#define USB_DEVICE_ID_LENOVO_X1_TAB3 0x60b5
|
||||
#define USB_DEVICE_ID_LENOVO_X12_TAB 0x60fe
|
||||
#define USB_DEVICE_ID_LENOVO_X12_TAB2 0x61ae
|
||||
#define USB_DEVICE_ID_LENOVO_OPTICAL_USB_MOUSE_600E 0x600e
|
||||
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D 0x608d
|
||||
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019 0x6019
|
||||
|
@ -373,14 +373,6 @@ static const struct hid_device_id hid_battery_quirks[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
|
||||
USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX3402_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_UX6404_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN),
|
||||
@ -391,32 +383,13 @@ static const struct hid_device_id hid_battery_quirks[] = {
|
||||
HID_BATTERY_QUIRK_AVOID_QUERY },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW),
|
||||
HID_BATTERY_QUIRK_AVOID_QUERY },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_EU0009NV),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_15),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_LENOVO_YOGA_C630_TOUCHSCREEN),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_13T_AW100),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V1),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V2),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15_EU0556NG),
|
||||
HID_BATTERY_QUIRK_IGNORE },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM),
|
||||
HID_BATTERY_QUIRK_AVOID_QUERY },
|
||||
/*
|
||||
* Elan I2C-HID touchscreens seem to all report a non present battery,
|
||||
* set HID_BATTERY_QUIRK_IGNORE for all Elan I2C-HID devices.
|
||||
*/
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#define QUIRK_TOUCHPAD_ON_OFF_REPORT BIT(0)
|
||||
|
||||
static __u8 *ite_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize)
|
||||
static const __u8 *ite_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize)
|
||||
{
|
||||
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
/* Replace the broken report descriptor of this device with rather
|
||||
* a default one */
|
||||
static __u8 keytouch_fixed_rdesc[] = {
|
||||
static const __u8 keytouch_fixed_rdesc[] = {
|
||||
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15,
|
||||
0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08,
|
||||
0x81, 0x01, 0x95, 0x03, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91,
|
||||
@ -24,15 +24,13 @@ static __u8 keytouch_fixed_rdesc[] = {
|
||||
0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00, 0xc0
|
||||
};
|
||||
|
||||
static __u8 *keytouch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *keytouch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
hid_info(hdev, "fixing up Keytouch IEC report descriptor\n");
|
||||
|
||||
rdesc = keytouch_fixed_rdesc;
|
||||
*rsize = sizeof(keytouch_fixed_rdesc);
|
||||
|
||||
return rdesc;
|
||||
return keytouch_fixed_rdesc;
|
||||
}
|
||||
|
||||
static const struct hid_device_id keytouch_devices[] = {
|
||||
|
@ -466,7 +466,7 @@ static __u8 *kye_tablet_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int
|
||||
return rdesc;
|
||||
}
|
||||
|
||||
static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
|
@ -133,7 +133,7 @@ static const __u8 lenovo_tpIIbtkbd_need_fixup_collection[] = {
|
||||
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
|
||||
};
|
||||
|
||||
static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
|
@ -58,7 +58,7 @@
|
||||
* These descriptors remove the combined Y axis and instead report
|
||||
* separate throttle (Y) and brake (RZ).
|
||||
*/
|
||||
static __u8 df_rdesc_fixed[] = {
|
||||
static const __u8 df_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -124,7 +124,7 @@ static __u8 df_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 dfp_rdesc_fixed[] = {
|
||||
static const __u8 dfp_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -172,7 +172,7 @@ static __u8 dfp_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 fv_rdesc_fixed[] = {
|
||||
static const __u8 fv_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -239,7 +239,7 @@ static __u8 fv_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 momo_rdesc_fixed[] = {
|
||||
static const __u8 momo_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -285,7 +285,7 @@ static __u8 momo_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 momo2_rdesc_fixed[] = {
|
||||
static const __u8 momo2_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -333,7 +333,7 @@ static __u8 momo2_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 ffg_rdesc_fixed[] = {
|
||||
static const __u8 ffg_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -379,7 +379,7 @@ static __u8 ffg_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 fg_rdesc_fixed[] = {
|
||||
static const __u8 fg_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystik), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -427,7 +427,7 @@ static __u8 fg_rdesc_fixed[] = {
|
||||
* above the logical maximum described in descriptor. This extends
|
||||
* the original value of 0x28c of logical maximum to 0x104d
|
||||
*/
|
||||
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
|
||||
@ -453,8 +453,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
if (*rsize == FG_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Wingman Formula GP report descriptor\n");
|
||||
rdesc = fg_rdesc_fixed;
|
||||
*rsize = sizeof(fg_rdesc_fixed);
|
||||
return fg_rdesc_fixed;
|
||||
} else {
|
||||
hid_info(hdev,
|
||||
"rdesc size test failed for formula gp\n");
|
||||
@ -466,8 +466,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
if (*rsize == FFG_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Wingman Formula Force GP report descriptor\n");
|
||||
rdesc = ffg_rdesc_fixed;
|
||||
*rsize = sizeof(ffg_rdesc_fixed);
|
||||
return ffg_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -476,8 +476,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
if (*rsize == DF_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Driving Force report descriptor\n");
|
||||
rdesc = df_rdesc_fixed;
|
||||
*rsize = sizeof(df_rdesc_fixed);
|
||||
return df_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -485,8 +485,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
if (*rsize == MOMO_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Momo Force (Red) report descriptor\n");
|
||||
rdesc = momo_rdesc_fixed;
|
||||
*rsize = sizeof(momo_rdesc_fixed);
|
||||
return momo_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -494,8 +494,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Momo Racing Force (Black) report descriptor\n");
|
||||
rdesc = momo2_rdesc_fixed;
|
||||
*rsize = sizeof(momo2_rdesc_fixed);
|
||||
return momo2_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -503,8 +503,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
if (*rsize == FV_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Formula Vibration report descriptor\n");
|
||||
rdesc = fv_rdesc_fixed;
|
||||
*rsize = sizeof(fv_rdesc_fixed);
|
||||
return fv_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -512,8 +512,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
if (*rsize == DFP_RDESC_ORIG_SIZE) {
|
||||
hid_info(hdev,
|
||||
"fixing up Logitech Driving Force Pro report descriptor\n");
|
||||
rdesc = dfp_rdesc_fixed;
|
||||
*rsize = sizeof(dfp_rdesc_fixed);
|
||||
return dfp_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -3767,8 +3767,8 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp)
|
||||
/* Generic HID++ devices */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static u8 *hidpp_report_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const u8 *hidpp_report_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
|
||||
|
@ -18,8 +18,8 @@ MODULE_LICENSE("GPL");
|
||||
* The Macally ikey keyboard says that its logical and usage maximums are both
|
||||
* 101, but the power key is 102 and the equals key is 103
|
||||
*/
|
||||
static __u8 *macally_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *macally_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 60 && rdesc[53] == 0x65 && rdesc[59] == 0x65) {
|
||||
hid_info(hdev,
|
||||
|
@ -907,8 +907,8 @@ static void magicmouse_remove(struct hid_device *hdev)
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
/*
|
||||
* Change the usage from:
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "hid-ids.h"
|
||||
|
||||
/* The original buggy USB descriptor */
|
||||
static u8 maltron_rdesc_o[] = {
|
||||
static const u8 maltron_rdesc_o[] = {
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
|
||||
0x09, 0x80, /* Usage (Sys Control) */
|
||||
0xA1, 0x01, /* Collection (Application) */
|
||||
@ -79,7 +79,7 @@ static u8 maltron_rdesc_o[] = {
|
||||
};
|
||||
|
||||
/* The patched descriptor, allowing media key events to be accepted as valid */
|
||||
static u8 maltron_rdesc[] = {
|
||||
static const u8 maltron_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
|
||||
0x09, 0x80, /* Usage (Sys Control) */
|
||||
0xA1, 0x01, /* Collection (Application) */
|
||||
@ -137,8 +137,8 @@ static u8 maltron_rdesc[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == sizeof(maltron_rdesc_o) &&
|
||||
!memcmp(maltron_rdesc_o, rdesc, sizeof(maltron_rdesc_o))) {
|
||||
|
@ -56,7 +56,7 @@ struct xb1s_ff_report {
|
||||
__u8 loop_count;
|
||||
} __packed;
|
||||
|
||||
static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 31 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
|
||||
|
@ -212,6 +212,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
|
||||
#define MT_CLS_GOOGLE 0x0111
|
||||
#define MT_CLS_RAZER_BLADE_STEALTH 0x0112
|
||||
#define MT_CLS_SMART_TECH 0x0113
|
||||
#define MT_CLS_SIS 0x0457
|
||||
|
||||
#define MT_DEFAULT_MAXCONTACT 10
|
||||
#define MT_MAX_MAXCONTACT 250
|
||||
@ -396,6 +397,11 @@ static const struct mt_class mt_classes[] = {
|
||||
MT_QUIRK_CONTACT_CNT_ACCURATE |
|
||||
MT_QUIRK_SEPARATE_APP_REPORT,
|
||||
},
|
||||
{ .name = MT_CLS_SIS,
|
||||
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
|
||||
MT_QUIRK_ALWAYS_VALID |
|
||||
MT_QUIRK_CONTACT_CNT_ACCURATE,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -1441,12 +1447,13 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u8 *mt_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *mt_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *size)
|
||||
{
|
||||
if (hdev->vendor == I2C_VENDOR_ID_GOODIX &&
|
||||
(hdev->product == I2C_DEVICE_ID_GOODIX_01E8 ||
|
||||
hdev->product == I2C_DEVICE_ID_GOODIX_01E9)) {
|
||||
hdev->product == I2C_DEVICE_ID_GOODIX_01E9 ||
|
||||
hdev->product == I2C_DEVICE_ID_GOODIX_01E0)) {
|
||||
if (rdesc[607] == 0x15) {
|
||||
rdesc[607] = 0x25;
|
||||
dev_info(
|
||||
@ -1811,6 +1818,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID)
|
||||
mt_fix_const_fields(hdev, HID_DG_CONTACTID);
|
||||
|
||||
if (hdev->vendor == USB_VENDOR_ID_SIS_TOUCH)
|
||||
hdev->quirks |= HID_QUIRK_NOGET;
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -2065,7 +2075,10 @@ static const struct hid_device_id mt_devices[] = {
|
||||
I2C_DEVICE_ID_GOODIX_01E8) },
|
||||
{ .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU,
|
||||
HID_DEVICE(BUS_I2C, HID_GROUP_ANY, I2C_VENDOR_ID_GOODIX,
|
||||
I2C_DEVICE_ID_GOODIX_01E8) },
|
||||
I2C_DEVICE_ID_GOODIX_01E9) },
|
||||
{ .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU,
|
||||
HID_DEVICE(BUS_I2C, HID_GROUP_ANY, I2C_VENDOR_ID_GOODIX,
|
||||
I2C_DEVICE_ID_GOODIX_01E0) },
|
||||
|
||||
/* GoodTouch panels */
|
||||
{ .driver_data = MT_CLS_NSMU,
|
||||
@ -2113,6 +2126,12 @@ static const struct hid_device_id mt_devices[] = {
|
||||
USB_VENDOR_ID_LENOVO,
|
||||
USB_DEVICE_ID_LENOVO_X12_TAB) },
|
||||
|
||||
/* Lenovo X12 TAB Gen 2 */
|
||||
{ .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU,
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8,
|
||||
USB_VENDOR_ID_LENOVO,
|
||||
USB_DEVICE_ID_LENOVO_X12_TAB2) },
|
||||
|
||||
/* Logitech devices */
|
||||
{ .driver_data = MT_CLS_NSMU,
|
||||
HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_MULTITOUCH_WIN_8,
|
||||
@ -2275,6 +2294,11 @@ static const struct hid_device_id mt_devices[] = {
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_GOOGLE,
|
||||
USB_DEVICE_ID_GOOGLE_WHISKERS) },
|
||||
|
||||
/* sis */
|
||||
{ .driver_data = MT_CLS_SIS,
|
||||
HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_SIS_TOUCH,
|
||||
HID_ANY_ID) },
|
||||
|
||||
/* Generic MT device */
|
||||
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },
|
||||
|
||||
|
@ -29,7 +29,7 @@ MODULE_DESCRIPTION("HID driver for Network Technologies USB-SUN keyboard adapter
|
||||
/*
|
||||
* NTI Sun keyboard adapter has wrong logical maximum in report descriptor
|
||||
*/
|
||||
static __u8 *nti_usbsun_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *nti_usbsun_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 60 && rdesc[53] == 0x65 && rdesc[59] == 0x65) {
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) {
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "hid-ids.h"
|
||||
|
||||
/* Petalynx Maxter Remote has maximum for consumer page set too low */
|
||||
static __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 62 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
|
||||
|
@ -31,7 +31,8 @@ static int picolcd_set_brightness(struct backlight_device *bdev)
|
||||
data->lcd_brightness = bdev->props.brightness & 0x0ff;
|
||||
data->lcd_power = bdev->props.power;
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
|
||||
hid_set_field(report->field[0], 0,
|
||||
data->lcd_power == BACKLIGHT_POWER_ON ? data->lcd_brightness : 0);
|
||||
if (!(data->status & PICOLCD_FAILED))
|
||||
hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
@ -94,7 +95,7 @@ void picolcd_suspend_backlight(struct picolcd_data *data)
|
||||
if (!data->backlight)
|
||||
return;
|
||||
|
||||
data->backlight->props.power = FB_BLANK_POWERDOWN;
|
||||
data->backlight->props.power = BACKLIGHT_POWER_OFF;
|
||||
picolcd_set_brightness(data->backlight);
|
||||
data->lcd_power = data->backlight->props.power = bl_power;
|
||||
}
|
||||
|
@ -2143,6 +2143,26 @@ static void dualshock4_output_worker(struct work_struct *work)
|
||||
|
||||
spin_lock_irqsave(&ds4->base.lock, flags);
|
||||
|
||||
/*
|
||||
* Some 3rd party gamepads expect updates to rumble and lightbar
|
||||
* together, and setting one may cancel the other.
|
||||
*
|
||||
* Let's maximise compatibility by always sending rumble and lightbar
|
||||
* updates together, even when only one has been scheduled, resulting
|
||||
* in:
|
||||
*
|
||||
* ds4->valid_flag0 >= 0x03
|
||||
*
|
||||
* Hopefully this will maximise compatibility with third-party pads.
|
||||
*
|
||||
* Any further update bits, such as 0x04 for lightbar blinking, will
|
||||
* be or'd on top of this like before.
|
||||
*/
|
||||
if (ds4->update_rumble || ds4->update_lightbar) {
|
||||
ds4->update_rumble = true; /* 0x01 */
|
||||
ds4->update_lightbar = true; /* 0x02 */
|
||||
}
|
||||
|
||||
if (ds4->update_rumble) {
|
||||
/* Select classic rumble style haptics and enable it. */
|
||||
common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR;
|
||||
|
@ -728,7 +728,7 @@ static int pcmidi_snd_terminate(struct pcmidi_snd *pm)
|
||||
/*
|
||||
* PC-MIDI report descriptor for report id is wrong.
|
||||
*/
|
||||
static __u8 *pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == 178 &&
|
||||
|
@ -17,7 +17,7 @@ struct pxrc_priv {
|
||||
bool alternate;
|
||||
};
|
||||
|
||||
static __u8 pxrc_rdesc_fixed[] = {
|
||||
static const __u8 pxrc_rdesc_fixed[] = {
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x04, // Usage (Joystick)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
@ -42,8 +42,8 @@ static __u8 pxrc_rdesc_fixed[] = {
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
static __u8 *pxrc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *pxrc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
hid_info(hdev, "fixing up PXRC report descriptor\n");
|
||||
*rsize = sizeof(pxrc_rdesc_fixed);
|
||||
|
@ -33,7 +33,7 @@
|
||||
* key codes are generated.
|
||||
*/
|
||||
|
||||
static __u8 *redragon_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *redragon_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 102 && rdesc[100] == 0x81 && rdesc[101] == 0x00) {
|
||||
|
@ -66,7 +66,7 @@ static int saitek_probe(struct hid_device *hdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct saitek_sc *ssc = hid_get_drvdata(hdev);
|
||||
|
@ -469,7 +469,7 @@ static int samsung_universal_kbd_input_mapping(struct hid_device *hdev,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (hdev->product == USB_DEVICE_ID_SAMSUNG_IR_REMOTE && hid_is_usb(hdev))
|
||||
|
@ -11,8 +11,8 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static __u8 *semitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *semitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
/* In the report descriptor for interface 2, fix the incorrect
|
||||
description of report ID 0x04 (the report contains a
|
||||
|
@ -732,7 +732,7 @@ static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst)
|
||||
|
||||
sensor_inst->custom_dev.minor = MISC_DYNAMIC_MINOR;
|
||||
sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev);
|
||||
sensor_inst->custom_dev.fops = &hid_sensor_custom_fops,
|
||||
sensor_inst->custom_dev.fops = &hid_sensor_custom_fops;
|
||||
ret = misc_register(&sensor_inst->custom_dev);
|
||||
if (ret) {
|
||||
kfifo_free(&sensor_inst->data_fifo);
|
||||
|
@ -580,7 +580,7 @@ void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sensor_hub_device_close);
|
||||
|
||||
static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
/*
|
||||
|
@ -99,8 +99,8 @@ static const __u8 sm_0059_rdesc[] = {
|
||||
0xc0, /* End Collection 166 */
|
||||
};
|
||||
|
||||
static __u8 *sm_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *sm_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize == sizeof(sm_0059_rdesc) &&
|
||||
!memcmp(sm_0059_rdesc, rdesc, *rsize)) {
|
||||
|
@ -99,7 +99,7 @@ static const char ghl_ps4_magic_data[] = {
|
||||
};
|
||||
|
||||
/* PS/3 Motion controller */
|
||||
static u8 motion_rdesc[] = {
|
||||
static const u8 motion_rdesc[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x04, /* Usage (Joystick), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -195,7 +195,7 @@ static u8 motion_rdesc[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static u8 ps3remote_rdesc[] = {
|
||||
static const u8 ps3remote_rdesc[] = {
|
||||
0x05, 0x01, /* GUsagePage Generic Desktop */
|
||||
0x09, 0x05, /* LUsage 0x05 [Game Pad] */
|
||||
0xA1, 0x01, /* MCollection Application (mouse, keyboard) */
|
||||
@ -599,15 +599,15 @@ static int guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
*rsize = sizeof(motion_rdesc);
|
||||
return motion_rdesc;
|
||||
}
|
||||
|
||||
static u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
*rsize = sizeof(ps3remote_rdesc);
|
||||
return ps3remote_rdesc;
|
||||
@ -743,7 +743,7 @@ static int sixaxis_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
static const u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct sony_sc *sc = hid_get_drvdata(hdev);
|
||||
|
@ -51,7 +51,7 @@ struct steelseries_srws1_data {
|
||||
* appear in the 'Generic Desktop' usage.
|
||||
*/
|
||||
|
||||
static __u8 steelseries_srws1_rdesc_fixed[] = {
|
||||
static const __u8 steelseries_srws1_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop) */
|
||||
0x09, 0x08, /* Usage (MultiAxis), Changed */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -570,8 +570,8 @@ static void steelseries_remove(struct hid_device *hdev)
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __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)
|
||||
@ -580,8 +580,8 @@ static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *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");
|
||||
rdesc = steelseries_srws1_rdesc_fixed;
|
||||
*rsize = sizeof(steelseries_srws1_rdesc_fixed);
|
||||
return steelseries_srws1_rdesc_fixed;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
static __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 112 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
|
||||
|
@ -21,8 +21,8 @@ MODULE_LICENSE("GPL");
|
||||
* events it's actually sending. It claims to send array events but is instead
|
||||
* sending variable events.
|
||||
*/
|
||||
static __u8 *topre_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *topre_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 119 && rdesc[69] == 0x29 && rdesc[70] == 0xe7 &&
|
||||
rdesc[71] == 0x81 && rdesc[72] == 0x00) {
|
||||
|
@ -50,14 +50,14 @@ static void uclogic_inrange_timeout(struct timer_list *t)
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
if (drvdata->desc_ptr != NULL) {
|
||||
rdesc = drvdata->desc_ptr;
|
||||
*rsize = drvdata->desc_size;
|
||||
return drvdata->desc_ptr;
|
||||
}
|
||||
return rdesc;
|
||||
}
|
||||
|
@ -681,7 +681,7 @@ void uclogic_params_cleanup(struct uclogic_params *params)
|
||||
* -ENOMEM, if failed to allocate memory.
|
||||
*/
|
||||
int uclogic_params_get_desc(const struct uclogic_params *params,
|
||||
__u8 **pdesc,
|
||||
const __u8 **pdesc,
|
||||
unsigned int *psize)
|
||||
{
|
||||
int rc = -ENOMEM;
|
||||
@ -769,7 +769,7 @@ static void uclogic_params_init_invalid(struct uclogic_params *params)
|
||||
static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
|
||||
struct hid_device *hdev,
|
||||
unsigned int orig_desc_size,
|
||||
__u8 *desc_ptr,
|
||||
const __u8 *desc_ptr,
|
||||
unsigned int desc_size)
|
||||
{
|
||||
__u8 *desc_copy_ptr = NULL;
|
||||
|
@ -79,7 +79,7 @@ struct uclogic_params_pen {
|
||||
* Pointer to report descriptor part describing the pen inputs.
|
||||
* Allocated with kmalloc. NULL if the part is not specified.
|
||||
*/
|
||||
__u8 *desc_ptr;
|
||||
const __u8 *desc_ptr;
|
||||
/*
|
||||
* Size of the report descriptor.
|
||||
* Only valid, if "desc_ptr" is not NULL.
|
||||
@ -118,7 +118,7 @@ struct uclogic_params_frame {
|
||||
* Pointer to report descriptor part describing the frame inputs.
|
||||
* Allocated with kmalloc. NULL if the part is not specified.
|
||||
*/
|
||||
__u8 *desc_ptr;
|
||||
const __u8 *desc_ptr;
|
||||
/*
|
||||
* Size of the report descriptor.
|
||||
* Only valid, if "desc_ptr" is not NULL.
|
||||
@ -212,7 +212,7 @@ struct uclogic_params {
|
||||
* allocated with kmalloc. NULL if no common part is needed.
|
||||
* Only valid, if "invalid" is false.
|
||||
*/
|
||||
__u8 *desc_ptr;
|
||||
const __u8 *desc_ptr;
|
||||
/*
|
||||
* Size of the common part of the replacement report descriptor.
|
||||
* Only valid, if "desc_ptr" is valid and not NULL.
|
||||
@ -239,7 +239,7 @@ struct uclogic_drvdata {
|
||||
/* Interface parameters */
|
||||
struct uclogic_params params;
|
||||
/* Pointer to the replacement report descriptor. NULL if none. */
|
||||
__u8 *desc_ptr;
|
||||
const __u8 *desc_ptr;
|
||||
/*
|
||||
* Size of the replacement report descriptor.
|
||||
* Only valid if desc_ptr is not NULL
|
||||
@ -261,7 +261,7 @@ extern int uclogic_params_init(struct uclogic_params *params,
|
||||
|
||||
/* Get a replacement report descriptor for a tablet's interface. */
|
||||
extern int uclogic_params_get_desc(const struct uclogic_params *params,
|
||||
__u8 **pdesc,
|
||||
const __u8 **pdesc,
|
||||
unsigned int *psize);
|
||||
|
||||
/* Free resources used by tablet interface's parameters */
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
/* Fixed WP4030U report descriptor */
|
||||
__u8 uclogic_rdesc_wp4030u_fixed_arr[] = {
|
||||
const __u8 uclogic_rdesc_wp4030u_fixed_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -65,7 +65,7 @@ const size_t uclogic_rdesc_wp4030u_fixed_size =
|
||||
sizeof(uclogic_rdesc_wp4030u_fixed_arr);
|
||||
|
||||
/* Fixed WP5540U report descriptor */
|
||||
__u8 uclogic_rdesc_wp5540u_fixed_arr[] = {
|
||||
const __u8 uclogic_rdesc_wp5540u_fixed_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -142,7 +142,7 @@ const size_t uclogic_rdesc_wp5540u_fixed_size =
|
||||
sizeof(uclogic_rdesc_wp5540u_fixed_arr);
|
||||
|
||||
/* Fixed WP8060U report descriptor */
|
||||
__u8 uclogic_rdesc_wp8060u_fixed_arr[] = {
|
||||
const __u8 uclogic_rdesc_wp8060u_fixed_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -219,7 +219,7 @@ const size_t uclogic_rdesc_wp8060u_fixed_size =
|
||||
sizeof(uclogic_rdesc_wp8060u_fixed_arr);
|
||||
|
||||
/* Fixed WP1062 report descriptor */
|
||||
__u8 uclogic_rdesc_wp1062_fixed_arr[] = {
|
||||
const __u8 uclogic_rdesc_wp1062_fixed_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -267,7 +267,7 @@ const size_t uclogic_rdesc_wp1062_fixed_size =
|
||||
sizeof(uclogic_rdesc_wp1062_fixed_arr);
|
||||
|
||||
/* Fixed PF1209 report descriptor */
|
||||
__u8 uclogic_rdesc_pf1209_fixed_arr[] = {
|
||||
const __u8 uclogic_rdesc_pf1209_fixed_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -344,7 +344,7 @@ const size_t uclogic_rdesc_pf1209_fixed_size =
|
||||
sizeof(uclogic_rdesc_pf1209_fixed_arr);
|
||||
|
||||
/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
|
||||
__u8 uclogic_rdesc_twhl850_fixed0_arr[] = {
|
||||
const __u8 uclogic_rdesc_twhl850_fixed0_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -390,7 +390,7 @@ const size_t uclogic_rdesc_twhl850_fixed0_size =
|
||||
sizeof(uclogic_rdesc_twhl850_fixed0_arr);
|
||||
|
||||
/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
|
||||
__u8 uclogic_rdesc_twhl850_fixed1_arr[] = {
|
||||
const __u8 uclogic_rdesc_twhl850_fixed1_arr[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x02, /* Usage (Mouse), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -430,7 +430,7 @@ const size_t uclogic_rdesc_twhl850_fixed1_size =
|
||||
sizeof(uclogic_rdesc_twhl850_fixed1_arr);
|
||||
|
||||
/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
|
||||
__u8 uclogic_rdesc_twhl850_fixed2_arr[] = {
|
||||
const __u8 uclogic_rdesc_twhl850_fixed2_arr[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x06, /* Usage (Keyboard), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -456,7 +456,7 @@ const size_t uclogic_rdesc_twhl850_fixed2_size =
|
||||
sizeof(uclogic_rdesc_twhl850_fixed2_arr);
|
||||
|
||||
/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
|
||||
__u8 uclogic_rdesc_twha60_fixed0_arr[] = {
|
||||
const __u8 uclogic_rdesc_twha60_fixed0_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -505,7 +505,7 @@ const size_t uclogic_rdesc_twha60_fixed0_size =
|
||||
sizeof(uclogic_rdesc_twha60_fixed0_arr);
|
||||
|
||||
/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
|
||||
__u8 uclogic_rdesc_twha60_fixed1_arr[] = {
|
||||
const __u8 uclogic_rdesc_twha60_fixed1_arr[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x06, /* Usage (Keyboard), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
|
@ -23,15 +23,15 @@
|
||||
#define UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE 212
|
||||
|
||||
/* Fixed WP4030U report descriptor */
|
||||
extern __u8 uclogic_rdesc_wp4030u_fixed_arr[];
|
||||
extern const __u8 uclogic_rdesc_wp4030u_fixed_arr[];
|
||||
extern const size_t uclogic_rdesc_wp4030u_fixed_size;
|
||||
|
||||
/* Fixed WP5540U report descriptor */
|
||||
extern __u8 uclogic_rdesc_wp5540u_fixed_arr[];
|
||||
extern const __u8 uclogic_rdesc_wp5540u_fixed_arr[];
|
||||
extern const size_t uclogic_rdesc_wp5540u_fixed_size;
|
||||
|
||||
/* Fixed WP8060U report descriptor */
|
||||
extern __u8 uclogic_rdesc_wp8060u_fixed_arr[];
|
||||
extern const __u8 uclogic_rdesc_wp8060u_fixed_arr[];
|
||||
extern const size_t uclogic_rdesc_wp8060u_fixed_size;
|
||||
|
||||
/* Size of the original descriptor of the new WP5540U tablet */
|
||||
@ -41,14 +41,14 @@ extern const size_t uclogic_rdesc_wp8060u_fixed_size;
|
||||
#define UCLOGIC_RDESC_WP1062_ORIG_SIZE 254
|
||||
|
||||
/* Fixed WP1062 report descriptor */
|
||||
extern __u8 uclogic_rdesc_wp1062_fixed_arr[];
|
||||
extern const __u8 uclogic_rdesc_wp1062_fixed_arr[];
|
||||
extern const size_t uclogic_rdesc_wp1062_fixed_size;
|
||||
|
||||
/* Size of the original descriptor of PF1209 tablet */
|
||||
#define UCLOGIC_RDESC_PF1209_ORIG_SIZE 234
|
||||
|
||||
/* Fixed PF1209 report descriptor */
|
||||
extern __u8 uclogic_rdesc_pf1209_fixed_arr[];
|
||||
extern const __u8 uclogic_rdesc_pf1209_fixed_arr[];
|
||||
extern const size_t uclogic_rdesc_pf1209_fixed_size;
|
||||
|
||||
/* Size of the original descriptors of TWHL850 tablet */
|
||||
@ -57,15 +57,15 @@ extern const size_t uclogic_rdesc_pf1209_fixed_size;
|
||||
#define UCLOGIC_RDESC_TWHL850_ORIG2_SIZE 92
|
||||
|
||||
/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
|
||||
extern __u8 uclogic_rdesc_twhl850_fixed0_arr[];
|
||||
extern const __u8 uclogic_rdesc_twhl850_fixed0_arr[];
|
||||
extern const size_t uclogic_rdesc_twhl850_fixed0_size;
|
||||
|
||||
/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
|
||||
extern __u8 uclogic_rdesc_twhl850_fixed1_arr[];
|
||||
extern const __u8 uclogic_rdesc_twhl850_fixed1_arr[];
|
||||
extern const size_t uclogic_rdesc_twhl850_fixed1_size;
|
||||
|
||||
/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
|
||||
extern __u8 uclogic_rdesc_twhl850_fixed2_arr[];
|
||||
extern const __u8 uclogic_rdesc_twhl850_fixed2_arr[];
|
||||
extern const size_t uclogic_rdesc_twhl850_fixed2_size;
|
||||
|
||||
/* Size of the original descriptors of TWHA60 tablet */
|
||||
@ -73,11 +73,11 @@ extern const size_t uclogic_rdesc_twhl850_fixed2_size;
|
||||
#define UCLOGIC_RDESC_TWHA60_ORIG1_SIZE 139
|
||||
|
||||
/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
|
||||
extern __u8 uclogic_rdesc_twha60_fixed0_arr[];
|
||||
extern const __u8 uclogic_rdesc_twha60_fixed0_arr[];
|
||||
extern const size_t uclogic_rdesc_twha60_fixed0_size;
|
||||
|
||||
/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
|
||||
extern __u8 uclogic_rdesc_twha60_fixed1_arr[];
|
||||
extern const __u8 uclogic_rdesc_twha60_fixed1_arr[];
|
||||
extern const size_t uclogic_rdesc_twha60_fixed1_size;
|
||||
|
||||
/* Report descriptor template placeholder head */
|
||||
|
@ -22,7 +22,7 @@
|
||||
#define PD1011_RDESC_ORIG_SIZE 408
|
||||
|
||||
/* Fixed report descriptor of PD1011 signature pad */
|
||||
static __u8 pd1011_rdesc_fixed[] = {
|
||||
static const __u8 pd1011_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x01, /* Usage (Digitizer), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -70,15 +70,15 @@ static __u8 pd1011_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *viewsonic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *viewsonic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_VIEWSONIC_PD1011:
|
||||
case USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011:
|
||||
if (*rsize == PD1011_RDESC_ORIG_SIZE) {
|
||||
rdesc = pd1011_rdesc_fixed;
|
||||
*rsize = sizeof(pd1011_rdesc_fixed);
|
||||
return pd1011_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define USB_VENDOR_ID_VRC2 (0x07c0)
|
||||
#define USB_DEVICE_ID_VRC2 (0x1125)
|
||||
|
||||
static __u8 vrc2_rdesc_fixed[] = {
|
||||
static const __u8 vrc2_rdesc_fixed[] = {
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x04, // Usage (Joystick)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
@ -38,8 +38,8 @@ static __u8 vrc2_rdesc_fixed[] = {
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
static __u8 *vrc2_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *vrc2_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
hid_info(hdev, "fixing up VRC-2 report descriptor\n");
|
||||
*rsize = sizeof(vrc2_rdesc_fixed);
|
||||
|
@ -43,7 +43,7 @@
|
||||
#define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222
|
||||
|
||||
/* Fixed Slim Tablet 5.8 inch descriptor */
|
||||
static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = {
|
||||
static const __u8 slim_tablet_5_8_inch_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -94,7 +94,7 @@ static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = {
|
||||
#define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269
|
||||
|
||||
/* Fixed Slim Tablet 12.1 inch descriptor */
|
||||
static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = {
|
||||
static const __u8 slim_tablet_12_1_inch_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -145,7 +145,7 @@ static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = {
|
||||
#define Q_PAD_RDESC_ORIG_SIZE 241
|
||||
|
||||
/* Fixed Q Pad descriptor */
|
||||
static __u8 q_pad_rdesc_fixed[] = {
|
||||
static const __u8 q_pad_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -198,7 +198,7 @@ static __u8 q_pad_rdesc_fixed[] = {
|
||||
/*
|
||||
* Fixed report descriptor for tablet with PID 0038.
|
||||
*/
|
||||
static __u8 pid_0038_rdesc_fixed[] = {
|
||||
static const __u8 pid_0038_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -249,7 +249,7 @@ static __u8 pid_0038_rdesc_fixed[] = {
|
||||
#define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300
|
||||
|
||||
/* Fixed Media Tablet 10.6 inch descriptor */
|
||||
static __u8 media_tablet_10_6_inch_rdesc_fixed[] = {
|
||||
static const __u8 media_tablet_10_6_inch_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -362,7 +362,7 @@ static __u8 media_tablet_10_6_inch_rdesc_fixed[] = {
|
||||
#define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309
|
||||
|
||||
/* Fixed Media Tablet 14.1 inch descriptor */
|
||||
static __u8 media_tablet_14_1_inch_rdesc_fixed[] = {
|
||||
static const __u8 media_tablet_14_1_inch_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -473,7 +473,7 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = {
|
||||
#define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335
|
||||
|
||||
/* Fixed Sirius Battery Free Tablet descriptor */
|
||||
static __u8 sirius_battery_free_tablet_rdesc_fixed[] = {
|
||||
static const __u8 sirius_battery_free_tablet_rdesc_fixed[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -599,50 +599,50 @@ static __u8 sirius_battery_free_tablet_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH:
|
||||
if (*rsize == SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE) {
|
||||
rdesc = slim_tablet_5_8_inch_rdesc_fixed;
|
||||
*rsize = sizeof(slim_tablet_5_8_inch_rdesc_fixed);
|
||||
return slim_tablet_5_8_inch_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH:
|
||||
if (*rsize == SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE) {
|
||||
rdesc = slim_tablet_12_1_inch_rdesc_fixed;
|
||||
*rsize = sizeof(slim_tablet_12_1_inch_rdesc_fixed);
|
||||
return slim_tablet_12_1_inch_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_WALTOP_Q_PAD:
|
||||
if (*rsize == Q_PAD_RDESC_ORIG_SIZE) {
|
||||
rdesc = q_pad_rdesc_fixed;
|
||||
*rsize = sizeof(q_pad_rdesc_fixed);
|
||||
return q_pad_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_WALTOP_PID_0038:
|
||||
if (*rsize == PID_0038_RDESC_ORIG_SIZE) {
|
||||
rdesc = pid_0038_rdesc_fixed;
|
||||
*rsize = sizeof(pid_0038_rdesc_fixed);
|
||||
return pid_0038_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH:
|
||||
if (*rsize == MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE) {
|
||||
rdesc = media_tablet_10_6_inch_rdesc_fixed;
|
||||
*rsize = sizeof(media_tablet_10_6_inch_rdesc_fixed);
|
||||
return media_tablet_10_6_inch_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH:
|
||||
if (*rsize == MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE) {
|
||||
rdesc = media_tablet_14_1_inch_rdesc_fixed;
|
||||
*rsize = sizeof(media_tablet_14_1_inch_rdesc_fixed);
|
||||
return media_tablet_14_1_inch_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET:
|
||||
if (*rsize == SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE) {
|
||||
rdesc = sirius_battery_free_tablet_rdesc_fixed;
|
||||
*rsize = sizeof(sirius_battery_free_tablet_rdesc_fixed);
|
||||
return sirius_battery_free_tablet_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ struct winwing_led_info {
|
||||
const char *led_name;
|
||||
};
|
||||
|
||||
static struct winwing_led_info led_info[3] = {
|
||||
static const struct winwing_led_info led_info[3] = {
|
||||
{ 0, 255, "backlight" },
|
||||
{ 1, 1, "a-a" },
|
||||
{ 2, 1, "a-g" },
|
||||
@ -94,7 +94,7 @@ static int winwing_init_led(struct hid_device *hdev,
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 3; i += 1) {
|
||||
struct winwing_led_info *info = &led_info[i];
|
||||
const struct winwing_led_info *info = &led_info[i];
|
||||
|
||||
led = &data->leds[i];
|
||||
led->hdev = hdev;
|
||||
@ -150,7 +150,7 @@ static int winwing_input_configured(struct hid_device *hdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __u8 original_rdesc_buttons[] = {
|
||||
static const __u8 original_rdesc_buttons[] = {
|
||||
0x05, 0x09, 0x19, 0x01, 0x29, 0x6F,
|
||||
0x15, 0x00, 0x25, 0x01, 0x35, 0x00,
|
||||
0x45, 0x01, 0x75, 0x01, 0x95, 0x6F,
|
||||
@ -165,7 +165,7 @@ static __u8 original_rdesc_buttons[] = {
|
||||
* This module skips numbers 32-63, unused on some throttle grips.
|
||||
*/
|
||||
|
||||
static __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
int sig_length = sizeof(original_rdesc_buttons);
|
||||
|
@ -14,7 +14,7 @@
|
||||
/* Fixed Mi Silent Mouse report descriptor */
|
||||
/* Button's Usage Maximum changed from 3 to 5 to make side buttons work */
|
||||
#define MI_SILENT_MOUSE_ORIG_RDESC_LENGTH 87
|
||||
static __u8 mi_silent_mouse_rdesc_fixed[] = {
|
||||
static const __u8 mi_silent_mouse_rdesc_fixed[] = {
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x09, 0x02, /* Usage (Mouse), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
@ -61,15 +61,15 @@ static __u8 mi_silent_mouse_rdesc_fixed[] = {
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
static __u8 *xiaomi_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
static const __u8 *xiaomi_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_MI_SILENT_MOUSE:
|
||||
if (*rsize == MI_SILENT_MOUSE_ORIG_RDESC_LENGTH) {
|
||||
hid_info(hdev, "fixing up Mi Silent Mouse report descriptor\n");
|
||||
rdesc = mi_silent_mouse_rdesc_fixed;
|
||||
*rsize = sizeof(mi_silent_mouse_rdesc_fixed);
|
||||
return mi_silent_mouse_rdesc_fixed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ struct zc_device {
|
||||
* Zydacron remote control has an invalid HID report descriptor,
|
||||
* that needs fixing before we can parse it.
|
||||
*/
|
||||
static __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
static const __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
unsigned int *rsize)
|
||||
{
|
||||
if (*rsize >= 253 &&
|
||||
|
@ -38,12 +38,20 @@ static const struct class hidraw_class = {
|
||||
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
|
||||
static DECLARE_RWSEM(minors_rwsem);
|
||||
|
||||
static inline bool hidraw_is_revoked(struct hidraw_list *list)
|
||||
{
|
||||
return list->revoked;
|
||||
}
|
||||
|
||||
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hidraw_list *list = file->private_data;
|
||||
int ret = 0, len;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
if (hidraw_is_revoked(list))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&list->read_mutex);
|
||||
|
||||
while (ret == 0) {
|
||||
@ -161,9 +169,13 @@ out:
|
||||
|
||||
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hidraw_list *list = file->private_data;
|
||||
ssize_t ret;
|
||||
down_read(&minors_rwsem);
|
||||
ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
|
||||
if (hidraw_is_revoked(list))
|
||||
ret = -ENODEV;
|
||||
else
|
||||
ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
|
||||
up_read(&minors_rwsem);
|
||||
return ret;
|
||||
}
|
||||
@ -256,7 +268,7 @@ static __poll_t hidraw_poll(struct file *file, poll_table *wait)
|
||||
poll_wait(file, &list->hidraw->wait, wait);
|
||||
if (list->head != list->tail)
|
||||
mask |= EPOLLIN | EPOLLRDNORM;
|
||||
if (!list->hidraw->exist)
|
||||
if (!list->hidraw->exist || hidraw_is_revoked(list))
|
||||
mask |= EPOLLERR | EPOLLHUP;
|
||||
return mask;
|
||||
}
|
||||
@ -320,6 +332,9 @@ static int hidraw_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct hidraw_list *list = file->private_data;
|
||||
|
||||
if (hidraw_is_revoked(list))
|
||||
return -ENODEV;
|
||||
|
||||
return fasync_helper(fd, file, on, &list->fasync);
|
||||
}
|
||||
|
||||
@ -372,6 +387,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidraw_revoke(struct hidraw_list *list)
|
||||
{
|
||||
list->revoked = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long hidraw_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@ -379,11 +401,12 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned int minor = iminor(inode);
|
||||
long ret = 0;
|
||||
struct hidraw *dev;
|
||||
struct hidraw_list *list = file->private_data;
|
||||
void __user *user_arg = (void __user*) arg;
|
||||
|
||||
down_read(&minors_rwsem);
|
||||
dev = hidraw_table[minor];
|
||||
if (!dev || !dev->exist) {
|
||||
if (!dev || !dev->exist || hidraw_is_revoked(list)) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
@ -421,6 +444,14 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
case HIDIOCREVOKE:
|
||||
{
|
||||
if (user_arg)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = hidraw_revoke(list);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
struct hid_device *hid = dev->hid;
|
||||
@ -527,7 +558,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
|
||||
list_for_each_entry(list, &dev->list, node) {
|
||||
int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
|
||||
|
||||
if (new_head == list->tail)
|
||||
if (hidraw_is_revoked(list) || new_head == list->tail)
|
||||
continue;
|
||||
|
||||
if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {
|
||||
|
@ -105,6 +105,7 @@ struct i2c_hid {
|
||||
|
||||
wait_queue_head_t wait; /* For waiting the interrupt */
|
||||
|
||||
struct mutex cmd_lock; /* protects cmdbuf and rawbuf */
|
||||
struct mutex reset_lock;
|
||||
|
||||
struct i2chid_ops *ops;
|
||||
@ -220,6 +221,8 @@ static int i2c_hid_xfer(struct i2c_hid *ihid,
|
||||
static int i2c_hid_read_register(struct i2c_hid *ihid, __le16 reg,
|
||||
void *buf, size_t len)
|
||||
{
|
||||
guard(mutex)(&ihid->cmd_lock);
|
||||
|
||||
*(__le16 *)ihid->cmdbuf = reg;
|
||||
|
||||
return i2c_hid_xfer(ihid, ihid->cmdbuf, sizeof(__le16), buf, len);
|
||||
@ -252,6 +255,8 @@ static int i2c_hid_get_report(struct i2c_hid *ihid,
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
|
||||
guard(mutex)(&ihid->cmd_lock);
|
||||
|
||||
/* Command register goes first */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length += sizeof(__le16);
|
||||
@ -342,6 +347,8 @@ static int i2c_hid_set_or_send_report(struct i2c_hid *ihid,
|
||||
if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
guard(mutex)(&ihid->cmd_lock);
|
||||
|
||||
if (do_set) {
|
||||
/* Command register goes first */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
@ -384,6 +391,8 @@ static int i2c_hid_set_power_command(struct i2c_hid *ihid, int power_state)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
guard(mutex)(&ihid->cmd_lock);
|
||||
|
||||
/* SET_POWER uses command register */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length = sizeof(__le16);
|
||||
@ -440,25 +449,27 @@ static int i2c_hid_start_hwreset(struct i2c_hid *ihid)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Prepare reset command. Command register goes first. */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length += sizeof(__le16);
|
||||
/* Next is RESET command itself */
|
||||
length += i2c_hid_encode_command(ihid->cmdbuf + length,
|
||||
I2C_HID_OPCODE_RESET, 0, 0);
|
||||
scoped_guard(mutex, &ihid->cmd_lock) {
|
||||
/* Prepare reset command. Command register goes first. */
|
||||
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
|
||||
length += sizeof(__le16);
|
||||
/* Next is RESET command itself */
|
||||
length += i2c_hid_encode_command(ihid->cmdbuf + length,
|
||||
I2C_HID_OPCODE_RESET, 0, 0);
|
||||
|
||||
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
|
||||
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
|
||||
|
||||
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to reset device: %d\n", ret);
|
||||
goto err_clear_reset;
|
||||
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to reset device: %d\n", ret);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clear_reset:
|
||||
/* Clean up if sending reset command failed */
|
||||
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
|
||||
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
|
||||
return ret;
|
||||
@ -1200,6 +1211,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
|
||||
ihid->is_panel_follower = drm_is_panel_follower(&client->dev);
|
||||
|
||||
init_waitqueue_head(&ihid->wait);
|
||||
mutex_init(&ihid->cmd_lock);
|
||||
mutex_init(&ihid->reset_lock);
|
||||
INIT_WORK(&ihid->panel_follower_prepare_work, ihid_core_panel_prepare_work);
|
||||
|
||||
|
@ -152,6 +152,13 @@ static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = {
|
||||
.main_supply_name = "vcc33",
|
||||
};
|
||||
|
||||
static const struct elan_i2c_hid_chip_data elan_ekth6a12nay_chip_data = {
|
||||
.post_power_delay_ms = 10,
|
||||
.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,
|
||||
@ -174,6 +181,7 @@ static const struct elan_i2c_hid_chip_data ilitek_ili2901_chip_data = {
|
||||
|
||||
static const struct of_device_id elan_i2c_hid_of_match[] = {
|
||||
{ .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data },
|
||||
{ .compatible = "elan,ekth6a12nay", .data = &elan_ekth6a12nay_chip_data },
|
||||
{ .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data },
|
||||
{ .compatible = "ilitek,ili2901", .data = &ilitek_ili2901_chip_data },
|
||||
{ }
|
||||
|
@ -28,11 +28,14 @@ enum ishtp_driver_data_index {
|
||||
ISHTP_DRIVER_DATA_LNL_M,
|
||||
};
|
||||
|
||||
#define ISH_FW_FILENAME_LNL_M "intel/ish/ish_lnlm.bin"
|
||||
#define ISH_FW_GEN_LNL_M "lnlm"
|
||||
|
||||
#define ISH_FIRMWARE_PATH(gen) "intel/ish/ish_" gen ".bin"
|
||||
#define ISH_FIRMWARE_PATH_ALL "intel/ish/ish_*.bin"
|
||||
|
||||
static struct ishtp_driver_data ishtp_driver_data[] = {
|
||||
[ISHTP_DRIVER_DATA_LNL_M] = {
|
||||
.fw_filename = ISH_FW_FILENAME_LNL_M,
|
||||
.fw_generation = ISH_FW_GEN_LNL_M,
|
||||
},
|
||||
};
|
||||
|
||||
@ -397,4 +400,5 @@ MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_FIRMWARE(ISH_FW_FILENAME_LNL_M);
|
||||
MODULE_FIRMWARE(ISH_FIRMWARE_PATH(ISH_FW_GEN_LNL_M));
|
||||
MODULE_FIRMWARE(ISH_FIRMWARE_PATH_ALL);
|
||||
|
@ -46,7 +46,6 @@ struct ishtp_cl_device {
|
||||
};
|
||||
|
||||
int ishtp_bus_new_client(struct ishtp_device *dev);
|
||||
void ishtp_remove_all_clients(struct ishtp_device *dev);
|
||||
int ishtp_cl_device_bind(struct ishtp_cl *cl);
|
||||
void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device);
|
||||
|
||||
|
@ -109,7 +109,6 @@ struct ishtp_cl {
|
||||
};
|
||||
|
||||
/* Client connection managenment internal functions */
|
||||
int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, guid_t *uuid);
|
||||
int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id);
|
||||
void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl);
|
||||
void recv_ishtp_cl_msg(struct ishtp_device *dev,
|
||||
|
@ -129,13 +129,15 @@ struct ishtp_hw_ops {
|
||||
* ISHTP device instance. It allows for the storage of data that is unique to
|
||||
* a particular driver or hardware variant.
|
||||
*
|
||||
* @fw_filename: The firmware filename associated with a specific hardware
|
||||
* @fw_generation: The generation name associated with a specific hardware
|
||||
* variant of the Intel Integrated Sensor Hub (ISH). This allows
|
||||
* the driver to load the correct firmware based on the device's
|
||||
* hardware variant.
|
||||
* hardware variant. For example, "lnlm" for the Lunar Lake-M
|
||||
* platform. The generation name must not exceed 8 characters
|
||||
* in length.
|
||||
*/
|
||||
struct ishtp_driver_data {
|
||||
char *fw_filename;
|
||||
char *fw_generation;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -35,14 +35,17 @@
|
||||
|
||||
#include <linux/cacheflush.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
@ -192,6 +195,119 @@ static int prepare_dma_bufs(struct ishtp_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ISH_FW_FILE_VENDOR_NAME_SKU_FMT "intel/ish/ish_%s_%08x_%08x_%08x.bin"
|
||||
#define ISH_FW_FILE_VENDOR_SKU_FMT "intel/ish/ish_%s_%08x_%08x.bin"
|
||||
#define ISH_FW_FILE_VENDOR_NAME_FMT "intel/ish/ish_%s_%08x_%08x.bin"
|
||||
#define ISH_FW_FILE_VENDOR_FMT "intel/ish/ish_%s_%08x.bin"
|
||||
#define ISH_FW_FILE_DEFAULT_FMT "intel/ish/ish_%s.bin"
|
||||
|
||||
#define ISH_FW_FILENAME_LEN_MAX 56
|
||||
|
||||
#define ISH_CRC_INIT (~0u)
|
||||
#define ISH_CRC_XOROUT (~0u)
|
||||
|
||||
static int _request_ish_firmware(const struct firmware **firmware_p,
|
||||
const char *name, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "Try to load firmware: %s\n", name);
|
||||
ret = firmware_request_nowarn(firmware_p, name, dev);
|
||||
if (!ret)
|
||||
dev_info(dev, "load firmware: %s\n", name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* request_ish_firmware() - Request and load the ISH firmware.
|
||||
* @firmware_p: Pointer to the firmware image.
|
||||
* @dev: Device for which firmware is being requested.
|
||||
*
|
||||
* This function attempts to load the Integrated Sensor Hub (ISH) firmware
|
||||
* for the given device in the following order, prioritizing custom firmware
|
||||
* with more precise matching patterns:
|
||||
*
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}_$(PRODUCT_NAME_CRC32)_${PRODUCT_SKU_CRC32}.bin
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}_${PRODUCT_SKU_CRC32}.bin
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}_$(PRODUCT_NAME_CRC32).bin
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}.bin
|
||||
* ish_${fw_generation}.bin
|
||||
*
|
||||
* The driver will load the first matching firmware and skip the rest. If no
|
||||
* matching firmware is found, it will proceed to the next pattern in the
|
||||
* specified order. If all searches fail, the default Intel firmware, listed
|
||||
* last in the order above, will be loaded.
|
||||
*
|
||||
* The firmware file name is constructed using CRC32 checksums of strings.
|
||||
* This is done to create a valid file name that does not contain spaces
|
||||
* or special characters which may be present in the original strings.
|
||||
*
|
||||
* The CRC-32 algorithm uses the following parameters:
|
||||
* Poly: 0x04C11DB7
|
||||
* Init: 0xFFFFFFFF
|
||||
* RefIn: true
|
||||
* RefOut: true
|
||||
* XorOut: 0xFFFFFFFF
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int request_ish_firmware(const struct firmware **firmware_p,
|
||||
struct device *dev)
|
||||
{
|
||||
const char *gen, *sys_vendor, *product_name, *product_sku;
|
||||
struct ishtp_device *ishtp = dev_get_drvdata(dev);
|
||||
u32 vendor_crc, name_crc, sku_crc;
|
||||
char filename[ISH_FW_FILENAME_LEN_MAX];
|
||||
int ret;
|
||||
|
||||
gen = ishtp->driver_data->fw_generation;
|
||||
sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
|
||||
product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||
product_sku = dmi_get_system_info(DMI_PRODUCT_SKU);
|
||||
|
||||
if (sys_vendor)
|
||||
vendor_crc = crc32(ISH_CRC_INIT, sys_vendor, strlen(sys_vendor)) ^ ISH_CRC_XOROUT;
|
||||
if (product_name)
|
||||
name_crc = crc32(ISH_CRC_INIT, product_name, strlen(product_name)) ^ ISH_CRC_XOROUT;
|
||||
if (product_sku)
|
||||
sku_crc = crc32(ISH_CRC_INIT, product_sku, strlen(product_sku)) ^ ISH_CRC_XOROUT;
|
||||
|
||||
if (sys_vendor && product_name && product_sku) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_NAME_SKU_FMT, gen,
|
||||
vendor_crc, name_crc, sku_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sys_vendor && product_sku) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_SKU_FMT, gen, vendor_crc,
|
||||
sku_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sys_vendor && product_name) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_NAME_FMT, gen, vendor_crc,
|
||||
name_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sys_vendor) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_FMT, gen, vendor_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_DEFAULT_FMT, gen);
|
||||
return _request_ish_firmware(firmware_p, filename, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_loader_work() - Load the ISHTP firmware
|
||||
* @work: The work structure
|
||||
@ -220,7 +336,6 @@ void ishtp_loader_work(struct work_struct *work)
|
||||
struct loader_xfer_query query = { .header = cpu_to_le32(query_hdr.val32), };
|
||||
struct loader_start start = { .header = cpu_to_le32(start_hdr.val32), };
|
||||
union loader_recv_message recv_msg;
|
||||
char *filename = dev->driver_data->fw_filename;
|
||||
const struct firmware *ish_fw;
|
||||
void *dma_bufs[FRAGMENT_MAX_NUM] = {};
|
||||
u32 fragment_size;
|
||||
@ -228,9 +343,9 @@ void ishtp_loader_work(struct work_struct *work)
|
||||
int retry = ISHTP_LOADER_RETRY_TIMES;
|
||||
int rv;
|
||||
|
||||
rv = request_firmware(&ish_fw, filename, dev->devc);
|
||||
rv = request_ish_firmware(&ish_fw, dev->devc);
|
||||
if (rv < 0) {
|
||||
dev_err(dev->devc, "request firmware %s failed:%d\n", filename, rv);
|
||||
dev_err(dev->devc, "request ISH firmware failed:%d\n", rv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1906,11 +1906,12 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
|
||||
if ((code == ABS_X || code == ABS_Y) && !resolution) {
|
||||
resolution = WACOM_INTUOS_RES;
|
||||
hid_warn(input,
|
||||
"Wacom usage (%d) missing resolution \n",
|
||||
code);
|
||||
"Using default resolution for axis type 0x%x code 0x%x\n",
|
||||
type, code);
|
||||
}
|
||||
input_abs_set_res(input, code, resolution);
|
||||
break;
|
||||
case EV_REL:
|
||||
case EV_KEY:
|
||||
case EV_MSC:
|
||||
case EV_SW:
|
||||
@ -2047,7 +2048,23 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
|
||||
features->device_type |= WACOM_DEVICETYPE_PAD;
|
||||
break;
|
||||
case WACOM_HID_WD_TOUCHRING:
|
||||
wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
|
||||
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
|
||||
wacom_wac->relring_count++;
|
||||
if (wacom_wac->relring_count == 1) {
|
||||
wacom_map_usage(input, usage, field, EV_REL, REL_WHEEL_HI_RES, 0);
|
||||
set_bit(REL_WHEEL, input->relbit);
|
||||
}
|
||||
else if (wacom_wac->relring_count == 2) {
|
||||
wacom_map_usage(input, usage, field, EV_REL, REL_HWHEEL_HI_RES, 0);
|
||||
set_bit(REL_HWHEEL, input->relbit);
|
||||
}
|
||||
} else {
|
||||
wacom_wac->absring_count++;
|
||||
if (wacom_wac->absring_count == 1)
|
||||
wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
|
||||
else if (wacom_wac->absring_count == 2)
|
||||
wacom_map_usage(input, usage, field, EV_ABS, ABS_THROTTLE, 0);
|
||||
}
|
||||
features->device_type |= WACOM_DEVICETYPE_PAD;
|
||||
break;
|
||||
case WACOM_HID_WD_TOUCHRINGSTATUS:
|
||||
@ -2112,7 +2129,10 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
|
||||
return;
|
||||
|
||||
if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) {
|
||||
if (usage->hid != WACOM_HID_WD_TOUCHRING)
|
||||
bool is_abs_touchring = usage->hid == WACOM_HID_WD_TOUCHRING &&
|
||||
!(field->flags & HID_MAIN_ITEM_RELATIVE);
|
||||
|
||||
if (!is_abs_touchring)
|
||||
wacom_wac->hid_data.inrange_state |= value;
|
||||
}
|
||||
|
||||
@ -2165,6 +2185,52 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
|
||||
hdev->product == 0x3AA)
|
||||
value = wacom_offset_rotation(input, usage, value, 1, 2);
|
||||
}
|
||||
else if (field->flags & HID_MAIN_ITEM_RELATIVE) {
|
||||
int hires_value = value * 120 / usage->resolution_multiplier;
|
||||
int *ring_value;
|
||||
int lowres_code;
|
||||
|
||||
if (usage->code == REL_WHEEL_HI_RES) {
|
||||
/* We must invert the sign for vertical
|
||||
* relative scrolling. Clockwise
|
||||
* rotation produces positive values
|
||||
* from HW, but userspace treats
|
||||
* positive REL_WHEEL as a scroll *up*!
|
||||
*/
|
||||
hires_value = -hires_value;
|
||||
ring_value = &wacom_wac->hid_data.ring_value;
|
||||
lowres_code = REL_WHEEL;
|
||||
}
|
||||
else if (usage->code == REL_HWHEEL_HI_RES) {
|
||||
/* No need to invert the sign for
|
||||
* horizontal relative scrolling.
|
||||
* Clockwise rotation produces positive
|
||||
* values from HW and userspace treats
|
||||
* positive REL_HWHEEL as a scroll
|
||||
* right.
|
||||
*/
|
||||
ring_value = &wacom_wac->hid_data.ring2_value;
|
||||
lowres_code = REL_HWHEEL;
|
||||
}
|
||||
else {
|
||||
hid_err(wacom->hdev, "unrecognized relative wheel with code %d\n",
|
||||
usage->code);
|
||||
break;
|
||||
}
|
||||
|
||||
value = hires_value;
|
||||
*ring_value += hires_value;
|
||||
|
||||
/* Emulate a legacy wheel click for every 120
|
||||
* units of hi-res travel.
|
||||
*/
|
||||
if (*ring_value >= 120 || *ring_value <= -120) {
|
||||
int clicks = *ring_value / 120;
|
||||
|
||||
input_event(input, usage->type, lowres_code, clicks);
|
||||
*ring_value -= clicks * 120;
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = wacom_offset_rotation(input, usage, value, 1, 4);
|
||||
}
|
||||
@ -2322,6 +2388,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
|
||||
wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS3, 0);
|
||||
features->quirks &= ~WACOM_QUIRK_PEN_BUTTON3;
|
||||
break;
|
||||
case WACOM_HID_WD_SEQUENCENUMBER:
|
||||
wacom_wac->hid_data.sequence_number = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2446,9 +2515,15 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
|
||||
wacom_wac->hid_data.barrelswitch3 = value;
|
||||
return;
|
||||
case WACOM_HID_WD_SEQUENCENUMBER:
|
||||
if (wacom_wac->hid_data.sequence_number != value)
|
||||
hid_warn(hdev, "Dropped %hu packets", (unsigned short)(value - wacom_wac->hid_data.sequence_number));
|
||||
if (wacom_wac->hid_data.sequence_number != value &&
|
||||
wacom_wac->hid_data.sequence_number >= 0) {
|
||||
int sequence_size = field->logical_maximum - field->logical_minimum + 1;
|
||||
int drop_count = (value - wacom_wac->hid_data.sequence_number) % sequence_size;
|
||||
hid_warn(hdev, "Dropped %d packets", drop_count);
|
||||
}
|
||||
wacom_wac->hid_data.sequence_number = value + 1;
|
||||
if (wacom_wac->hid_data.sequence_number > field->logical_maximum)
|
||||
wacom_wac->hid_data.sequence_number = field->logical_minimum;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -312,6 +312,8 @@ struct hid_data {
|
||||
int width;
|
||||
int height;
|
||||
int id;
|
||||
int ring_value;
|
||||
int ring2_value;
|
||||
int cc_report;
|
||||
int cc_index;
|
||||
int cc_value_index;
|
||||
@ -324,7 +326,7 @@ struct hid_data {
|
||||
int bat_connected;
|
||||
int ps_connected;
|
||||
bool pad_input_event_flag;
|
||||
unsigned short sequence_number;
|
||||
int sequence_number;
|
||||
ktime_t time_delayed;
|
||||
};
|
||||
|
||||
@ -355,6 +357,8 @@ struct wacom_wac {
|
||||
int num_contacts_left;
|
||||
u8 bt_features;
|
||||
u8 bt_high_speed;
|
||||
u8 absring_count;
|
||||
u8 relring_count;
|
||||
int mode_report;
|
||||
int mode_value;
|
||||
struct hid_data hid_data;
|
||||
|
@ -46,7 +46,7 @@ struct hid_item {
|
||||
__s16 s16;
|
||||
__u32 u32;
|
||||
__s32 s32;
|
||||
__u8 *longdata;
|
||||
const __u8 *longdata;
|
||||
} data;
|
||||
};
|
||||
|
||||
@ -600,9 +600,9 @@ struct hid_driver;
|
||||
struct hid_ll_driver;
|
||||
|
||||
struct hid_device { /* device report descriptor */
|
||||
__u8 *dev_rdesc;
|
||||
const __u8 *dev_rdesc;
|
||||
unsigned dev_rsize;
|
||||
__u8 *rdesc;
|
||||
const __u8 *rdesc;
|
||||
unsigned rsize;
|
||||
struct hid_collection *collection; /* List of HID collections */
|
||||
unsigned collection_size; /* Number of allocated hid_collections */
|
||||
@ -822,7 +822,7 @@ struct hid_driver {
|
||||
struct hid_usage *usage, __s32 value);
|
||||
void (*report)(struct hid_device *hdev, struct hid_report *report);
|
||||
|
||||
__u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf,
|
||||
const __u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf,
|
||||
unsigned int *size);
|
||||
|
||||
int (*input_mapping)(struct hid_device *hdev,
|
||||
@ -940,6 +940,8 @@ extern void hidinput_report_event(struct hid_device *hid, struct hid_report *rep
|
||||
extern int hidinput_connect(struct hid_device *hid, unsigned int force);
|
||||
extern void hidinput_disconnect(struct hid_device *);
|
||||
|
||||
struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type,
|
||||
unsigned int application, unsigned int usage);
|
||||
int hid_set_field(struct hid_field *, unsigned, __s32);
|
||||
int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
|
||||
int interrupt);
|
||||
@ -953,7 +955,7 @@ struct hid_device *hid_allocate_device(void);
|
||||
struct hid_report *hid_register_report(struct hid_device *device,
|
||||
enum hid_report_type type, unsigned int id,
|
||||
unsigned int application);
|
||||
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
|
||||
int hid_parse_report(struct hid_device *hid, const __u8 *start, unsigned size);
|
||||
struct hid_report *hid_validate_values(struct hid_device *hid,
|
||||
enum hid_report_type type, unsigned int id,
|
||||
unsigned int field_index,
|
||||
|
@ -212,7 +212,7 @@ int hid_bpf_connect_device(struct hid_device *hdev);
|
||||
void hid_bpf_disconnect_device(struct hid_device *hdev);
|
||||
void hid_bpf_destroy_device(struct hid_device *hid);
|
||||
int hid_bpf_device_init(struct hid_device *hid);
|
||||
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size);
|
||||
u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size);
|
||||
#else /* CONFIG_HID_BPF */
|
||||
static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type,
|
||||
u8 *data, u32 *size, int interrupt,
|
||||
|
@ -32,6 +32,7 @@ struct hidraw_list {
|
||||
struct hidraw *hidraw;
|
||||
struct list_head node;
|
||||
struct mutex read_mutex;
|
||||
bool revoked;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HIDRAW
|
||||
|
@ -46,6 +46,7 @@ struct hidraw_devinfo {
|
||||
/* The first byte of SOUTPUT and GOUTPUT is the report number */
|
||||
#define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len)
|
||||
#define HIDIOCGOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0C, len)
|
||||
#define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */
|
||||
|
||||
#define HIDRAW_FIRST_MINOR 0
|
||||
#define HIDRAW_MAX_DEVICES 64
|
||||
|
1
tools/testing/selftests/hid/.gitignore
vendored
1
tools/testing/selftests/hid/.gitignore
vendored
@ -2,4 +2,5 @@ bpftool
|
||||
*.skel.h
|
||||
/tools
|
||||
hid_bpf
|
||||
hidraw
|
||||
results
|
||||
|
@ -32,7 +32,7 @@ CFLAGS += -Wno-unused-command-line-argument
|
||||
endif
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_GEN_PROGS = hid_bpf
|
||||
TEST_GEN_PROGS = hid_bpf hidraw
|
||||
|
||||
# Emit succinct information message describing current building step
|
||||
# $1 - generic step name (e.g., CC, LINK, etc);
|
||||
|
@ -1,93 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022 Red Hat */
|
||||
/* Copyright (c) 2022-2024 Red Hat */
|
||||
#include "hid.skel.h"
|
||||
|
||||
#include "../kselftest_harness.h"
|
||||
|
||||
#include "hid_common.h"
|
||||
#include <bpf/bpf.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <dirent.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/uhid.h>
|
||||
|
||||
#define SHOW_UHID_DEBUG 0
|
||||
|
||||
#define min(a, b) \
|
||||
({ __typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
|
||||
static unsigned char rdesc[] = {
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x09, 0x21, /* Usage (Vendor Usage 0x21) */
|
||||
0xa1, 0x01, /* COLLECTION (Application) */
|
||||
0x09, 0x01, /* Usage (Vendor Usage 0x01) */
|
||||
0xa1, 0x00, /* COLLECTION (Physical) */
|
||||
0x85, 0x02, /* REPORT_ID (2) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (1) */
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0xff, /* LOGICAL_MAXIMUM (255) */
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
0x09, 0x01, /* Usage (Vendor Usage 0x01) */
|
||||
0xa1, 0x00, /* COLLECTION (Physical) */
|
||||
0x85, 0x01, /* REPORT_ID (1) */
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (1) */
|
||||
0x29, 0x03, /* USAGE_MAXIMUM (3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x81, 0x01, /* INPUT (Cnst,Var,Abs) */
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
|
||||
0x09, 0x30, /* USAGE (X) */
|
||||
0x09, 0x31, /* USAGE (Y) */
|
||||
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
|
||||
0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */
|
||||
0x75, 0x10, /* REPORT_SIZE (16) */
|
||||
0x95, 0x02, /* REPORT_COUNT (2) */
|
||||
0x81, 0x06, /* INPUT (Data,Var,Rel) */
|
||||
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (1) */
|
||||
0x29, 0x03, /* USAGE_MAXIMUM (3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x91, 0x02, /* Output (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x91, 0x01, /* Output (Cnst,Var,Abs) */
|
||||
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x19, 0x06, /* USAGE_MINIMUM (6) */
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (8) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0xb1, 0x02, /* Feature (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x91, 0x01, /* Output (Cnst,Var,Abs) */
|
||||
|
||||
0xc0, /* END_COLLECTION */
|
||||
0xc0, /* END_COLLECTION */
|
||||
};
|
||||
|
||||
static __u8 feature_data[] = { 1, 2 };
|
||||
|
||||
struct attach_prog_args {
|
||||
int prog_fd;
|
||||
@ -105,354 +20,6 @@ struct hid_hw_request_syscall_args {
|
||||
__u8 request_type;
|
||||
};
|
||||
|
||||
#define ASSERT_OK(data) ASSERT_FALSE(data)
|
||||
#define ASSERT_OK_PTR(ptr) ASSERT_NE(NULL, ptr)
|
||||
|
||||
#define UHID_LOG(fmt, ...) do { \
|
||||
if (SHOW_UHID_DEBUG) \
|
||||
TH_LOG(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static pthread_mutex_t uhid_output_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t uhid_output_cond = PTHREAD_COND_INITIALIZER;
|
||||
static unsigned char output_report[10];
|
||||
|
||||
/* no need to protect uhid_stopped, only one thread accesses it */
|
||||
static bool uhid_stopped;
|
||||
|
||||
static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uhid_event *ev)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = write(fd, ev, sizeof(*ev));
|
||||
if (ret < 0) {
|
||||
TH_LOG("Cannot write to uhid: %m");
|
||||
return -errno;
|
||||
} else if (ret != sizeof(*ev)) {
|
||||
TH_LOG("Wrong size written to uhid: %zd != %zu",
|
||||
ret, sizeof(ev));
|
||||
return -EFAULT;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
char buf[25];
|
||||
|
||||
sprintf(buf, "test-uhid-device-%d", rand_nb);
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_CREATE;
|
||||
strcpy((char *)ev.u.create.name, buf);
|
||||
ev.u.create.rd_data = rdesc;
|
||||
ev.u.create.rd_size = sizeof(rdesc);
|
||||
ev.u.create.bus = BUS_USB;
|
||||
ev.u.create.vendor = 0x0001;
|
||||
ev.u.create.product = 0x0a37;
|
||||
ev.u.create.version = 0;
|
||||
ev.u.create.country = 0;
|
||||
|
||||
sprintf(buf, "%d", rand_nb);
|
||||
strcpy((char *)ev.u.create.phys, buf);
|
||||
|
||||
return uhid_write(_metadata, fd, &ev);
|
||||
}
|
||||
|
||||
static void uhid_destroy(struct __test_metadata *_metadata, int fd)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_DESTROY;
|
||||
|
||||
uhid_write(_metadata, fd, &ev);
|
||||
}
|
||||
|
||||
static int uhid_event(struct __test_metadata *_metadata, int fd)
|
||||
{
|
||||
struct uhid_event ev, answer;
|
||||
ssize_t ret;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ret = read(fd, &ev, sizeof(ev));
|
||||
if (ret == 0) {
|
||||
UHID_LOG("Read HUP on uhid-cdev");
|
||||
return -EFAULT;
|
||||
} else if (ret < 0) {
|
||||
UHID_LOG("Cannot read uhid-cdev: %m");
|
||||
return -errno;
|
||||
} else if (ret != sizeof(ev)) {
|
||||
UHID_LOG("Invalid size read from uhid-dev: %zd != %zu",
|
||||
ret, sizeof(ev));
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (ev.type) {
|
||||
case UHID_START:
|
||||
pthread_mutex_lock(&uhid_started_mtx);
|
||||
pthread_cond_signal(&uhid_started);
|
||||
pthread_mutex_unlock(&uhid_started_mtx);
|
||||
|
||||
UHID_LOG("UHID_START from uhid-dev");
|
||||
break;
|
||||
case UHID_STOP:
|
||||
uhid_stopped = true;
|
||||
|
||||
UHID_LOG("UHID_STOP from uhid-dev");
|
||||
break;
|
||||
case UHID_OPEN:
|
||||
UHID_LOG("UHID_OPEN from uhid-dev");
|
||||
break;
|
||||
case UHID_CLOSE:
|
||||
UHID_LOG("UHID_CLOSE from uhid-dev");
|
||||
break;
|
||||
case UHID_OUTPUT:
|
||||
UHID_LOG("UHID_OUTPUT from uhid-dev");
|
||||
|
||||
pthread_mutex_lock(&uhid_output_mtx);
|
||||
memcpy(output_report,
|
||||
ev.u.output.data,
|
||||
min(ev.u.output.size, sizeof(output_report)));
|
||||
pthread_cond_signal(&uhid_output_cond);
|
||||
pthread_mutex_unlock(&uhid_output_mtx);
|
||||
break;
|
||||
case UHID_GET_REPORT:
|
||||
UHID_LOG("UHID_GET_REPORT from uhid-dev");
|
||||
|
||||
answer.type = UHID_GET_REPORT_REPLY;
|
||||
answer.u.get_report_reply.id = ev.u.get_report.id;
|
||||
answer.u.get_report_reply.err = ev.u.get_report.rnum == 1 ? 0 : -EIO;
|
||||
answer.u.get_report_reply.size = sizeof(feature_data);
|
||||
memcpy(answer.u.get_report_reply.data, feature_data, sizeof(feature_data));
|
||||
|
||||
uhid_write(_metadata, fd, &answer);
|
||||
|
||||
break;
|
||||
case UHID_SET_REPORT:
|
||||
UHID_LOG("UHID_SET_REPORT from uhid-dev");
|
||||
break;
|
||||
default:
|
||||
TH_LOG("Invalid event from uhid-dev: %u", ev.type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct uhid_thread_args {
|
||||
int fd;
|
||||
struct __test_metadata *_metadata;
|
||||
};
|
||||
static void *uhid_read_events_thread(void *arg)
|
||||
{
|
||||
struct uhid_thread_args *args = (struct uhid_thread_args *)arg;
|
||||
struct __test_metadata *_metadata = args->_metadata;
|
||||
struct pollfd pfds[1];
|
||||
int fd = args->fd;
|
||||
int ret = 0;
|
||||
|
||||
pfds[0].fd = fd;
|
||||
pfds[0].events = POLLIN;
|
||||
|
||||
uhid_stopped = false;
|
||||
|
||||
while (!uhid_stopped) {
|
||||
ret = poll(pfds, 1, 100);
|
||||
if (ret < 0) {
|
||||
TH_LOG("Cannot poll for fds: %m");
|
||||
break;
|
||||
}
|
||||
if (pfds[0].revents & POLLIN) {
|
||||
ret = uhid_event(_metadata, fd);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (void *)(long)ret;
|
||||
}
|
||||
|
||||
static int uhid_start_listener(struct __test_metadata *_metadata, pthread_t *tid, int uhid_fd)
|
||||
{
|
||||
struct uhid_thread_args args = {
|
||||
.fd = uhid_fd,
|
||||
._metadata = _metadata,
|
||||
};
|
||||
int err;
|
||||
|
||||
pthread_mutex_lock(&uhid_started_mtx);
|
||||
err = pthread_create(tid, NULL, uhid_read_events_thread, (void *)&args);
|
||||
ASSERT_EQ(0, err) {
|
||||
TH_LOG("Could not start the uhid thread: %d", err);
|
||||
pthread_mutex_unlock(&uhid_started_mtx);
|
||||
close(uhid_fd);
|
||||
return -EIO;
|
||||
}
|
||||
pthread_cond_wait(&uhid_started, &uhid_started_mtx);
|
||||
pthread_mutex_unlock(&uhid_started_mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf, size_t size)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
|
||||
if (size > sizeof(ev.u.input.data))
|
||||
return -E2BIG;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_INPUT2;
|
||||
ev.u.input2.size = size;
|
||||
|
||||
memcpy(ev.u.input2.data, buf, size);
|
||||
|
||||
return uhid_write(_metadata, fd, &ev);
|
||||
}
|
||||
|
||||
static int setup_uhid(struct __test_metadata *_metadata, int rand_nb)
|
||||
{
|
||||
int fd;
|
||||
const char *path = "/dev/uhid";
|
||||
int ret;
|
||||
|
||||
fd = open(path, O_RDWR | O_CLOEXEC);
|
||||
ASSERT_GE(fd, 0) TH_LOG("open uhid-cdev failed; %d", fd);
|
||||
|
||||
ret = uhid_create(_metadata, fd, rand_nb);
|
||||
ASSERT_EQ(0, ret) {
|
||||
TH_LOG("create uhid device failed: %d", ret);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir)
|
||||
{
|
||||
const char *target = "0003:0001:0A37.*";
|
||||
char phys[512];
|
||||
char uevent[1024];
|
||||
char temp[512];
|
||||
int fd, nread;
|
||||
bool found = false;
|
||||
|
||||
if (fnmatch(target, dir->d_name, 0))
|
||||
return false;
|
||||
|
||||
/* we found the correct VID/PID, now check for phys */
|
||||
sprintf(uevent, "%s/%s/uevent", workdir, dir->d_name);
|
||||
|
||||
fd = open(uevent, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
sprintf(phys, "PHYS=%d", dev_id);
|
||||
|
||||
nread = read(fd, temp, ARRAY_SIZE(temp));
|
||||
if (nread > 0 && (strstr(temp, phys)) != NULL)
|
||||
found = true;
|
||||
|
||||
close(fd);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int get_hid_id(int dev_id)
|
||||
{
|
||||
const char *workdir = "/sys/devices/virtual/misc/uhid";
|
||||
const char *str_id;
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
int found = -1, attempts = 3;
|
||||
|
||||
/* it would be nice to be able to use nftw, but the no_alu32 target doesn't support it */
|
||||
|
||||
while (found < 0 && attempts > 0) {
|
||||
attempts--;
|
||||
d = opendir(workdir);
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (!match_sysfs_device(dev_id, workdir, dir))
|
||||
continue;
|
||||
|
||||
str_id = dir->d_name + sizeof("0003:0001:0A37.");
|
||||
found = (int)strtol(str_id, NULL, 16);
|
||||
|
||||
break;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
if (found < 0)
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int get_hidraw(int dev_id)
|
||||
{
|
||||
const char *workdir = "/sys/devices/virtual/misc/uhid";
|
||||
char sysfs[1024];
|
||||
DIR *d, *subd;
|
||||
struct dirent *dir, *subdir;
|
||||
int i, found = -1;
|
||||
|
||||
/* retry 5 times in case the system is loaded */
|
||||
for (i = 5; i > 0; i--) {
|
||||
usleep(10);
|
||||
d = opendir(workdir);
|
||||
|
||||
if (!d)
|
||||
continue;
|
||||
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (!match_sysfs_device(dev_id, workdir, dir))
|
||||
continue;
|
||||
|
||||
sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name);
|
||||
|
||||
subd = opendir(sysfs);
|
||||
if (!subd)
|
||||
continue;
|
||||
|
||||
while ((subdir = readdir(subd)) != NULL) {
|
||||
if (fnmatch("hidraw*", subdir->d_name, 0))
|
||||
continue;
|
||||
|
||||
found = atoi(subdir->d_name + strlen("hidraw"));
|
||||
}
|
||||
|
||||
closedir(subd);
|
||||
|
||||
if (found > 0)
|
||||
break;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int open_hidraw(int dev_id)
|
||||
{
|
||||
int hidraw_number;
|
||||
char hidraw_path[64] = { 0 };
|
||||
|
||||
hidraw_number = get_hidraw(dev_id);
|
||||
if (hidraw_number < 0)
|
||||
return hidraw_number;
|
||||
|
||||
/* open hidraw node to check the other side of the pipe */
|
||||
sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number);
|
||||
return open(hidraw_path, O_RDWR | O_NONBLOCK);
|
||||
}
|
||||
|
||||
FIXTURE(hid_bpf) {
|
||||
int dev_id;
|
||||
int uhid_fd;
|
||||
|
436
tools/testing/selftests/hid/hid_common.h
Normal file
436
tools/testing/selftests/hid/hid_common.h
Normal file
@ -0,0 +1,436 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2022-2024 Red Hat */
|
||||
|
||||
#include "../kselftest_harness.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <dirent.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <linux/uhid.h>
|
||||
|
||||
#define SHOW_UHID_DEBUG 0
|
||||
|
||||
#define min(a, b) \
|
||||
({ __typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
|
||||
static unsigned char rdesc[] = {
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x09, 0x21, /* Usage (Vendor Usage 0x21) */
|
||||
0xa1, 0x01, /* COLLECTION (Application) */
|
||||
0x09, 0x01, /* Usage (Vendor Usage 0x01) */
|
||||
0xa1, 0x00, /* COLLECTION (Physical) */
|
||||
0x85, 0x02, /* REPORT_ID (2) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (1) */
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0xff, /* LOGICAL_MAXIMUM (255) */
|
||||
0x95, 0x08, /* REPORT_COUNT (8) */
|
||||
0x75, 0x08, /* REPORT_SIZE (8) */
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */
|
||||
0xc0, /* END_COLLECTION */
|
||||
0x09, 0x01, /* Usage (Vendor Usage 0x01) */
|
||||
0xa1, 0x00, /* COLLECTION (Physical) */
|
||||
0x85, 0x01, /* REPORT_ID (1) */
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (1) */
|
||||
0x29, 0x03, /* USAGE_MAXIMUM (3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x81, 0x02, /* INPUT (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x81, 0x01, /* INPUT (Cnst,Var,Abs) */
|
||||
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
|
||||
0x09, 0x30, /* USAGE (X) */
|
||||
0x09, 0x31, /* USAGE (Y) */
|
||||
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
|
||||
0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */
|
||||
0x75, 0x10, /* REPORT_SIZE (16) */
|
||||
0x95, 0x02, /* REPORT_COUNT (2) */
|
||||
0x81, 0x06, /* INPUT (Data,Var,Rel) */
|
||||
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x19, 0x01, /* USAGE_MINIMUM (1) */
|
||||
0x29, 0x03, /* USAGE_MAXIMUM (3) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0x91, 0x02, /* Output (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x91, 0x01, /* Output (Cnst,Var,Abs) */
|
||||
|
||||
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
|
||||
0x19, 0x06, /* USAGE_MINIMUM (6) */
|
||||
0x29, 0x08, /* USAGE_MAXIMUM (8) */
|
||||
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
|
||||
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
|
||||
0x95, 0x03, /* REPORT_COUNT (3) */
|
||||
0x75, 0x01, /* REPORT_SIZE (1) */
|
||||
0xb1, 0x02, /* Feature (Data,Var,Abs) */
|
||||
0x95, 0x01, /* REPORT_COUNT (1) */
|
||||
0x75, 0x05, /* REPORT_SIZE (5) */
|
||||
0x91, 0x01, /* Output (Cnst,Var,Abs) */
|
||||
|
||||
0xc0, /* END_COLLECTION */
|
||||
0xc0, /* END_COLLECTION */
|
||||
};
|
||||
|
||||
static __u8 feature_data[] = { 1, 2 };
|
||||
|
||||
#define ASSERT_OK(data) ASSERT_FALSE(data)
|
||||
#define ASSERT_OK_PTR(ptr) ASSERT_NE(NULL, ptr)
|
||||
|
||||
#define UHID_LOG(fmt, ...) do { \
|
||||
if (SHOW_UHID_DEBUG) \
|
||||
TH_LOG(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static pthread_mutex_t uhid_output_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t uhid_output_cond = PTHREAD_COND_INITIALIZER;
|
||||
static unsigned char output_report[10];
|
||||
|
||||
/* no need to protect uhid_stopped, only one thread accesses it */
|
||||
static bool uhid_stopped;
|
||||
|
||||
static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uhid_event *ev)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = write(fd, ev, sizeof(*ev));
|
||||
if (ret < 0) {
|
||||
TH_LOG("Cannot write to uhid: %m");
|
||||
return -errno;
|
||||
} else if (ret != sizeof(*ev)) {
|
||||
TH_LOG("Wrong size written to uhid: %zd != %zu",
|
||||
ret, sizeof(ev));
|
||||
return -EFAULT;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
char buf[25];
|
||||
|
||||
sprintf(buf, "test-uhid-device-%d", rand_nb);
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_CREATE;
|
||||
strcpy((char *)ev.u.create.name, buf);
|
||||
ev.u.create.rd_data = rdesc;
|
||||
ev.u.create.rd_size = sizeof(rdesc);
|
||||
ev.u.create.bus = BUS_USB;
|
||||
ev.u.create.vendor = 0x0001;
|
||||
ev.u.create.product = 0x0a37;
|
||||
ev.u.create.version = 0;
|
||||
ev.u.create.country = 0;
|
||||
|
||||
sprintf(buf, "%d", rand_nb);
|
||||
strcpy((char *)ev.u.create.phys, buf);
|
||||
|
||||
return uhid_write(_metadata, fd, &ev);
|
||||
}
|
||||
|
||||
static void uhid_destroy(struct __test_metadata *_metadata, int fd)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_DESTROY;
|
||||
|
||||
uhid_write(_metadata, fd, &ev);
|
||||
}
|
||||
|
||||
static int uhid_event(struct __test_metadata *_metadata, int fd)
|
||||
{
|
||||
struct uhid_event ev, answer;
|
||||
ssize_t ret;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ret = read(fd, &ev, sizeof(ev));
|
||||
if (ret == 0) {
|
||||
UHID_LOG("Read HUP on uhid-cdev");
|
||||
return -EFAULT;
|
||||
} else if (ret < 0) {
|
||||
UHID_LOG("Cannot read uhid-cdev: %m");
|
||||
return -errno;
|
||||
} else if (ret != sizeof(ev)) {
|
||||
UHID_LOG("Invalid size read from uhid-dev: %zd != %zu",
|
||||
ret, sizeof(ev));
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (ev.type) {
|
||||
case UHID_START:
|
||||
pthread_mutex_lock(&uhid_started_mtx);
|
||||
pthread_cond_signal(&uhid_started);
|
||||
pthread_mutex_unlock(&uhid_started_mtx);
|
||||
|
||||
UHID_LOG("UHID_START from uhid-dev");
|
||||
break;
|
||||
case UHID_STOP:
|
||||
uhid_stopped = true;
|
||||
|
||||
UHID_LOG("UHID_STOP from uhid-dev");
|
||||
break;
|
||||
case UHID_OPEN:
|
||||
UHID_LOG("UHID_OPEN from uhid-dev");
|
||||
break;
|
||||
case UHID_CLOSE:
|
||||
UHID_LOG("UHID_CLOSE from uhid-dev");
|
||||
break;
|
||||
case UHID_OUTPUT:
|
||||
UHID_LOG("UHID_OUTPUT from uhid-dev");
|
||||
|
||||
pthread_mutex_lock(&uhid_output_mtx);
|
||||
memcpy(output_report,
|
||||
ev.u.output.data,
|
||||
min(ev.u.output.size, sizeof(output_report)));
|
||||
pthread_cond_signal(&uhid_output_cond);
|
||||
pthread_mutex_unlock(&uhid_output_mtx);
|
||||
break;
|
||||
case UHID_GET_REPORT:
|
||||
UHID_LOG("UHID_GET_REPORT from uhid-dev");
|
||||
|
||||
answer.type = UHID_GET_REPORT_REPLY;
|
||||
answer.u.get_report_reply.id = ev.u.get_report.id;
|
||||
answer.u.get_report_reply.err = ev.u.get_report.rnum == 1 ? 0 : -EIO;
|
||||
answer.u.get_report_reply.size = sizeof(feature_data);
|
||||
memcpy(answer.u.get_report_reply.data, feature_data, sizeof(feature_data));
|
||||
|
||||
uhid_write(_metadata, fd, &answer);
|
||||
|
||||
break;
|
||||
case UHID_SET_REPORT:
|
||||
UHID_LOG("UHID_SET_REPORT from uhid-dev");
|
||||
break;
|
||||
default:
|
||||
TH_LOG("Invalid event from uhid-dev: %u", ev.type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct uhid_thread_args {
|
||||
int fd;
|
||||
struct __test_metadata *_metadata;
|
||||
};
|
||||
static void *uhid_read_events_thread(void *arg)
|
||||
{
|
||||
struct uhid_thread_args *args = (struct uhid_thread_args *)arg;
|
||||
struct __test_metadata *_metadata = args->_metadata;
|
||||
struct pollfd pfds[1];
|
||||
int fd = args->fd;
|
||||
int ret = 0;
|
||||
|
||||
pfds[0].fd = fd;
|
||||
pfds[0].events = POLLIN;
|
||||
|
||||
uhid_stopped = false;
|
||||
|
||||
while (!uhid_stopped) {
|
||||
ret = poll(pfds, 1, 100);
|
||||
if (ret < 0) {
|
||||
TH_LOG("Cannot poll for fds: %m");
|
||||
break;
|
||||
}
|
||||
if (pfds[0].revents & POLLIN) {
|
||||
ret = uhid_event(_metadata, fd);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (void *)(long)ret;
|
||||
}
|
||||
|
||||
static int uhid_start_listener(struct __test_metadata *_metadata, pthread_t *tid, int uhid_fd)
|
||||
{
|
||||
struct uhid_thread_args args = {
|
||||
.fd = uhid_fd,
|
||||
._metadata = _metadata,
|
||||
};
|
||||
int err;
|
||||
|
||||
pthread_mutex_lock(&uhid_started_mtx);
|
||||
err = pthread_create(tid, NULL, uhid_read_events_thread, (void *)&args);
|
||||
ASSERT_EQ(0, err) {
|
||||
TH_LOG("Could not start the uhid thread: %d", err);
|
||||
pthread_mutex_unlock(&uhid_started_mtx);
|
||||
close(uhid_fd);
|
||||
return -EIO;
|
||||
}
|
||||
pthread_cond_wait(&uhid_started, &uhid_started_mtx);
|
||||
pthread_mutex_unlock(&uhid_started_mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf, size_t size)
|
||||
{
|
||||
struct uhid_event ev;
|
||||
|
||||
if (size > sizeof(ev.u.input.data))
|
||||
return -E2BIG;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_INPUT2;
|
||||
ev.u.input2.size = size;
|
||||
|
||||
memcpy(ev.u.input2.data, buf, size);
|
||||
|
||||
return uhid_write(_metadata, fd, &ev);
|
||||
}
|
||||
|
||||
static int setup_uhid(struct __test_metadata *_metadata, int rand_nb)
|
||||
{
|
||||
int fd;
|
||||
const char *path = "/dev/uhid";
|
||||
int ret;
|
||||
|
||||
fd = open(path, O_RDWR | O_CLOEXEC);
|
||||
ASSERT_GE(fd, 0) TH_LOG("open uhid-cdev failed; %d", fd);
|
||||
|
||||
ret = uhid_create(_metadata, fd, rand_nb);
|
||||
ASSERT_EQ(0, ret) {
|
||||
TH_LOG("create uhid device failed: %d", ret);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir)
|
||||
{
|
||||
const char *target = "0003:0001:0A37.*";
|
||||
char phys[512];
|
||||
char uevent[1024];
|
||||
char temp[512];
|
||||
int fd, nread;
|
||||
bool found = false;
|
||||
|
||||
if (fnmatch(target, dir->d_name, 0))
|
||||
return false;
|
||||
|
||||
/* we found the correct VID/PID, now check for phys */
|
||||
sprintf(uevent, "%s/%s/uevent", workdir, dir->d_name);
|
||||
|
||||
fd = open(uevent, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
sprintf(phys, "PHYS=%d", dev_id);
|
||||
|
||||
nread = read(fd, temp, ARRAY_SIZE(temp));
|
||||
if (nread > 0 && (strstr(temp, phys)) != NULL)
|
||||
found = true;
|
||||
|
||||
close(fd);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int get_hid_id(int dev_id)
|
||||
{
|
||||
const char *workdir = "/sys/devices/virtual/misc/uhid";
|
||||
const char *str_id;
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
int found = -1, attempts = 3;
|
||||
|
||||
/* it would be nice to be able to use nftw, but the no_alu32 target doesn't support it */
|
||||
|
||||
while (found < 0 && attempts > 0) {
|
||||
attempts--;
|
||||
d = opendir(workdir);
|
||||
if (d) {
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (!match_sysfs_device(dev_id, workdir, dir))
|
||||
continue;
|
||||
|
||||
str_id = dir->d_name + sizeof("0003:0001:0A37.");
|
||||
found = (int)strtol(str_id, NULL, 16);
|
||||
|
||||
break;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
if (found < 0)
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int get_hidraw(int dev_id)
|
||||
{
|
||||
const char *workdir = "/sys/devices/virtual/misc/uhid";
|
||||
char sysfs[1024];
|
||||
DIR *d, *subd;
|
||||
struct dirent *dir, *subdir;
|
||||
int i, found = -1;
|
||||
|
||||
/* retry 5 times in case the system is loaded */
|
||||
for (i = 5; i > 0; i--) {
|
||||
usleep(10);
|
||||
d = opendir(workdir);
|
||||
|
||||
if (!d)
|
||||
continue;
|
||||
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (!match_sysfs_device(dev_id, workdir, dir))
|
||||
continue;
|
||||
|
||||
sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name);
|
||||
|
||||
subd = opendir(sysfs);
|
||||
if (!subd)
|
||||
continue;
|
||||
|
||||
while ((subdir = readdir(subd)) != NULL) {
|
||||
if (fnmatch("hidraw*", subdir->d_name, 0))
|
||||
continue;
|
||||
|
||||
found = atoi(subdir->d_name + strlen("hidraw"));
|
||||
}
|
||||
|
||||
closedir(subd);
|
||||
|
||||
if (found > 0)
|
||||
break;
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int open_hidraw(int dev_id)
|
||||
{
|
||||
int hidraw_number;
|
||||
char hidraw_path[64] = { 0 };
|
||||
|
||||
hidraw_number = get_hidraw(dev_id);
|
||||
if (hidraw_number < 0)
|
||||
return hidraw_number;
|
||||
|
||||
/* open hidraw node to check the other side of the pipe */
|
||||
sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number);
|
||||
return open(hidraw_path, O_RDWR | O_NONBLOCK);
|
||||
}
|
237
tools/testing/selftests/hid/hidraw.c
Normal file
237
tools/testing/selftests/hid/hidraw.c
Normal file
@ -0,0 +1,237 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022-2024 Red Hat */
|
||||
|
||||
#include "hid_common.h"
|
||||
|
||||
/* for older kernels */
|
||||
#ifndef HIDIOCREVOKE
|
||||
#define HIDIOCREVOKE _IOW('H', 0x0D, int) /* Revoke device access */
|
||||
#endif /* HIDIOCREVOKE */
|
||||
|
||||
FIXTURE(hidraw) {
|
||||
int dev_id;
|
||||
int uhid_fd;
|
||||
int hidraw_fd;
|
||||
int hid_id;
|
||||
pthread_t tid;
|
||||
};
|
||||
static void close_hidraw(FIXTURE_DATA(hidraw) * self)
|
||||
{
|
||||
if (self->hidraw_fd)
|
||||
close(self->hidraw_fd);
|
||||
self->hidraw_fd = 0;
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(hidraw) {
|
||||
void *uhid_err;
|
||||
|
||||
uhid_destroy(_metadata, self->uhid_fd);
|
||||
|
||||
close_hidraw(self);
|
||||
pthread_join(self->tid, &uhid_err);
|
||||
}
|
||||
#define TEARDOWN_LOG(fmt, ...) do { \
|
||||
TH_LOG(fmt, ##__VA_ARGS__); \
|
||||
hidraw_teardown(_metadata, self, variant); \
|
||||
} while (0)
|
||||
|
||||
FIXTURE_SETUP(hidraw)
|
||||
{
|
||||
time_t t;
|
||||
int err;
|
||||
|
||||
/* initialize random number generator */
|
||||
srand((unsigned int)time(&t));
|
||||
|
||||
self->dev_id = rand() % 1024;
|
||||
|
||||
self->uhid_fd = setup_uhid(_metadata, self->dev_id);
|
||||
|
||||
/* locate the uev, self, variant);ent file of the created device */
|
||||
self->hid_id = get_hid_id(self->dev_id);
|
||||
ASSERT_GT(self->hid_id, 0)
|
||||
TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id);
|
||||
|
||||
err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd);
|
||||
ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err);
|
||||
|
||||
self->hidraw_fd = open_hidraw(self->dev_id);
|
||||
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
|
||||
}
|
||||
|
||||
/*
|
||||
* A simple test to see if the fixture is working fine.
|
||||
* If this fails, none of the other tests will pass.
|
||||
*/
|
||||
TEST_F(hidraw, test_create_uhid)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Inject one event in the uhid device,
|
||||
* check that we get the same data through hidraw
|
||||
*/
|
||||
TEST_F(hidraw, raw_event)
|
||||
{
|
||||
__u8 buf[10] = {0};
|
||||
int err;
|
||||
|
||||
/* inject one event */
|
||||
buf[0] = 1;
|
||||
buf[1] = 42;
|
||||
uhid_send_event(_metadata, self->uhid_fd, buf, 6);
|
||||
|
||||
/* read the data from hidraw */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
err = read(self->hidraw_fd, buf, sizeof(buf));
|
||||
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
|
||||
ASSERT_EQ(buf[0], 1);
|
||||
ASSERT_EQ(buf[1], 42);
|
||||
}
|
||||
|
||||
/*
|
||||
* After initial opening/checks of hidraw, revoke the hidraw
|
||||
* node and check that we can not read any more data.
|
||||
*/
|
||||
TEST_F(hidraw, raw_event_revoked)
|
||||
{
|
||||
__u8 buf[10] = {0};
|
||||
int err;
|
||||
|
||||
/* inject one event */
|
||||
buf[0] = 1;
|
||||
buf[1] = 42;
|
||||
uhid_send_event(_metadata, self->uhid_fd, buf, 6);
|
||||
|
||||
/* read the data from hidraw */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
err = read(self->hidraw_fd, buf, sizeof(buf));
|
||||
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
|
||||
ASSERT_EQ(buf[0], 1);
|
||||
ASSERT_EQ(buf[1], 42);
|
||||
|
||||
/* call the revoke ioctl */
|
||||
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
|
||||
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
|
||||
|
||||
/* inject one other event */
|
||||
buf[0] = 1;
|
||||
buf[1] = 43;
|
||||
uhid_send_event(_metadata, self->uhid_fd, buf, 6);
|
||||
|
||||
/* read the data from hidraw */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
err = read(self->hidraw_fd, buf, sizeof(buf));
|
||||
ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
|
||||
ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d",
|
||||
errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Revoke the hidraw node and check that we can not do any ioctl.
|
||||
*/
|
||||
TEST_F(hidraw, ioctl_revoked)
|
||||
{
|
||||
int err, desc_size = 0;
|
||||
|
||||
/* call the revoke ioctl */
|
||||
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
|
||||
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
|
||||
|
||||
/* do an ioctl */
|
||||
err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
|
||||
ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw");
|
||||
ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d",
|
||||
errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup polling of the fd, and check that revoke works properly.
|
||||
*/
|
||||
TEST_F(hidraw, poll_revoked)
|
||||
{
|
||||
struct pollfd pfds[1];
|
||||
__u8 buf[10] = {0};
|
||||
int err, ready;
|
||||
|
||||
/* setup polling */
|
||||
pfds[0].fd = self->hidraw_fd;
|
||||
pfds[0].events = POLLIN;
|
||||
|
||||
/* inject one event */
|
||||
buf[0] = 1;
|
||||
buf[1] = 42;
|
||||
uhid_send_event(_metadata, self->uhid_fd, buf, 6);
|
||||
|
||||
while (true) {
|
||||
ready = poll(pfds, 1, 5000);
|
||||
ASSERT_EQ(ready, 1) TH_LOG("poll return value");
|
||||
|
||||
if (pfds[0].revents & POLLIN) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
err = read(self->hidraw_fd, buf, sizeof(buf));
|
||||
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
|
||||
ASSERT_EQ(buf[0], 1);
|
||||
ASSERT_EQ(buf[1], 42);
|
||||
|
||||
/* call the revoke ioctl */
|
||||
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
|
||||
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE(pfds[0].revents & POLLHUP);
|
||||
}
|
||||
|
||||
/*
|
||||
* After initial opening/checks of hidraw, revoke the hidraw
|
||||
* node and check that we can not read any more data.
|
||||
*/
|
||||
TEST_F(hidraw, write_event_revoked)
|
||||
{
|
||||
struct timespec time_to_wait;
|
||||
__u8 buf[10] = {0};
|
||||
int err;
|
||||
|
||||
/* inject one event from hidraw */
|
||||
buf[0] = 1; /* report ID */
|
||||
buf[1] = 2;
|
||||
buf[2] = 42;
|
||||
|
||||
pthread_mutex_lock(&uhid_output_mtx);
|
||||
|
||||
memset(output_report, 0, sizeof(output_report));
|
||||
clock_gettime(CLOCK_REALTIME, &time_to_wait);
|
||||
time_to_wait.tv_sec += 2;
|
||||
|
||||
err = write(self->hidraw_fd, buf, 3);
|
||||
ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err);
|
||||
|
||||
err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait);
|
||||
ASSERT_OK(err) TH_LOG("error while calling waiting for the condition");
|
||||
|
||||
ASSERT_EQ(output_report[0], 1);
|
||||
ASSERT_EQ(output_report[1], 2);
|
||||
ASSERT_EQ(output_report[2], 42);
|
||||
|
||||
/* call the revoke ioctl */
|
||||
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
|
||||
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
|
||||
|
||||
/* inject one other event */
|
||||
buf[0] = 1;
|
||||
buf[1] = 43;
|
||||
err = write(self->hidraw_fd, buf, 3);
|
||||
ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err);
|
||||
ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d",
|
||||
errno);
|
||||
|
||||
pthread_mutex_unlock(&uhid_output_mtx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
return test_harness_run(argc, argv);
|
||||
}
|
Loading…
Reference in New Issue
Block a user