From fccef49540a97a3152502bc591858f441a4c7b11 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:19:38 +0800 Subject: [PATCH 01/54] HID: Intel-thc-hid: Intel-thc: Add thc_dma_content into kernel doc Convert thc_dma_content structure comments format into kernel doc format, let it can be included into kernel doc. Signed-off-by: Even Xu Signed-off-by: Jiri Kosina --- drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h index ca923ff2bef9..f799d1180e55 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -117,9 +117,10 @@ struct thc_dma_configuration { u32 dma_cntrl; }; -/* - * THC DMA context - * Store all THC Channel configures +/** + * struct thc_dma_context - THC DMA context + * @thc_dma_configuration: Array of all THC Channel configures + * @use_write_interrupts: Indicate TxDMA using interrupt or polling */ struct thc_dma_context { struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL]; From 13dd60947f439bc2e7a37ed14fbec6b6ff591572 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:19:39 +0800 Subject: [PATCH 02/54] HID: Intel-thc-hid: Intel-thc: Refine code comments Align capitalization of the first characters for THC structure definition comments in header files. Signed-off-by: Even Xu Signed-off-by: Jiri Kosina --- .../intel-thc-hid/intel-thc/intel-thc-dev.h | 16 ++++++------- .../intel-thc-hid/intel-thc/intel-thc-dma.h | 24 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h index 0517fee2c668..fb4d9d875860 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -52,16 +52,16 @@ enum thc_int_type { * struct thc_device - THC private device struct * @thc_regmap: MMIO regmap structure for accessing THC registers * @mmio_addr: MMIO registers address - * @thc_bus_lock: mutex locker for THC config - * @port_type: port type of THC port instance + * @thc_bus_lock: Mutex locker for THC config + * @port_type: Port type of THC port instance * @pio_int_supported: PIO interrupt supported flag * @dma_ctx: DMA specific data - * @write_complete_wait: signal event for DMA write complete - * @swdma_complete_wait: signal event for SWDMA sequence complete - * @write_done: bool value that indicates if DMA write is done - * @swdma_done: bool value that indicates if SWDMA swquence is done - * @perf_limit: the delay between read operation and write operation - * @i2c_subip_regs: the copy of THC I2C sub-system registers for resuming restore + * @write_complete_wait: Signal event for DMA write complete + * @swdma_complete_wait: Signal event for SWDMA sequence complete + * @write_done: Bool value that indicates if DMA write is done + * @swdma_done: Bool value that indicates if SWDMA sequence is done + * @perf_limit: The delay between read operation and write operation + * @i2c_subip_regs: The copy of THC I2C sub-system registers for resuming restore */ struct thc_device { struct device *dev; diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h index f799d1180e55..2664f35383d1 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -27,7 +27,7 @@ /** * THC DMA channels: - * @THC_RXDMA1: legacy channel, reserved for raw data reading + * @THC_RXDMA1: Legacy channel, reserved for raw data reading * @THC_RXDMA2: DMA to read HID data from touch device * @THC_TXDMA: DMA to write to touch device * @THC_SWDMA: SW triggered DMA to write and read from touch device @@ -42,11 +42,11 @@ enum thc_dma_channel { /** * THC DMA Physical Memory Descriptor (PRD) - * @dest_addr: bit[53:0], destination address in system memory - * @int_on_completion: bit[63], if set, thc will trigger interrupt to driver - * @len: bit[87:64], length of this entry - * @end_of_prd: bit[88], if set, this entry is last one of current PRD table - * @hw_status: bit[90:89], hw status bits + * @dest_addr: Bit[53:0], destination address in system memory + * @int_on_completion: Bit[63], if set, thc will trigger interrupt to driver + * @len: Bit[87:64], length of this entry + * @end_of_prd: Bit[88], if set, this entry is last one of current PRD table + * @hw_status: Bit[90:89], hardware status bits */ struct thc_prd_entry { u64 dest_addr : 54; @@ -88,14 +88,14 @@ struct thc_prd_table { * struct thc_dma_configuration - THC DMA configure * @dma_channel: DMA channel for current DMA configuration * @prd_tbls_dma_handle: DMA buffer handle - * @dir: direction of DMA for this config + * @dir: Direction of DMA for this config * @prd_tbls: PRD tables for current DMA - * @sgls: array of pointers to scatter-gather lists - * @sgls_nent: actual number of entries per sg list - * @prd_tbl_num: actual number of PRD tables - * @max_packet_size: size of the buffer needed for 1 DMA message (1 PRD table) + * @sgls: Array of pointers to scatter-gather lists + * @sgls_nent: Actual number of entries per scatter-gather list + * @prd_tbl_num: Actual number of PRD tables + * @max_packet_size: Size of the buffer needed for 1 DMA message (1 PRD table) * @prd_base_addr_high: High 32bits memory address where stores PRD table - * @prd_base_addr_low: low 32bits memory address where stores PRD table + * @prd_base_addr_low: Low 32bits memory address where stores PRD table * @prd_cntrl: PRD control register value * @dma_cntrl: DMA control register value */ From 45e92a093099eaf71ff2915a5f6ab5c04c8385e6 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:19:40 +0800 Subject: [PATCH 03/54] HID: Intel-thc-hid: Intel-thc: Introduce max input size control This patch adds support for a new feature, named "Max Input Size Control", allowing driver to set a maximum input size for RxDMA. This enhancement aims to prevent RxDMA buffer overruns caused by data corruption on the I2C bus, thereby improving overall system stability. APIs added: - thc_i2c_set_rx_max_size(): Set the maximum input size for I2C RxDMA. - thc_i2c_rx_max_size_enable(): Enable or disable the max input size control. As this max input size control feature is only applicable to RxDMA and must remain disabled during SWDMA operations, it also involves a change in SWDMA code to record the max input size control feature state before SWDMA and restore the state after SWDMA. Signed-off-by: Even Xu Tested-by: Chong Han Reviewed-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- Documentation/hid/intel-thc-hid.rst | 14 ++++ .../intel-thc-hid/intel-thc/intel-thc-dev.c | 69 +++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 7 ++ .../intel-thc-hid/intel-thc/intel-thc-dma.c | 22 ++++++ .../intel-thc-hid/intel-thc/intel-thc-dma.h | 4 ++ .../intel-thc-hid/intel-thc/intel-thc-hw.h | 3 + 6 files changed, 119 insertions(+) diff --git a/Documentation/hid/intel-thc-hid.rst b/Documentation/hid/intel-thc-hid.rst index dc9250787fc5..15b04fa215c7 100644 --- a/Documentation/hid/intel-thc-hid.rst +++ b/Documentation/hid/intel-thc-hid.rst @@ -188,6 +188,20 @@ Control register. Reset line is controlled by BIOS (or EFI) through ACPI _RST method, driver needs to call this device ACPI _RST method to reset touch IC during initialization. +2.3 Max input size control +-------------------------- + +This is a new feature introduced in Panther Lake platform, THC hardware allows driver to set +a max input size for RxDMA. After this max size gets set and enabled, for every input report +packet reading, THC hardware sequencer will first read incoming input packet size, then compare +input packet size with the given max size: +- if input packet size <= max size, THC continues using input packet size to finish the reading +- if input packet size > max size, there is potential input data crash risk during + transferring, THC will use max size instead of input packet size for reading + +This feature is used to avoid data corruption which will cause RxDMA buffer overrun issue for +I2C bus, and enhance whole system stability. + 3. High level concept ===================== diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c index c105df7f6c87..8fc6e8484cd8 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c @@ -1571,6 +1571,75 @@ int thc_i2c_subip_regs_restore(struct thc_device *dev) } EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_restore, "INTEL_THC"); +/** + * thc_i2c_set_rx_max_size - Set I2C Rx transfer max input size + * @dev: The pointer of THC private device context + * @max_rx_size: Max input report packet size for input report + * + * Set @max_rx_size for I2C RxDMA max input size control feature. + * + * Return: 0 on success, other error codes on failure. + */ +int thc_i2c_set_rx_max_size(struct thc_device *dev, u32 max_rx_size) +{ + u32 val; + int ret; + + if (!dev) + return -EINVAL; + + if (!max_rx_size) + return -EOPNOTSUPP; + + ret = regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &val); + if (ret) + return ret; + + val |= FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE, max_rx_size); + + ret = regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, val); + if (ret) + return ret; + + dev->i2c_max_rx_size = max_rx_size; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_set_rx_max_size, "INTEL_THC"); + +/** + * thc_i2c_rx_max_size_enable - Enable I2C Rx max input size control + * @dev: The pointer of THC private device context + * @enable: Enable max input size control or not + * + * Enable or disable I2C RxDMA max input size control feature. + * Max input size control only can be enabled after max input size + * was set by thc_i2c_set_rx_max_size(). + * + * Return: 0 on success, other error codes on failure. + */ +int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable) +{ + u32 mask = THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE_EN; + u32 val = enable ? mask : 0; + int ret; + + if (!dev) + return -EINVAL; + + if (!dev->i2c_max_rx_size) + return -EOPNOTSUPP; + + ret = regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, mask, val); + if (ret) + return ret; + + dev->i2c_max_rx_size_en = enable; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_rx_max_size_enable, "INTEL_THC"); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h index fb4d9d875860..3e0bb0b31ed9 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -62,6 +62,8 @@ enum thc_int_type { * @swdma_done: Bool value that indicates if SWDMA sequence is done * @perf_limit: The delay between read operation and write operation * @i2c_subip_regs: The copy of THC I2C sub-system registers for resuming restore + * @i2c_max_rx_size: I2C Rx transfer max input size + * @i2c_max_rx_size_en: Bool value that indicates I2C max input size control enabled or not */ struct thc_device { struct device *dev; @@ -81,6 +83,9 @@ struct thc_device { u32 perf_limit; u32 *i2c_subip_regs; + + u32 i2c_max_rx_size; + bool i2c_max_rx_size_en; }; struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); @@ -112,5 +117,7 @@ int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address, const u32 speed, const u32 hcnt, const u32 lcnt); int thc_i2c_subip_regs_save(struct thc_device *dev); int thc_i2c_subip_regs_restore(struct thc_device *dev); +int thc_i2c_set_rx_max_size(struct thc_device *dev, u32 max_rx_size); +int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable); #endif /* _INTEL_THC_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c index 8f97e71df7f4..5bef342cd738 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c @@ -712,6 +712,19 @@ static int thc_swdma_read_start(struct thc_device *dev, void *write_buff, thc_reset_dma_settings(dev); + /* + * Max input size control feature is only available for RxDMA, it must keep disabled + * during SWDMA operation, and restore to previous state after SWDMA is done. + * Max input size variables in THC device context track hardware state, and keep change + * when feature state was changed, so those variables cannot be used to record feature + * state after state was changed during SWDMA operation. Here have to use a temp variable + * in DMA context to record feature state before SWDMA operation. + */ + if (dev->i2c_max_rx_size_en) { + thc_i2c_rx_max_size_enable(dev, false); + dev->dma_ctx->rx_max_size_en = true; + } + mask = THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC | THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN; val = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC, write_len) | @@ -754,6 +767,15 @@ static int thc_swdma_read_completion(struct thc_device *dev) if (ret) return ret; + /* + * Restore max input size control feature to previous state after SWDMA if it was + * enabled before SWDMA, and reset temp rx_max_size_en variable for next time. + */ + if (dev->dma_ctx->rx_max_size_en) { + thc_i2c_rx_max_size_enable(dev, true); + dev->dma_ctx->rx_max_size_en = false; + } + thc_reset_dma_settings(dev); dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h index 2664f35383d1..42cfd55c3e2c 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -121,10 +121,14 @@ struct thc_dma_configuration { * struct thc_dma_context - THC DMA context * @thc_dma_configuration: Array of all THC Channel configures * @use_write_interrupts: Indicate TxDMA using interrupt or polling + * @rx_max_size_en: Temp flag to indicate THC I2C Rx max input size control feature + * enabled or not, only be used during SWDMA operation. */ struct thc_dma_context { struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL]; u8 use_write_interrupts; + + bool rx_max_size_en; }; struct thc_device; diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h index 6729c4c25dab..ed8e28858e0c 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h @@ -399,6 +399,9 @@ #define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO GENMASK(23, 16) #define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO GENMASK(15, 8) +#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE GENMASK(15, 0) +#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE_EN BIT(31) + #define THC_M_PRT_INT_EN_SIPE BIT(0) #define THC_M_PRT_INT_EN_SBO BIT(1) #define THC_M_PRT_INT_EN_SIDR BIT(2) From 22da60f0304b6bfd2c7cba8ee88086f344ef5206 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:19:41 +0800 Subject: [PATCH 04/54] HID: Intel-thc-hid: Intel-thc: Introduce interrupt delay control This patch adds support for a new feature, named "Interrupt Delay", allowing driver to set a specific delay time for next interrupt detection. It gives driver a capability to control THC waiting time for the next interrupt, to reduce the likelihood of spurious readings. APIs added: - thc_i2c_set_rx_int_delay(): Set I2C Rx input interrupt delay value - thc_i2c_rx_int_delay_enable(): Enable or disable I2C Rx interrupt delay As this interrupt delay feature is only applicable to RxDMA and must remain disabled during SWDMA operations, it also involves a change in SWDMA code to record the max input size control feature state before SWDMA and restore the state after SWDMA. Signed-off-by: Even Xu Tested-by: Chong Han Reviewed-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- Documentation/hid/intel-thc-hid.rst | 13 ++++ .../intel-thc-hid/intel-thc/intel-thc-dev.c | 71 +++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 6 ++ .../intel-thc-hid/intel-thc/intel-thc-dma.c | 18 +++++ .../intel-thc-hid/intel-thc/intel-thc-dma.h | 3 + .../intel-thc-hid/intel-thc/intel-thc-hw.h | 2 + 6 files changed, 113 insertions(+) diff --git a/Documentation/hid/intel-thc-hid.rst b/Documentation/hid/intel-thc-hid.rst index 15b04fa215c7..23d5110cb871 100644 --- a/Documentation/hid/intel-thc-hid.rst +++ b/Documentation/hid/intel-thc-hid.rst @@ -202,6 +202,19 @@ input packet size with the given max size: This feature is used to avoid data corruption which will cause RxDMA buffer overrun issue for I2C bus, and enhance whole system stability. +2.4 Interrupt delay +------------------- + +Because of MCU performance limitation, some touch devices cannot de-assert interrupt pin +immediately after input data is transferred, which cause an interrupt toggle delay. But THC +always detects next interrupt immediately after last input interrupt is handled. In this +case, the delayed interrupt de-assertion will be recognized as a new interrupt signal by THC, +and causes THC to start an input report reading spuriously. + +In order to avoid this situation, THC introduced interrupt delay new feature in Panther Lake +platform, where THC allows driver to set an interrupt delay. After this feature is enabled, +THC will delay this given time for next interrupt detection. + 3. High level concept ===================== diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c index 8fc6e8484cd8..6f2263869b20 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c @@ -2,6 +2,7 @@ /* Copyright (c) 2024 Intel Corporation */ #include +#include #include #include "intel-thc-dev.h" @@ -1640,6 +1641,76 @@ int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable) } EXPORT_SYMBOL_NS_GPL(thc_i2c_rx_max_size_enable, "INTEL_THC"); +/** + * thc_i2c_set_rx_int_delay - Set I2C Rx input interrupt delay value + * @dev: The pointer of THC private device context + * @delay_us: Interrupt delay value, unit is us + * + * Set @delay_us for I2C RxDMA input interrupt delay feature. + * + * Return: 0 on success, other error codes on failure. + */ +int thc_i2c_set_rx_int_delay(struct thc_device *dev, u32 delay_us) +{ + u32 val; + int ret; + + if (!dev) + return -EINVAL; + + if (!delay_us) + return -EOPNOTSUPP; + + ret = regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &val); + if (ret) + return ret; + + /* THC hardware counts at 10us unit */ + val |= FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL, DIV_ROUND_UP(delay_us, 10)); + + ret = regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, val); + if (ret) + return ret; + + dev->i2c_int_delay_us = delay_us; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_set_rx_int_delay, "INTEL_THC"); + +/** + * thc_i2c_rx_int_delay_enable - Enable I2C Rx interrupt delay + * @dev: The pointer of THC private device context + * @enable: Enable interrupt delay or not + * + * Enable or disable I2C RxDMA input interrupt delay feature. + * Input interrupt delay can only be enabled after interrupt delay value + * was set by thc_i2c_set_rx_int_delay(). + * + * Return: 0 on success, other error codes on failure. + */ +int thc_i2c_rx_int_delay_enable(struct thc_device *dev, bool enable) +{ + u32 mask = THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL_EN; + u32 val = enable ? mask : 0; + int ret; + + if (!dev) + return -EINVAL; + + if (!dev->i2c_int_delay_us) + return -EOPNOTSUPP; + + ret = regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, mask, val); + if (ret) + return ret; + + dev->i2c_int_delay_en = enable; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_rx_int_delay_enable, "INTEL_THC"); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h index 3e0bb0b31ed9..8666e2362e32 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -63,7 +63,9 @@ enum thc_int_type { * @perf_limit: The delay between read operation and write operation * @i2c_subip_regs: The copy of THC I2C sub-system registers for resuming restore * @i2c_max_rx_size: I2C Rx transfer max input size + * @i2c_int_delay_us: I2C input interrupt delay, unit is us * @i2c_max_rx_size_en: Bool value that indicates I2C max input size control enabled or not + * @i2c_int_delay_en: Bool value that indicates I2C input interrupt delay enabled or not */ struct thc_device { struct device *dev; @@ -85,7 +87,9 @@ struct thc_device { u32 *i2c_subip_regs; u32 i2c_max_rx_size; + u32 i2c_int_delay_us; bool i2c_max_rx_size_en; + bool i2c_int_delay_en; }; struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); @@ -119,5 +123,7 @@ int thc_i2c_subip_regs_save(struct thc_device *dev); int thc_i2c_subip_regs_restore(struct thc_device *dev); int thc_i2c_set_rx_max_size(struct thc_device *dev, u32 max_rx_size); int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable); +int thc_i2c_set_rx_int_delay(struct thc_device *dev, u32 delay_us); +int thc_i2c_rx_int_delay_enable(struct thc_device *dev, bool enable); #endif /* _INTEL_THC_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c index 5bef342cd738..82b8854843e0 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c @@ -725,6 +725,15 @@ static int thc_swdma_read_start(struct thc_device *dev, void *write_buff, dev->dma_ctx->rx_max_size_en = true; } + /* + * Interrupt delay feature is in the same situation with max input size control feature, + * needs record feature state before SWDMA. + */ + if (dev->i2c_int_delay_en) { + thc_i2c_rx_int_delay_enable(dev, false); + dev->dma_ctx->rx_int_delay_en = true; + } + mask = THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC | THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN; val = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC, write_len) | @@ -776,6 +785,15 @@ static int thc_swdma_read_completion(struct thc_device *dev) dev->dma_ctx->rx_max_size_en = false; } + /* + * Restore input interrupt delay feature to previous state after SWDMA if it was + * enabled before SWDMA, and reset temp rx_int_delay_en variable for next time. + */ + if (dev->dma_ctx->rx_int_delay_en) { + thc_i2c_rx_int_delay_enable(dev, true); + dev->dma_ctx->rx_int_delay_en = false; + } + thc_reset_dma_settings(dev); dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h index 42cfd55c3e2c..78917400492c 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -123,12 +123,15 @@ struct thc_dma_configuration { * @use_write_interrupts: Indicate TxDMA using interrupt or polling * @rx_max_size_en: Temp flag to indicate THC I2C Rx max input size control feature * enabled or not, only be used during SWDMA operation. + * @rx_int_delay_en: Temp flag to indicate THC I2C Rx interrupt delay feature + * enabled or not, only be used during SWDMA operation. */ struct thc_dma_context { struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL]; u8 use_write_interrupts; bool rx_max_size_en; + bool rx_int_delay_en; }; struct thc_device; diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h index ed8e28858e0c..413730f8e3f7 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h @@ -400,6 +400,8 @@ #define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO GENMASK(15, 8) #define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE GENMASK(15, 0) +#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL GENMASK(23, 16) +#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL_EN BIT(30) #define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE_EN BIT(31) #define THC_M_PRT_INT_EN_SIPE BIT(0) From bccbe21e766f9bd1721905ece30adeac6a61b4f6 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:19:42 +0800 Subject: [PATCH 05/54] HID: Intel-thc-hid: Intel-quicki2c: Refine code comments Align capitalization of the first characters for all code comments and refine some descriptions. Signed-off-by: Even Xu Reviewed-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- .../intel-quicki2c/pci-quicki2c.c | 123 ++++++++---------- .../intel-quicki2c/quicki2c-dev.h | 25 ++-- 2 files changed, 68 insertions(+), 80 deletions(-) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 8a8c4a46f927..90812c246f20 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -29,17 +29,16 @@ static guid_t thc_platform_guid = /** * quicki2c_acpi_get_dsm_property - Query device ACPI DSM parameter - * - * @adev: point to ACPI device + * @adev: Point to ACPI device * @guid: ACPI method's guid * @rev: ACPI method's revision * @func: ACPI method's function number * @type: ACPI parameter's data type - * @prop_buf: point to return buffer + * @prop_buf: Point to return buffer * * This is a helper function for device to query its ACPI DSM parameters. * - * Return: 0 if success or ENODEV on failed. + * Return: 0 if success or ENODEV on failure. */ static int quicki2c_acpi_get_dsm_property(struct acpi_device *adev, const guid_t *guid, u64 rev, u64 func, acpi_object_type type, void *prop_buf) @@ -67,11 +66,10 @@ static int quicki2c_acpi_get_dsm_property(struct acpi_device *adev, const guid_t /** * quicki2c_acpi_get_dsd_property - Query device ACPI DSD parameter - * - * @adev: point to ACPI device + * @adev: Point to ACPI device * @dsd_method_name: ACPI method's property name * @type: ACPI parameter's data type - * @prop_buf: point to return buffer + * @prop_buf: Point to return buffer * * This is a helper function for device to query its ACPI DSD parameters. * @@ -100,13 +98,12 @@ static int quicki2c_acpi_get_dsd_property(struct acpi_device *adev, acpi_string } /** - * quicki2c_get_acpi_resources - Query all quicki2c devices' ACPI parameters + * quicki2c_get_acpi_resources - Query all QuickI2C devices' ACPI parameters + * @qcdev: Point to quicki2c_device structure * - * @qcdev: point to quicki2c device + * This function gets all QuickI2C devices' ACPI resource. * - * This function gets all quicki2c devices' ACPI resource. - * - * Return: 0 if success or error code on failed. + * Return: 0 if success or error code on failure. */ static int quicki2c_get_acpi_resources(struct quicki2c_device *qcdev) { @@ -192,10 +189,9 @@ static int quicki2c_get_acpi_resources(struct quicki2c_device *qcdev) } /** - * quicki2c_irq_quick_handler - The ISR of the quicki2c driver - * + * quicki2c_irq_quick_handler - The ISR of the QuickI2C driver * @irq: The irq number - * @dev_id: pointer to the device structure + * @dev_id: Pointer to the quicki2c_device structure * * Return: IRQ_WAKE_THREAD if further process needed. */ @@ -214,13 +210,13 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id) /** * try_recover - Try to recovery THC and Device - * @qcdev: pointer to quicki2c device + * @qcdev: Pointer to quicki2c_device structure * - * This function is a error handler, called when fatal error happens. - * It try to reset Touch Device and re-configure THC to recovery - * transferring between Device and THC. + * This function is an error handler, called when fatal error happens. + * It try to reset touch device and re-configure THC to recovery + * communication between touch device and THC. * - * Return: 0 if successful or error code on failed + * Return: 0 if successful or error code on failure */ static int try_recover(struct quicki2c_device *qcdev) { @@ -264,7 +260,7 @@ static int handle_input_report(struct quicki2c_device *qcdev) continue; } - /* discard samples before driver probe complete */ + /* Discard samples before driver probe complete */ if (qcdev->state != QUICKI2C_ENABLED) continue; @@ -276,10 +272,9 @@ static int handle_input_report(struct quicki2c_device *qcdev) } /** - * quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver - * + * quicki2c_irq_thread_handler - IRQ thread handler of QuickI2C driver * @irq: The IRQ number - * @dev_id: pointer to the quicki2c device structure + * @dev_id: Pointer to the quicki2c_device structure * * Return: IRQ_HANDLED to finish this handler. */ @@ -325,18 +320,17 @@ exit: } /** - * quicki2c_dev_init - Initialize quicki2c device + * quicki2c_dev_init - Initialize QuickI2C device + * @pdev: Pointer to the THC PCI device + * @mem_addr: The Pointer of MMIO memory address * - * @pdev: pointer to the thc pci device - * @mem_addr: The pointer of MMIO memory address - * - * Alloc quicki2c device structure and initialized THC device, + * Alloc quicki2c_device structure and initialized THC device, * then configure THC to HIDI2C mode. * * If success, enable THC hardware interrupt. * - * Return: pointer to the quicki2c device structure if success - * or NULL on failed. + * Return: Pointer to the quicki2c_device structure if success + * or NULL on failure. */ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __iomem *mem_addr) { @@ -355,7 +349,7 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io init_waitqueue_head(&qcdev->reset_ack_wq); - /* thc hw init */ + /* THC hardware init */ qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr); if (IS_ERR(qcdev->thc_hw)) { ret = PTR_ERR(qcdev->thc_hw); @@ -398,9 +392,8 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io } /** - * quicki2c_dev_deinit - De-initialize quicki2c device - * - * @qcdev: pointer to the quicki2c device structure + * quicki2c_dev_deinit - De-initialize QuickI2C device + * @qcdev: Pointer to the quicki2c_device structure * * Disable THC interrupt and deinitilize THC. */ @@ -413,13 +406,13 @@ static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) } /** - * quicki2c_dma_init - Configure THC DMA for quicki2c device - * @qcdev: pointer to the quicki2c device structure + * quicki2c_dma_init - Configure THC DMA for QuickI2C device + * @qcdev: Pointer to the quicki2c_device structure * * This function uses TIC's parameters(such as max input length, max output * length) to allocate THC DMA buffers and configure THC DMA engines. * - * Return: 0 if success or error code on failed. + * Return: 0 if success or error code on failure. */ static int quicki2c_dma_init(struct quicki2c_device *qcdev) { @@ -451,12 +444,12 @@ static int quicki2c_dma_init(struct quicki2c_device *qcdev) return ret; } - return ret; + return 0; } /** - * quicki2c_dma_deinit - Release THC DMA for quicki2c device - * @qcdev: pointer to the quicki2c device structure + * quicki2c_dma_deinit - Release THC DMA for QuickI2C device + * @qcdev: Pointer to the quicki2c_device structure * * Stop THC DMA engines and release all DMA buffers. * @@ -469,7 +462,7 @@ static void quicki2c_dma_deinit(struct quicki2c_device *qcdev) /** * quicki2c_alloc_report_buf - Alloc report buffers - * @qcdev: pointer to the quicki2c device structure + * @qcdev: Pointer to the quicki2c_device structure * * Allocate report descriptor buffer, it will be used for restore TIC HID * report descriptor. @@ -480,7 +473,7 @@ static void quicki2c_dma_deinit(struct quicki2c_device *qcdev) * Allocate output report buffer, it will be used for store HID output report, * such as set feature. * - * Return: 0 if success or error code on failed. + * Return: 0 if success or error code on failure. */ static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev) { @@ -518,24 +511,23 @@ static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev) } /* - * quicki2c_probe: Quicki2c driver probe function - * - * @pdev: point to pci device - * @id: point to pci_device_id structure + * quicki2c_probe: QuickI2C driver probe function + * @pdev: Point to PCI device + * @id: Point to pci_device_id structure * * This function initializes THC and HIDI2C device, the flow is: - * - do THC pci device initialization - * - query HIDI2C ACPI parameters - * - configure THC to HIDI2C mode - * - go through HIDI2C enumeration flow - * |- read device descriptor - * |- reset HIDI2C device - * - enable THC interrupt and DMA - * - read report descriptor - * - register HID device - * - enable runtime power management + * - Do THC pci device initialization + * - Query HIDI2C ACPI parameters + * - Configure THC to HIDI2C mode + * - Go through HIDI2C enumeration flow + * |- Read device descriptor + * |- Reset HIDI2C device + * - Enable THC interrupt and DMA + * - Read report descriptor + * - Register HID device + * - Enable runtime power management * - * Return 0 if success or error code on failed. + * Return 0 if success or error code on failure. */ static int quicki2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -668,11 +660,10 @@ disable_pci_device: /** * quicki2c_remove - Device Removal Routine + * @pdev: Point to PCI device structure * - * @pdev: PCI device structure - * - * This is called by the PCI subsystem to alert the driver - * that it should release a PCI device. + * This is called by the PCI subsystem to alert the driver that it should + * release a PCI device. */ static void quicki2c_remove(struct pci_dev *pdev) { @@ -694,12 +685,10 @@ static void quicki2c_remove(struct pci_dev *pdev) /** * quicki2c_shutdown - Device Shutdown Routine + * @pdev: Point to PCI device structure * - * @pdev: PCI device structure - * - * This is called from the reboot notifier - * it's a simplified version of remove so we go down - * faster. + * This is called from the reboot notifier, it's a simplified version of remove + * so we go down faster. */ static void quicki2c_shutdown(struct pci_dev *pdev) { diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index 6ddb584bd611..43372253ba9c 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -130,15 +130,14 @@ struct acpi_device; /** * struct quicki2c_device - THC QuickI2C device struct - * @dev: point to kernel device - * @pdev: point to PCI device - * @thc_hw: point to THC device - * @hid_dev: point to hid device - * @acpi_dev: point to ACPI device - * @driver_data: point to quicki2c specific driver data + * @dev: Point to kernel device + * @pdev: Point to PCI device + * @thc_hw: Point to THC device + * @hid_dev: Point to HID device + * @acpi_dev: Point to ACPI device * @state: THC I2C device state * @mem_addr: MMIO memory address - * @dev_desc: device descriptor for HIDI2C protocol + * @dev_desc: Device descriptor for HIDI2C protocol * @i2c_slave_addr: HIDI2C device slave address * @hid_desc_addr: Register address for retrieve HID device descriptor * @active_ltr_val: THC active LTR value @@ -146,12 +145,12 @@ struct acpi_device; * @i2c_speed_mode: 0 - standard mode, 1 - fast mode, 2 - fast mode plus * @i2c_clock_hcnt: I2C CLK high period time (unit in cycle count) * @i2c_clock_lcnt: I2C CLK low period time (unit in cycle count) - * @report_descriptor: store a copy of device report descriptor - * @input_buf: store a copy of latest input report data - * @report_buf: store a copy of latest input/output report packet from set/get feature - * @report_len: the length of input/output report packet - * @reset_ack_wq: workqueue for waiting reset response from device - * @reset_ack: indicate reset response received or not + * @report_descriptor: Store a copy of device report descriptor + * @input_buf: Store a copy of latest input report data + * @report_buf: Store a copy of latest input/output report packet from set/get feature + * @report_len: The length of input/output report packet + * @reset_ack_wq: Workqueue for waiting reset response from device + * @reset_ack: Indicate reset response received or not */ struct quicki2c_device { struct device *dev; From 48f151a537542f232b328d20c27e46405a408a5a Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:19:43 +0800 Subject: [PATCH 06/54] HID: Intel-thc-hid: Intel-quicki2c: Add driver data support This patch defines driver data structure and adds it into QuickI2C device structure. Changes PCI ID table to use PCI_DEVICE_DATA() to pass platform specific driver data into driver, let driver has capability to enable different hardware features according to different platform driver data. Signed-off-by: Even Xu Signed-off-by: Jiri Kosina --- .../intel-quicki2c/pci-quicki2c.c | 25 +++++++++++-------- .../intel-quicki2c/quicki2c-dev.h | 24 +++++++++++++----- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 90812c246f20..b8c38ed4f043 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -323,6 +323,7 @@ exit: * quicki2c_dev_init - Initialize QuickI2C device * @pdev: Pointer to the THC PCI device * @mem_addr: The Pointer of MMIO memory address + * @ddata: Point to quicki2c_ddata structure * * Alloc quicki2c_device structure and initialized THC device, * then configure THC to HIDI2C mode. @@ -332,7 +333,8 @@ exit: * Return: Pointer to the quicki2c_device structure if success * or NULL on failure. */ -static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __iomem *mem_addr) +static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __iomem *mem_addr, + const struct quicki2c_ddata *ddata) { struct device *dev = &pdev->dev; struct quicki2c_device *qcdev; @@ -346,6 +348,7 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io qcdev->dev = dev; qcdev->mem_addr = mem_addr; qcdev->state = QUICKI2C_DISABLED; + qcdev->ddata = ddata; init_waitqueue_head(&qcdev->reset_ack_wq); @@ -529,9 +532,9 @@ static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev) * * Return 0 if success or error code on failure. */ -static int quicki2c_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +static int quicki2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + const struct quicki2c_ddata *ddata = (const struct quicki2c_ddata *)id->driver_data; struct quicki2c_device *qcdev; void __iomem *mem_addr; int ret; @@ -569,7 +572,7 @@ static int quicki2c_probe(struct pci_dev *pdev, pdev->irq = pci_irq_vector(pdev, 0); - qcdev = quicki2c_dev_init(pdev, mem_addr); + qcdev = quicki2c_dev_init(pdev, mem_addr, ddata); if (IS_ERR(qcdev)) { dev_err_once(&pdev->dev, "QuickI2C device init failed\n"); ret = PTR_ERR(qcdev); @@ -919,13 +922,13 @@ static const struct dev_pm_ops quicki2c_pm_ops = { }; static const struct pci_device_id quicki2c_pci_tbl[] = { - {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1), }, - {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2), }, - {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1), }, - {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2), }, - {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1), }, - {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2), }, - {} + { PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1, NULL) }, + { PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2, NULL) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1, NULL) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2, NULL) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1, NULL) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2, NULL) }, + { } }; MODULE_DEVICE_TABLE(pci, quicki2c_pci_tbl); diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index 43372253ba9c..e130598d13c1 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -7,12 +7,12 @@ #include #include -#define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848 -#define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A -#define THC_PTL_H_DEVICE_ID_I2C_PORT1 0xE348 -#define THC_PTL_H_DEVICE_ID_I2C_PORT2 0xE34A -#define THC_PTL_U_DEVICE_ID_I2C_PORT1 0xE448 -#define THC_PTL_U_DEVICE_ID_I2C_PORT2 0xE44A +#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_I2C_PORT1 0xA848 +#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A +#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_I2C_PORT1 0xE348 +#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_I2C_PORT2 0xE34A +#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_I2C_PORT1 0xE448 +#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_I2C_PORT2 0xE44A /* Packet size value, the unit is 16 bytes */ #define MAX_PACKET_SIZE_VALUE_LNL 256 @@ -122,6 +122,16 @@ struct quicki2c_subip_acpi_config { u64 HMSL; }; +/** + * struct quicki2c_ddata - Driver specific data for quicki2c device + * @max_detect_size: Identify max packet size detect for rx + * @interrupt_delay: Identify interrupt detect delay for rx + */ +struct quicki2c_ddata { + u32 max_detect_size; + u32 interrupt_delay; +}; + struct device; struct pci_dev; struct thc_device; @@ -135,6 +145,7 @@ struct acpi_device; * @thc_hw: Point to THC device * @hid_dev: Point to HID device * @acpi_dev: Point to ACPI device + * @ddata: Point to QuickI2C platform specific driver data * @state: THC I2C device state * @mem_addr: MMIO memory address * @dev_desc: Device descriptor for HIDI2C protocol @@ -158,6 +169,7 @@ struct quicki2c_device { struct thc_device *thc_hw; struct hid_device *hid_dev; struct acpi_device *acpi_dev; + const struct quicki2c_ddata *ddata; enum quicki2c_dev_state state; void __iomem *mem_addr; From 2c7c9c5db3bc418fc52b222651524470eeaf6f36 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:19:44 +0800 Subject: [PATCH 07/54] HID: Intel-thc-hid: Intel-quicki2c: Add two new features to PTL On Panther Lake platform (PTL), THC hardware introduces two new features for I2C subsystem: - Input max input size control - Input interrupt delay This patch adds above new advanced features into QuickI2C driver, and enables max input size control feature on PTL to improve QuickI2C driver compatibility. Signed-off-by: Even Xu Tested-by: Chong Han Signed-off-by: Jiri Kosina --- .../intel-quicki2c/pci-quicki2c.c | 62 +++++++++++++++++-- .../intel-quicki2c/quicki2c-dev.h | 6 ++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index b8c38ed4f043..4691d100582b 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -18,6 +18,10 @@ #include "quicki2c-hid.h" #include "quicki2c-protocol.h" +struct quicki2c_ddata ptl_ddata = { + .max_detect_size = MAX_RX_DETECT_SIZE_PTL, +}; + /* THC QuickI2C ACPI method to get device properties */ /* HIDI2C device method */ static guid_t i2c_hid_guid = @@ -408,6 +412,50 @@ static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) qcdev->state = QUICKI2C_DISABLED; } +/** + * quicki2c_dma_adv_enable - Configure and enable DMA advanced features + * @qcdev: Pointer to the quicki2c_device structure + * + * If platform supports THC DMA advanced features, such as max input size + * control or interrupt delay, configures and enables them. + */ +static void quicki2c_dma_adv_enable(struct quicki2c_device *qcdev) +{ + /* + * If platform supports max input size control feature and touch device + * max input length <= THC detect capability, enable the feature with device + * max input length. + */ + if (qcdev->ddata->max_detect_size >= + le16_to_cpu(qcdev->dev_desc.max_input_len)) { + thc_i2c_set_rx_max_size(qcdev->thc_hw, + le16_to_cpu(qcdev->dev_desc.max_input_len)); + thc_i2c_rx_max_size_enable(qcdev->thc_hw, true); + } + + /* If platform supports interrupt delay feature, enable it with given delay */ + if (qcdev->ddata->interrupt_delay) { + thc_i2c_set_rx_int_delay(qcdev->thc_hw, + qcdev->ddata->interrupt_delay); + thc_i2c_rx_int_delay_enable(qcdev->thc_hw, true); + } +} + +/** + * quicki2c_dma_adv_disable - Disable DMA advanced features + * @qcdev: Pointer to the quicki2c device structure + * + * Disable all DMA advanced features if platform supports. + */ +static void quicki2c_dma_adv_disable(struct quicki2c_device *qcdev) +{ + if (qcdev->ddata->max_detect_size) + thc_i2c_rx_max_size_enable(qcdev->thc_hw, false); + + if (qcdev->ddata->interrupt_delay) + thc_i2c_rx_int_delay_enable(qcdev->thc_hw, false); +} + /** * quicki2c_dma_init - Configure THC DMA for QuickI2C device * @qcdev: Pointer to the quicki2c_device structure @@ -447,6 +495,9 @@ static int quicki2c_dma_init(struct quicki2c_device *qcdev) return ret; } + if (qcdev->ddata) + quicki2c_dma_adv_enable(qcdev); + return 0; } @@ -461,6 +512,9 @@ static void quicki2c_dma_deinit(struct quicki2c_device *qcdev) { thc_dma_unconfigure(qcdev->thc_hw); thc_dma_release(qcdev->thc_hw); + + if (qcdev->ddata) + quicki2c_dma_adv_disable(qcdev); } /** @@ -924,10 +978,10 @@ static const struct dev_pm_ops quicki2c_pm_ops = { static const struct pci_device_id quicki2c_pci_tbl[] = { { PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1, NULL) }, { PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2, NULL) }, - { PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1, NULL) }, - { PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2, NULL) }, - { PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1, NULL) }, - { PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2, NULL) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1, &ptl_ddata) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2, &ptl_ddata) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1, &ptl_ddata) }, + { PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2, &ptl_ddata) }, { } }; MODULE_DEVICE_TABLE(pci, quicki2c_pci_tbl); diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index e130598d13c1..93d6fa982d60 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -36,6 +36,12 @@ #define QUICKI2C_DEFAULT_LP_LTR_VALUE 500 #define QUICKI2C_RPM_TIMEOUT_MS 500 +/* PTL Max packet size detection capability is 255 Bytes */ +#define MAX_RX_DETECT_SIZE_PTL 255 + +/* Default interrupt delay is 1ms, suitable for most devices */ +#define DEFAULT_INTERRUPT_DELAY_US (1 * USEC_PER_MSEC) + /* * THC uses runtime auto suspend to dynamically switch between THC active LTR * and low power LTR to save CPU power. From dcb2ccb93095fcaa2969370dcbbe82fc83d42229 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:37:33 +0800 Subject: [PATCH 08/54] HID: Intel-thc-hid: Intel-thc: Add Wake-on-Touch support Wake-on-Touch (WoT) feature gives system the capability to wake from sleep state by user touch event, it requires platform providing wake GPIO through ACPI resource. Intel UEFI provides a user setting to enable or disable THC device WoT feature. If it's enabled, UEFI assigns an additional wake GPIO resource to THC device ACPI configuration, facilitating system wakeup. This patch provides helper APIs for THC device driver to query wake GPIO resource, enable WoT feature and unconfigure WoT. APIs added: - thc_wot_config(): Query and configure wake-on-touch feature. - thc_wot_unconfig(): Unconfig wake-on-touch feature. Signed-off-by: Even Xu Tested-by: Chong Han Signed-off-by: Jiri Kosina --- drivers/hid/intel-thc-hid/Makefile | 1 + .../intel-thc-hid/intel-thc/intel-thc-dev.h | 4 + .../intel-thc-hid/intel-thc/intel-thc-wot.c | 94 +++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-wot.h | 26 +++++ 4 files changed, 125 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c create mode 100644 drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index 6f762d87af07..f1182253b5b7 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_INTEL_THC_HID) += intel-thc.o intel-thc-objs += intel-thc/intel-thc-dev.o intel-thc-objs += intel-thc/intel-thc-dma.o +intel-thc-objs += intel-thc/intel-thc-wot.o obj-$(CONFIG_INTEL_QUICKSPI) += intel-quickspi.o intel-quickspi-objs += intel-quickspi/pci-quickspi.o diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h index 8666e2362e32..0db435335e24 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -9,6 +9,7 @@ #include #include "intel-thc-dma.h" +#include "intel-thc-wot.h" #define THC_REGMAP_COMMON_OFFSET 0x10 #define THC_REGMAP_MMIO_OFFSET 0x1000 @@ -56,6 +57,7 @@ enum thc_int_type { * @port_type: Port type of THC port instance * @pio_int_supported: PIO interrupt supported flag * @dma_ctx: DMA specific data + * @wot: THC Wake-on-Touch data * @write_complete_wait: Signal event for DMA write complete * @swdma_complete_wait: Signal event for SWDMA sequence complete * @write_done: Bool value that indicates if DMA write is done @@ -77,6 +79,8 @@ struct thc_device { struct thc_dma_context *dma_ctx; + struct thc_wot wot; + wait_queue_head_t write_complete_wait; wait_queue_head_t swdma_complete_wait; bool write_done; diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c new file mode 100644 index 000000000000..1291b4ea2cd8 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Intel Corporation */ + +#include +#include + +#include "intel-thc-dev.h" +#include "intel-thc-wot.h" + +/** + * thc_wot_config - Query and configure wake-on-touch feature + * @thc_dev: Point to thc_device structure + * @gpio_map: Point to ACPI GPIO resource mapping structure + * + * THC ACPI device only provides _CRS with GpioInt() resources, doesn't contain + * _DSD to map this GPIO resource, so this function first registers wake GPIO + * mapping manually, then queries wake-on-touch GPIO resource from ACPI, + * if it exists and is wake-able, configure driver to enable it, otherwise, + * return immediately. + * This function will not return error as it doesn't impact major function. + */ +void thc_wot_config(struct thc_device *thc_dev, const struct acpi_gpio_mapping *gpio_map) +{ + struct acpi_device *adev; + struct thc_wot *wot; + int ret; + + if (!thc_dev) + return; + + adev = ACPI_COMPANION(thc_dev->dev); + if (!adev) + return; + + wot = &thc_dev->wot; + + ret = acpi_dev_add_driver_gpios(adev, gpio_map); + if (ret) { + dev_warn(thc_dev->dev, "Can't add wake GPIO resource, ret = %d\n", ret); + return; + } + + wot->gpio_irq = acpi_dev_gpio_irq_wake_get_by(adev, "wake-on-touch", 0, + &wot->gpio_irq_wakeable); + if (wot->gpio_irq <= 0) { + dev_warn(thc_dev->dev, "Can't find wake GPIO resource\n"); + return; + } + + if (!wot->gpio_irq_wakeable) { + dev_warn(thc_dev->dev, "GPIO resource isn't wakeable\n"); + return; + } + + ret = device_init_wakeup(thc_dev->dev, true); + if (ret) { + dev_warn(thc_dev->dev, "Failed to init wake up.\n"); + return; + } + + ret = dev_pm_set_dedicated_wake_irq(thc_dev->dev, wot->gpio_irq); + if (ret) { + dev_warn(thc_dev->dev, "Failed to set wake up IRQ.\n"); + device_init_wakeup(thc_dev->dev, false); + } +} +EXPORT_SYMBOL_NS_GPL(thc_wot_config, "INTEL_THC"); + +/** + * thc_wot_unconfig - Unconfig wake-on-touch feature + * @thc_dev: Point to thc_device structure + * + * Configure driver to disable wake-on-touch and release ACPI resource. + */ +void thc_wot_unconfig(struct thc_device *thc_dev) +{ + struct acpi_device *adev; + + if (!thc_dev) + return; + + adev = ACPI_COMPANION(thc_dev->dev); + if (!adev) + return; + + if (thc_dev->wot.gpio_irq_wakeable) + device_init_wakeup(thc_dev->dev, false); + + if (thc_dev->wot.gpio_irq > 0) { + dev_pm_clear_wake_irq(thc_dev->dev); + acpi_dev_remove_driver_gpios(adev); + } +} +EXPORT_SYMBOL_NS_GPL(thc_wot_unconfig, "INTEL_THC"); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h new file mode 100644 index 000000000000..6c700621b242 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Intel Corporation */ + +#ifndef _INTEL_THC_WOT_H_ +#define _INTEL_THC_WOT_H_ + +#include + +#include + +/** + * struct thc_wot - THC Wake-on-Touch data structure + * @gpio_irq : GPIO interrupt IRQ number for wake-on-touch + * @gpio_irq_wakeable : Indicate GPIO IRQ workable or not + */ +struct thc_wot { + int gpio_irq; + bool gpio_irq_wakeable; +}; + +struct thc_device; + +void thc_wot_config(struct thc_device *thc_dev, const struct acpi_gpio_mapping *gpio_map); +void thc_wot_unconfig(struct thc_device *thc_dev); + +#endif /* _INTEL_THC_WOT_H_ */ From 60c9fca90e3338d7d7a76e5db92071f07a164d55 Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:37:34 +0800 Subject: [PATCH 09/54] HID: Intel-thc-hid: Intel-quickspi: Enable Wake-on-Touch feature This patch call THC helper functions to enable Wake-on-Touch (WoT) during driver initialization and disable it when driver is removed. Signed-off-by: Even Xu Tested-by: Chong Han Signed-off-by: Jiri Kosina --- .../intel-thc-hid/intel-quickspi/pci-quickspi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c index d4f89f44c3b4..5e5f179dd113 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -11,8 +11,11 @@ #include #include +#include + #include "intel-thc-dev.h" #include "intel-thc-hw.h" +#include "intel-thc-wot.h" #include "quickspi-dev.h" #include "quickspi-hid.h" @@ -46,6 +49,15 @@ static guid_t thc_platform_guid = GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); + +/* QuickSPI Wake-on-Touch GPIO resource */ +static const struct acpi_gpio_params wake_gpio = { 0, 0, true }; + +static const struct acpi_gpio_mapping quickspi_gpios[] = { + { "wake-on-touch", &wake_gpio, 1 }, + { } +}; + /** * thc_acpi_get_property - Query device ACPI parameter * @@ -426,6 +438,8 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io thc_interrupt_enable(qsdev->thc_hw, true); + thc_wot_config(qsdev->thc_hw, &quickspi_gpios[0]); + qsdev->state = QUICKSPI_INITIATED; return qsdev; @@ -442,6 +456,7 @@ static void quickspi_dev_deinit(struct quickspi_device *qsdev) { thc_interrupt_enable(qsdev->thc_hw, false); thc_ltr_unconfig(qsdev->thc_hw); + thc_wot_unconfig(qsdev->thc_hw); qsdev->state = QUICKSPI_DISABLED; } From 3fdfa1e1fae3ea111f8912d028a38543059a2d7f Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 14 May 2025 14:37:35 +0800 Subject: [PATCH 10/54] HID: Intel-thc-hid: Intel-quicki2c: Enable Wake-on-Touch feature This patch call THC helper functions to enable Wake-on-Touch (WoT) during driver initialization and disable it when driver is removed. Signed-off-by: Even Xu Tested-by: Chong Han Signed-off-by: Jiri Kosina --- .../intel-thc-hid/intel-quicki2c/pci-quicki2c.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 4691d100582b..208e933e23d8 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -11,8 +11,11 @@ #include #include +#include + #include "intel-thc-dev.h" #include "intel-thc-hw.h" +#include "intel-thc-wot.h" #include "quicki2c-dev.h" #include "quicki2c-hid.h" @@ -31,6 +34,14 @@ static guid_t i2c_hid_guid = static guid_t thc_platform_guid = GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); +/* QuickI2C Wake-on-Touch GPIO resource */ +static const struct acpi_gpio_params wake_gpio = { 0, 0, true }; + +static const struct acpi_gpio_mapping quicki2c_gpios[] = { + { "wake-on-touch", &wake_gpio, 1 }, + { } +}; + /** * quicki2c_acpi_get_dsm_property - Query device ACPI DSM parameter * @adev: Point to ACPI device @@ -393,6 +404,8 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io thc_interrupt_enable(qcdev->thc_hw, true); + thc_wot_config(qcdev->thc_hw, &quicki2c_gpios[0]); + qcdev->state = QUICKI2C_INITED; return qcdev; @@ -408,6 +421,7 @@ static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) { thc_interrupt_enable(qcdev->thc_hw, false); thc_ltr_unconfig(qcdev->thc_hw); + thc_wot_unconfig(qcdev->thc_hw); qcdev->state = QUICKI2C_DISABLED; } From 4e960bb550962fe5bfbcbb34ed59c1af9daff9fa Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 19 May 2025 17:46:16 +0530 Subject: [PATCH 11/54] HID: apple: move backlight report structs to other backlight structs The apple_backlight_config_report and apple_backlight_set_report structs were incorrectly placed between the translation tables. Fix this. Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index ed34f5cd5a91..6c71baa38715 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -89,6 +89,19 @@ struct apple_sc_backlight { struct hid_device *hdev; }; +struct apple_backlight_config_report { + u8 report_id; + u8 version; + u16 backlight_off, backlight_on_min, backlight_on_max; +}; + +struct apple_backlight_set_report { + u8 report_id; + u8 version; + u16 backlight; + u16 rate; +}; + struct apple_magic_backlight { struct led_classdev cdev; struct hid_report *brightness; @@ -152,20 +165,6 @@ static const struct apple_key_translation magic_keyboard_2015_fn_keys[] = { { } }; -struct apple_backlight_config_report { - u8 report_id; - u8 version; - u16 backlight_off, backlight_on_min, backlight_on_max; -}; - -struct apple_backlight_set_report { - u8 report_id; - u8 version; - u16 backlight; - u16 rate; -}; - - static const struct apple_key_translation apple2021_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_ENTER, KEY_INSERT }, From b45944946a19636f7bbded0763b536ce007c3c9b Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 19 May 2025 17:46:17 +0530 Subject: [PATCH 12/54] HID: apple: use switch case to set fn translation table There has been a continuous increase in the number of devices requiring hid-apple driver during the last few years. Moreover, unlike previous releases, the PIDs of the newer devices released cannot be combined in a specific range, thus filling up the if else if statements with individual device IDs. For such large table, its now more suitable to use switch case instead of if else if for improved readability. Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 73 +++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 6c71baa38715..1c8475598f51 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -465,42 +465,51 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, asc->fn_on = !!value; if (real_fnmode) { - if (hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO || - hid->product == USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) + switch (hid->product) { + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO: + case USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS: table = magic_keyboard_alu_fn_keys; - else if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015 || - hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015) + break; + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015: + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015: table = magic_keyboard_2015_fn_keys; - else if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 || - hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024 || - hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 || - hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021) + break; + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021: + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024: + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021: + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021: table = apple2021_fn_keys; - else if (hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132 || - hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680 || - hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213) - table = macbookpro_no_esc_fn_keys; - else if (hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K || - hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223 || - hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F) - table = macbookpro_dedicated_esc_fn_keys; - else if (hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K || - hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K) - table = apple_fn_keys; - else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && - hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) - table = macbookair_fn_keys; - else if (hid->product < 0x21d || hid->product >= 0x300) - table = powerbook_fn_keys; - else + break; + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132: + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213: + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680: + table = macbookpro_no_esc_fn_keys; + break; + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F: + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K: + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223: + table = macbookpro_dedicated_esc_fn_keys; + break; + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K: + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K: table = apple_fn_keys; + break; + default: + if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && + hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) + table = macbookair_fn_keys; + else if (hid->product < 0x21d || hid->product >= 0x300) + table = powerbook_fn_keys; + else + table = apple_fn_keys; + } trans = apple_find_translation(table, code); From 46d74dd9b45f84bf8e900447c5b4b69d58733f42 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 19 May 2025 17:46:18 +0530 Subject: [PATCH 13/54] HID: apple: remove unused APPLE_IGNORE_MOUSE quirk The APPLE_IGNORE_MOUSE quirk was not used anywhere in this driver, so can be removed. Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 1c8475598f51..7bbc811b06f8 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -30,7 +30,7 @@ #include "hid-ids.h" #define APPLE_RDESC_JIS BIT(0) -#define APPLE_IGNORE_MOUSE BIT(1) +/* BIT(1) reserved, was: APPLE_IGNORE_MOUSE */ #define APPLE_HAS_FN BIT(2) /* BIT(3) reserved, was: APPLE_HIDDEV */ #define APPLE_ISO_TILDE_QUIRK BIT(4) From e77bdf51de070610328255d011c439634a9ea932 Mon Sep 17 00:00:00 2001 From: Grigorii Sokolik Date: Mon, 19 May 2025 17:46:19 +0530 Subject: [PATCH 14/54] HID: apple: Add Apple Magic Keyboard A3118 USB-C support Add Apple Magic Keyboard 2024 with Touch ID device ID (05ac:0321) to those recognized by the hid-apple driver. Keyboard is otherwise compatible with the existing implementation for its earlier 2021 model. Signed-off-by: Grigorii Sokolik Co-developed-by: Aditya Garg Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 21 +++++++++++++-------- drivers/hid/hid-ids.h | 9 +++++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 7bbc811b06f8..5c040976638b 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -165,7 +165,7 @@ static const struct apple_key_translation magic_keyboard_2015_fn_keys[] = { { } }; -static const struct apple_key_translation apple2021_fn_keys[] = { +static const struct apple_key_translation magic_keyboard_2021_and_2024_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_ENTER, KEY_INSERT }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, @@ -482,10 +482,11 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, table = magic_keyboard_2015_fn_keys; break; case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021: - case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024: case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021: case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021: - table = apple2021_fn_keys; + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024: + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2024: + table = magic_keyboard_2021_and_2024_fn_keys; break; case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132: case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213: @@ -690,7 +691,7 @@ static void apple_setup_input(struct input_dev *input) apple_setup_key_translation(input, apple_iso_keyboard); apple_setup_key_translation(input, magic_keyboard_alu_fn_keys); apple_setup_key_translation(input, magic_keyboard_2015_fn_keys); - apple_setup_key_translation(input, apple2021_fn_keys); + apple_setup_key_translation(input, magic_keyboard_2021_and_2024_fn_keys); apple_setup_key_translation(input, macbookpro_no_esc_fn_keys); apple_setup_key_translation(input, macbookpro_dedicated_esc_fn_keys); } @@ -1165,10 +1166,6 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, - { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021), @@ -1177,6 +1174,14 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT), .driver_data = APPLE_MAGIC_BACKLIGHT }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e3fb4e2fe911..6e66067347f7 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -167,6 +167,11 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS 0x0257 #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015 0x0267 #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015 0x026c +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 0x029c +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 0x029a +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024 0x0320 +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2024 0x0321 #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 @@ -188,10 +193,6 @@ #define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 #define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 -#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 0x029c -#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024 0x0320 -#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 0x029a -#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f #define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102 #define USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY 0x8302 From 4604baafaaeeb131dfb8d9325b1e8e90f364c468 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 19 May 2025 17:46:20 +0530 Subject: [PATCH 15/54] HID: apple: Add Apple Magic Keyboard A3119 USB-C support Add Apple Magic Keyboard 2024 with Touch ID and Numeric Keypad device ID (05ac:0322) to those recognized by the hid-apple driver. Keyboard is otherwise compatible with the existing implementation for its earlier 2021 model. Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 5 +++++ drivers/hid/hid-ids.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 5c040976638b..d9f13f610b28 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -486,6 +486,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021: case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024: case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2024: + case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2024: table = magic_keyboard_2021_and_2024_fn_keys; break; case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132: @@ -1182,6 +1183,10 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2024), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT), .driver_data = APPLE_MAGIC_BACKLIGHT }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6e66067347f7..dd0a48595d57 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -172,6 +172,7 @@ #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024 0x0320 #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2024 0x0321 +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2024 0x0322 #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 From a23ff6080e7197922e3a9fd8856e142b612eb50a Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 19 May 2025 17:46:21 +0530 Subject: [PATCH 16/54] HID: apple: add fnmode=4 to disable translation of fkeys and make it default on Macs with Touch Bar The kernel now has a dedicated driver for Touch Bar on Macs. Since function keys can now be accessed via the Touch Bar, emulating them using non standard ways like Fn+1=F1 should be avoided. This patch adds an fnmode=4 which ignores only the Function key translation, and is enabled by default on MacBook Pros with a Touch Bar. Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 90 ++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index d9f13f610b28..155d514fb4c5 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -42,8 +42,10 @@ #define APPLE_BACKLIGHT_CTL BIT(10) #define APPLE_IS_NON_APPLE BIT(11) #define APPLE_MAGIC_BACKLIGHT BIT(12) +#define APPLE_DISABLE_FKEYS BIT(13) -#define APPLE_FLAG_FKEY 0x01 +#define APPLE_FLAG_FKEY BIT(0) +#define APPLE_FLAG_TB_FKEY BIT(1) #define HID_COUNTRY_INTERNATIONAL_ISO 13 #define APPLE_BATTERY_TIMEOUT_MS 60000 @@ -55,7 +57,7 @@ static unsigned int fnmode = 3; module_param(fnmode, uint, 0644); MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " - "1 = fkeyslast, 2 = fkeysfirst, [3] = auto)"); + "1 = fkeyslast, 2 = fkeysfirst, [3] = auto, 4 = fkeysdisabled)"); static int iso_layout = -1; module_param(iso_layout, int, 0644); @@ -121,7 +123,7 @@ struct apple_sc { struct apple_key_translation { u16 from; u16 to; - u8 flags; + unsigned long flags; }; static const struct apple_key_translation magic_keyboard_alu_fn_keys[] = { @@ -211,19 +213,19 @@ static const struct apple_key_translation macbookair_fn_keys[] = { static const struct apple_key_translation macbookpro_no_esc_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_ENTER, KEY_INSERT }, - { KEY_GRAVE, KEY_ESC }, - { KEY_1, KEY_F1 }, - { KEY_2, KEY_F2 }, - { KEY_3, KEY_F3 }, - { KEY_4, KEY_F4 }, - { KEY_5, KEY_F5 }, - { KEY_6, KEY_F6 }, - { KEY_7, KEY_F7 }, - { KEY_8, KEY_F8 }, - { KEY_9, KEY_F9 }, - { KEY_0, KEY_F10 }, - { KEY_MINUS, KEY_F11 }, - { KEY_EQUAL, KEY_F12 }, + { KEY_GRAVE, KEY_ESC, APPLE_FLAG_TB_FKEY }, + { KEY_1, KEY_F1, APPLE_FLAG_TB_FKEY }, + { KEY_2, KEY_F2, APPLE_FLAG_TB_FKEY }, + { KEY_3, KEY_F3, APPLE_FLAG_TB_FKEY }, + { KEY_4, KEY_F4, APPLE_FLAG_TB_FKEY }, + { KEY_5, KEY_F5, APPLE_FLAG_TB_FKEY }, + { KEY_6, KEY_F6, APPLE_FLAG_TB_FKEY }, + { KEY_7, KEY_F7, APPLE_FLAG_TB_FKEY }, + { KEY_8, KEY_F8, APPLE_FLAG_TB_FKEY }, + { KEY_9, KEY_F9, APPLE_FLAG_TB_FKEY }, + { KEY_0, KEY_F10, APPLE_FLAG_TB_FKEY }, + { KEY_MINUS, KEY_F11, APPLE_FLAG_TB_FKEY }, + { KEY_EQUAL, KEY_F12, APPLE_FLAG_TB_FKEY }, { KEY_UP, KEY_PAGEUP }, { KEY_DOWN, KEY_PAGEDOWN }, { KEY_LEFT, KEY_HOME }, @@ -234,18 +236,18 @@ static const struct apple_key_translation macbookpro_no_esc_fn_keys[] = { static const struct apple_key_translation macbookpro_dedicated_esc_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_ENTER, KEY_INSERT }, - { KEY_1, KEY_F1 }, - { KEY_2, KEY_F2 }, - { KEY_3, KEY_F3 }, - { KEY_4, KEY_F4 }, - { KEY_5, KEY_F5 }, - { KEY_6, KEY_F6 }, - { KEY_7, KEY_F7 }, - { KEY_8, KEY_F8 }, - { KEY_9, KEY_F9 }, - { KEY_0, KEY_F10 }, - { KEY_MINUS, KEY_F11 }, - { KEY_EQUAL, KEY_F12 }, + { KEY_1, KEY_F1, APPLE_FLAG_TB_FKEY }, + { KEY_2, KEY_F2, APPLE_FLAG_TB_FKEY }, + { KEY_3, KEY_F3, APPLE_FLAG_TB_FKEY }, + { KEY_4, KEY_F4, APPLE_FLAG_TB_FKEY }, + { KEY_5, KEY_F5, APPLE_FLAG_TB_FKEY }, + { KEY_6, KEY_F6, APPLE_FLAG_TB_FKEY }, + { KEY_7, KEY_F7, APPLE_FLAG_TB_FKEY }, + { KEY_8, KEY_F8, APPLE_FLAG_TB_FKEY }, + { KEY_9, KEY_F9, APPLE_FLAG_TB_FKEY }, + { KEY_0, KEY_F10, APPLE_FLAG_TB_FKEY }, + { KEY_MINUS, KEY_F11, APPLE_FLAG_TB_FKEY }, + { KEY_EQUAL, KEY_F12, APPLE_FLAG_TB_FKEY }, { KEY_UP, KEY_PAGEUP }, { KEY_DOWN, KEY_PAGEDOWN }, { KEY_LEFT, KEY_HOME }, @@ -424,7 +426,12 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, unsigned int real_fnmode; if (fnmode == 3) { - real_fnmode = (asc->quirks & APPLE_IS_NON_APPLE) ? 2 : 1; + if (asc->quirks & APPLE_DISABLE_FKEYS) + real_fnmode = 4; + else if (asc->quirks & APPLE_IS_NON_APPLE) + real_fnmode = 2; + else + real_fnmode = 1; } else { real_fnmode = fnmode; } @@ -534,9 +541,17 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, do_translate = asc->fn_on; break; default: - /* should never happen */ + /* case 4 */ do_translate = false; } + } else if (trans->flags & APPLE_FLAG_TB_FKEY) { + switch (real_fnmode) { + case 4: + do_translate = false; + break; + default: + do_translate = asc->fn_on; + } } else { do_translate = asc->fn_on; } @@ -1139,19 +1154,22 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K), .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132), - .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | + APPLE_DISABLE_FKEYS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680), - .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | + APPLE_DISABLE_FKEYS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213), - .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | + APPLE_DISABLE_FKEYS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), From c5f3a74310fa807d0383c109799100217f739563 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 19 May 2025 17:46:22 +0530 Subject: [PATCH 17/54] HID: quirks: remove T2 devices from hid_mouse_ignore_list While the T2 devices have been added to hid_mouse_ignore_list, no driver exists outside the HID subsystem for their trackpads. So, remove them and let the trackpad be at least used without the gestures. This patch will also enable using hid-magicmouse for these trackpads later, which is currently wip downstream. Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-quirks.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 7fefeb413ec3..8a418f4878e2 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -969,14 +969,6 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K) }, - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } From a71338bb8bb1bbaa43e682137d8dc484a1391ea6 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 19 May 2025 17:46:23 +0530 Subject: [PATCH 18/54] HID: apple: Add necessary IDs and support for replacement trackpad on MacBookPro15,1 Some third party trackpad replacements like that of HAWSON may have a different PID than the stock trackpad. This commit adds one such trackpad to hid-apple, available for MacBookPro15,1 Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 4 ++++ drivers/hid/hid-ids.h | 17 +++++++++-------- drivers/hid/hid-quirks.c | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 155d514fb4c5..b8b99eb01a35 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -499,6 +499,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132: case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213: case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680: + case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680_ALT: table = macbookpro_no_esc_fn_keys; break; case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F: @@ -1159,6 +1160,9 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680), .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680_ALT), + .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | + APPLE_DISABLE_FKEYS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213), .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index dd0a48595d57..041b80bf3c99 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -179,14 +179,15 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272 #define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273 #define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274 -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K 0x027a -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132 0x027b -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680 0x027c -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213 0x027d -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K 0x027e -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223 0x027f -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K 0x0280 -#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F 0x0340 +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K 0x027a +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132 0x027b +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680 0x027c +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680_ALT 0x0278 +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213 0x027d +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K 0x027e +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223 0x027f +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K 0x0280 +#define USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F 0x0340 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 8a418f4878e2..1fba3b7f6d41 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -314,6 +314,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680_ALT) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223) }, From f181c1a4b0e8b7e76590e4e9e9ef2fc6f3e042f2 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 10 Jun 2025 21:25:00 +0200 Subject: [PATCH 19/54] HID: intel-thc: make ptl_ddata static ptl_ddata is local to pci-quicki2c.c, so it'd better be static. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202505171535.Yrj5T8jh-lkp@intel.com/ Signed-off-by: Jiri Kosina --- drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 208e933e23d8..e944a6ccb776 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -21,7 +21,7 @@ #include "quicki2c-hid.h" #include "quicki2c-protocol.h" -struct quicki2c_ddata ptl_ddata = { +static struct quicki2c_ddata ptl_ddata = { .max_detect_size = MAX_RX_DETECT_SIZE_PTL, }; From 9256c4bed64fe29198a922ac2d94cdf27506d47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:14 +0200 Subject: [PATCH 20/54] HID: universal-pidff: Fix missing blank lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes errors from checkpatch script Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/hid-universal-pidff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c index 001a0f5efb9d..dad6c5a6e2df 100644 --- a/drivers/hid/hid-universal-pidff.c +++ b/drivers/hid/hid-universal-pidff.c @@ -57,6 +57,7 @@ static int universal_pidff_probe(struct hid_device *hdev, const struct hid_device_id *id) { int i, error; + error = hid_parse(hdev); if (error) { hid_err(hdev, "HID parse failed\n"); @@ -91,8 +92,8 @@ static int universal_pidff_probe(struct hid_device *hdev, /* Check if HID_PID support is enabled */ int (*init_function)(struct hid_device *, u32); - init_function = hid_pidff_init_with_quirks; + init_function = hid_pidff_init_with_quirks; if (!init_function) { hid_warn(hdev, "HID_PID support not enabled!\n"); return 0; From b913c5c2b4f9f57913f25b439361c4ff2d7a27ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:15 +0200 Subject: [PATCH 21/54] HID: hid-ids.h: Fix LITE_STAR_GT987 device id define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name was missing "ID" part Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 2 +- drivers/hid/hid-universal-pidff.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e3fb4e2fe911..55734db1fb9c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -842,7 +842,7 @@ #define USB_DEVICE_ID_PXN_V12 0x1212 #define USB_DEVICE_ID_PXN_V12_LITE 0x1112 #define USB_DEVICE_ID_PXN_V12_LITE_2 0x1211 -#define USB_DEVICE_LITE_STAR_GT987_FF 0x2141 +#define USB_DEVICE_ID_LITE_STAR_GT987 0x2141 #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_Z_10_SPK 0x0a07 diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c index dad6c5a6e2df..554a6559aeb7 100644 --- a/drivers/hid/hid-universal-pidff.c +++ b/drivers/hid/hid-universal-pidff.c @@ -178,7 +178,7 @@ static const struct hid_device_id universal_pidff_devices[] = { .driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY }, { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12_LITE_2), .driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY }, - { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_LITE_STAR_GT987_FF), + { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_LITE_STAR_GT987), .driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY }, { HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_INVICTA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_FORTE) }, From 18243efd7134ff461a4b1a795fbdef1d9b336b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:16 +0200 Subject: [PATCH 22/54] HID: hid-pidff.h: Fix comment styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes checkpatch.pl warnings Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.h b/drivers/hid/usbhid/hid-pidff.h index dda571e0a5bd..a53a8b436baa 100644 --- a/drivers/hid/usbhid/hid-pidff.h +++ b/drivers/hid/usbhid/hid-pidff.h @@ -9,8 +9,7 @@ /* Delay field (0xA7) missing. Skip it during set effect report upload */ #define HID_PIDFF_QUIRK_MISSING_DELAY BIT(0) -/* Missing Paramter block offset (0x23). Skip it during SET_CONDITION - report upload */ +/* Missing Paramter block offset (0x23). Skip it during SET_CONDITION upload */ #define HID_PIDFF_QUIRK_MISSING_PBO BIT(1) /* Initialise device control field even if logical_minimum != 1 */ From 42a2bd61650f13605f9283b5a9e6b1a26b7d4ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:17 +0200 Subject: [PATCH 23/54] HID: pidff: Fix missing blank lines after declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes chackpatch.pl warnings Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 8dfd2c554a27..3c2b20cf7853 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -241,8 +241,8 @@ static u32 pidff_rescale_time(u16 time, struct hid_field *field) { u32 scaled_time = time; int exponent = field->unit_exponent; - pr_debug("time field exponent: %d\n", exponent); + pr_debug("time field exponent: %d\n", exponent); for (;exponent < FF_TIME_EXPONENT; exponent++) scaled_time *= 10; for (;exponent > FF_TIME_EXPONENT; exponent--) @@ -332,6 +332,7 @@ static int pidff_needs_set_envelope(struct ff_envelope *envelope, struct ff_envelope *old) { bool needs_new_envelope; + needs_new_envelope = envelope->attack_level != 0 || envelope->fade_level != 0 || envelope->attack_length != 0 || @@ -715,6 +716,7 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) static int pidff_playback(struct input_dev *dev, int effect_id, int value) { struct pidff_device *pidff = dev->ff->private; + pidff_playback_pid(pidff, pidff->pid_id[effect_id], value); return 0; } @@ -1207,6 +1209,7 @@ static int pidff_find_effects(struct pidff_device *pidff, for (i = 0; i < sizeof(pidff_effect_types); i++) { int pidff_type = pidff->type_id[i]; + if (pidff->set_effect_type->usage[pidff_type].hid != pidff->create_new_effect_type->usage[pidff_type].hid) { hid_err(pidff->hid, From a0ea54654dd7b53df85d81f5201a78f842ea713f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:18 +0200 Subject: [PATCH 24/54] HID: pidff: Remove unneeded debug from pidff_clamp() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes chechpatch.pl warning about missing blank line Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 3c2b20cf7853..44cbd2a0461a 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -210,9 +210,7 @@ struct pidff_device { */ static s32 pidff_clamp(s32 i, struct hid_field *field) { - s32 clamped = clamp(i, field->logical_minimum, field->logical_maximum); - pr_debug("clamped from %d to %d", i, clamped); - return clamped; + return (s32)clamp(i, field->logical_minimum, field->logical_maximum); } /* From bed72bd240ce77623845853397b89a08f6dc5770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:19 +0200 Subject: [PATCH 25/54] HID: pidff: Rework pidff_set_time() to fix warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes blank line warning from checkpatch.pl script Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 44cbd2a0461a..743b76c8d3b2 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -273,8 +273,8 @@ static void pidff_set_signed(struct pidff_usage *usage, s16 value) static void pidff_set_time(struct pidff_usage *usage, u16 time) { - u32 modified_time = pidff_rescale_time(time, usage->field); - usage->value[0] = pidff_clamp(modified_time, usage->field); + usage->value[0] = pidff_clamp( + pidff_rescale_time(time, usage->field), usage->field); } static void pidff_set_duration(struct pidff_usage *usage, u16 duration) From 3ab6f57dbebb007c177168be0011238fe8fddcee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:20 +0200 Subject: [PATCH 26/54] HID: pidff: Use __func__ in debugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes checkpatch.pl warnings Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 743b76c8d3b2..88eb832265a4 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -940,7 +940,7 @@ static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, struct hid_report *report, int count, int strict) { if (!report) { - pr_debug("pidff_find_fields, null report\n"); + pr_debug("%s, null report\n", __func__); return -1; } @@ -1069,7 +1069,7 @@ static struct hid_field *pidff_find_special_field(struct hid_report *report, int usage, int enforce_min) { if (!report) { - pr_debug("pidff_find_special_field, null report\n"); + pr_debug("%s, null report\n", __func__); return NULL; } From 9c2f3ecd37ddcd714e4a476a5469d9b6663992ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:21 +0200 Subject: [PATCH 27/54] HID: pidff: Remove unneeded else in pidff_find_special_field() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes checkpatch.pl warning Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 88eb832265a4..6f25490ce464 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -1081,10 +1081,9 @@ static struct hid_field *pidff_find_special_field(struct hid_report *report, if (!enforce_min || report->field[i]->logical_minimum == 1) return report->field[i]; - else { - pr_err("logical_minimum is not 1 as it should be\n"); - return NULL; - } + + pr_err("logical_minimum is not 1 as it should be\n"); + return NULL; } } return NULL; From 3f7fd8cb8f408095ff97276d4e30ff87c112018e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:22 +0200 Subject: [PATCH 28/54] HID: pidff: Move trailing statements in pidff_rescale_signed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds newline before trailing if statements. Fixes checkpatch.pl errors Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 6f25490ce464..0e19d61f4936 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -227,8 +227,10 @@ static int pidff_rescale(int i, int max, struct hid_field *field) */ static int pidff_rescale_signed(int i, struct hid_field *field) { - if (i > 0) return i * field->logical_maximum / S16_MAX; - if (i < 0) return i * field->logical_minimum / S16_MIN; + if (i > 0) + return i * field->logical_maximum / S16_MAX; + if (i < 0) + return i * field->logical_minimum / S16_MIN; return 0; } From 61ea33ded9327a07d9ef85a6933cb6316e2f185f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:23 +0200 Subject: [PATCH 29/54] HID: pidff: Add missing spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes checkpatch.pl errors Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 0e19d61f4936..a07c5efddbe4 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -243,9 +243,9 @@ static u32 pidff_rescale_time(u16 time, struct hid_field *field) int exponent = field->unit_exponent; pr_debug("time field exponent: %d\n", exponent); - for (;exponent < FF_TIME_EXPONENT; exponent++) + for (; exponent < FF_TIME_EXPONENT; exponent++) scaled_time *= 10; - for (;exponent > FF_TIME_EXPONENT; exponent--) + for (; exponent > FF_TIME_EXPONENT; exponent--) scaled_time /= 10; pr_debug("time calculated from %d to %d\n", time, scaled_time); @@ -569,7 +569,7 @@ static void pidff_set_device_control(struct pidff_device *pidff, int field) hid_dbg(pidff->hid, "DEVICE_CONTROL is a bitmask\n"); /* Clear current bitmask */ - for(i = 0; i < sizeof(pidff_device_control); i++) { + for (i = 0; i < sizeof(pidff_device_control); i++) { index = pidff->control_id[i]; if (index < 1) continue; @@ -620,7 +620,7 @@ static void pidff_fetch_pool(struct pidff_device *pidff) struct hid_device *hid = pidff->hid; /* Repeat if PID_SIMULTANEOUS_MAX < 2 to make sure it's correct */ - for(i = 0; i < 20; i++) { + for (i = 0; i < 20; i++) { hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT); hid_hw_wait(hid); @@ -851,7 +851,7 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, case FF_INERTIA: case FF_FRICTION: if (!old) { - switch(effect->type) { + switch (effect->type) { case FF_SPRING: type_id = PID_SPRING; break; From 703e55aacbf73da570cc276e69ec6cbe16198376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sat, 24 May 2025 19:47:24 +0200 Subject: [PATCH 30/54] HID: pidff: Move else if statements to follow closing braces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes checkpatch.pl errors Signed-off-by: Tomasz Pakuła Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-pidff.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index a07c5efddbe4..614a20b62023 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -976,13 +976,11 @@ static int pidff_find_fields(struct pidff_usage *usage, const u8 *table, pr_debug("Delay field not found, but that's OK\n"); pr_debug("Setting MISSING_DELAY quirk\n"); return_value |= HID_PIDFF_QUIRK_MISSING_DELAY; - } - else if (!found && table[k] == pidff_set_condition[PID_PARAM_BLOCK_OFFSET]) { + } else if (!found && table[k] == pidff_set_condition[PID_PARAM_BLOCK_OFFSET]) { pr_debug("PBO field not found, but that's OK\n"); pr_debug("Setting MISSING_PBO quirk\n"); return_value |= HID_PIDFF_QUIRK_MISSING_PBO; - } - else if (!found && strict) { + } else if (!found && strict) { pr_debug("failed to locate %d\n", k); return -1; } From d3db1ce9bf8f4a8e03d3bd19000b32290bca7d74 Mon Sep 17 00:00:00 2001 From: Zihuan Zhang Date: Fri, 9 May 2025 10:25:54 +0800 Subject: [PATCH 31/54] HID: debug: Use the __set_current_state() When detecting an invalid list->hdev, the process needs to manually set its state to TASK_RUNNING and exit. In the original code, set_current_state() (which includes a memory barrier) is used here, but it is immediately followed by a mutex_unlock() call. Since mutex_unlock() internally includes a memory barrier, this ensures that all modifications within the critical section (including the process state) are visible to other CPUs. Therefore, replacing it with __set_current_state() (without an implicit barrier) avoids redundant memory barriers. Signed-off-by: Zihuan Zhang Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 8433306148d5..00a73983ef19 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3726,7 +3726,7 @@ static ssize_t hid_debug_events_read(struct file *file, char __user *buffer, */ if (!list->hdev || !list->hdev->debug) { ret = -EIO; - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); goto out; } From f41d736acc039d86512951f4e874b0f5e666babf Mon Sep 17 00:00:00 2001 From: Kerem Karabay Date: Tue, 27 May 2025 22:13:13 +0530 Subject: [PATCH 32/54] HID: multitouch: Get the contact ID from HID_DG_TRANSDUCER_INDEX fields in case of Apple Touch Bar In Apple Touch Bar, the contact ID is contained in fields with the HID_DG_TRANSDUCER_INDEX usage rather than HID_DG_CONTACTID, thus differing from the HID spec. Add a quirk for the same. Acked-by: Benjamin Tissoires Signed-off-by: Kerem Karabay Co-developed-by: Aditya Garg Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index ded0fef7d8c7..849344b0ee57 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -73,6 +73,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_FORCE_MULTI_INPUT BIT(20) #define MT_QUIRK_DISABLE_WAKEUP BIT(21) #define MT_QUIRK_ORIENTATION_INVERT BIT(22) +#define MT_QUIRK_APPLE_TOUCHBAR BIT(23) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -625,6 +626,7 @@ static struct mt_application *mt_find_application(struct mt_device *td, static struct mt_report_data *mt_allocate_report_data(struct mt_device *td, struct hid_report *report) { + struct mt_class *cls = &td->mtclass; struct mt_report_data *rdata; struct hid_field *field; int r, n; @@ -649,7 +651,11 @@ static struct mt_report_data *mt_allocate_report_data(struct mt_device *td, if (field->logical == HID_DG_FINGER || td->hdev->group != HID_GROUP_MULTITOUCH_WIN_8) { for (n = 0; n < field->report_count; n++) { - if (field->usage[n].hid == HID_DG_CONTACTID) { + unsigned int hid = field->usage[n].hid; + + if (hid == HID_DG_CONTACTID || + (cls->quirks & MT_QUIRK_APPLE_TOUCHBAR && + hid == HID_DG_TRANSDUCER_INDEX)) { rdata->is_mt_collection = true; break; } @@ -827,6 +833,14 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, EV_KEY, BTN_TOUCH); MT_STORE_FIELD(tip_state); return 1; + case HID_DG_TRANSDUCER_INDEX: + /* + * Contact ID in case of Apple Touch Bars is contained + * in fields with HID_DG_TRANSDUCER_INDEX usage. + */ + if (!(cls->quirks & MT_QUIRK_APPLE_TOUCHBAR)) + return 0; + fallthrough; case HID_DG_CONTACTID: MT_STORE_FIELD(contactid); app->touches_by_report++; From e0976a61a543b5e03bc0d08030a0ea036ee3751d Mon Sep 17 00:00:00 2001 From: Kerem Karabay Date: Tue, 27 May 2025 22:13:14 +0530 Subject: [PATCH 33/54] HID: multitouch: support getting the tip state from HID_DG_TOUCH fields in Apple Touch Bar In Apple Touch Bar, the tip state is contained in fields with the HID_DG_TOUCH usage. This feature is gated by a quirk in order to prevent breaking other devices, see commit c2ef8f21ea8f ("HID: multitouch: add support for trackpads"). Acked-by: Benjamin Tissoires Signed-off-by: Kerem Karabay Co-developed-by: Aditya Garg Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 849344b0ee57..e3382afc74c9 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -827,6 +827,17 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, MT_STORE_FIELD(confidence_state); return 1; + case HID_DG_TOUCH: + /* + * Legacy devices use TIPSWITCH and not TOUCH. + * One special case here is of the Apple Touch Bars. + * In these devices, the tip state is contained in + * fields with the HID_DG_TOUCH usage. + * Let's just ignore this field for other devices. + */ + if (!(cls->quirks & MT_QUIRK_APPLE_TOUCHBAR)) + return -1; + fallthrough; case HID_DG_TIPSWITCH: if (field->application != HID_GD_SYSTEM_MULTIAXIS) input_set_capability(hi->input, @@ -897,10 +908,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_CONTACTMAX: /* contact max are global to the report */ return -1; - case HID_DG_TOUCH: - /* Legacy devices use TIPSWITCH and not TOUCH. - * Let's just ignore this field. */ - return -1; } /* let hid-input decide for the others */ return 0; From 7dfe48bdc9d38db46283f2e0281bc1626277b8bf Mon Sep 17 00:00:00 2001 From: Kerem Karabay Date: Tue, 27 May 2025 22:13:15 +0530 Subject: [PATCH 34/54] HID: multitouch: take cls->maxcontacts into account for Apple Touch Bar even without a HID_DG_CONTACTMAX field In Apple Touch Bar, the HID_DG_CONTACTMAX is not present, but the maximum contact count is still greater than the default. Add quirks for the same. Acked-by: Benjamin Tissoires Signed-off-by: Kerem Karabay Co-developed-by: Aditya Garg Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e3382afc74c9..756136f84168 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1335,6 +1335,13 @@ static int mt_touch_input_configured(struct hid_device *hdev, struct input_dev *input = hi->input; int ret; + /* + * HID_DG_CONTACTMAX field is not present on Apple Touch Bars, + * but the maximum contact count is greater than the default. + */ + if (cls->quirks & MT_QUIRK_APPLE_TOUCHBAR && cls->maxcontacts) + td->maxcontacts = cls->maxcontacts; + if (!td->maxcontacts) td->maxcontacts = MT_DEFAULT_MAXCONTACT; From 45ca23c5ee8b2b3074377fecc92fa72aa595f7c9 Mon Sep 17 00:00:00 2001 From: Kerem Karabay Date: Tue, 27 May 2025 22:13:16 +0530 Subject: [PATCH 35/54] HID: multitouch: specify that Apple Touch Bar is direct Currently the driver determines the device type based on the application, but this value is not reliable on Apple Touch Bar, where the application is HID_DG_TOUCHPAD even though this device is direct, so add a quirk for the same. Acked-by: Benjamin Tissoires Signed-off-by: Kerem Karabay Co-developed-by: Aditya Garg Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 756136f84168..31b88eb43527 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1349,6 +1349,13 @@ static int mt_touch_input_configured(struct hid_device *hdev, if (td->serial_maybe) mt_post_parse_default_settings(td, app); + /* + * The application for Apple Touch Bars is HID_DG_TOUCHPAD, + * but these devices are direct. + */ + if (cls->quirks & MT_QUIRK_APPLE_TOUCHBAR) + app->mt_flags |= INPUT_MT_DIRECT; + if (cls->is_indirect) app->mt_flags |= INPUT_MT_POINTER; From 2c31ec923c323229566d799267000f8123af4449 Mon Sep 17 00:00:00 2001 From: Kerem Karabay Date: Tue, 27 May 2025 22:13:17 +0530 Subject: [PATCH 36/54] HID: multitouch: add device ID for Apple Touch Bar This patch adds the device ID of Apple Touch Bar found on x86 MacBook Pros to the hid-multitouch driver. Note that this is device ID is for T2 Macs. Testing on T1 Macs would be appreciated. Signed-off-by: Kerem Karabay Co-developed-by: Aditya Garg Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/hid-multitouch.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 43859fc75747..a57901203aeb 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -771,6 +771,7 @@ config HID_MULTITOUCH Say Y here if you have one of the following devices: - 3M PCT touch screens - ActionStar dual touch panels + - Apple Touch Bar on x86 MacBook Pros - Atmel panels - Cando dual touch panels - Chunghwa panels diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 31b88eb43527..6ab838c36cad 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -221,6 +221,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_APPLE_TOUCHBAR 0x0114 #define MT_CLS_SIS 0x0457 #define MT_DEFAULT_MAXCONTACT 10 @@ -406,6 +407,12 @@ static const struct mt_class mt_classes[] = { MT_QUIRK_CONTACT_CNT_ACCURATE | MT_QUIRK_SEPARATE_APP_REPORT, }, + { .name = MT_CLS_APPLE_TOUCHBAR, + .quirks = MT_QUIRK_HOVERING | + MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE | + MT_QUIRK_APPLE_TOUCHBAR, + .maxcontacts = 11, + }, { .name = MT_CLS_SIS, .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | MT_QUIRK_ALWAYS_VALID | @@ -1858,6 +1865,11 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret != 0) return ret; + if (mtclass->name == MT_CLS_APPLE_TOUCHBAR && + !hid_find_field(hdev, HID_INPUT_REPORT, + HID_DG_TOUCHPAD, HID_DG_TRANSDUCER_INDEX)) + return -ENODEV; + if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID) mt_fix_const_fields(hdev, HID_DG_CONTACTID); @@ -2349,6 +2361,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, + /* Apple Touch Bar */ + { .driver_data = MT_CLS_APPLE_TOUCHBAR, + HID_USB_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, + /* Google MT devices */ { .driver_data = MT_CLS_GOOGLE, HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, From 0d6b550dce55f2bd47579a92ffc000cb0186e4c4 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sun, 8 Jun 2025 18:33:15 +0200 Subject: [PATCH 37/54] HID: mcp2221: set gpio pin mode in case we have GPIOLIB enabled the gpio pins are used from the current driver as gpio pins. But may the gpio functions of this pins are not enabled in the flash of the chip and so gpio access fails. In case CONFIG_IIO is not enabled we can prevent this issue of the driver simply by enabling the gpio mode for all pins. Signed-off-by: Heiko Schocher Signed-off-by: Jiri Kosina --- drivers/hid/hid-mcp2221.c | 97 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index 6c0ac14f11a6..3bc5884e572e 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -55,6 +55,27 @@ enum { MCP2221_ALT_F_NOT_GPIOD = 0xEF, }; +/* MCP SRAM read offsets cmd: MCP2221_GET_SRAM_SETTINGS */ +enum { + MCP2221_SRAM_RD_GP0 = 22, + MCP2221_SRAM_RD_GP1 = 23, + MCP2221_SRAM_RD_GP2 = 24, + MCP2221_SRAM_RD_GP3 = 25, +}; + +/* MCP SRAM write offsets cmd: MCP2221_SET_SRAM_SETTINGS */ +enum { + MCP2221_SRAM_WR_GP_ENA_ALTER = 7, + MCP2221_SRAM_WR_GP0 = 8, + MCP2221_SRAM_WR_GP1 = 9, + MCP2221_SRAM_WR_GP2 = 10, + MCP2221_SRAM_WR_GP3 = 11, +}; + +#define MCP2221_SRAM_GP_DESIGN_MASK 0x07 +#define MCP2221_SRAM_GP_DIRECTION_MASK 0x08 +#define MCP2221_SRAM_GP_VALUE_MASK 0x10 + /* MCP GPIO direction encoding */ enum { MCP2221_DIR_OUT = 0x00, @@ -607,6 +628,80 @@ static const struct i2c_algorithm mcp_i2c_algo = { }; #if IS_REACHABLE(CONFIG_GPIOLIB) +static int mcp_gpio_read_sram(struct mcp2221 *mcp) +{ + int ret; + + memset(mcp->txbuf, 0, 64); + mcp->txbuf[0] = MCP2221_GET_SRAM_SETTINGS; + + mutex_lock(&mcp->lock); + ret = mcp_send_data_req_status(mcp, mcp->txbuf, 64); + mutex_unlock(&mcp->lock); + + return ret; +} + +/* + * If CONFIG_IIO is not enabled, check for the gpio pins + * if they are in gpio mode. For the ones which are not + * in gpio mode, set them into gpio mode. + */ +static int mcp2221_check_gpio_pinfunc(struct mcp2221 *mcp) +{ + int i; + int needgpiofix = 0; + int ret; + + if (IS_ENABLED(CONFIG_IIO)) + return 0; + + ret = mcp_gpio_read_sram(mcp); + if (ret) + return ret; + + for (i = 0; i < MCP_NGPIO; i++) { + if ((mcp->mode[i] & MCP2221_SRAM_GP_DESIGN_MASK) != 0x0) { + dev_warn(&mcp->hdev->dev, + "GPIO %d not in gpio mode\n", i); + needgpiofix = 1; + } + } + + if (!needgpiofix) + return 0; + + /* + * Set all bytes to 0, so Bit 7 is not set. The chip + * only changes content of a register when bit 7 is set. + */ + memset(mcp->txbuf, 0, 64); + mcp->txbuf[0] = MCP2221_SET_SRAM_SETTINGS; + + /* + * Set bit 7 in MCP2221_SRAM_WR_GP_ENA_ALTER to enable + * loading of a new set of gpio settings to GP SRAM + */ + mcp->txbuf[MCP2221_SRAM_WR_GP_ENA_ALTER] = 0x80; + for (i = 0; i < MCP_NGPIO; i++) { + if ((mcp->mode[i] & MCP2221_SRAM_GP_DESIGN_MASK) == 0x0) { + /* write current GPIO mode */ + mcp->txbuf[MCP2221_SRAM_WR_GP0 + i] = mcp->mode[i]; + } else { + /* pin is not in gpio mode, set it to input mode */ + mcp->txbuf[MCP2221_SRAM_WR_GP0 + i] = 0x08; + dev_warn(&mcp->hdev->dev, + "Set GPIO mode for gpio pin %d!\n", i); + } + } + + mutex_lock(&mcp->lock); + ret = mcp_send_data_req_status(mcp, mcp->txbuf, 64); + mutex_unlock(&mcp->lock); + + return ret; +} + static int mcp_gpio_get(struct gpio_chip *gc, unsigned int offset) { @@ -1218,6 +1313,8 @@ static int mcp2221_probe(struct hid_device *hdev, ret = devm_gpiochip_add_data(&hdev->dev, mcp->gc, mcp); if (ret) return ret; + + mcp2221_check_gpio_pinfunc(mcp); #endif #if IS_REACHABLE(CONFIG_IIO) From 7852beb143509d407cb8370604bb7bc97955ec84 Mon Sep 17 00:00:00 2001 From: Bagas Sanjaya Date: Fri, 13 Jun 2025 08:43:27 +0700 Subject: [PATCH 38/54] HID: intel-thc-hid: Separate max input size control conditional list Stephen Rothwell reports htmldocs warning: Documentation/hid/intel-thc-hid.rst:200: ERROR: Unexpected indentation. [docutils] Separate conditional list for max input size control by a blank line to fix the warning. Fixes: 45e92a093099 ("HID: Intel-thc-hid: Intel-thc: Introduce max input size control") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/linux-next/20250611142409.7d4683b0@canb.auug.org.au/ Signed-off-by: Bagas Sanjaya Reviewed-by: Even Xu Signed-off-by: Jiri Kosina --- Documentation/hid/intel-thc-hid.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/hid/intel-thc-hid.rst b/Documentation/hid/intel-thc-hid.rst index 23d5110cb871..8b378c57b5aa 100644 --- a/Documentation/hid/intel-thc-hid.rst +++ b/Documentation/hid/intel-thc-hid.rst @@ -195,6 +195,7 @@ This is a new feature introduced in Panther Lake platform, THC hardware allows d a max input size for RxDMA. After this max size gets set and enabled, for every input report packet reading, THC hardware sequencer will first read incoming input packet size, then compare input packet size with the given max size: + - if input packet size <= max size, THC continues using input packet size to finish the reading - if input packet size > max size, there is potential input data crash risk during transferring, THC will use max size instead of input packet size for reading From a5db1591d0829bda219d6967e5860764c648edbd Mon Sep 17 00:00:00 2001 From: Yu Jiaoliang Date: Thu, 19 Jun 2025 10:47:41 +0800 Subject: [PATCH 39/54] HID: mcp-2221: Replace manual comparison with min() macro This improves code readability by using the standard kernel macro for minimal value selection while maintaining identical functionality. Signed-off-by: Yu Jiaoliang Signed-off-by: Jiri Kosina --- drivers/hid/hid-mcp2221.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index 3bc5884e572e..fcfe9370a887 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "hid-ids.h" /* Commands codes in a raw output report */ @@ -262,10 +263,7 @@ static int mcp_i2c_write(struct mcp2221 *mcp, idx = 0; sent = 0; - if (msg->len < 60) - len = msg->len; - else - len = 60; + len = min(msg->len, 60); do { mcp->txbuf[0] = type; @@ -292,10 +290,7 @@ static int mcp_i2c_write(struct mcp2221 *mcp, break; idx = idx + len; - if ((msg->len - sent) < 60) - len = msg->len - sent; - else - len = 60; + len = min(msg->len - sent, 60); /* * Testing shows delay is needed between successive writes From c8be000387e47624d14b328daca260f897756402 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 19 Jun 2025 09:34:56 +0100 Subject: [PATCH 40/54] HID: uclogic: make read-only array reconnect_event static const Don't populate the read-only array reconnect_event on the stack at run time, instead make it static const. Signed-off-by: Colin Ian King Signed-off-by: Jiri Kosina --- drivers/hid/hid-uclogic-params.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index a6044996abf2..b3d56057cec9 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -1341,7 +1341,7 @@ static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev, struct uclogic_params *p) { struct uclogic_raw_event_hook *event_hook; - __u8 reconnect_event[] = { + static const __u8 reconnect_event[] = { /* Event received on wireless tablet reconnection */ 0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; From 37a9acb971c2f338e7a1b602b0ee40ad70668e81 Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Thu, 19 Jun 2025 08:46:27 -0700 Subject: [PATCH 41/54] HID: replace scnprintf() with sysfs_emit() Documentation/filesystems/sysfs.rst mentions that show() should only use sysfs_emit() or sysfs_emit_at() when formating the value to be returned to user space. So replace scnprintf() with sysfs_emit(). Signed-off-by: Chelsy Ratnawat Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 +- drivers/hid/hid-lg4ff.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b348d0464314..4a00bd4a4224 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2797,7 +2797,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, { struct hid_device *hdev = container_of(dev, struct hid_device, dev); - return scnprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n", + return sysfs_emit(buf, "hid:b%04Xg%04Xv%08Xp%08X\n", hdev->bus, hdev->group, hdev->vendor, hdev->product); } static DEVICE_ATTR_RO(modalias); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 445623dd1bd6..32b711723f2a 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -956,7 +956,7 @@ static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *a return 0; } - count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine); + count = sysfs_emit(buf, "%u\n", entry->wdata.combine); return count; } @@ -1009,7 +1009,7 @@ static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *att return 0; } - count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range); + count = sysfs_emit(buf, "%u\n", entry->wdata.range); return count; } @@ -1073,7 +1073,7 @@ static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *a return 0; } - count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name); + count = sysfs_emit(buf, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name); return count; } From 4051ead99888f101be92c7ce90d2de09aac6fd1c Mon Sep 17 00:00:00 2001 From: Li Chen Date: Fri, 20 Jun 2025 20:02:31 +0800 Subject: [PATCH 42/54] HID: rate-limit hid_warn to prevent log flooding Syzkaller can create many uhid devices that trigger repeated warnings like: "hid-generic xxxx: unknown main item tag 0x0" These messages can flood the system log, especially if a crash occurs (e.g., with a slow UART console, leading to soft lockups). To mitigate this, convert `hid_warn()` to use `dev_warn_ratelimited()`. This helps reduce log noise and improves system stability under fuzzing or faulty device scenarios. Signed-off-by: Li Chen Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 4 ++-- include/linux/hid.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 4a00bd4a4224..ef1f79951d9b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -659,9 +659,9 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) default: if (item->tag >= HID_MAIN_ITEM_TAG_RESERVED_MIN && item->tag <= HID_MAIN_ITEM_TAG_RESERVED_MAX) - hid_warn(parser->device, "reserved main item tag 0x%x\n", item->tag); + hid_warn_ratelimited(parser->device, "reserved main item tag 0x%x\n", item->tag); else - hid_warn(parser->device, "unknown main item tag 0x%x\n", item->tag); + hid_warn_ratelimited(parser->device, "unknown main item tag 0x%x\n", item->tag); ret = 0; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 568a9d8c749b..7f260e0e2049 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1239,6 +1239,8 @@ void hid_quirks_exit(__u16 bus); dev_notice(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_warn(hid, fmt, ...) \ dev_warn(&(hid)->dev, fmt, ##__VA_ARGS__) +#define hid_warn_ratelimited(hid, fmt, ...) \ + dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_info(hid, fmt, ...) \ dev_info(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_dbg(hid, fmt, ...) \ From 9671854582f971c84507728d4a00aa779e5a0811 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 22 Jun 2025 22:42:54 -0400 Subject: [PATCH 43/54] HID: uclogic: Add support for XP-PEN Artist 22R Pro Adds support for the XP-PEN Artist 22R Pro, including stylus, tablet frame and pen pressure. The tablet has 20 buttons, but need to be remapped in order since the device reports invalid keycodes. Existing tablet functionality should not be inhibited, as BTN0-8 is still used. New initialization functions are added since the device differs slightly from other UGEE v2 devices. Signed-off-by: Joshua Goins Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-uclogic-core.c | 66 +++++++++++++++- drivers/hid/hid-uclogic-params.c | 132 +++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-params.h | 5 ++ drivers/hid/hid-uclogic-rdesc.c | 44 +++++++++++ drivers/hid/hid-uclogic-rdesc.h | 4 + 6 files changed, 248 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e3fb4e2fe911..3e454b0c5367 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1402,6 +1402,7 @@ #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S 0x0909 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW 0x0933 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO 0x091b #define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055 diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 61a4019ddc74..d53e3cb4c3ea 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -62,6 +62,30 @@ static const __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } +/* Buttons considered valid tablet pad inputs. */ +static const unsigned int uclogic_extra_input_mapping[] = { + BTN_0, + BTN_1, + BTN_2, + BTN_3, + BTN_4, + BTN_5, + BTN_6, + BTN_7, + BTN_8, + BTN_RIGHT, + BTN_MIDDLE, + BTN_SIDE, + BTN_EXTRA, + BTN_FORWARD, + BTN_BACK, + BTN_B, + BTN_A, + BTN_BASE, + BTN_BASE2, + BTN_X +}; + static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, @@ -72,9 +96,27 @@ static int uclogic_input_mapping(struct hid_device *hdev, struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_params *params = &drvdata->params; - /* Discard invalid pen usages */ - if (params->pen.usage_invalid && (field->application == HID_DG_PEN)) - return -1; + if (field->application == HID_GD_KEYPAD) { + /* + * Remap input buttons to sensible ones that are not invalid. + * This only affects previous behavior for devices with more than ten or so buttons. + */ + const int key = (usage->hid & HID_USAGE) - 1; + + if (key < ARRAY_SIZE(uclogic_extra_input_mapping)) { + hid_map_usage(hi, + usage, + bit, + max, + EV_KEY, + uclogic_extra_input_mapping[key]); + return 1; + } + } else if (field->application == HID_DG_PEN) { + /* Discard invalid pen usages */ + if (params->pen.usage_invalid) + return -1; + } /* Let hid-core decide what to do */ return 0; @@ -407,8 +449,22 @@ static int uclogic_raw_event_frame( /* If need to, and can, transform the bitmap dial reports */ if (frame->bitmap_dial_byte > 0 && frame->bitmap_dial_byte < size) { - if (data[frame->bitmap_dial_byte] == 2) + switch (data[frame->bitmap_dial_byte]) { + case 2: data[frame->bitmap_dial_byte] = -1; + break; + + /* Everything below here is for tablets that shove multiple dials into 1 byte */ + case 16: + data[frame->bitmap_dial_byte] = 0; + data[frame->bitmap_second_dial_destination_byte] = 1; + break; + + case 32: + data[frame->bitmap_dial_byte] = 0; + data[frame->bitmap_second_dial_destination_byte] = -1; + break; + } } return 0; @@ -546,6 +602,8 @@ static const struct hid_device_id uclogic_devices[] = { .driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index b3d56057cec9..4a17f7332c3f 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -103,6 +103,8 @@ static void uclogic_params_frame_hid_dbg( frame->touch_flip_at); hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n", frame->bitmap_dial_byte); + hid_dbg(hdev, "\t\t.bitmap_second_dial_destination_byte = %u\n", + frame->bitmap_second_dial_destination_byte); } /** @@ -1529,6 +1531,126 @@ cleanup: return rc; } +/* + * uclogic_params_init_ugee_xppen_pro_22r() - Initializes a UGEE XP-Pen Pro 22R tablet device. + * + * @hdev: The HID device of the tablet interface to initialize and get + * parameters from. Cannot be NULL. + * @params: Parameters to fill in (to be cleaned with + * uclogic_params_cleanup()). Not modified in case of error. + * Cannot be NULL. + * + * Returns: + * Zero, if successful. A negative errno code on error. + */ +static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params, + struct hid_device *hdev, + const u8 rdesc_frame_arr[], + const size_t rdesc_frame_size) +{ + int rc = 0; + struct usb_interface *iface; + __u8 bInterfaceNumber; + const int str_desc_len = 12; + u8 *str_desc = NULL; + __u8 *rdesc_pen = NULL; + s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; + enum uclogic_params_frame_type frame_type; + /* The resulting parameters (noop) */ + struct uclogic_params p = {0, }; + + if (!hdev || !params) { + rc = -EINVAL; + goto cleanup; + } + + iface = to_usb_interface(hdev->dev.parent); + bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; + + /* Ignore non-pen interfaces */ + if (bInterfaceNumber != 2) { + rc = -EINVAL; + uclogic_params_init_invalid(&p); + goto cleanup; + } + + /* + * Initialize the interface by sending magic data. + * This magic data is the same as other UGEE v2 tablets. + */ + rc = uclogic_probe_interface(hdev, + uclogic_ugee_v2_probe_arr, + uclogic_ugee_v2_probe_size, + uclogic_ugee_v2_probe_endpoint); + if (rc) { + uclogic_params_init_invalid(&p); + goto cleanup; + } + + /** + * Read the string descriptor containing pen and frame parameters. + * These are slightly different than typical UGEE v2 devices. + */ + rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len); + if (rc != str_desc_len) { + rc = (rc < 0) ? rc : -EINVAL; + hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc); + uclogic_params_init_invalid(&p); + goto cleanup; + } + + rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len, + desc_params, + ARRAY_SIZE(desc_params), + &frame_type); + if (rc) + goto cleanup; + + // str_desc doesn't report the correct amount of buttons, so manually fix it + desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 20; + + kfree(str_desc); + str_desc = NULL; + + /* Initialize the pen interface */ + rdesc_pen = uclogic_rdesc_template_apply( + uclogic_rdesc_ugee_v2_pen_template_arr, + uclogic_rdesc_ugee_v2_pen_template_size, + desc_params, ARRAY_SIZE(desc_params)); + if (!rdesc_pen) { + rc = -ENOMEM; + goto cleanup; + } + + p.pen.desc_ptr = rdesc_pen; + p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; + p.pen.id = 0x02; + p.pen.subreport_list[0].value = 0xf0; + p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; + + /* Initialize the frame interface */ + rc = uclogic_params_frame_init_with_desc( + &p.frame_list[0], + rdesc_frame_arr, + rdesc_frame_size, + UCLOGIC_RDESC_V1_FRAME_ID); + if (rc < 0) { + hid_err(hdev, "initializing frame params failed: %d\n", rc); + goto cleanup; + } + + p.frame_list[0].bitmap_dial_byte = 7; + p.frame_list[0].bitmap_second_dial_destination_byte = 8; + + /* Output parameters */ + memcpy(params, &p, sizeof(*params)); + memset(&p, 0, sizeof(p)); +cleanup: + kfree(str_desc); + uclogic_params_cleanup(&p); + return rc; +} + /** * uclogic_params_init() - initialize a tablet interface and discover its * parameters. @@ -1845,6 +1967,16 @@ int uclogic_params_init(struct uclogic_params *params, uclogic_params_init_invalid(&p); } + break; + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO): + rc = uclogic_params_init_ugee_xppen_pro_22r(&p, + hdev, + uclogic_rdesc_xppen_artist_22r_pro_frame_arr, + uclogic_rdesc_xppen_artist_22r_pro_frame_size); + if (rc != 0) + goto cleanup; + break; } diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h index 35ff062d09b5..6ec8643d2ee5 100644 --- a/drivers/hid/hid-uclogic-params.h +++ b/drivers/hid/hid-uclogic-params.h @@ -175,6 +175,11 @@ struct uclogic_params_frame { * counterclockwise, as opposed to the normal 1 and -1. */ unsigned int bitmap_dial_byte; + /* + * Destination offset for the second bitmap dial byte, if the tablet + * supports a second dial at all. + */ + unsigned int bitmap_second_dial_destination_byte; }; /* diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 9b9cbc2aae36..08a89c6aae3b 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -1193,6 +1193,50 @@ const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = { const size_t uclogic_rdesc_xppen_deco01_frame_size = sizeof(uclogic_rdesc_xppen_deco01_frame_arr); +/* Fixed report descriptor for XP-Pen Arist 22R Pro frame */ +const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, UCLOGIC_RDESC_V1_FRAME_ID, + /* Report ID (Virtual report), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x14, /* Usage Maximum (14h), */ + 0x95, 0x14, /* Report Count (20), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x14, /* Report Count (20), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x08, /* Logical Maximum (8), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x05, 0x0C, /* Usage Page (Consumer Devices), */ + 0x0A, 0x38, 0x02, /* Usage (AC PAN), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size = + sizeof(uclogic_rdesc_xppen_artist_22r_pro_frame_arr); + /** * uclogic_rdesc_template_apply() - apply report descriptor parameters to a * report descriptor template, creating a report descriptor. Copies the diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index 3878a0e8c464..644a35ff12f2 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -210,4 +210,8 @@ extern const size_t uclogic_rdesc_ugee_g5_frame_size; /* Least-significant bit of Ugee G5 frame rotary encoder state */ #define UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB 38 +/* Fixed report descriptor for XP-Pen Arist 22R Pro frame */ +extern const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[]; +extern const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size; + #endif /* _HID_UCLOGIC_RDESC_H */ From 3a807f3ff9eaaeead81576c5a72d226d519a2fe7 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Mon, 23 Jun 2025 14:24:53 +0530 Subject: [PATCH 44/54] HID: amd_sfh: Enable operating mode Add changes to enable operating modes in the driver to allow the FW to activate and retrieve data from relevant sensors. This enables the FW to take necessary actions based on the operating modes. Tested-by: Eric Naim Co-developed-by: Akshata MukundShetty Signed-off-by: Akshata MukundShetty Signed-off-by: Basavaraj Natikar Reviewed-by: Mario Limonciello Signed-off-by: Jiri Kosina --- drivers/hid/amd-sfh-hid/amd_sfh_client.c | 23 +++++++++++++++++++++++ drivers/hid/amd-sfh-hid/amd_sfh_hid.h | 2 +- drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 4 ++++ drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c index 3438d392920f..0f2cbae39b2b 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c @@ -146,6 +146,8 @@ static const char *get_sensor_name(int idx) return "gyroscope"; case mag_idx: return "magnetometer"; + case op_idx: + return "operating-mode"; case als_idx: case ACS_IDX: /* ambient color sensor */ return "ALS"; @@ -243,6 +245,20 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) rc = -ENOMEM; goto cleanup; } + + if (cl_data->sensor_idx[i] == op_idx) { + info.period = AMD_SFH_IDLE_LOOP; + info.sensor_idx = cl_data->sensor_idx[i]; + info.dma_address = cl_data->sensor_dma_addr[i]; + mp2_ops->start(privdata, info); + cl_data->sensor_sts[i] = amd_sfh_wait_for_response(privdata, + cl_data->sensor_idx[i], + SENSOR_ENABLED); + if (cl_data->sensor_sts[i] == SENSOR_ENABLED) + cl_data->is_any_sensor_enabled = true; + continue; + } + cl_data->sensor_sts[i] = SENSOR_DISABLED; cl_data->sensor_requested_cnt[i] = 0; cl_data->cur_hid_dev = i; @@ -303,6 +319,13 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) for (i = 0; i < cl_data->num_hid_devices; i++) { cl_data->cur_hid_dev = i; + if (cl_data->sensor_idx[i] == op_idx) { + dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + continue; + } + if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { rc = amdtp_hid_probe(i, cl_data); if (rc) diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h index 1c91be8daedd..7452b0302953 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h @@ -11,7 +11,7 @@ #ifndef AMDSFH_HID_H #define AMDSFH_HID_H -#define MAX_HID_DEVICES 6 +#define MAX_HID_DEVICES 7 #define AMD_SFH_HID_VENDOR 0x1022 #define AMD_SFH_HID_PRODUCT 0x0001 diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 1c1fd63330c9..2983af969579 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -29,6 +29,7 @@ #define ACEL_EN BIT(0) #define GYRO_EN BIT(1) #define MAGNO_EN BIT(2) +#define OP_EN BIT(15) #define HPD_EN BIT(16) #define ALS_EN BIT(19) #define ACS_EN BIT(22) @@ -232,6 +233,9 @@ int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) if (MAGNO_EN & activestatus) sensor_id[num_of_sensors++] = mag_idx; + if (OP_EN & activestatus) + sensor_id[num_of_sensors++] = op_idx; + if (ALS_EN & activestatus) sensor_id[num_of_sensors++] = als_idx; diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h index 05e400a4a83e..2eb61f4e8434 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h @@ -79,6 +79,7 @@ enum sensor_idx { accel_idx = 0, gyro_idx = 1, mag_idx = 2, + op_idx = 15, als_idx = 19 }; From c061046fe9ce3ff31fb9a807144a2630ad349c17 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 30 Jun 2025 12:37:13 +0000 Subject: [PATCH 45/54] HID: apple: avoid setting up battery timer for devices without battery Currently, the battery timer is set up for all devices using hid-apple, irrespective of whether they actually have a battery or not. APPLE_RDESC_BATTERY is a quirk that indicates the device has a battery and needs the battery timer. This patch checks for this quirk before setting up the timer, ensuring that only devices with a battery will have the timer set up. Fixes: 6e143293e17a ("HID: apple: Report Magic Keyboard battery over USB") Cc: stable@vger.kernel.org Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 0639b1f43d88..8ab15a8f3524 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -933,10 +933,12 @@ static int apple_probe(struct hid_device *hdev, return ret; } - timer_setup(&asc->battery_timer, apple_battery_timer_tick, 0); - mod_timer(&asc->battery_timer, - jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); - apple_fetch_battery(hdev); + if (quirks & APPLE_RDESC_BATTERY) { + timer_setup(&asc->battery_timer, apple_battery_timer_tick, 0); + mod_timer(&asc->battery_timer, + jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); + apple_fetch_battery(hdev); + } if (quirks & APPLE_BACKLIGHT_CTL) apple_backlight_init(hdev); @@ -950,7 +952,9 @@ static int apple_probe(struct hid_device *hdev, return 0; out_err: - timer_delete_sync(&asc->battery_timer); + if (quirks & APPLE_RDESC_BATTERY) + timer_delete_sync(&asc->battery_timer); + hid_hw_stop(hdev); return ret; } @@ -959,7 +963,8 @@ static void apple_remove(struct hid_device *hdev) { struct apple_sc *asc = hid_get_drvdata(hdev); - timer_delete_sync(&asc->battery_timer); + if (asc->quirks & APPLE_RDESC_BATTERY) + timer_delete_sync(&asc->battery_timer); hid_hw_stop(hdev); } From 9bdc30e35cbc1aa78ccf01040354209f1e11ca22 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 30 Jun 2025 12:37:13 +0000 Subject: [PATCH 46/54] HID: magicmouse: avoid setting up battery timer when not needed Currently, the battery timer is set up for all devices using hid-magicmouse, irrespective of whether they actually need it or not. The current implementation requires the battery timer for Magic Mouse 2 and Magic Trackpad 2 when connected via USB only. Add checks to ensure that the battery timer is only set up when they are connected via USB. Fixes: 0b91b4e4dae6 ("HID: magicmouse: Report battery level over USB") Cc: stable@vger.kernel.org Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-magicmouse.c | 60 +++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 36f034ac605d..226682762db3 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -791,17 +791,31 @@ static void magicmouse_enable_mt_work(struct work_struct *work) hid_err(msc->hdev, "unable to request touch data (%d)\n", ret); } +static bool is_usb_magicmouse2(__u32 vendor, __u32 product) +{ + if (vendor != USB_VENDOR_ID_APPLE) + return false; + return product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 || + product == USB_DEVICE_ID_APPLE_MAGICMOUSE2_USBC; +} + +static bool is_usb_magictrackpad2(__u32 vendor, __u32 product) +{ + if (vendor != USB_VENDOR_ID_APPLE) + return false; + return product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC; +} + static int magicmouse_fetch_battery(struct hid_device *hdev) { #ifdef CONFIG_HID_BATTERY_STRENGTH struct hid_report_enum *report_enum; struct hid_report *report; - if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE || - (hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 && - hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2_USBC && - hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && - hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC)) + if (!hdev->battery || + (!is_usb_magicmouse2(hdev->vendor, hdev->product) && + !is_usb_magictrackpad2(hdev->vendor, hdev->product))) return -1; report_enum = &hdev->report_enum[hdev->battery_report_type]; @@ -863,17 +877,17 @@ static int magicmouse_probe(struct hid_device *hdev, return ret; } - timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0); - mod_timer(&msc->battery_timer, - jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS)); - magicmouse_fetch_battery(hdev); + if (is_usb_magicmouse2(id->vendor, id->product) || + is_usb_magictrackpad2(id->vendor, id->product)) { + timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0); + mod_timer(&msc->battery_timer, + jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS)); + magicmouse_fetch_battery(hdev); + } - if (id->vendor == USB_VENDOR_ID_APPLE && - (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 || - id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2_USBC || - ((id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || - id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) && - hdev->type != HID_TYPE_USBMOUSE))) + if (is_usb_magicmouse2(id->vendor, id->product) || + (is_usb_magictrackpad2(id->vendor, id->product) && + hdev->type != HID_TYPE_USBMOUSE)) return 0; if (!msc->input) { @@ -936,7 +950,10 @@ static int magicmouse_probe(struct hid_device *hdev, return 0; err_stop_hw: - timer_delete_sync(&msc->battery_timer); + if (is_usb_magicmouse2(id->vendor, id->product) || + is_usb_magictrackpad2(id->vendor, id->product)) + timer_delete_sync(&msc->battery_timer); + hid_hw_stop(hdev); return ret; } @@ -947,7 +964,9 @@ static void magicmouse_remove(struct hid_device *hdev) if (msc) { cancel_delayed_work_sync(&msc->work); - timer_delete_sync(&msc->battery_timer); + if (is_usb_magicmouse2(hdev->vendor, hdev->product) || + is_usb_magictrackpad2(hdev->vendor, hdev->product)) + timer_delete_sync(&msc->battery_timer); } hid_hw_stop(hdev); @@ -964,11 +983,8 @@ static const __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, * 0x05, 0x01, // Usage Page (Generic Desktop) 0 * 0x09, 0x02, // Usage (Mouse) 2 */ - if (hdev->vendor == USB_VENDOR_ID_APPLE && - (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 || - hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2_USBC || - hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || - hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) && + if ((is_usb_magicmouse2(hdev->vendor, hdev->product) || + is_usb_magictrackpad2(hdev->vendor, hdev->product)) && *rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) { hid_info(hdev, "fixing up magicmouse battery report descriptor\n"); From 6907c976fff38710406fa148cd82e892e7292d56 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 30 Jun 2025 12:37:14 +0000 Subject: [PATCH 47/54] HID: apple: use secs_to_jiffies() for battery timeout The kernel now has a secs_to_jiffies() function which expands to a simpler code than msecs_to_jiffies(). Use the same for battery timeout which was using 60000 milliseconds (60 seconds). Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 8ab15a8f3524..321475843fe7 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -46,7 +46,7 @@ #define APPLE_FLAG_FKEY 0x01 #define HID_COUNTRY_INTERNATIONAL_ISO 13 -#define APPLE_BATTERY_TIMEOUT_MS 60000 +#define APPLE_BATTERY_TIMEOUT_SEC 60 #define HID_USAGE_MAGIC_BL 0xff00000f #define APPLE_MAGIC_REPORT_ID_POWER 3 @@ -619,7 +619,7 @@ static void apple_battery_timer_tick(struct timer_list *t) if (apple_fetch_battery(hdev) == 0) { mod_timer(&asc->battery_timer, - jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); + jiffies + secs_to_jiffies(APPLE_BATTERY_TIMEOUT_SEC)); } } @@ -936,7 +936,7 @@ static int apple_probe(struct hid_device *hdev, if (quirks & APPLE_RDESC_BATTERY) { timer_setup(&asc->battery_timer, apple_battery_timer_tick, 0); mod_timer(&asc->battery_timer, - jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); + jiffies + secs_to_jiffies(APPLE_BATTERY_TIMEOUT_SEC)); apple_fetch_battery(hdev); } From 230cdd8a5f4bc7ebe8cadb6f532911544af6fb3c Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Mon, 30 Jun 2025 12:37:14 +0000 Subject: [PATCH 48/54] HID: magicmouse: use secs_to_jiffies() for battery timeout The kernel now has a secs_to_jiffies() function which expands to a simpler code than msecs_to_jiffies(). Use the same for battery timeout which was using 60000 milliseconds (60 seconds). Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-magicmouse.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 226682762db3..7d4a25c6de0e 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -60,7 +60,7 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define MOUSE_REPORT_ID 0x29 #define MOUSE2_REPORT_ID 0x12 #define DOUBLE_REPORT_ID 0xf7 -#define USB_BATTERY_TIMEOUT_MS 60000 +#define USB_BATTERY_TIMEOUT_SEC 60 /* These definitions are not precise, but they're close enough. (Bits * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem @@ -841,7 +841,7 @@ static void magicmouse_battery_timer_tick(struct timer_list *t) if (magicmouse_fetch_battery(hdev) == 0) { mod_timer(&msc->battery_timer, - jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS)); + jiffies + secs_to_jiffies(USB_BATTERY_TIMEOUT_SEC)); } } @@ -881,7 +881,7 @@ static int magicmouse_probe(struct hid_device *hdev, is_usb_magictrackpad2(id->vendor, id->product)) { timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0); mod_timer(&msc->battery_timer, - jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS)); + jiffies + secs_to_jiffies(USB_BATTERY_TIMEOUT_SEC)); magicmouse_fetch_battery(hdev); } From c85a8cb9b8d3a3dd9bb12879b28fc377dd24272b Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 9 Jul 2025 11:08:49 +0200 Subject: [PATCH 49/54] selftests/hid: run ruff format on the python part We aim at syncing with the hid-tools repo on gitlab.freedesktop.org/libevdev/hid-tools. One of the commits is this mechanical formatting, so pull it over here so changes are not hidden by those. Reviewed-by: Peter Hutterer Link: https://patch.msgid.link/20250709-wip-fix-ci-v1-1-b7df4c271cf8@kernel.org Signed-off-by: Benjamin Tissoires --- .../selftests/hid/tests/test_tablet.py | 4 +- .../selftests/hid/tests/test_wacom_generic.py | 436 +++++++++++++----- 2 files changed, 325 insertions(+), 115 deletions(-) diff --git a/tools/testing/selftests/hid/tests/test_tablet.py b/tools/testing/selftests/hid/tests/test_tablet.py index a9e2de1e8861..52fb22cac91e 100644 --- a/tools/testing/selftests/hid/tests/test_tablet.py +++ b/tools/testing/selftests/hid/tests/test_tablet.py @@ -1228,9 +1228,9 @@ class Huion_Kamvas_Pro_19_256c_006b(PenDigitizer): pen.current_state = state def call_input_event(self, report): - if report[0] == 0x0a: + if report[0] == 0x0A: # ensures the original second Eraser usage is null - report[1] &= 0xdf + report[1] &= 0xDF # ensures the original last bit is equal to bit 6 (In Range) if report[1] & 0x40: diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py index b62c7dba6777..5cbc0cc9308f 100644 --- a/tools/testing/selftests/hid/tests/test_wacom_generic.py +++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py @@ -892,7 +892,7 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest locations. The value of `t` may be incremented over time to move the points along a linear path. """ - return [ self.make_contact(id, t) for id in range(0, n) ] + return [self.make_contact(id, t) for id in range(0, n)] def assert_contact(self, uhdev, evdev, contact_ids, t=0): """ @@ -997,12 +997,17 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events - self.assert_contacts(uhdev, evdev, - [ self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), - self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), - self.ContactIds(contact_id = 2, tracking_id = -1, slot_num = None), - self.ContactIds(contact_id = 3, tracking_id = 1, slot_num = 1), - self.ContactIds(contact_id = 4, tracking_id = -1, slot_num = None) ]) + self.assert_contacts( + uhdev, + evdev, + [ + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + self.ContactIds(contact_id=2, tracking_id=-1, slot_num=None), + self.ContactIds(contact_id=3, tracking_id=1, slot_num=1), + self.ContactIds(contact_id=4, tracking_id=-1, slot_num=None), + ], + ) def confidence_change_assert_playback(self, uhdev, evdev, timeline): """ @@ -1026,7 +1031,7 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) - ids = [ x[0] for x in state ] + ids = [x[0] for x in state] self.assert_contacts(uhdev, evdev, ids, t) t += 1 @@ -1044,27 +1049,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # Both fingers confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == !Down + confident; Contact 1 == Down + confident - # First finger looses confidence and clears only the tipswitch flag - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # Both fingers confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == !Down + confident; Contact 1 == Down + confident + # First finger looses confidence and clears only the tipswitch flag + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) def test_confidence_loss_b(self): """ @@ -1079,27 +1125,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # Both fingers confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger looses confidence and has both flags cleared simultaneously - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # Both fingers confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger looses confidence and has both flags cleared simultaneously + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) def test_confidence_loss_c(self): """ @@ -1113,27 +1200,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # Both fingers confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident - # First finger looses confidence and clears only the confidence flag - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident - # First finger has lost confidence and has both flags cleared - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # Both fingers confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident + # First finger looses confidence and clears only the confidence flag + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident + # First finger has lost confidence and has both flags cleared + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) def test_confidence_gain_a(self): """ @@ -1144,27 +1272,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + !confident; Contact 1 == Down + confident - # Only second finger is confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)], - - # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident - # First finger gains confidence - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = None), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)], - - # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident - # First finger remains confident - [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)], - - # t=3: Contact 0 == Down + confident; Contact 1 == Down + confident - # First finger remains confident - [(self.ContactIds(contact_id = 0, tracking_id = 1, slot_num = 1), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 0, slot_num = 0), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + !confident; Contact 1 == Down + confident + # Only second finger is confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident + # First finger gains confidence + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident + # First finger remains confident + [ + ( + self.ContactIds(contact_id=0, tracking_id=1, slot_num=1), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + # t=3: Contact 0 == Down + confident; Contact 1 == Down + confident + # First finger remains confident + [ + ( + self.ContactIds(contact_id=0, tracking_id=1, slot_num=1), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=0, slot_num=0), + True, + True, + ), + ], + ], + ) def test_confidence_gain_b(self): """ @@ -1175,24 +1344,65 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest uhdev = self.uhdev evdev = uhdev.get_evdev() - self.confidence_change_assert_playback(uhdev, evdev, [ - # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident - # First and second finger confidently in contact - [(self.ContactIds(contact_id = 0, tracking_id = 0, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident - # Firtst finger looses confidence - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), True, False), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident - # First finger gains confidence - [(self.ContactIds(contact_id = 0, tracking_id = 2, slot_num = 0), True, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)], - - # t=3: Contact 0 == !Down + confident; Contact 1 == Down + confident - # First finger goes up - [(self.ContactIds(contact_id = 0, tracking_id = -1, slot_num = 0), False, True), - (self.ContactIds(contact_id = 1, tracking_id = 1, slot_num = 1), True, True)] - ]) + self.confidence_change_assert_playback( + uhdev, + evdev, + [ + # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident + # First and second finger confidently in contact + [ + ( + self.ContactIds(contact_id=0, tracking_id=0, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident + # Firtst finger looses confidence + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + True, + False, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident + # First finger gains confidence + [ + ( + self.ContactIds(contact_id=0, tracking_id=2, slot_num=0), + True, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + # t=3: Contact 0 == !Down + confident; Contact 1 == Down + confident + # First finger goes up + [ + ( + self.ContactIds(contact_id=0, tracking_id=-1, slot_num=0), + False, + True, + ), + ( + self.ContactIds(contact_id=1, tracking_id=1, slot_num=1), + True, + True, + ), + ], + ], + ) From 642f9b2d608cc2239d22957ca1dc557d07470b50 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 9 Jul 2025 11:08:50 +0200 Subject: [PATCH 50/54] selftests/hid: sync the python tests to hid-tools 0.8 Instead of backporting one by one each commits, let's pull them in bulk and refer to the hid-tools project for a detailed history. The short summary is: - make use of dataclass when possible, to avoid tuples - wacom: remove unused uhdev parameter - various small fixes not worth mentioning Reviewed-by: Peter Hutterer Link: https://patch.msgid.link/20250709-wip-fix-ci-v1-2-b7df4c271cf8@kernel.org Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/tests/base.py | 46 ++++++++++++------- .../selftests/hid/tests/base_device.py | 30 +++++++----- .../hid/tests/test_apple_keyboard.py | 3 +- .../selftests/hid/tests/test_gamepad.py | 3 +- .../selftests/hid/tests/test_ite_keyboard.py | 3 +- .../selftests/hid/tests/test_multitouch.py | 2 +- .../testing/selftests/hid/tests/test_sony.py | 7 +-- .../selftests/hid/tests/test_tablet.py | 7 +-- .../selftests/hid/tests/test_wacom_generic.py | 11 ++--- 9 files changed, 69 insertions(+), 43 deletions(-) diff --git a/tools/testing/selftests/hid/tests/base.py b/tools/testing/selftests/hid/tests/base.py index 3a465768e507..5175cf235b2f 100644 --- a/tools/testing/selftests/hid/tests/base.py +++ b/tools/testing/selftests/hid/tests/base.py @@ -5,6 +5,7 @@ # Copyright (c) 2017 Benjamin Tissoires # Copyright (c) 2017 Red Hat, Inc. +import dataclasses import libevdev import os import pytest @@ -145,6 +146,18 @@ class UHIDTestDevice(BaseDevice): self.name = name +@dataclasses.dataclass +class HidBpf: + object_name: str + has_rdesc_fixup: bool + + +@dataclasses.dataclass +class KernelModule: + driver_name: str + module_name: str + + class BaseTestCase: class TestUhid(object): syn_event = libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT) # type: ignore @@ -155,20 +168,20 @@ class BaseTestCase: # List of kernel modules to load before starting the test # if any module is not available (not compiled), the test will skip. - # Each element is a tuple '(kernel driver name, kernel module)', - # for example ("playstation", "hid-playstation") - kernel_modules: List[Tuple[str, str]] = [] + # Each element is a KernelModule object, for example + # KernelModule("playstation", "hid-playstation") + kernel_modules: List[KernelModule] = [] # List of in kernel HID-BPF object files to load # before starting the test # Any existing pre-loaded HID-BPF module will be removed # before the ones in this list will be manually loaded. - # Each Element is a tuple '(hid_bpf_object, rdesc_fixup_present)', - # for example '("xppen-ArtistPro16Gen2.bpf.o", True)' - # If 'rdesc_fixup_present' is True, the test needs to wait + # Each Element is a HidBpf object, for example + # 'HidBpf("xppen-ArtistPro16Gen2.bpf.o", True)' + # If 'has_rdesc_fixup' is True, the test needs to wait # for one unbind and rebind before it can be sure the kernel is # ready - hid_bpfs: List[Tuple[str, bool]] = [] + hid_bpfs: List[HidBpf] = [] def assertInputEventsIn(self, expected_events, effective_events): effective_events = effective_events.copy() @@ -232,25 +245,26 @@ class BaseTestCase: @pytest.fixture() def load_kernel_module(self): - for kernel_driver, kernel_module in self.kernel_modules: - self._load_kernel_module(kernel_driver, kernel_module) + for k in self.kernel_modules: + self._load_kernel_module(k.driver_name, k.module_name) yield def load_hid_bpfs(self): + # this function will only work when run in the kernel tree script_dir = Path(os.path.dirname(os.path.realpath(__file__))) root_dir = (script_dir / "../../../../..").resolve() bpf_dir = root_dir / "drivers/hid/bpf/progs" + if not bpf_dir.exists(): + pytest.skip("looks like we are not in the kernel tree, skipping") + udev_hid_bpf = shutil.which("udev-hid-bpf") if not udev_hid_bpf: pytest.skip("udev-hid-bpf not found in $PATH, skipping") - wait = False - for _, rdesc_fixup in self.hid_bpfs: - if rdesc_fixup: - wait = True + wait = any(b.has_rdesc_fixup for b in self.hid_bpfs) - for hid_bpf, _ in self.hid_bpfs: + for hid_bpf in self.hid_bpfs: # We need to start `udev-hid-bpf` in the background # and dispatch uhid events in case the kernel needs # to fetch features on the device @@ -260,13 +274,13 @@ class BaseTestCase: "--verbose", "add", str(self.uhdev.sys_path), - str(bpf_dir / hid_bpf), + str(bpf_dir / hid_bpf.object_name), ], ) while process.poll() is None: self.uhdev.dispatch(1) - if process.poll() != 0: + if process.returncode != 0: pytest.fail( f"Couldn't insert hid-bpf program '{hid_bpf}', marking the test as failed" ) diff --git a/tools/testing/selftests/hid/tests/base_device.py b/tools/testing/selftests/hid/tests/base_device.py index e0515be97f83..e13035fe1deb 100644 --- a/tools/testing/selftests/hid/tests/base_device.py +++ b/tools/testing/selftests/hid/tests/base_device.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import dataclasses import fcntl import functools import libevdev @@ -104,6 +105,12 @@ class PowerSupply(object): return self._type.str_value +@dataclasses.dataclass +class HidReadiness: + is_ready: bool = False + count: int = 0 + + class HIDIsReady(object): """ Companion class that binds to a kernel mechanism @@ -115,18 +122,18 @@ class HIDIsReady(object): def __init__(self: "HIDIsReady", uhid: UHIDDevice) -> None: self.uhid = uhid - def is_ready(self: "HIDIsReady") -> bool: + def is_ready(self: "HIDIsReady") -> HidReadiness: """ Overwrite in subclasses: should return True or False whether the attached uhid device is ready or not. """ - return False + return HidReadiness() class UdevHIDIsReady(HIDIsReady): _pyudev_context: ClassVar[Optional[pyudev.Context]] = None _pyudev_monitor: ClassVar[Optional[pyudev.Monitor]] = None - _uhid_devices: ClassVar[Dict[int, Tuple[bool, int]]] = {} + _uhid_devices: ClassVar[Dict[int, HidReadiness]] = {} def __init__(self: "UdevHIDIsReady", uhid: UHIDDevice) -> None: super().__init__(uhid) @@ -157,18 +164,19 @@ class UdevHIDIsReady(HIDIsReady): id = int(event.sys_path.strip().split(".")[-1], 16) - device_ready, count = cls._uhid_devices.get(id, (False, 0)) + readiness = cls._uhid_devices.setdefault(id, HidReadiness()) ready = event.action == "bind" - if not device_ready and ready: - count += 1 - cls._uhid_devices[id] = (ready, count) + if not readiness.is_ready and ready: + readiness.count += 1 - def is_ready(self: "UdevHIDIsReady") -> Tuple[bool, int]: + readiness.is_ready = ready + + def is_ready(self: "UdevHIDIsReady") -> HidReadiness: try: return self._uhid_devices[self.uhid.hid_id] except KeyError: - return (False, 0) + return HidReadiness() class EvdevMatch(object): @@ -322,11 +330,11 @@ class BaseDevice(UHIDDevice): @property def kernel_is_ready(self: "BaseDevice") -> bool: - return self._kernel_is_ready.is_ready()[0] and self.started + return self._kernel_is_ready.is_ready().is_ready and self.started @property def kernel_ready_count(self: "BaseDevice") -> int: - return self._kernel_is_ready.is_ready()[1] + return self._kernel_is_ready.is_ready().count @property def input_nodes(self: "BaseDevice") -> List[EvdevDevice]: diff --git a/tools/testing/selftests/hid/tests/test_apple_keyboard.py b/tools/testing/selftests/hid/tests/test_apple_keyboard.py index f81071d46166..0e17588b945c 100644 --- a/tools/testing/selftests/hid/tests/test_apple_keyboard.py +++ b/tools/testing/selftests/hid/tests/test_apple_keyboard.py @@ -8,13 +8,14 @@ from .test_keyboard import ArrayKeyboard, TestArrayKeyboard from hidtools.util import BusType +from . import base import libevdev import logging logger = logging.getLogger("hidtools.test.apple-keyboard") -KERNEL_MODULE = ("apple", "hid-apple") +KERNEL_MODULE = base.KernelModule("apple", "hid-apple") class KbdData(object): diff --git a/tools/testing/selftests/hid/tests/test_gamepad.py b/tools/testing/selftests/hid/tests/test_gamepad.py index 8d5b5ffdae49..612197805931 100644 --- a/tools/testing/selftests/hid/tests/test_gamepad.py +++ b/tools/testing/selftests/hid/tests/test_gamepad.py @@ -12,6 +12,7 @@ import pytest from .base_gamepad import BaseGamepad, JoystickGamepad, AxisMapping from hidtools.util import BusType +from .base import HidBpf import logging @@ -654,7 +655,7 @@ class TestAsusGamepad(BaseTest.TestGamepad): class TestRaptorMach2Joystick(BaseTest.TestGamepad): - hid_bpfs = [("FR-TEC__Raptor-Mach-2.bpf.o", True)] + hid_bpfs = [HidBpf("FR-TEC__Raptor-Mach-2.bpf.o", True)] def create_device(self): return RaptorMach2Joystick( diff --git a/tools/testing/selftests/hid/tests/test_ite_keyboard.py b/tools/testing/selftests/hid/tests/test_ite_keyboard.py index 38550c167bae..f695eaad1648 100644 --- a/tools/testing/selftests/hid/tests/test_ite_keyboard.py +++ b/tools/testing/selftests/hid/tests/test_ite_keyboard.py @@ -11,10 +11,11 @@ from hidtools.util import BusType import libevdev import logging +from . import base logger = logging.getLogger("hidtools.test.ite-keyboard") -KERNEL_MODULE = ("itetech", "hid_ite") +KERNEL_MODULE = base.KernelModule("itetech", "hid_ite") class KbdData(object): diff --git a/tools/testing/selftests/hid/tests/test_multitouch.py b/tools/testing/selftests/hid/tests/test_multitouch.py index 4265012231c6..5d2ffa3d5977 100644 --- a/tools/testing/selftests/hid/tests/test_multitouch.py +++ b/tools/testing/selftests/hid/tests/test_multitouch.py @@ -17,7 +17,7 @@ import time logger = logging.getLogger("hidtools.test.multitouch") -KERNEL_MODULE = ("hid-multitouch", "hid_multitouch") +KERNEL_MODULE = base.KernelModule("hid-multitouch", "hid_multitouch") def BIT(x): diff --git a/tools/testing/selftests/hid/tests/test_sony.py b/tools/testing/selftests/hid/tests/test_sony.py index 7e52c28e59c5..7fd3a8e6137d 100644 --- a/tools/testing/selftests/hid/tests/test_sony.py +++ b/tools/testing/selftests/hid/tests/test_sony.py @@ -7,6 +7,7 @@ # from .base import application_matches +from .base import KernelModule from .test_gamepad import BaseTest from hidtools.device.sony_gamepad import ( PS3Controller, @@ -24,9 +25,9 @@ import pytest logger = logging.getLogger("hidtools.test.sony") -PS3_MODULE = ("sony", "hid_sony") -PS4_MODULE = ("playstation", "hid_playstation") -PS5_MODULE = ("playstation", "hid_playstation") +PS3_MODULE = KernelModule("sony", "hid_sony") +PS4_MODULE = KernelModule("playstation", "hid_playstation") +PS5_MODULE = KernelModule("playstation", "hid_playstation") class SonyBaseTest: diff --git a/tools/testing/selftests/hid/tests/test_tablet.py b/tools/testing/selftests/hid/tests/test_tablet.py index 52fb22cac91e..50d5699812bb 100644 --- a/tools/testing/selftests/hid/tests/test_tablet.py +++ b/tools/testing/selftests/hid/tests/test_tablet.py @@ -10,6 +10,7 @@ from . import base import copy from enum import Enum from hidtools.util import BusType +from .base import HidBpf import libevdev import logging import pytest @@ -1472,7 +1473,7 @@ class TestGoodix_27c6_0e00(BaseTest.TestTablet): class TestXPPen_ArtistPro16Gen2_28bd_095b(BaseTest.TestTablet): - hid_bpfs = [("XPPen__ArtistPro16Gen2.bpf.o", True)] + hid_bpfs = [HidBpf("XPPen__ArtistPro16Gen2.bpf.o", True)] def create_device(self): dev = XPPen_ArtistPro16Gen2_28bd_095b( @@ -1484,7 +1485,7 @@ class TestXPPen_ArtistPro16Gen2_28bd_095b(BaseTest.TestTablet): class TestXPPen_Artist24_28bd_093a(BaseTest.TestTablet): - hid_bpfs = [("XPPen__Artist24.bpf.o", True)] + hid_bpfs = [HidBpf("XPPen__Artist24.bpf.o", True)] def create_device(self): return XPPen_Artist24_28bd_093a( @@ -1495,7 +1496,7 @@ class TestXPPen_Artist24_28bd_093a(BaseTest.TestTablet): class TestHuion_Kamvas_Pro_19_256c_006b(BaseTest.TestTablet): - hid_bpfs = [("Huion__Kamvas-Pro-19.bpf.o", True)] + hid_bpfs = [HidBpf("Huion__Kamvas-Pro-19.bpf.o", True)] def create_device(self): return Huion_Kamvas_Pro_19_256c_006b( diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py index 5cbc0cc9308f..2d6d04f0ff80 100644 --- a/tools/testing/selftests/hid/tests/test_wacom_generic.py +++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py @@ -40,7 +40,7 @@ import logging logger = logging.getLogger("hidtools.test.wacom") -KERNEL_MODULE = ("wacom", "wacom") +KERNEL_MODULE = base.KernelModule("wacom", "wacom") class ProximityState(Enum): @@ -894,7 +894,7 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest """ return [self.make_contact(id, t) for id in range(0, n)] - def assert_contact(self, uhdev, evdev, contact_ids, t=0): + def assert_contact(self, evdev, contact_ids, t=0): """ Assert properties of a contact generated by make_contact. """ @@ -916,12 +916,12 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_X] == x assert evdev.slots[slot_num][libevdev.EV_ABS.ABS_MT_POSITION_Y] == y - def assert_contacts(self, uhdev, evdev, data, t=0): + def assert_contacts(self, evdev, data, t=0): """ Assert properties of a list of contacts generated by make_contacts. """ for contact_ids in data: - self.assert_contact(uhdev, evdev, contact_ids, t) + self.assert_contact(evdev, contact_ids, t) def test_contact_id_0(self): """ @@ -998,7 +998,6 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events self.assert_contacts( - uhdev, evdev, [ self.ContactIds(contact_id=0, tracking_id=-1, slot_num=None), @@ -1032,7 +1031,7 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest self.debug_reports(r, uhdev, events) ids = [x[0] for x in state] - self.assert_contacts(uhdev, evdev, ids, t) + self.assert_contacts(evdev, ids, t) t += 1 From 1aee3a44fad2adeaa8f1a3aafd7c0154924af666 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 9 Jul 2025 11:08:51 +0200 Subject: [PATCH 51/54] selftests/hid: sync python tests to hid-tools 0.10 hid-tools 0.10 fixes one inconvenience introduced by commit 6a9e76f75c1a ("HID: multitouch: Disable touchpad on firmware level while not in use") This change added a new callback when a hid-nultitouch device is opened or closed to put the underlying device into a given operating mode. However, in the test cases, that means that while the single threaded test is run, it opens the device but has to react to the device while the open() is still running. hid-tools now implements a minimal thread to circumvent this. This makes the HID kernel tests in sync with hid-tools 0.10. This has the net effect of running the full HID python testsuite in 6 minutes instead of 1 hour. Reviewed-by: Peter Hutterer Link: https://patch.msgid.link/20250709-wip-fix-ci-v1-3-b7df4c271cf8@kernel.org Signed-off-by: Benjamin Tissoires --- .../selftests/hid/tests/base_device.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/testing/selftests/hid/tests/base_device.py b/tools/testing/selftests/hid/tests/base_device.py index e13035fe1deb..59465c58d94d 100644 --- a/tools/testing/selftests/hid/tests/base_device.py +++ b/tools/testing/selftests/hid/tests/base_device.py @@ -23,6 +23,7 @@ import fcntl import functools import libevdev import os +import threading try: import pyudev @@ -344,10 +345,28 @@ class BaseDevice(UHIDDevice): if not self.kernel_is_ready or not self.started: return [] + # Starting with kernel v6.16, an event is emitted when + # userspace opens a kernel device, and for some devices + # this translates into a SET_REPORT. + # Because EvdevDevice(path) opens every single evdev node + # we need to have a separate thread to process the incoming + # SET_REPORT or we end up having to wait for the kernel + # timeout of 5 seconds. + done = False + + def dispatch(): + while not done: + self.dispatch(1) + + t = threading.Thread(target=dispatch) + t.start() + self._input_nodes = [ EvdevDevice(path) for path in self.walk_sysfs("input", "input/input*/event*") ] + done = True + t.join() return self._input_nodes def match_evdev_rule(self, application, evdev): From 12f33ef6c2aaa410b7ccf039289fe2b04ab2252f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Sun, 13 Jul 2025 11:36:12 -0400 Subject: [PATCH 52/54] HID: core: Improve the kerneldoc for hid_report_len() The kerneldoc for hid_report_len() needs to be improved. The description of the @report argument is ungrammatical, and the documentation does not explain under what circumstances the report length will include the byte reserved for the report ID. Let's fix up the kerneldoc. Signed-off-by: Alan Stern Link: https://patch.msgid.link/1c8416cb-7347-4a06-b00a-20518069d263@rowland.harvard.edu Signed-off-by: Benjamin Tissoires --- include/linux/hid.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/hid.h b/include/linux/hid.h index 7f260e0e2049..2cc4f1e4ea96 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1216,7 +1216,11 @@ static inline void hid_hw_wait(struct hid_device *hdev) /** * hid_report_len - calculate the report length * - * @report: the report we want to know the length + * @report: the report whose length we want to know + * + * The length counts the report ID byte, but only if the ID is nonzero + * and therefore is included in the report. Reports whose ID is zero + * never include an ID byte. */ static inline u32 hid_report_len(struct hid_report *report) { From 1bb3363da862e0464ec050eea2fb5472a36ad86b Mon Sep 17 00:00:00 2001 From: Qasim Ijaz Date: Mon, 14 Jul 2025 00:30:08 +0100 Subject: [PATCH 53/54] HID: apple: validate feature-report field count to prevent NULL pointer dereference A malicious HID device with quirk APPLE_MAGIC_BACKLIGHT can trigger a NULL pointer dereference whilst the power feature-report is toggled and sent to the device in apple_magic_backlight_report_set(). The power feature-report is expected to have two data fields, but if the descriptor declares one field then accessing field[1] and dereferencing it in apple_magic_backlight_report_set() becomes invalid since field[1] will be NULL. An example of a minimal descriptor which can cause the crash is something like the following where the report with ID 3 (power report) only references a single 1-byte field. When hid core parses the descriptor it will encounter the final feature tag, allocate a hid_report (all members of field[] will be zeroed out), create field structure and populate it, increasing the maxfield to 1. The subsequent field[1] access and dereference causes the crash. Usage Page (Vendor Defined 0xFF00) Usage (0x0F) Collection (Application) Report ID (1) Usage (0x01) Logical Minimum (0) Logical Maximum (255) Report Size (8) Report Count (1) Feature (Data,Var,Abs) Usage (0x02) Logical Maximum (32767) Report Size (16) Report Count (1) Feature (Data,Var,Abs) Report ID (3) Usage (0x03) Logical Minimum (0) Logical Maximum (1) Report Size (8) Report Count (1) Feature (Data,Var,Abs) End Collection Here we see the KASAN splat when the kernel dereferences the NULL pointer and crashes: [ 15.164723] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000006: 0000 [#1] SMP KASAN NOPTI [ 15.165691] KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037] [ 15.165691] CPU: 0 UID: 0 PID: 10 Comm: kworker/0:1 Not tainted 6.15.0 #31 PREEMPT(voluntary) [ 15.165691] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 [ 15.165691] RIP: 0010:apple_magic_backlight_report_set+0xbf/0x210 [ 15.165691] Call Trace: [ 15.165691] [ 15.165691] apple_probe+0x571/0xa20 [ 15.165691] hid_device_probe+0x2e2/0x6f0 [ 15.165691] really_probe+0x1ca/0x5c0 [ 15.165691] __driver_probe_device+0x24f/0x310 [ 15.165691] driver_probe_device+0x4a/0xd0 [ 15.165691] __device_attach_driver+0x169/0x220 [ 15.165691] bus_for_each_drv+0x118/0x1b0 [ 15.165691] __device_attach+0x1d5/0x380 [ 15.165691] device_initial_probe+0x12/0x20 [ 15.165691] bus_probe_device+0x13d/0x180 [ 15.165691] device_add+0xd87/0x1510 [...] To fix this issue we should validate the number of fields that the backlight and power reports have and if they do not have the required number of fields then bail. Fixes: 394ba612f941 ("HID: apple: Add support for magic keyboard backlight on T2 Macs") Cc: stable@vger.kernel.org Signed-off-by: Qasim Ijaz Reviewed-by: Orlando Chamberlain Tested-by: Aditya Garg Link: https://patch.msgid.link/20250713233008.15131-1-qasdev00@gmail.com Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-apple.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index b8b99eb01a35..78920a8afa85 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -916,7 +916,8 @@ static int apple_magic_backlight_init(struct hid_device *hdev) backlight->brightness = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_BRIGHTNESS]; backlight->power = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_POWER]; - if (!backlight->brightness || !backlight->power) + if (!backlight->brightness || backlight->brightness->maxfield < 2 || + !backlight->power || backlight->power->maxfield < 2) return -ENODEV; backlight->cdev.name = ":white:" LED_FUNCTION_KBD_BACKLIGHT; From a6b87bfc2ab5bccb7ad953693c85d9062aef3fdd Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 23 Jul 2025 10:37:04 -0400 Subject: [PATCH 54/54] HID: core: Harden s32ton() against conversion to 0 bits Testing by the syzbot fuzzer showed that the HID core gets a shift-out-of-bounds exception when it tries to convert a 32-bit quantity to a 0-bit quantity. Ideally this should never occur, but there are buggy devices and some might have a report field with size set to zero; we shouldn't reject the report or the device just because of that. Instead, harden the s32ton() routine so that it returns a reasonable result instead of crashing when it is called with the number of bits set to 0 -- the same as what snto32() does. Signed-off-by: Alan Stern Reported-by: syzbot+b63d677d63bcac06cf90@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-usb/68753a08.050a0220.33d347.0008.GAE@google.com/ Tested-by: syzbot+b63d677d63bcac06cf90@syzkaller.appspotmail.com Fixes: dde5845a529f ("[PATCH] Generic HID layer - code split") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/613a66cd-4309-4bce-a4f7-2905f9bce0c9@rowland.harvard.edu Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ef1f79951d9b..f7d4efcae603 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -66,8 +66,12 @@ static s32 snto32(__u32 value, unsigned int n) static u32 s32ton(__s32 value, unsigned int n) { - s32 a = value >> (n - 1); + s32 a; + if (!value || !n) + return 0; + + a = value >> (n - 1); if (a && a != -1) return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; return value & ((1 << n) - 1);