--- /dev/null
+From 0a0b38afee0e82901b62c98c5510aca5d79b2d0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Feb 2025 15:35:09 +0100
+Subject: HID: hid-universal-pidff: Add Asetek wheelbases support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit c385f61108d403633e8cfbdae15b35ccf7cee686 ]
+
+Adds Asetek vendor id and product ids for:
+- Invicta
+- Forte
+- La Prima
+- Tony Kanaan
+
+v2:
+- Misc spelling fix in driver loaded info
+
+v3:
+- Chanage Oleg's name order
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-ids.h | 6 ++++++
+ drivers/hid/hid-universal-pidff.c | 10 +++++++---
+ 2 files changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
+index e1e002df64de2..db88831633466 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -190,6 +190,12 @@
+ #define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102
+ #define USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY 0x8302
+
++#define USB_VENDOR_ID_ASETEK 0x2433
++#define USB_DEVICE_ID_ASETEK_INVICTA 0xf300
++#define USB_DEVICE_ID_ASETEK_FORTE 0xf301
++#define USB_DEVICE_ID_ASETEK_LA_PRIMA 0xf303
++#define USB_DEVICE_ID_ASETEK_TONY_KANAAN 0xf306
++
+ #define USB_VENDOR_ID_ASUS 0x0486
+ #define USB_DEVICE_ID_ASUS_T91MT 0x0185
+ #define USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO 0x0186
+diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c
+index 1b713b741d192..5b89ec7b5c26c 100644
+--- a/drivers/hid/hid-universal-pidff.c
++++ b/drivers/hid/hid-universal-pidff.c
+@@ -4,7 +4,7 @@
+ * hid-pidff wrapper for PID-enabled devices
+ * Handles device reports, quirks and extends usable button range
+ *
+- * Copyright (c) 2024, 2025 Makarenko Oleg
++ * Copyright (c) 2024, 2025 Oleg Makarenko
+ * Copyright (c) 2024, 2025 Tomasz Pakuła
+ */
+
+@@ -104,7 +104,7 @@ static int universal_pidff_probe(struct hid_device *hdev,
+ goto err;
+ }
+
+- hid_info(hdev, "Universal pidff driver loaded sucesfully!");
++ hid_info(hdev, "Universal pidff driver loaded sucessfully!");
+
+ return 0;
+ err:
+@@ -179,6 +179,10 @@ 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_LITE_STAR_GT987_FF),
+ .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) },
++ { HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_LA_PRIMA) },
++ { HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_TONY_KANAAN) },
+ { }
+ };
+ MODULE_DEVICE_TABLE(hid, universal_pidff_devices);
+@@ -194,5 +198,5 @@ module_hid_driver(universal_pidff);
+
+ MODULE_DESCRIPTION("Universal driver for USB PID Force Feedback devices");
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Makarenko Oleg <oleg@makarenk.ooo>");
++MODULE_AUTHOR("Oleg Makarenko <oleg@makarenk.ooo>");
+ MODULE_AUTHOR("Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>");
+--
+2.39.5
+
--- /dev/null
+From 18c55e3e5b2c33a02da7b212a4c2976409f8e90c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 25 Feb 2025 23:30:01 +0100
+Subject: HID: pidff: Clamp effect playback LOOP_COUNT value
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 0c6673e3d17b258b8c5c7331d28bf6c49f25ed30 ]
+
+Ensures the loop count will never exceed the logical_maximum.
+
+Fixes implementation errors happening when applications use the max
+value of int32/DWORD as the effect iterations. This could be observed
+when running software both native and in wine.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index ffecc712be003..74b033a4ac1b8 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -690,7 +690,8 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
+ } else {
+ pidff->effect_operation_status->value[0] =
+ pidff->operation_id[PID_EFFECT_START];
+- pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
++ pidff->effect_operation[PID_LOOP_COUNT].value[0] =
++ pidff_clamp(n, pidff->effect_operation[PID_LOOP_COUNT].field);
+ }
+
+ hid_hw_request(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
+--
+2.39.5
+
--- /dev/null
+From 302108835bbf1426dc34238367aa6867b9364e7f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:38:47 +0100
+Subject: HID: pidff: Clamp PERIODIC effect period to device's logical range
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit f538183e997a9fb6087e94e71e372de967b9e56a ]
+
+This ensures the effect can actually be played on the connected force
+feedback device. Adds clamping functions used instead of rescaling, as we
+don't want to change the characteristics of the periodic effects.
+
+Fixes edge cases found on Moza Racing and some other hardware where
+the effects would not play if the period is outside the defined
+logical range.
+
+Changes in v6:
+- Use in-kernel clamp macro instead of a custom solution
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 25dbed076f530..6b55345ce75ac 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -15,10 +15,9 @@
+ #include <linux/input.h>
+ #include <linux/slab.h>
+ #include <linux/usb.h>
+-
+ #include <linux/hid.h>
++#include <linux/minmax.h>
+
+-#include "usbhid.h"
+
+ #define PID_EFFECTS_MAX 64
+ #define PID_INFINITE 0xffff
+@@ -192,6 +191,16 @@ struct pidff_device {
+ u32 quirks;
+ };
+
++/*
++ * Clamp value for a given field
++ */
++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;
++}
++
+ /*
+ * Scale an unsigned value with range 0..max for the given field
+ */
+@@ -372,7 +381,11 @@ static void pidff_set_periodic_report(struct pidff_device *pidff,
+ pidff_set_signed(&pidff->set_periodic[PID_OFFSET],
+ effect->u.periodic.offset);
+ pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase);
+- pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period;
++
++ /* Clamp period to ensure the device can play the effect */
++ pidff->set_periodic[PID_PERIOD].value[0] =
++ pidff_clamp(effect->u.periodic.period,
++ pidff->set_periodic[PID_PERIOD].field);
+
+ hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC],
+ HID_REQ_SET_REPORT);
+--
+2.39.5
+
--- /dev/null
+From 18fb9bbc8cd16f86addce464172b451415e2fa2e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Feb 2025 15:35:10 +0100
+Subject: HID: pidff: Comment and code style update
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit e19675c2477491401b236ed939ad5a43ddc339af ]
+
+Update comments to fully conform to the Linux comment styling.
+Define Linux infinite effect duration (0) as FF_INFINITE
+
+Chanage Oleg's name order
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 57 +++++++++++++++-------------------
+ 1 file changed, 25 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 503f643b59cad..e2508a4d754d3 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -3,13 +3,9 @@
+ * Force feedback driver for USB HID PID compliant devices
+ *
+ * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
++ * Upgraded 2025 by Oleg Makarenko and Tomasz Pakuła
+ */
+
+-/*
+- */
+-
+-/* #define DEBUG */
+-
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+ #include "hid-pidff.h"
+@@ -25,9 +21,9 @@
+
+ /* Linux Force Feedback API uses miliseconds as time unit */
+ #define FF_TIME_EXPONENT -3
++#define FF_INFINITE 0
+
+ /* Report usage table used to put reports into an array */
+-
+ #define PID_SET_EFFECT 0
+ #define PID_EFFECT_OPERATION 1
+ #define PID_DEVICE_GAIN 2
+@@ -48,12 +44,12 @@ static const u8 pidff_reports[] = {
+ 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab,
+ 0x5a, 0x5f, 0x6e, 0x73, 0x74
+ };
+-
+-/* device_control is really 0x95, but 0x96 specified as it is the usage of
+-the only field in that report */
++/*
++ * device_control is really 0x95, but 0x96 specified
++ * as it is the usage of the only field in that report.
++ */
+
+ /* PID special fields */
+-
+ #define PID_EFFECT_TYPE 0x25
+ #define PID_DIRECTION 0x57
+ #define PID_EFFECT_OPERATION_ARRAY 0x78
+@@ -61,7 +57,6 @@ the only field in that report */
+ #define PID_DEVICE_CONTROL_ARRAY 0x96
+
+ /* Value usage tables used to put fields and values into arrays */
+-
+ #define PID_EFFECT_BLOCK_INDEX 0
+
+ #define PID_DURATION 1
+@@ -119,7 +114,6 @@ static const u8 pidff_device_gain[] = { 0x7e };
+ static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
+
+ /* Special field key tables used to put special field keys into arrays */
+-
+ #define PID_ENABLE_ACTUATORS 0
+ #define PID_DISABLE_ACTUATORS 1
+ #define PID_STOP_ALL_EFFECTS 2
+@@ -176,8 +170,10 @@ struct pidff_device {
+ struct pidff_usage effect_operation[sizeof(pidff_effect_operation)];
+ struct pidff_usage block_free[sizeof(pidff_block_free)];
+
+- /* Special field is a field that is not composed of
+- usage<->value pairs that pidff_usage values are */
++ /*
++ * Special field is a field that is not composed of
++ * usage<->value pairs that pidff_usage values are
++ */
+
+ /* Special field in create_new_effect */
+ struct hid_field *create_new_effect_type;
+@@ -222,7 +218,7 @@ static s32 pidff_clamp(s32 i, struct hid_field *field)
+ static int pidff_rescale(int i, int max, struct hid_field *field)
+ {
+ return i * (field->logical_maximum - field->logical_minimum) / max +
+- field->logical_minimum;
++ field->logical_minimum;
+ }
+
+ /*
+@@ -282,9 +278,8 @@ static void pidff_set_time(struct pidff_usage *usage, u16 time)
+
+ static void pidff_set_duration(struct pidff_usage *usage, u16 duration)
+ {
+- /* Convert infinite length from Linux API (0)
+- to PID standard (NULL) if needed */
+- if (duration == 0)
++ /* Infinite value conversion from Linux API -> PID */
++ if (duration == FF_INFINITE)
+ duration = PID_INFINITE;
+
+ if (duration == PID_INFINITE) {
+@@ -302,16 +297,16 @@ static void pidff_set_envelope_report(struct pidff_device *pidff,
+ struct ff_envelope *envelope)
+ {
+ pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] =
+- pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
++ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+
+ pidff->set_envelope[PID_ATTACK_LEVEL].value[0] =
+- pidff_rescale(envelope->attack_level >
+- S16_MAX ? S16_MAX : envelope->attack_level, S16_MAX,
+- pidff->set_envelope[PID_ATTACK_LEVEL].field);
++ pidff_rescale(envelope->attack_level >
++ S16_MAX ? S16_MAX : envelope->attack_level, S16_MAX,
++ pidff->set_envelope[PID_ATTACK_LEVEL].field);
+ pidff->set_envelope[PID_FADE_LEVEL].value[0] =
+- pidff_rescale(envelope->fade_level >
+- S16_MAX ? S16_MAX : envelope->fade_level, S16_MAX,
+- pidff->set_envelope[PID_FADE_LEVEL].field);
++ pidff_rescale(envelope->fade_level >
++ S16_MAX ? S16_MAX : envelope->fade_level, S16_MAX,
++ pidff->set_envelope[PID_FADE_LEVEL].field);
+
+ pidff_set_time(&pidff->set_envelope[PID_ATTACK_TIME],
+ envelope->attack_length);
+@@ -702,9 +697,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;
+ }
+
+@@ -732,8 +725,11 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id)
+
+ hid_dbg(pidff->hid, "starting to erase %d/%d\n",
+ effect_id, pidff->pid_id[effect_id]);
+- /* Wait for the queue to clear. We do not want a full fifo to
+- prevent the effect removal. */
++
++ /*
++ * Wait for the queue to clear. We do not want
++ * a full fifo to prevent the effect removal.
++ */
+ hid_hw_wait(pidff->hid);
+ pidff_playback_pid(pidff, pid_id, 0);
+ pidff_erase_pid(pidff, pid_id);
+@@ -1239,7 +1235,6 @@ static int pidff_find_effects(struct pidff_device *pidff,
+ set_bit(FF_FRICTION, dev->ffbit);
+
+ return 0;
+-
+ }
+
+ #define PIDFF_FIND_FIELDS(name, report, strict) \
+@@ -1370,12 +1365,10 @@ static int pidff_check_autocenter(struct pidff_device *pidff,
+ hid_notice(pidff->hid,
+ "device has unknown autocenter control method\n");
+ }
+-
+ pidff_erase_pid(pidff,
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]);
+
+ return 0;
+-
+ }
+
+ /*
+--
+2.39.5
+
--- /dev/null
+From c3a3eb88636b587ca89edfa209e5d19c7ec6b14b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:38:57 +0100
+Subject: HID: pidff: Completely rework and fix pidff_reset function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit cb3fd788e3fa5358602a49809c4eb4911539c9d0 ]
+
+Previously, it was assumed that DEVICE_CONTROL usage is always an array
+but a lot of devices implements it as a bitmask variable. This led to
+the pidff_reset function not working and causing errors in such cases.
+
+Selectors can come in three types. One selection of a set, N selections
+and Any selection in form of bitmask as from USB Hid Usage Tables v1.5,
+subsection 3.4.2.1
+
+Added pidff_send_device_control which handles usage flag check which
+decides whether DEVICE_CONTROL should be handled as "One selection of a
+set" or "Any selection of a set".
+
+Reset was triggered once, on device initialization. Now, it's triggered
+every time when uploading an effect to an empty device (no currently
+stored effects), tracked by pidff->effect_count variable.
+
+Co-developed-by: Makarenko Oleg <oleg@makarenk.ooo>
+Signed-off-by: Makarenko Oleg <oleg@makarenk.ooo>
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 138 +++++++++++++++++++++------------
+ 1 file changed, 89 insertions(+), 49 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 635596a57c75d..99b5d3deb40d0 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -109,9 +109,10 @@ static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
+ /* Special field key tables used to put special field keys into arrays */
+
+ #define PID_ENABLE_ACTUATORS 0
+-#define PID_STOP_ALL_EFFECTS 1
+-#define PID_RESET 2
+-static const u8 pidff_device_control[] = { 0x97, 0x99, 0x9a };
++#define PID_DISABLE_ACTUATORS 1
++#define PID_STOP_ALL_EFFECTS 2
++#define PID_RESET 3
++static const u8 pidff_device_control[] = { 0x97, 0x98, 0x99, 0x9a };
+
+ #define PID_CONSTANT 0
+ #define PID_RAMP 1
+@@ -190,6 +191,7 @@ struct pidff_device {
+ int pid_id[PID_EFFECTS_MAX];
+
+ u32 quirks;
++ u8 effect_count;
+ };
+
+ /*
+@@ -490,9 +492,83 @@ static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old)
+ effect->u.ramp.end_level != old->u.ramp.end_level;
+ }
+
++/*
++ * Clear device control report
++ */
++static void pidff_send_device_control(struct pidff_device *pidff, int field)
++{
++ int i, tmp;
++ int field_index = pidff->control_id[field];
++
++ /* Detect if the field is a bitmask variable or an array */
++ if (pidff->device_control->flags & HID_MAIN_ITEM_VARIABLE) {
++ hid_dbg(pidff->hid, "DEVICE_CONTROL is a bitmask\n");
++ /* Clear current bitmask */
++ for(i = 0; i < sizeof(pidff_device_control); i++) {
++ tmp = pidff->control_id[i];
++ pidff->device_control->value[tmp] = 0;
++ }
++ pidff->device_control->value[field_index - 1] = 1;
++ } else {
++ hid_dbg(pidff->hid, "DEVICE_CONTROL is an array\n");
++ pidff->device_control->value[0] = field_index;
++ }
++
++ hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
++ hid_hw_wait(pidff->hid);
++}
++
++/*
++ * Modify actuators state
++ */
++static void pidff_modify_actuators_state(struct pidff_device *pidff, bool enable)
++{
++ hid_dbg(pidff->hid, "%s actuators\n", enable ? "Enable" : "Disable");
++ pidff_send_device_control(pidff,
++ enable ? PID_ENABLE_ACTUATORS : PID_DISABLE_ACTUATORS);
++}
++
++/*
++ * Reset the device, stop all effects, enable actuators
++ * Refetch pool report
++ */
++static void pidff_reset(struct pidff_device *pidff)
++{
++ int i = 0;
++
++ /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
++ pidff_send_device_control(pidff, PID_RESET);
++ pidff_send_device_control(pidff, PID_RESET);
++ pidff->effect_count = 0;
++
++ pidff_send_device_control(pidff, PID_STOP_ALL_EFFECTS);
++ pidff_modify_actuators_state(pidff, 1);
++
++ /* pool report is sometimes messed up, refetch it */
++ hid_hw_request(pidff->hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT);
++ hid_hw_wait(pidff->hid);
++
++ if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
++ while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
++ if (i++ > 20) {
++ hid_warn(pidff->hid,
++ "device reports %d simultaneous effects\n",
++ pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
++ break;
++ }
++ hid_dbg(pidff->hid, "pid_pool requested again\n");
++ hid_hw_request(pidff->hid, pidff->reports[PID_POOL],
++ HID_REQ_GET_REPORT);
++ hid_hw_wait(pidff->hid);
++ }
++ }
++}
++
+ /*
+ * Send a request for effect upload to the device
+ *
++ * Reset and enable actuators if no effects were present on the device
++ *
+ * Returns 0 if device reported success, -ENOSPC if the device reported memory
+ * is full. Upon unknown response the function will retry for 60 times, if
+ * still unsuccessful -EIO is returned.
+@@ -501,6 +577,9 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
+ {
+ int j;
+
++ if (!pidff->effect_count)
++ pidff_reset(pidff);
++
+ pidff->create_new_effect_type->value[0] = efnum;
+ hid_hw_request(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT],
+ HID_REQ_SET_REPORT);
+@@ -520,6 +599,8 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
+ hid_dbg(pidff->hid, "device reported free memory: %d bytes\n",
+ pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
+ pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
++
++ pidff->effect_count++;
+ return 0;
+ }
+ if (pidff->block_load_status->value[0] ==
+@@ -568,12 +649,16 @@ static int pidff_playback(struct input_dev *dev, int effect_id, int value)
+
+ /*
+ * Erase effect with PID id
++ * Decrease the device effect counter
+ */
+ static void pidff_erase_pid(struct pidff_device *pidff, int pid_id)
+ {
+ pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
+ hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_FREE],
+ HID_REQ_SET_REPORT);
++
++ if (pidff->effect_count > 0)
++ pidff->effect_count--;
+ }
+
+ /*
+@@ -1221,50 +1306,6 @@ static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
+ return 0;
+ }
+
+-/*
+- * Reset the device
+- */
+-static void pidff_reset(struct pidff_device *pidff)
+-{
+- struct hid_device *hid = pidff->hid;
+- int i = 0;
+-
+- pidff->device_control->value[0] = pidff->control_id[PID_RESET];
+- /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
+- hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
+- hid_hw_wait(hid);
+- hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
+- hid_hw_wait(hid);
+-
+- pidff->device_control->value[0] = pidff->control_id[PID_STOP_ALL_EFFECTS];
+- hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
+- hid_hw_wait(hid);
+-
+- pidff->device_control->value[0] =
+- pidff->control_id[PID_ENABLE_ACTUATORS];
+- hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
+- hid_hw_wait(hid);
+-
+- /* pool report is sometimes messed up, refetch it */
+- hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT);
+- hid_hw_wait(hid);
+-
+- if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
+- while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
+- if (i++ > 20) {
+- hid_warn(pidff->hid,
+- "device reports %d simultaneous effects\n",
+- pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
+- break;
+- }
+- hid_dbg(pidff->hid, "pid_pool requested again\n");
+- hid_hw_request(hid, pidff->reports[PID_POOL],
+- HID_REQ_GET_REPORT);
+- hid_hw_wait(hid);
+- }
+- }
+-}
+-
+ /*
+ * Test if autocenter modification is using the supported method
+ */
+@@ -1330,6 +1371,7 @@ int hid_pidff_init_with_quirks(struct hid_device *hid, __u32 initial_quirks)
+
+ pidff->hid = hid;
+ pidff->quirks = initial_quirks;
++ pidff->effect_count = 0;
+
+ hid_device_io_start(hid);
+
+@@ -1346,8 +1388,6 @@ int hid_pidff_init_with_quirks(struct hid_device *hid, __u32 initial_quirks)
+ if (error)
+ goto fail;
+
+- pidff_reset(pidff);
+-
+ if (test_bit(FF_GAIN, dev->ffbit)) {
+ pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff);
+ hid_hw_request(hid, pidff->reports[PID_DEVICE_GAIN],
+--
+2.39.5
+
--- /dev/null
+From 7cd7fbc41bfea5e4f94a4e0a233bc46c0f66e151 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 25 Feb 2025 23:30:02 +0100
+Subject: HID: pidff: Compute INFINITE value instead of using hardcoded 0xffff
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 1a575044d516972a1d036d54c0180b9085e21dc6 ]
+
+As per USB PID standard:
+INFINITE - Referrers to the maximum value of a range. i.e. if in an 8
+bit unsigned field the value of 255 would indicate INFINITE.
+
+Detecting 0xffff (U16_MAX) is still important as we MIGHT get this value
+as infinite from some native software as 0 was never actually defined
+in Linux' FF api as the infinite value. I'm working on it though.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 74b033a4ac1b8..a614438e43bd8 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -283,8 +283,9 @@ static void pidff_set_duration(struct pidff_usage *usage, u16 duration)
+ if (duration == FF_INFINITE)
+ duration = PID_INFINITE;
+
++ /* PID defines INFINITE as the max possible value for duration field */
+ if (duration == PID_INFINITE) {
+- usage->value[0] = PID_INFINITE;
++ usage->value[0] = (1U << usage->field->report_size) - 1;
+ return;
+ }
+
+--
+2.39.5
+
--- /dev/null
+From 2ff0ff4d3df8e2b26d32cd4ee5761c22f0369f26 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:38:59 +0100
+Subject: HID: pidff: Define values used in pidff_find_special_fields
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 1c12f136891cf4d2d4e6aa202d671a9d2171a716 ]
+
+Makes it clear where did these values came from
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 42c951a1d65bf..bd913d57e4d75 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -48,6 +48,14 @@ static const u8 pidff_reports[] = {
+ /* device_control is really 0x95, but 0x96 specified as it is the usage of
+ the only field in that report */
+
++/* PID special fields */
++
++#define PID_EFFECT_TYPE 0x25
++#define PID_DIRECTION 0x57
++#define PID_EFFECT_OPERATION_ARRAY 0x78
++#define PID_BLOCK_LOAD_STATUS 0x8b
++#define PID_DEVICE_CONTROL_ARRAY 0x96
++
+ /* Value usage tables used to put fields and values into arrays */
+
+ #define PID_EFFECT_BLOCK_INDEX 0
+@@ -1056,23 +1064,24 @@ static int pidff_find_special_fields(struct pidff_device *pidff)
+
+ pidff->create_new_effect_type =
+ pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT],
+- 0x25, 1);
++ PID_EFFECT_TYPE, 1);
+ pidff->set_effect_type =
+ pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
+- 0x25, 1);
++ PID_EFFECT_TYPE, 1);
+ pidff->effect_direction =
+ pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
+- 0x57, 0);
++ PID_DIRECTION, 0);
+ pidff->device_control =
+ pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL],
+- 0x96, !(pidff->quirks & HID_PIDFF_QUIRK_PERMISSIVE_CONTROL));
++ PID_DEVICE_CONTROL_ARRAY,
++ !(pidff->quirks & HID_PIDFF_QUIRK_PERMISSIVE_CONTROL));
+
+ pidff->block_load_status =
+ pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD],
+- 0x8b, 1);
++ PID_BLOCK_LOAD_STATUS, 1);
+ pidff->effect_operation_status =
+ pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION],
+- 0x78, 1);
++ PID_EFFECT_OPERATION_ARRAY, 1);
+
+ hid_dbg(pidff->hid, "search done\n");
+
+--
+2.39.5
+
--- /dev/null
+From 159ce5f3d0e9462a48955532fbf2352cd6fa377e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:39:01 +0100
+Subject: HID: pidff: Factor out code for setting gain
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit f7ebf0b11b9e04bf59c438ad14f0115b12aa2f44 ]
+
+Makes it possible to easily set gain from inside hid-pidff.c
+
+Changes in v7:
+- Check if device gain field exists before setting device gain
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 26 +++++++++++++++-----------
+ 1 file changed, 15 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 180b2cf66e4c7..ac6f940abd901 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -539,6 +539,19 @@ static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old)
+ effect->u.ramp.end_level != old->u.ramp.end_level;
+ }
+
++/*
++ * Set device gain
++ */
++static void pidff_set_gain_report(struct pidff_device *pidff, u16 gain)
++{
++ if (!pidff->device_gain[PID_DEVICE_GAIN_FIELD].field)
++ return;
++
++ pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain);
++ hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
++ HID_REQ_SET_REPORT);
++}
++
+ /*
+ * Clear device control report
+ */
+@@ -865,11 +878,7 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
+ */
+ static void pidff_set_gain(struct input_dev *dev, u16 gain)
+ {
+- struct pidff_device *pidff = dev->ff->private;
+-
+- pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain);
+- hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
+- HID_REQ_SET_REPORT);
++ pidff_set_gain_report(dev->ff->private, gain);
+ }
+
+ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
+@@ -1414,12 +1423,7 @@ int hid_pidff_init_with_quirks(struct hid_device *hid, __u32 initial_quirks)
+ if (error)
+ goto fail;
+
+- if (test_bit(FF_GAIN, dev->ffbit)) {
+- pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff);
+- hid_hw_request(hid, pidff->reports[PID_DEVICE_GAIN],
+- HID_REQ_SET_REPORT);
+- }
+-
++ pidff_set_gain_report(pidff, 0xffff);
+ error = pidff_check_autocenter(pidff, dev);
+ if (error)
+ goto fail;
+--
+2.39.5
+
--- /dev/null
+From 56cfa0759e801c5bcad99a3849bd2b1524b5a90b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:39:06 +0100
+Subject: HID: pidff: Factor out pool report fetch and remove excess
+ declaration
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 5d98079b2d0186e1f586301a9c00144a669416a8 ]
+
+We only want to refetch the pool report during device init. Reset
+function is now called when uploading effects to an empty device so
+extract pool fetch to separate function and call it from init before
+autocenter check (autocenter check triggered reset during init).
+
+Remove a superfluous pointer declaration and assigment as well.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 45 ++++++++++++++++++----------------
+ 1 file changed, 24 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index b21e844f5f3a3..f23381b6e3447 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -591,12 +591,9 @@ static void pidff_modify_actuators_state(struct pidff_device *pidff, bool enable
+
+ /*
+ * Reset the device, stop all effects, enable actuators
+- * Refetch pool report
+ */
+ static void pidff_reset(struct pidff_device *pidff)
+ {
+- int i = 0;
+-
+ /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
+ pidff_send_device_control(pidff, PID_RESET);
+ pidff_send_device_control(pidff, PID_RESET);
+@@ -604,23 +601,29 @@ static void pidff_reset(struct pidff_device *pidff)
+
+ pidff_send_device_control(pidff, PID_STOP_ALL_EFFECTS);
+ pidff_modify_actuators_state(pidff, 1);
++}
+
+- /* pool report is sometimes messed up, refetch it */
+- hid_hw_request(pidff->hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT);
+- hid_hw_wait(pidff->hid);
++/*
++ * Refetch pool report
++ */
++static void pidff_fetch_pool(struct pidff_device *pidff)
++{
++ if (!pidff->pool[PID_SIMULTANEOUS_MAX].value)
++ return;
+
+- if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
+- while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
+- if (i++ > 20) {
+- hid_warn(pidff->hid,
+- "device reports %d simultaneous effects\n",
+- pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
+- break;
+- }
+- hid_dbg(pidff->hid, "pid_pool requested again\n");
+- hid_hw_request(pidff->hid, pidff->reports[PID_POOL],
+- HID_REQ_GET_REPORT);
+- hid_hw_wait(pidff->hid);
++ int i = 0;
++ while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
++ hid_dbg(pidff->hid, "pid_pool requested again\n");
++ hid_hw_request(pidff->hid, pidff->reports[PID_POOL],
++ HID_REQ_GET_REPORT);
++ hid_hw_wait(pidff->hid);
++
++ /* break after 20 tries with SIMULTANEOUS_MAX < 2 */
++ if (i++ > 20) {
++ hid_warn(pidff->hid,
++ "device reports %d simultaneous effects\n",
++ pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
++ break;
+ }
+ }
+ }
+@@ -916,9 +919,7 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
+ */
+ static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude)
+ {
+- struct pidff_device *pidff = dev->ff->private;
+-
+- pidff_autocenter(pidff, magnitude);
++ pidff_autocenter(dev->ff->private, magnitude);
+ }
+
+ /*
+@@ -1424,6 +1425,8 @@ int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks)
+ if (error)
+ goto fail;
+
++ /* pool report is sometimes messed up, refetch it */
++ pidff_fetch_pool(pidff);
+ pidff_set_gain_report(pidff, U16_MAX);
+ error = pidff_check_autocenter(pidff, dev);
+ if (error)
+--
+2.39.5
+
--- /dev/null
+From 67be8f2859efa7abf3df4b1d446e02f05b6e9837 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 25 Feb 2025 23:30:03 +0100
+Subject: HID: pidff: Fix 90 degrees direction name North -> East
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit f98ecedbeca34a8df1460c3a03cce32639c99a9d ]
+
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index a614438e43bd8..6eb7934c8f53b 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -145,7 +145,7 @@ static const u8 pidff_block_load_status[] = { 0x8c, 0x8d, 0x8e};
+ #define PID_EFFECT_STOP 1
+ static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b };
+
+-/* Polar direction 90 degrees (North) */
++/* Polar direction 90 degrees (East) */
+ #define PIDFF_FIXED_WHEEL_DIRECTION 0x4000
+
+ struct pidff_usage {
+--
+2.39.5
+
--- /dev/null
+From 7f7e1dc79ab8a02d52eac5a493699b30de646ea3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 25 Feb 2025 23:30:04 +0100
+Subject: HID: pidff: Fix set_device_control()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit e2fa0bdf08a70623f24ed52f2037a330999d9800 ]
+
+As the search for Device Control report is permissive, make sure the
+desired field was actually found, before trying to set it.
+
+Fix bitmask clearing as it was erronously using index instead of
+index - 1 (HID arrays index is 1-based).
+
+Add last two missing Device Control usages to the defined array.
+PID_PAUSE and PID_CONTINUE.
+
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 20 +++++++++++++++-----
+ 1 file changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 6eb7934c8f53b..8dfd2c554a276 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -118,7 +118,9 @@ static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
+ #define PID_DISABLE_ACTUATORS 1
+ #define PID_STOP_ALL_EFFECTS 2
+ #define PID_RESET 3
+-static const u8 pidff_device_control[] = { 0x97, 0x98, 0x99, 0x9a };
++#define PID_PAUSE 4
++#define PID_CONTINUE 5
++static const u8 pidff_device_control[] = { 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c };
+
+ #define PID_CONSTANT 0
+ #define PID_RAMP 1
+@@ -551,21 +553,29 @@ static void pidff_set_gain_report(struct pidff_device *pidff, u16 gain)
+ }
+
+ /*
+- * Clear device control report
++ * Send device control report to the device
+ */
+ static void pidff_set_device_control(struct pidff_device *pidff, int field)
+ {
+- int i, tmp;
++ int i, index;
+ int field_index = pidff->control_id[field];
+
++ if (field_index < 1)
++ return;
++
+ /* Detect if the field is a bitmask variable or an array */
+ if (pidff->device_control->flags & HID_MAIN_ITEM_VARIABLE) {
+ hid_dbg(pidff->hid, "DEVICE_CONTROL is a bitmask\n");
++
+ /* Clear current bitmask */
+ for(i = 0; i < sizeof(pidff_device_control); i++) {
+- tmp = pidff->control_id[i];
+- pidff->device_control->value[tmp] = 0;
++ index = pidff->control_id[i];
++ if (index < 1)
++ continue;
++
++ pidff->device_control->value[index - 1] = 0;
+ }
++
+ pidff->device_control->value[field_index - 1] = 1;
+ } else {
+ hid_dbg(pidff->hid, "DEVICE_CONTROL is an array\n");
+--
+2.39.5
+
--- /dev/null
+From 9f1981ec1eaa69fb1d14e8d6c02bbed402ddd623 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Feb 2025 15:35:08 +0100
+Subject: HID: pidff: Make sure to fetch pool before checking SIMULTANEOUS_MAX
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 1f650dcec32d22deb1d6db12300a2b98483099a9 ]
+
+As noted by Anssi some 20 years ago, pool report is sometimes messed up.
+This worked fine on many devices but casued oops on VRS DirectForce PRO.
+
+Here, we're making sure pool report is refetched before trying to access
+any of it's fields. While loop was replaced with a for loop + exit
+conditions were moved aroud to decrease the possibility of creating an
+infinite loop scenario.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 29 +++++++++++++----------------
+ 1 file changed, 13 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index f23381b6e3447..503f643b59cad 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -604,28 +604,25 @@ static void pidff_reset(struct pidff_device *pidff)
+ }
+
+ /*
+- * Refetch pool report
++ * Fetch pool report
+ */
+ static void pidff_fetch_pool(struct pidff_device *pidff)
+ {
+- if (!pidff->pool[PID_SIMULTANEOUS_MAX].value)
+- return;
++ int i;
++ struct hid_device *hid = pidff->hid;
+
+- int i = 0;
+- while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
+- hid_dbg(pidff->hid, "pid_pool requested again\n");
+- hid_hw_request(pidff->hid, pidff->reports[PID_POOL],
+- HID_REQ_GET_REPORT);
+- hid_hw_wait(pidff->hid);
++ /* Repeat if PID_SIMULTANEOUS_MAX < 2 to make sure it's correct */
++ for(i = 0; i < 20; i++) {
++ hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT);
++ hid_hw_wait(hid);
+
+- /* break after 20 tries with SIMULTANEOUS_MAX < 2 */
+- if (i++ > 20) {
+- hid_warn(pidff->hid,
+- "device reports %d simultaneous effects\n",
+- pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
+- break;
+- }
++ if (!pidff->pool[PID_SIMULTANEOUS_MAX].value)
++ return;
++ if (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] >= 2)
++ return;
+ }
++ hid_warn(hid, "device reports %d simultaneous effects\n",
++ pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
+ }
+
+ /*
+--
+2.39.5
+
--- /dev/null
+From 3365a9dc9e2fcb2e5293178b093467e5f4f6225f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:39:03 +0100
+Subject: HID: pidff: Move all hid-pidff definitions to a dedicated header
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 0d24d4b1da96df9fc5ff36966f40f980ef864d46 ]
+
+Do not clutter hid includes with stuff not needed outside of
+the kernel.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/hid-universal-pidff.c | 3 ++-
+ drivers/hid/usbhid/hid-core.c | 1 +
+ drivers/hid/usbhid/hid-pidff.c | 3 ++-
+ drivers/hid/usbhid/hid-pidff.h | 33 +++++++++++++++++++++++++++++++
+ include/linux/hid.h | 15 --------------
+ 5 files changed, 38 insertions(+), 17 deletions(-)
+ create mode 100644 drivers/hid/usbhid/hid-pidff.h
+
+diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c
+index 7ef5ab9146b1c..1b713b741d192 100644
+--- a/drivers/hid/hid-universal-pidff.c
++++ b/drivers/hid/hid-universal-pidff.c
+@@ -13,6 +13,7 @@
+ #include <linux/module.h>
+ #include <linux/input-event-codes.h>
+ #include "hid-ids.h"
++#include "usbhid/hid-pidff.h"
+
+ #define JOY_RANGE (BTN_DEAD - BTN_JOYSTICK + 1)
+
+@@ -89,7 +90,7 @@ static int universal_pidff_probe(struct hid_device *hdev,
+ }
+
+ /* Check if HID_PID support is enabled */
+- int (*init_function)(struct hid_device *, __u32);
++ int (*init_function)(struct hid_device *, u32);
+ init_function = hid_pidff_init_with_quirks;
+
+ if (!init_function) {
+diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
+index a6eb6fe6130d1..44c2351b870fa 100644
+--- a/drivers/hid/usbhid/hid-core.c
++++ b/drivers/hid/usbhid/hid-core.c
+@@ -35,6 +35,7 @@
+ #include <linux/hid-debug.h>
+ #include <linux/hidraw.h>
+ #include "usbhid.h"
++#include "hid-pidff.h"
+
+ /*
+ * Version Information
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index ac6f940abd901..a8eaa77e80be3 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -12,6 +12,7 @@
+
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
++#include "hid-pidff.h"
+ #include <linux/input.h>
+ #include <linux/slab.h>
+ #include <linux/usb.h>
+@@ -1383,7 +1384,7 @@ static int pidff_check_autocenter(struct pidff_device *pidff,
+ * Check if the device is PID and initialize it
+ * Set initial quirks
+ */
+-int hid_pidff_init_with_quirks(struct hid_device *hid, __u32 initial_quirks)
++int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks)
+ {
+ struct pidff_device *pidff;
+ struct hid_input *hidinput = list_entry(hid->inputs.next,
+diff --git a/drivers/hid/usbhid/hid-pidff.h b/drivers/hid/usbhid/hid-pidff.h
+new file mode 100644
+index 0000000000000..dda571e0a5bd3
+--- /dev/null
++++ b/drivers/hid/usbhid/hid-pidff.h
+@@ -0,0 +1,33 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++#ifndef __HID_PIDFF_H
++#define __HID_PIDFF_H
++
++#include <linux/hid.h>
++
++/* HID PIDFF quirks */
++
++/* 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 */
++#define HID_PIDFF_QUIRK_MISSING_PBO BIT(1)
++
++/* Initialise device control field even if logical_minimum != 1 */
++#define HID_PIDFF_QUIRK_PERMISSIVE_CONTROL BIT(2)
++
++/* Use fixed 0x4000 direction during SET_EFFECT report upload */
++#define HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION BIT(3)
++
++/* Force all periodic effects to be uploaded as SINE */
++#define HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY BIT(4)
++
++#ifdef CONFIG_HID_PID
++int hid_pidff_init(struct hid_device *hid);
++int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks);
++#else
++#define hid_pidff_init NULL
++#define hid_pidff_init_with_quirks NULL
++#endif
++
++#endif
+diff --git a/include/linux/hid.h b/include/linux/hid.h
+index e180679ab284c..9ca7e26ac4e92 100644
+--- a/include/linux/hid.h
++++ b/include/linux/hid.h
+@@ -1222,21 +1222,6 @@ unsigned long hid_lookup_quirk(const struct hid_device *hdev);
+ int hid_quirks_init(char **quirks_param, __u16 bus, int count);
+ void hid_quirks_exit(__u16 bus);
+
+-#ifdef CONFIG_HID_PID
+-int hid_pidff_init(struct hid_device *hid);
+-int hid_pidff_init_with_quirks(struct hid_device *hid, __u32 initial_quirks);
+-#else
+-#define hid_pidff_init NULL
+-#define hid_pidff_init_with_quirks NULL
+-#endif
+-
+-/* HID PIDFF quirks */
+-#define HID_PIDFF_QUIRK_MISSING_DELAY BIT(0)
+-#define HID_PIDFF_QUIRK_MISSING_PBO BIT(1)
+-#define HID_PIDFF_QUIRK_PERMISSIVE_CONTROL BIT(2)
+-#define HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION BIT(3)
+-#define HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY BIT(4)
+-
+ #define dbg_hid(fmt, ...) pr_debug("%s: " fmt, __FILE__, ##__VA_ARGS__)
+
+ #define hid_err(hid, fmt, ...) \
+--
+2.39.5
+
--- /dev/null
+From 8b7b49c3973926eb82d92dd75ce5fabb4aeb81d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Feb 2025 15:35:12 +0100
+Subject: HID: pidff: Remove redundant call to pidff_find_special_keys
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 1bd55e79cbc0ea2d6a65f51e06c891806359c2f2 ]
+
+Probably left out as a mistake after Anssi created the helper macro
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index d5734cbf745d1..6f6c47bd57eaa 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -1159,10 +1159,6 @@ static int pidff_find_special_fields(struct pidff_device *pidff)
+ return -1;
+ }
+
+- pidff_find_special_keys(pidff->control_id, pidff->device_control,
+- pidff_device_control,
+- sizeof(pidff_device_control));
+-
+ PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control);
+
+ if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type,
+--
+2.39.5
+
--- /dev/null
+From 39fae9c77f9df0a2513dc84357780918ba952cc9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 25 Feb 2025 23:30:00 +0100
+Subject: HID: pidff: Rename two functions to align them with naming convention
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit bbeface1051142bcb0473fdcc89102ea5b31607d ]
+
+Driver uses "set" everywhere to indicate setting report values and
+requesting HID_REQ_SET_REPORT
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 6f6c47bd57eaa..ffecc712be003 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -552,7 +552,7 @@ static void pidff_set_gain_report(struct pidff_device *pidff, u16 gain)
+ /*
+ * Clear device control report
+ */
+-static void pidff_send_device_control(struct pidff_device *pidff, int field)
++static void pidff_set_device_control(struct pidff_device *pidff, int field)
+ {
+ int i, tmp;
+ int field_index = pidff->control_id[field];
+@@ -578,10 +578,10 @@ static void pidff_send_device_control(struct pidff_device *pidff, int field)
+ /*
+ * Modify actuators state
+ */
+-static void pidff_modify_actuators_state(struct pidff_device *pidff, bool enable)
++static void pidff_set_actuators(struct pidff_device *pidff, bool enable)
+ {
+ hid_dbg(pidff->hid, "%s actuators\n", enable ? "Enable" : "Disable");
+- pidff_send_device_control(pidff,
++ pidff_set_device_control(pidff,
+ enable ? PID_ENABLE_ACTUATORS : PID_DISABLE_ACTUATORS);
+ }
+
+@@ -591,12 +591,12 @@ static void pidff_modify_actuators_state(struct pidff_device *pidff, bool enable
+ static void pidff_reset(struct pidff_device *pidff)
+ {
+ /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
+- pidff_send_device_control(pidff, PID_RESET);
+- pidff_send_device_control(pidff, PID_RESET);
++ pidff_set_device_control(pidff, PID_RESET);
++ pidff_set_device_control(pidff, PID_RESET);
+ pidff->effect_count = 0;
+
+- pidff_send_device_control(pidff, PID_STOP_ALL_EFFECTS);
+- pidff_modify_actuators_state(pidff, 1);
++ pidff_set_device_control(pidff, PID_STOP_ALL_EFFECTS);
++ pidff_set_actuators(pidff, 1);
+ }
+
+ /*
+--
+2.39.5
+
--- /dev/null
+From 9e39fc1bd5b565a5404006bc53e73915edf33824 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:39:00 +0100
+Subject: HID: pidff: Rescale time values to match field units
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 8713107221a8ce4021ec5fa12bb50ecc8165cf08 ]
+
+PID devices can use different exponents for time fields, while Linux
+Force Feedback API only supports miliseconds.
+
+Read the exponent of a given time field and scale its value accordingly.
+
+Changes in v7:
+- Rescale all time fields, not only period
+
+changes in v9:
+- Properly assign fade_lenght, not attack_length to PID_FADE_TIME
+
+Co-developed-by: Makarenko Oleg <oleg@makarenk.ooo>
+Signed-off-by: Makarenko Oleg <oleg@makarenk.ooo>
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 69 ++++++++++++++++++++++++++--------
+ 1 file changed, 54 insertions(+), 15 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index bd913d57e4d75..180b2cf66e4c7 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -22,6 +22,9 @@
+ #define PID_EFFECTS_MAX 64
+ #define PID_INFINITE 0xffff
+
++/* Linux Force Feedback API uses miliseconds as time unit */
++#define FF_TIME_EXPONENT -3
++
+ /* Report usage table used to put reports into an array */
+
+ #define PID_SET_EFFECT 0
+@@ -231,6 +234,24 @@ static int pidff_rescale_signed(int i, struct hid_field *field)
+ field->logical_minimum / -0x8000;
+ }
+
++/*
++ * Scale time value from Linux default (ms) to field units
++ */
++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);
++
++ for (;exponent < FF_TIME_EXPONENT; exponent++)
++ scaled_time *= 10;
++ for (;exponent > FF_TIME_EXPONENT; exponent--)
++ scaled_time /= 10;
++
++ pr_debug("time calculated from %d to %d\n", time, scaled_time);
++ return scaled_time;
++}
++
+ static void pidff_set(struct pidff_usage *usage, u16 value)
+ {
+ usage->value[0] = pidff_rescale(value, 0xffff, usage->field);
+@@ -252,6 +273,27 @@ static void pidff_set_signed(struct pidff_usage *usage, s16 value)
+ pr_debug("calculated from %d to %d\n", value, usage->value[0]);
+ }
+
++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);
++}
++
++static void pidff_set_duration(struct pidff_usage *usage, u16 duration)
++{
++ /* Convert infinite length from Linux API (0)
++ to PID standard (NULL) if needed */
++ if (duration == 0)
++ duration = PID_INFINITE;
++
++ if (duration == PID_INFINITE) {
++ usage->value[0] = PID_INFINITE;
++ return;
++ }
++
++ pidff_set_time(usage, duration);
++}
++
+ /*
+ * Send envelope report to the device
+ */
+@@ -270,8 +312,10 @@ static void pidff_set_envelope_report(struct pidff_device *pidff,
+ 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff,
+ pidff->set_envelope[PID_FADE_LEVEL].field);
+
+- pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length;
+- pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length;
++ pidff_set_time(&pidff->set_envelope[PID_ATTACK_TIME],
++ envelope->attack_length);
++ pidff_set_time(&pidff->set_envelope[PID_FADE_TIME],
++ envelope->fade_length);
+
+ hid_dbg(pidff->hid, "attack %u => %d\n",
+ envelope->attack_level,
+@@ -340,14 +384,12 @@ static void pidff_set_effect_report(struct pidff_device *pidff,
+ pidff->set_effect_type->value[0] =
+ pidff->create_new_effect_type->value[0];
+
+- /* Convert infinite length from Linux API (0)
+- to PID standard (NULL) if needed */
+- pidff->set_effect[PID_DURATION].value[0] =
+- effect->replay.length == 0 ? PID_INFINITE : effect->replay.length;
++ pidff_set_duration(&pidff->set_effect[PID_DURATION],
++ effect->replay.length);
+
+ pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button;
+- pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] =
+- effect->trigger.interval;
++ pidff_set_time(&pidff->set_effect[PID_TRIGGER_REPEAT_INT],
++ effect->trigger.interval);
+ pidff->set_effect[PID_GAIN].value[0] =
+ pidff->set_effect[PID_GAIN].field->logical_maximum;
+ pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
+@@ -360,7 +402,8 @@ static void pidff_set_effect_report(struct pidff_device *pidff,
+
+ /* Omit setting delay field if it's missing */
+ if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_DELAY))
+- pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay;
++ pidff_set_time(&pidff->set_effect[PID_START_DELAY],
++ effect->replay.delay);
+
+ hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT],
+ HID_REQ_SET_REPORT);
+@@ -392,15 +435,11 @@ static void pidff_set_periodic_report(struct pidff_device *pidff,
+ pidff_set_signed(&pidff->set_periodic[PID_OFFSET],
+ effect->u.periodic.offset);
+ pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase);
+-
+- /* Clamp period to ensure the device can play the effect */
+- pidff->set_periodic[PID_PERIOD].value[0] =
+- pidff_clamp(effect->u.periodic.period,
+- pidff->set_periodic[PID_PERIOD].field);
++ pidff_set_time(&pidff->set_periodic[PID_PERIOD],
++ effect->u.periodic.period);
+
+ hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC],
+ HID_REQ_SET_REPORT);
+-
+ }
+
+ /*
+--
+2.39.5
+
--- /dev/null
+From 04f6c98ddccd154c60d887dc27d8faa2820d8996 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:39:04 +0100
+Subject: HID: pidff: Simplify pidff_rescale_signed
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 4eb9c2ee538b62dc5dcae192297c3a4044b7ade5 ]
+
+This function overrelies on ternary operators and makes it hard to parse
+it mentally. New version makes it very easy to understand.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index a8eaa77e80be3..8083eb7684e5e 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -230,9 +230,9 @@ static int pidff_rescale(int i, int max, struct hid_field *field)
+ */
+ static int pidff_rescale_signed(int i, struct hid_field *field)
+ {
+- return i == 0 ? 0 : i >
+- 0 ? i * field->logical_maximum / 0x7fff : i *
+- field->logical_minimum / -0x8000;
++ if (i > 0) return i * field->logical_maximum / 0x7fff;
++ if (i < 0) return i * field->logical_minimum / -0x8000;
++ return 0;
+ }
+
+ /*
+--
+2.39.5
+
--- /dev/null
+From e2159f9fba9c9b937fcfacb7c4a5f5c16d9242ae Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:38:58 +0100
+Subject: HID: pidff: Simplify pidff_upload_effect function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit e4bdc80ef14272ef56c38d8ca2f365fdf59cd0ba ]
+
+Merge a bit of code that reqeusts conditional effects upload.
+Makes it clear, that effect handling should be identical for
+SPRING, DAMPER, INERTIA and FRICTION.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 54 ++++++++++------------------------
+ 1 file changed, 16 insertions(+), 38 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 99b5d3deb40d0..42c951a1d65bf 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -770,48 +770,26 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
+ break;
+
+ case FF_SPRING:
+- if (!old) {
+- error = pidff_request_effect_upload(pidff,
+- pidff->type_id[PID_SPRING]);
+- if (error)
+- return error;
+- }
+- if (!old || pidff_needs_set_effect(effect, old))
+- pidff_set_effect_report(pidff, effect);
+- if (!old || pidff_needs_set_condition(effect, old))
+- pidff_set_condition_report(pidff, effect);
+- break;
+-
+- case FF_FRICTION:
+- if (!old) {
+- error = pidff_request_effect_upload(pidff,
+- pidff->type_id[PID_FRICTION]);
+- if (error)
+- return error;
+- }
+- if (!old || pidff_needs_set_effect(effect, old))
+- pidff_set_effect_report(pidff, effect);
+- if (!old || pidff_needs_set_condition(effect, old))
+- pidff_set_condition_report(pidff, effect);
+- break;
+-
+ case FF_DAMPER:
+- if (!old) {
+- error = pidff_request_effect_upload(pidff,
+- pidff->type_id[PID_DAMPER]);
+- if (error)
+- return error;
+- }
+- if (!old || pidff_needs_set_effect(effect, old))
+- pidff_set_effect_report(pidff, effect);
+- if (!old || pidff_needs_set_condition(effect, old))
+- pidff_set_condition_report(pidff, effect);
+- break;
+-
+ case FF_INERTIA:
++ case FF_FRICTION:
+ if (!old) {
++ switch(effect->type) {
++ case FF_SPRING:
++ type_id = PID_SPRING;
++ break;
++ case FF_DAMPER:
++ type_id = PID_DAMPER;
++ break;
++ case FF_INERTIA:
++ type_id = PID_INERTIA;
++ break;
++ case FF_FRICTION:
++ type_id = PID_FRICTION;
++ break;
++ }
+ error = pidff_request_effect_upload(pidff,
+- pidff->type_id[PID_INERTIA]);
++ pidff->type_id[type_id]);
+ if (error)
+ return error;
+ }
+--
+2.39.5
+
--- /dev/null
+From 0923ad51b20b53cb105f09473875a7431cc6c137 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:38:53 +0100
+Subject: HID: pidff: Stop all effects before enabling actuators
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit ce52c0c939fcb568d1abe454821d5623de38b424 ]
+
+Some PID compliant devices automatically play effects after boot (i.e.
+autocenter spring) that prevent the rendering of other effects since
+it is done outside the kernel driver.
+
+This makes sure all the effects currently played are stopped after
+resetting the device.
+It brings compatibility to the Brunner CLS-P joystick and others
+
+Reported-by: Jules Noirant <jules.noirant@orange.fr>
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 6b55345ce75ac..635596a57c75d 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -109,8 +109,9 @@ static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
+ /* Special field key tables used to put special field keys into arrays */
+
+ #define PID_ENABLE_ACTUATORS 0
+-#define PID_RESET 1
+-static const u8 pidff_device_control[] = { 0x97, 0x9a };
++#define PID_STOP_ALL_EFFECTS 1
++#define PID_RESET 2
++static const u8 pidff_device_control[] = { 0x97, 0x99, 0x9a };
+
+ #define PID_CONSTANT 0
+ #define PID_RAMP 1
+@@ -1235,6 +1236,10 @@ static void pidff_reset(struct pidff_device *pidff)
+ hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
+ hid_hw_wait(hid);
+
++ pidff->device_control->value[0] = pidff->control_id[PID_STOP_ALL_EFFECTS];
++ hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
++ hid_hw_wait(hid);
++
+ pidff->device_control->value[0] =
+ pidff->control_id[PID_ENABLE_ACTUATORS];
+ hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
+--
+2.39.5
+
--- /dev/null
+From ba582ecdfdcc0dcd3faeb1504f9d0e913697a105 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Feb 2025 15:35:11 +0100
+Subject: HID: pidff: Support device error response from PID_BLOCK_LOAD
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 9d4174dc4a234408d91fd83725e1899766cd1731 ]
+
+If an error happens on the device, the driver will no longer fall
+into the trap of reading this status 60 times before it decides that
+this reply won't change to success/memory full.
+
+Greatly reduces communication overhead during device error situation.
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index e2508a4d754d3..d5734cbf745d1 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -138,7 +138,8 @@ static const u8 pidff_effect_types[] = {
+
+ #define PID_BLOCK_LOAD_SUCCESS 0
+ #define PID_BLOCK_LOAD_FULL 1
+-static const u8 pidff_block_load_status[] = { 0x8c, 0x8d };
++#define PID_BLOCK_LOAD_ERROR 2
++static const u8 pidff_block_load_status[] = { 0x8c, 0x8d, 0x8e};
+
+ #define PID_EFFECT_START 0
+ #define PID_EFFECT_STOP 1
+@@ -666,6 +667,11 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
+ pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
+ return -ENOSPC;
+ }
++ if (pidff->block_load_status->value[0] ==
++ pidff->status_id[PID_BLOCK_LOAD_ERROR]) {
++ hid_dbg(pidff->hid, "device error during effect creation\n");
++ return -EREMOTEIO;
++ }
+ }
+ hid_err(pidff->hid, "pid_block_load failed 60 times\n");
+ return -EIO;
+--
+2.39.5
+
--- /dev/null
+From 52f5f2a790f36da96a3b05acd9e02c5f3cbd167c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 1 Feb 2025 12:39:05 +0100
+Subject: HID: pidff: Use macros instead of hardcoded min/max values for shorts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+
+[ Upstream commit 21755162456902998f8d9897086b8c980c540df5 ]
+
+Makes it obvious these magic values ARE in fact derived from min and
+max values for s16 and u16
+
+Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
+Reviewed-by: Michał Kopeć <michal@nozomi.space>
+Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Paul Dino Jones <paul@spacefreak18.xyz>
+Tested-by: Cristóferson Bueno <cbueno81@gmail.com>
+Tested-by: Pablo Cisneros <patchkez@protonmail.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/hid/usbhid/hid-pidff.c | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
+index 8083eb7684e5e..b21e844f5f3a3 100644
+--- a/drivers/hid/usbhid/hid-pidff.c
++++ b/drivers/hid/usbhid/hid-pidff.c
+@@ -21,7 +21,7 @@
+
+
+ #define PID_EFFECTS_MAX 64
+-#define PID_INFINITE 0xffff
++#define PID_INFINITE U16_MAX
+
+ /* Linux Force Feedback API uses miliseconds as time unit */
+ #define FF_TIME_EXPONENT -3
+@@ -226,12 +226,12 @@ static int pidff_rescale(int i, int max, struct hid_field *field)
+ }
+
+ /*
+- * Scale a signed value in range -0x8000..0x7fff for the given field
++ * Scale a signed value in range S16_MIN..S16_MAX for the given field
+ */
+ static int pidff_rescale_signed(int i, struct hid_field *field)
+ {
+- if (i > 0) return i * field->logical_maximum / 0x7fff;
+- if (i < 0) return i * field->logical_minimum / -0x8000;
++ if (i > 0) return i * field->logical_maximum / S16_MAX;
++ if (i < 0) return i * field->logical_minimum / S16_MIN;
+ return 0;
+ }
+
+@@ -255,7 +255,7 @@ static u32 pidff_rescale_time(u16 time, struct hid_field *field)
+
+ static void pidff_set(struct pidff_usage *usage, u16 value)
+ {
+- usage->value[0] = pidff_rescale(value, 0xffff, usage->field);
++ usage->value[0] = pidff_rescale(value, U16_MAX, usage->field);
+ pr_debug("calculated from %d to %d\n", value, usage->value[0]);
+ }
+
+@@ -266,10 +266,10 @@ static void pidff_set_signed(struct pidff_usage *usage, s16 value)
+ else {
+ if (value < 0)
+ usage->value[0] =
+- pidff_rescale(-value, 0x8000, usage->field);
++ pidff_rescale(-value, -S16_MIN, usage->field);
+ else
+ usage->value[0] =
+- pidff_rescale(value, 0x7fff, usage->field);
++ pidff_rescale(value, S16_MAX, usage->field);
+ }
+ pr_debug("calculated from %d to %d\n", value, usage->value[0]);
+ }
+@@ -306,11 +306,11 @@ static void pidff_set_envelope_report(struct pidff_device *pidff,
+
+ pidff->set_envelope[PID_ATTACK_LEVEL].value[0] =
+ pidff_rescale(envelope->attack_level >
+- 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff,
++ S16_MAX ? S16_MAX : envelope->attack_level, S16_MAX,
+ pidff->set_envelope[PID_ATTACK_LEVEL].field);
+ pidff->set_envelope[PID_FADE_LEVEL].value[0] =
+ pidff_rescale(envelope->fade_level >
+- 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff,
++ S16_MAX ? S16_MAX : envelope->fade_level, S16_MAX,
+ pidff->set_envelope[PID_FADE_LEVEL].field);
+
+ pidff_set_time(&pidff->set_envelope[PID_ATTACK_TIME],
+@@ -399,7 +399,7 @@ static void pidff_set_effect_report(struct pidff_device *pidff,
+ pidff->effect_direction->value[0] = pidff_rescale(
+ pidff->quirks & HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION ?
+ PIDFF_FIXED_WHEEL_DIRECTION : effect->direction,
+- 0xffff, pidff->effect_direction);
++ U16_MAX, pidff->effect_direction);
+
+ /* Omit setting delay field if it's missing */
+ if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_DELAY))
+@@ -1366,7 +1366,7 @@ static int pidff_check_autocenter(struct pidff_device *pidff,
+
+ if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] ==
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) {
+- pidff_autocenter(pidff, 0xffff);
++ pidff_autocenter(pidff, U16_MAX);
+ set_bit(FF_AUTOCENTER, dev->ffbit);
+ } else {
+ hid_notice(pidff->hid,
+@@ -1424,7 +1424,7 @@ int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks)
+ if (error)
+ goto fail;
+
+- pidff_set_gain_report(pidff, 0xffff);
++ pidff_set_gain_report(pidff, U16_MAX);
+ error = pidff_check_autocenter(pidff, dev);
+ if (error)
+ goto fail;
+--
+2.39.5
+
bpf-support-skf_net_off-and-skf_ll_off-on-skb-frags.patch
ext4-don-t-treat-fhandle-lookup-of-ea_inode-as-fs-co.patch
s390-pci-fix-s390_mmio_read-write-syscall-page-fault.patch
+hid-pidff-clamp-periodic-effect-period-to-device-s-l.patch
+hid-pidff-stop-all-effects-before-enabling-actuators.patch
+hid-pidff-completely-rework-and-fix-pidff_reset-func.patch
+hid-pidff-simplify-pidff_upload_effect-function.patch
+hid-pidff-define-values-used-in-pidff_find_special_f.patch
+hid-pidff-rescale-time-values-to-match-field-units.patch
+hid-pidff-factor-out-code-for-setting-gain.patch
+hid-pidff-move-all-hid-pidff-definitions-to-a-dedica.patch
+hid-pidff-simplify-pidff_rescale_signed.patch
+hid-pidff-use-macros-instead-of-hardcoded-min-max-va.patch
+hid-pidff-factor-out-pool-report-fetch-and-remove-ex.patch
+hid-pidff-make-sure-to-fetch-pool-before-checking-si.patch
+hid-hid-universal-pidff-add-asetek-wheelbases-suppor.patch
+hid-pidff-comment-and-code-style-update.patch
+hid-pidff-support-device-error-response-from-pid_blo.patch
+hid-pidff-remove-redundant-call-to-pidff_find_specia.patch
+hid-pidff-rename-two-functions-to-align-them-with-na.patch
+hid-pidff-clamp-effect-playback-loop_count-value.patch
+hid-pidff-compute-infinite-value-instead-of-using-ha.patch
+hid-pidff-fix-90-degrees-direction-name-north-east.patch
+hid-pidff-fix-set_device_control.patch