HID: hid-oxp: Add Second Generation Gamepad Mode Switch

Adds "gamepad_mode" attribute to second generation OneXPlayer
configuration HID devices. This attribute initiates a mode shift in the
device MCU that puts it into a state where all events are routed to an
hidraw interface instead of the xpad evdev interface. This allows for
debugging the hardware input mapping added in the next patch.

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>
This commit is contained in:
Derek J. Clark 2026-04-18 21:26:22 -07:00 committed by Jiri Kosina
parent 252c4bf1d9
commit 2f424f28fb

View File

@ -33,20 +33,33 @@
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_STATUS_EVENT = 0xb8,
};
static struct oxp_hid_cfg {
struct delayed_work oxp_rgb_queue;
struct delayed_work oxp_mcu_init;
struct led_classdev_mc *led_mc;
struct hid_device *hdev;
struct mutex cfg_mutex; /*ensure single synchronous output report*/
u8 rgb_brightness;
u8 gamepad_mode;
u8 rgb_effect;
u8 rgb_speed;
u8 rgb_en;
} drvdata;
enum oxp_gamepad_mode_index {
OXP_GP_MODE_XINPUT = 0x00,
OXP_GP_MODE_DEBUG = 0x03,
};
static const char *const oxp_gamepad_mode_text[] = {
[OXP_GP_MODE_XINPUT] = "xinput",
[OXP_GP_MODE_DEBUG] = "debug",
};
enum oxp_feature_en_index {
OXP_FEAT_DISABLED,
OXP_FEAT_ENABLED,
@ -182,6 +195,30 @@ static int oxp_hid_raw_event_gen_1(struct hid_device *hdev,
return 0;
}
static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, u8 data_size);
static void oxp_mcu_init_fn(struct work_struct *work)
{
u8 gp_mode_data[3] = { OXP_GP_MODE_DEBUG, 0x01, 0x02 };
int ret;
/* Cycle the gamepad mode */
ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3);
if (ret)
dev_err(&drvdata.hdev->dev,
"Error: Failed to set gamepad mode: %i\n", ret);
/* Remainder only applies for xinput mode */
if (drvdata.gamepad_mode == OXP_GP_MODE_DEBUG)
return;
gp_mode_data[0] = OXP_GP_MODE_XINPUT;
ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3);
if (ret)
dev_err(&drvdata.hdev->dev,
"Error: Failed to set gamepad mode: %i\n", ret);
}
static int oxp_hid_raw_event_gen_2(struct hid_device *hdev,
struct hid_report *report, u8 *data,
int size)
@ -192,6 +229,14 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *hdev,
if (data[0] != OXP_FID_GEN2_STATUS_EVENT)
return 0;
/* Sent ~6s after resume event, indicating the MCU has fully reset.
* Re-apply our settings after this has been received.
*/
if (data[3] == OXP_EFFECT_MONO_TRUE) {
mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50));
return 0;
}
if (data[3] != OXP_GET_PROPERTY)
return 0;
@ -289,6 +334,77 @@ static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data,
footer_size);
}
static ssize_t gamepad_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
u16 up = get_usage_page(drvdata.hdev);
u8 data[3] = { 0x00, 0x01, 0x02 };
int ret = -EINVAL;
int i;
if (up != GEN2_USAGE_PAGE)
return ret;
for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) {
if (oxp_gamepad_mode_text[i] && sysfs_streq(buf, oxp_gamepad_mode_text[i])) {
ret = i;
break;
}
}
if (ret < 0)
return ret;
data[0] = ret;
ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, data, 3);
if (ret)
return ret;
drvdata.gamepad_mode = data[0];
return count;
}
static ssize_t gamepad_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%s\n", oxp_gamepad_mode_text[drvdata.gamepad_mode]);
}
static DEVICE_ATTR_RW(gamepad_mode);
static ssize_t gamepad_mode_index_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t count = 0;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) {
if (!oxp_gamepad_mode_text[i] ||
oxp_gamepad_mode_text[i][0] == '\0')
continue;
count += sysfs_emit_at(buf, count, "%s ", oxp_gamepad_mode_text[i]);
}
if (count)
buf[count - 1] = '\n';
return count;
}
static DEVICE_ATTR_RO(gamepad_mode_index);
static struct attribute *oxp_cfg_attrs[] = {
&dev_attr_gamepad_mode.attr,
&dev_attr_gamepad_mode_index.attr,
NULL,
};
static const struct attribute_group oxp_cfg_attrs_group = {
.attrs = oxp_cfg_attrs,
};
static int oxp_rgb_status_store(u8 enabled, u8 speed, u8 brightness)
{
u16 up = get_usage_page(drvdata.hdev);
@ -733,7 +849,21 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16 up)
dev_warn(drvdata.led_mc->led_cdev.dev,
"Failed to query RGB initial state: %i\n", ret);
/* Below features are only implemented in gen 2 */
if (up != GEN2_USAGE_PAGE)
return 0;
skip_rgb:
drvdata.gamepad_mode = OXP_GP_MODE_XINPUT;
INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn);
mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50));
ret = devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group);
if (ret)
return dev_err_probe(&hdev->dev, ret,
"Failed to attach configuration attributes\n");
return 0;
}
@ -778,6 +908,7 @@ static int oxp_hid_probe(struct hid_device *hdev,
static void oxp_hid_remove(struct hid_device *hdev)
{
cancel_delayed_work(&drvdata.oxp_rgb_queue);
cancel_delayed_work(&drvdata.oxp_mcu_init);
hid_hw_close(hdev);
hid_hw_stop(hdev);
}