2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

hid-for-linus-2025073101

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEL65usyKPHcrRDEicpmLzj2vtYEkFAmiL2DAACgkQpmLzj2vt
 YEk9aRAApXAYs/LNZ6hS2FvqxSKJhThLEm4sib6CvdQ82Er92aqrZEutkpb8Rq0/
 KMPLYTv9QsZgFKilZJnxIRwZRpy37C7SGO8Y3PSARpakxpCPe76RyqcY+DghrB9+
 kBRUzfDq9JR8RM8lcxw5dgEjgI3cz/5gS8OFh/LOVqe2nqub3U3Dr2kQkDE3DSiG
 BqIPhIG//TW0al6IxJQVL59egzVqzzqcgK/efBwT7WjToaCjXuZTClDiKASVLcmW
 p/xvsPjIBeVuEdOTfboiHowOs3RyXa3zWlHS76/x9JXVDPOrPcleJyoxEedU4BBG
 selhkdVEYcCOYMlzz7/p1BrIkkpczWfye5rSGQ05qmOeApMAxicKRp0DfTLm0EGa
 bdmDLIncSBRBb5iATrRS+rQcV6PSzLCaB9+rGiWS/EQPFCKh54SZSNvl5rw0WCHY
 +b9zmxmr2KG7B8eQQXx0/x6RNCC3gANqkVO4+TXamcGhJaoW2M0+xT9tzid0sL+S
 Y7t5C8T4KoI4dc2be76KNs/LlR6F5LK1E0HTkapdG+ZoiLc/PsqK0pnGc0aXd+/z
 Ee246MZEMsURgOEA68dEm4XBdh2xp9xVZIsZtaWcfSSQZJnfpx89v9Tl4MUlfqzj
 8BPLeYxxg9gxfxpGcP+oGMjXbeKDjSbmRJroOP4jw/WdPZ2rs0s=
 =kYcB
 -----END PGP SIGNATURE-----

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

Pull HID updates from Jiri Kosina:

 - hardening of HID core parser against conversion to 0 bits in s32ton()
   by buggy/malicious devices (Alan Stern)

 - fix for potential NULL pointer dereference in hid-apple that could be
   caused by malicious device with APPLE_MAGIC_BACKLIGHT quirk present
   triggering overflow in data field (Qasim Ijaz)

 - support for Wake-on-touch in intel-thc (Even Xu)

 - support for "Input max input size control" and "Input interrupt
   delay" I2C features in order to improve compatibility of THC devices
   with legacy HIDI2C touch devices (Even Xu)

 - support for Touch Bars on x86 MacBook Pros (Kerem Karabay)

 - support for XP-PEN Artist 22R Pro (Joshua Goins)

 - third party trackpart support for MacBookPro15,1 (Aditya Garg)

 - Apple Magic Keyboard A311[89] USB-C support (Aditya Garg, Grigorii
   Sokoli)

 - support for operating modes in amd-sfh (Basavaraj Natikar)

 - avoid setting up battery timer for Apple and Magicmouse devices
   without battery (Aditya Garg)

 - fix for behavior of the hid-mcp2221 driver for !CONFIG_IIO cases
   (Heiko Schocher)

 - other assorted fixups and device ID additions

* tag 'hid-for-linus-2025073101' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (54 commits)
  HID: core: Harden s32ton() against conversion to 0 bits
  HID: apple: validate feature-report field count to prevent NULL pointer dereference
  HID: core: Improve the kerneldoc for hid_report_len()
  selftests/hid: sync python tests to hid-tools 0.10
  selftests/hid: sync the python tests to hid-tools 0.8
  selftests/hid: run ruff format on the python part
  HID: magicmouse: use secs_to_jiffies() for battery timeout
  HID: apple: use secs_to_jiffies() for battery timeout
  HID: magicmouse: avoid setting up battery timer when not needed
  HID: apple: avoid setting up battery timer for devices without battery
  HID: amd_sfh: Enable operating mode
  HID: uclogic: Add support for XP-PEN Artist 22R Pro
  HID: rate-limit hid_warn to prevent log flooding
  HID: replace scnprintf() with sysfs_emit()
  HID: uclogic: make read-only array reconnect_event static const
  HID: mcp-2221: Replace manual comparison with min() macro
  HID: intel-thc-hid: Separate max input size control conditional list
  HID: mcp2221: set gpio pin mode
  HID: multitouch: add device ID for Apple Touch Bar
  HID: multitouch: specify that Apple Touch Bar is direct
  ...
This commit is contained in:
Linus Torvalds 2025-07-31 21:26:05 -07:00
commit b80a75cf69
44 changed files with 1667 additions and 476 deletions

View File

@ -188,6 +188,34 @@ 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.
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
=====================

View File

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

View File

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

View File

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

View File

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

View File

