]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
HID: hid-lenovo-go: Add OS Mode Toggle
authorDerek J. Clark <derekjohn.clark@gmail.com>
Tue, 10 Mar 2026 07:29:26 +0000 (07:29 +0000)
committerJiri Kosina <jkosina@suse.com>
Tue, 10 Mar 2026 16:53:17 +0000 (17:53 +0100)
Adds OS Mode toggle, who's primary function is to change the built-in
functional chords to use the right handle legion button instead of the
left handle legion button as the mode shift key.

Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
drivers/hid/hid-lenovo-go.c

index 082d1b85d67921f8729737307484497e3972deff..54861f2e04fc40b3dc806820232b58c8c706285f 100644 (file)
@@ -76,6 +76,7 @@ static struct hid_go_cfg {
        u32 mcu_version_product;
        u32 mcu_version_protocol;
        u32 mouse_dpi;
+       u8 os_mode;
        u8 rgb_effect;
        u8 rgb_en;
        u8 rgb_mode;
@@ -166,6 +167,8 @@ enum feature_status_index {
        FEATURE_GAMEPAD_MODE = 0x0e,
 };
 
+#define FEATURE_OS_MODE 0x69
+
 enum fps_switch_status_index {
        FPS_STATUS_UNKNOWN,
        GAMEPAD,
@@ -311,6 +314,23 @@ enum device_status_index {
        GET_HOTKEY_TRIGG_STATUS,
 };
 
+enum os_mode_cfg_index {
+       SET_OS_MODE = 0x09,
+       GET_OS_MODE,
+};
+
+enum os_mode_type_index {
+       OS_UNKNOWN,
+       WINDOWS,
+       LINUX,
+};
+
+static const char *const os_mode_text[] = {
+       [OS_UNKNOWN] = "unknown",
+       [WINDOWS] = "windows",
+       [LINUX] = "linux",
+};
+
 static int hid_go_version_event(struct command_report *cmd_rep)
 {
        switch (cmd_rep->sub_cmd) {
@@ -593,6 +613,21 @@ static int hid_go_device_status_event(struct command_report *cmd_rep)
        }
 }
 
+static int hid_go_os_mode_cfg_event(struct command_report *cmd_rep)
+{
+       switch (cmd_rep->sub_cmd) {
+       case SET_OS_MODE:
+               if (cmd_rep->data[0] != 1)
+                       return -EIO;
+               return 0;
+       case GET_OS_MODE:
+               drvdata.os_mode = cmd_rep->data[0];
+               return 0;
+       default:
+               return -EINVAL;
+       };
+}
+
 static int hid_go_set_event_return(struct command_report *cmd_rep)
 {
        if (cmd_rep->data[0] != 0)
@@ -666,6 +701,9 @@ static int hid_go_raw_event(struct hid_device *hdev, struct hid_report *report,
                        break;
                };
                break;
+       case OS_MODE_DATA:
+               ret = hid_go_os_mode_cfg_event(cmd_rep);
+               break;
        default:
                goto passthrough;
        };
@@ -1343,6 +1381,64 @@ static ssize_t calibrate_config_options(struct device *dev,
        return count;
 }
 
+static ssize_t os_mode_store(struct device *dev, struct device_attribute *attr,
+                            const char *buf, size_t count)
+{
+       size_t size = 1;
+       int ret;
+       u8 val;
+
+       ret = sysfs_match_string(os_mode_text, buf);
+       if (ret <= 0)
+               return ret;
+
+       val = ret;
+       ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE,
+                              SET_OS_MODE, USB_MCU, &val, size);
+       if (ret < 0)
+               return ret;
+
+       drvdata.os_mode = val;
+
+       return count;
+}
+
+static ssize_t os_mode_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       ssize_t count = 0;
+       int ret;
+       u8 i;
+
+       ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE,
+                              GET_OS_MODE, USB_MCU, NULL, 0);
+       if (ret)
+               return ret;
+
+       i = drvdata.os_mode;
+       if (i >= ARRAY_SIZE(os_mode_text))
+               return -EINVAL;
+
+       count = sysfs_emit(buf, "%s\n", os_mode_text[i]);
+
+       return count;
+}
+
+static ssize_t os_mode_index_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       ssize_t count = 0;
+       unsigned int i;
+
+       for (i = 1; i < ARRAY_SIZE(os_mode_text); i++)
+               count += sysfs_emit_at(buf, count, "%s ", os_mode_text[i]);
+
+       if (count)
+               buf[count - 1] = '\n';
+
+       return count;
+}
+
 static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd,
                        enum rgb_config_index index, u8 *val, size_t size)
 {
@@ -1712,6 +1808,9 @@ static DEVICE_ATTR_RO_NAMED(gamepad_rumble_intensity_index,
 static DEVICE_ATTR_RW(fps_mode_dpi);
 static DEVICE_ATTR_RO(fps_mode_dpi_index);
 
+static DEVICE_ATTR_RW(os_mode);
+static DEVICE_ATTR_RO(os_mode_index);
+
 static struct attribute *mcu_attrs[] = {
        &dev_attr_fps_mode_dpi.attr,
        &dev_attr_fps_mode_dpi_index.attr,
@@ -1720,6 +1819,8 @@ static struct attribute *mcu_attrs[] = {
        &dev_attr_gamepad_mode_index.attr,
        &dev_attr_gamepad_rumble_intensity.attr,
        &dev_attr_gamepad_rumble_intensity_index.attr,
+       &dev_attr_os_mode.attr,
+       &dev_attr_os_mode_index.attr,
        &dev_attr_reset_mcu.attr,
        &dev_attr_version_firmware_mcu.attr,
        &dev_attr_version_gen_mcu.attr,