]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
HID: hid-oxp: Add Vibration Intensity Attribute
authorDerek J. Clark <derekjohn.clark@gmail.com>
Sun, 19 Apr 2026 04:26:24 +0000 (21:26 -0700)
committerJiri Kosina <jkosina@suse.com>
Tue, 12 May 2026 15:55:57 +0000 (17:55 +0200)
Adds attribute for setting the rumble intensity level. This setting must
be re-applied after the gamepad mode is set as doing so resets this to
the default value.

Reviewed-by: Zhouwang Huang <honjow311@gmail.com>
Tested-by: Zhouwang Huang <honjow311@gmail.com>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
drivers/hid/hid-oxp.c

index 52002d4cbd0b2adb7ccd2bc5ca12ba1ca10397b0..20a54f337220dc2aee3483a14d542b66c487bd60 100644 (file)
@@ -34,6 +34,7 @@ enum oxp_function_index {
        OXP_FID_GEN1_RGB_SET =          0x07,
        OXP_FID_GEN1_RGB_REPLY =        0x0f,
        OXP_FID_GEN2_TOGGLE_MODE =      0xb2,
+       OXP_FID_GEN2_RUMBLE_SET =       0xb3,
        OXP_FID_GEN2_KEY_STATE =        0xb4,
        OXP_FID_GEN2_STATUS_EVENT =     0xb8,
 };
@@ -181,6 +182,7 @@ static struct oxp_hid_cfg {
        struct mutex cfg_mutex; /*ensure single synchronous output report*/
        u8 rgb_brightness;
        u8 gamepad_mode;
+       u8 rumble_intensity;
        u8 rgb_effect;
        u8 rgb_speed;
        u8 rgb_en;
@@ -266,6 +268,11 @@ static const char *const oxp_rgb_effect_text[] = {
        [OXP_EFFECT_MONO_LIST] = "monocolor",
 };
 
+enum oxp_rumble_side_index {
+       OXP_RUMBLE_LEFT = 0x00,
+       OXP_RUMBLE_RIGHT,
+};
+
 struct oxp_gen_1_rgb_report {
        u8 report_id;
        u8 message_id;
@@ -341,6 +348,7 @@ static int oxp_hid_raw_event_gen_1(struct hid_device *hdev,
 
 static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, u8 data_size);
 static int oxp_set_buttons(void);
+static int oxp_rumble_intensity_set(u8 intensity);
 
 static void oxp_mcu_init_fn(struct work_struct *work)
 {
@@ -368,6 +376,12 @@ static void oxp_mcu_init_fn(struct work_struct *work)
        if (ret)
                dev_err(&drvdata.hdev->dev,
                        "Error: Failed to set gamepad mode: %i\n", ret);
+
+       /* Set vibration level */
+       ret = oxp_rumble_intensity_set(drvdata.rumble_intensity);
+       if (ret)
+               dev_err(&drvdata.hdev->dev,
+                       "Error: Failed to set rumble intensity: %i\n", ret);
 }
 
 static int oxp_hid_raw_event_gen_2(struct hid_device *hdev,
@@ -514,6 +528,14 @@ static ssize_t gamepad_mode_store(struct device *dev,
 
        drvdata.gamepad_mode = data[0];
 
+       if (drvdata.gamepad_mode == OXP_GP_MODE_DEBUG)
+               return count;
+
+       /* Re-apply rumble settings as switching gamepad mode will override */
+       ret = oxp_rumble_intensity_set(drvdata.rumble_intensity);
+       if (ret)
+               return ret;
+
        return count;
 }
 
@@ -857,6 +879,59 @@ static ssize_t button_mapping_options_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(button_mapping_options);
 
+static int oxp_rumble_intensity_set(u8 intensity)
+{
+       u8 header[15] = { 0x02, 0x38, 0x02, 0xe3, 0x39, 0xe3, 0x39, 0xe3,
+                         0x39, 0x01, intensity, 0x05, 0xe3, 0x39, 0xe3 };
+       u8 footer[9] = { 0x39, 0xe3, 0x39, 0xe3, 0xe3, 0x02, 0x04, 0x39, 0x39 };
+       size_t footer_size = ARRAY_SIZE(footer);
+       size_t header_size = ARRAY_SIZE(header);
+       u8 data[59] = { 0x0 };
+       size_t data_size = ARRAY_SIZE(data);
+
+       memcpy(data, header, header_size);
+       memcpy(data + data_size - footer_size, footer, footer_size);
+
+       return oxp_gen_2_property_out(OXP_FID_GEN2_RUMBLE_SET, data, data_size);
+}
+
+static ssize_t rumble_intensity_store(struct device *dev,
+                                     struct device_attribute *attr, const char *buf,
+                                     size_t count)
+{
+       int ret;
+       u8 val;
+
+       ret = kstrtou8(buf, 10, &val);
+       if (ret)
+               return ret;
+
+       if (val < 0 || val > 5)
+               return -EINVAL;
+
+       ret = oxp_rumble_intensity_set(val);
+       if (ret)
+               return ret;
+
+       drvdata.rumble_intensity = val;
+
+       return count;
+}
+
+static ssize_t rumble_intensity_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       return sysfs_emit(buf, "%i\n", drvdata.rumble_intensity);
+}
+static DEVICE_ATTR_RW(rumble_intensity);
+
+static ssize_t rumble_intensity_range_show(struct device *dev,
+                                          struct device_attribute *attr, char *buf)
+{
+       return sysfs_emit(buf, "0-5\n");
+}
+static DEVICE_ATTR_RO(rumble_intensity_range);
+
 #define OXP_DEVICE_ATTR_RW(_name, _group)                                     \
        static ssize_t _name##_store(struct device *dev,                      \
                                     struct device_attribute *attr,           \
@@ -948,6 +1023,8 @@ static struct attribute *oxp_cfg_attrs[] = {
        &dev_attr_gamepad_mode.attr,
        &dev_attr_gamepad_mode_index.attr,
        &dev_attr_reset_buttons.attr,
+       &dev_attr_rumble_intensity.attr,
+       &dev_attr_rumble_intensity_range.attr,
        NULL,
 };
 
@@ -1422,6 +1499,7 @@ skip_rgb:
        INIT_DELAYED_WORK(&drvdata.oxp_btn_queue, oxp_btn_queue_fn);
 
        drvdata.gamepad_mode = OXP_GP_MODE_XINPUT;
+       drvdata.rumble_intensity = 5;
 
        INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn);
        mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50));