@ -79,6 +79,7 @@ enum sensor_idx {
accel_idx = 0,
gyro_idx = 1,
mag_idx = 2,
op_idx = 15,
als_idx = 19
};

View File

@ -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)
@ -42,11 +42,13 @@
#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
#define APPLE_BATTERY_TIMEOUT_SEC 60
#define HID_USAGE_MAGIC_BL 0xff00000f
#define APPLE_MAGIC_REPORT_ID_POWER 3
@ -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);
@ -89,6 +91,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;
@ -108,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[] = {
@ -152,21 +167,7 @@ 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[] = {
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 },
@ -212,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 },
@ -235,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 },
@ -425,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;
}
@ -466,42 +472,54 @@ 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)
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)
break;
case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021:
case USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021:
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:
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;
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)
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;
else if (hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K ||
hid->product == USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K)
break;
case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K:
case USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K:
table = apple_fn_keys;
else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI &&
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);
@ -524,9 +542,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;
}
@ -619,7 +645,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));
}
}
@ -682,7 +708,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);
}
@ -890,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;
@ -933,10 +960,12 @@ static int apple_probe(struct hid_device *hdev,
return ret;
}
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);
}
if (quirks & APPLE_BACKLIGHT_CTL)
apple_backlight_init(hdev);
@ -950,7 +979,9 @@ static int apple_probe(struct hid_device *hdev,
return 0;
out_err:
if (quirks & APPLE_RDESC_BATTERY)
timer_delete_sync(&asc->battery_timer);
hid_hw_stop(hdev);
return ret;
}
@ -959,6 +990,7 @@ static void apple_remove(struct hid_device *hdev)
{
struct apple_sc *asc = hid_get_drvdata(hdev);
if (asc->quirks & APPLE_RDESC_BATTERY)
timer_delete_sync(&asc->battery_timer);
hid_hw_stop(hdev);
@ -1129,19 +1161,25 @@ 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_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 },
.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),
@ -1157,10 +1195,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),
@ -1169,6 +1203,18 @@ 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_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 },

View File

@ -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);
@ -659,9 +663,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;
}
@ -2809,7 +2813,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);

View File

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

View File

@ -167,6 +167,12 @@
#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_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
@ -176,6 +182,7 @@
#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
@ -188,10 +195,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
@ -846,7 +849,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
@ -1406,6 +1409,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

View File

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

View File

@ -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
@ -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];
@ -827,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));
}
}
@ -863,17 +877,17 @@ static int magicmouse_probe(struct hid_device *hdev,
return ret;
}
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));
jiffies + secs_to_jiffies(USB_BATTERY_TIMEOUT_SEC));
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:
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,6 +964,8 @@ static void magicmouse_remove(struct hid_device *hdev)
if (msc) {
cancel_delayed_work_sync(&msc->work);
if (is_usb_magicmouse2(hdev->vendor, hdev->product) ||
is_usb_magictrackpad2(hdev->vendor, hdev->product))
timer_delete_sync(&msc->battery_timer);
}
@ -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");

View File

@ -18,6 +18,7 @@
#include <linux/i2c.h>
#include <linux/gpio/driver.h>
#include <linux/iio/iio.h>
#include <linux/minmax.h>
#include "hid-ids.h"
/* Commands codes in a raw output report */
@ -55,6 +56,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,
@ -241,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;
@ -271,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
@ -607,6 +623,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 +1308,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)

View File

@ -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
@ -220,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
@ -405,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 |
@ -625,6 +633,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 +658,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;
}
@ -821,12 +834,31 @@ 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,
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++;
@ -883,10 +915,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;
@ -1314,6 +1342,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;
@ -1321,6 +1356,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;
@ -1823,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);
@ -2320,6 +2367,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,

View File

@ -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) },
@ -973,14 +974,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) },
{ }

View File

@ -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;
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 && (field->application == HID_DG_PEN))
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);

View File

@ -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);
}
/**
@ -1341,7 +1343,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
};
@ -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;
}

View File

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

View File

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

View File

@ -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 */

View File

@ -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;
@ -177,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) },

View File

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

View File

