]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
HID: pidff: Rescale time values to match field units
authorTomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
Sat, 1 Feb 2025 11:39:00 +0000 (12:39 +0100)
committerJiri Kosina <jkosina@suse.com>
Mon, 3 Feb 2025 14:17:03 +0000 (15:17 +0100)
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>
drivers/hid/usbhid/hid-pidff.c

index fbb79179e02be69e14cd4b9f9a2bb3613467a04d..8a2a4bed2632d50bb3a2b7ee83d7f1c04336558e 100644 (file)
@@ -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);
-
 }
 
 /*