@ -11,13 +11,20 @@
#include <linux/sizes.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include "intel-thc-dev.h"
#include "intel-thc-hw.h"
#include "intel-thc-wot.h"
#include "quicki2c-dev.h"
#include "quicki2c-hid.h"
#include "quicki2c-protocol.h"
static 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 =
@ -27,19 +34,26 @@ 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
* @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 +81,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 +113,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 +204,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 +225,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 +275,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 +287,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,20 +335,21 @@ 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
* @ddata: Point to quicki2c_ddata structure
*
* @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)
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;
@ -352,10 +363,11 @@ 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);
/* 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);
@ -392,15 +404,16 @@ 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;
}
/**
* 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.
*/
@ -408,18 +421,63 @@ 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;
}
/**
* quicki2c_dma_init - Configure THC DMA for quicki2c device
* @qcdev: pointer to the quicki2c device structure
* 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
*
* 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 +509,15 @@ static int quicki2c_dma_init(struct quicki2c_device *qcdev)
return ret;
}
return ret;
if (qcdev->ddata)
quicki2c_dma_adv_enable(qcdev);
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.
*
@ -465,11 +526,14 @@ 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);
}
/**
* 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 +544,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,28 +582,27 @@ 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)
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;
@ -577,7 +640,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);
@ -668,11 +731,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 +756,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)
{
@ -930,12 +990,12 @@ 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, &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);

View File

@ -7,12 +7,12 @@
#include <linux/hid-over-i2c.h>
#include <linux/workqueue.h>
#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
@ -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.
@ -122,6 +128,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;
@ -130,15 +146,15 @@ 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
* @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
* @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 +162,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;
@ -159,6 +175,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;

View File

@ -11,8 +11,11 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#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;
}

View File

@ -2,6 +2,7 @@
/* Copyright (c) 2024 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/math.h>
#include <linux/regmap.h>
#include "intel-thc-dev.h"
@ -1571,6 +1572,145 @@ 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");
/**
* 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 <xinpeng.sun@intel.com>");
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");

View File

@ -9,6 +9,7 @@
#include <linux/workqueue.h>
#include "intel-thc-dma.h"
#include "intel-thc-wot.h"
#define THC_REGMAP_COMMON_OFFSET 0x10
#define THC_REGMAP_MMIO_OFFSET 0x1000
@ -52,16 +53,21 @@ 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
* @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
* @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_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;
@ -73,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;
@ -81,6 +89,11 @@ struct thc_device {
u32 perf_limit;
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);
@ -112,5 +125,9 @@ 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);
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_ */

View File

@ -712,6 +712,28 @@ 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;
}
/*
* 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) |
@ -754,6 +776,24 @@ 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;
}
/*
* 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]);

View File

@ -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
*/
@ -117,13 +117,21 @@ 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
* @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;

View File

@ -399,6 +399,11 @@
#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_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)
#define THC_M_PRT_INT_EN_SBO BIT(1)
#define THC_M_PRT_INT_EN_SIDR BIT(2)

View File

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Intel Corporation */
#include <linux/acpi.h>
#include <linux/pm_wakeirq.h>
#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");

View File

@ -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 <linux/types.h>
#include <linux/gpio/consumer.h>
/**
* 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_ */

View File

@ -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);
}
/*
@ -229,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;
}
@ -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--)
@ -275,8 +275,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)
@ -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;
}
@ -940,7 +942,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;
}
@ -974,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;
}
@ -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;
}
@ -1081,12 +1081,11 @@ 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;
}
}
}
return NULL;
}
@ -1207,6 +1206,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,

View File

@ -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 */

View File

@ -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)
{
@ -1239,6 +1243,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, ...) \

View File

@ -5,6 +5,7 @@
# Copyright (c) 2017 Benjamin Tissoires <benjamin.tissoires@gmail.com>
# 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"
)

View File

@ -18,10 +18,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import dataclasses
import fcntl
import functools
import libevdev
import os
import threading
try:
import pyudev
@ -104,6 +106,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 +123,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 +165,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 +331,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]:
@ -336,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):

View File

@ -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):

View File

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

View File

@ -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):

View File

@ -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):

View File

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

View File

@ -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
@ -1228,9 +1229,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:
@ -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(

View File

@ -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):
"""
@ -997,12 +997,16 @@ 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.assert_contacts(
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.ContactIds(contact_id=4, tracking_id=-1, slot_num=None),
],
)
def confidence_change_assert_playback(self, uhdev, evdev, timeline):
"""
@ -1027,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
@ -1044,27 +1048,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest
uhdev = self.uhdev
evdev = uhdev.get_evdev()
self.confidence_change_assert_playback(uhdev, 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)],
[
(
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)],
[
(
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)],
[
(
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.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 +1124,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest
uhdev = self.uhdev
evdev = uhdev.get_evdev()
self.confidence_change_assert_playback(uhdev, 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)],
[
(
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)],
[
(
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)],
[
(
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.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 +1199,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest
uhdev = self.uhdev
evdev = uhdev.get_evdev()
self.confidence_change_assert_playback(uhdev, 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)],
[
(
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)],
[
(
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)],
[
(
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.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 +1271,68 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest
uhdev = self.uhdev
evdev = uhdev.get_evdev()
self.confidence_change_assert_playback(uhdev, 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)],
[
(
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)],
[
(
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)],
[
(
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.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 +1343,65 @@ class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest
uhdev = self.uhdev
evdev = uhdev.get_evdev()
self.confidence_change_assert_playback(uhdev, 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)],
[
(
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)],
[
(
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)],
[
(
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.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,
),
],
],
)