1
0
forked from drath/libratbag

hidpp20: support multiple quirks per device

The hidpp20 quirk was a single enum value, so a device could carry only
one quirk. Some devices need several at once. For example the G502 X
PLUS needs G502X_PLUS (LED in the 2nd slot) and INDEX_OFFSET (1-indexed
active profile) together, which a single field cannot express. #1799
noted this was worth adding.

Make the quirk enum combinable bit flags stored as a bitmask. The
"Quirk" key now takes a ;-separated list (like the asus driver's
"Quirks"); existing single-value device files parse the same as before.
This commit is contained in:
perfectra1n
2026-07-10 14:29:12 -07:00
committed by staticssleever668
parent 43f76eee67
commit b0f2779270
5 changed files with 48 additions and 38 deletions

View File

@@ -411,7 +411,7 @@ hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
hidpp20_rgb_effects_get_device_info(drv_data->dev, &device_info);
cluster_info = drv_data->led_infos.color_leds_8071[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
if (drv_data->dev->quirk == HIDPP20_QUIRK_G502X_PLUS) {
if (drv_data->dev->quirks & HIDPP20_QUIRK_G502X_PLUS) {
// On G502X+ the second slot controls the user configurable LED.
// (Note: There is only 1 reported LED, it just happens to use 2nd index though)
h_led = &profile->leds[1];
@@ -697,7 +697,7 @@ hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* p
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
h_profile = &drv_data->profiles->profiles[profile->index];
if (drv_data->dev->quirk == HIDPP20_QUIRK_G502X_PLUS) {
if (drv_data->dev->quirks & HIDPP20_QUIRK_G502X_PLUS) {
// On G502X+ the second slot controls the user configurable LED.
// (Note: There is only 1 reported LED, it just happens to use 2nd index though)
h_led = &(h_profile->leds[1]);
@@ -1434,7 +1434,7 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
/* The 8070 feature implemented in the G602 doesn't follow the spec,
* so we ignore it */
if (ratbag_device_data_hidpp20_get_quirk(device->data) == HIDPP20_QUIRK_G602)
if (ratbag_device_data_hidpp20_get_quirks(device->data) & HIDPP20_QUIRK_G602)
break;
log_debug(ratbag, "device has color effects\n");
@@ -1708,14 +1708,15 @@ hidpp20drv_probe(struct ratbag_device *device)
goto err;
}
dev->quirk = ratbag_device_data_hidpp20_get_quirk(device->data);
dev->quirks = ratbag_device_data_hidpp20_get_quirks(device->data);
drv_data->dev = dev;
log_debug(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), dev->proto_major, dev->proto_minor);
if(dev->quirk != HIDPP20_QUIRK_NONE)
log_debug(device->ratbag, "'%s' is quirked (%s)\n", ratbag_device_get_name(device), hidpp20_get_quirk_string(dev->quirk));
/* quirks is a bitmask; log each flag that is set */
for (uint32_t q = dev->quirks; q != 0; q &= q - 1)
log_debug(device->ratbag, "'%s' is quirked (%s)\n", ratbag_device_get_name(device), hidpp20_get_quirk_string(q & ~(q - 1)));
/* add some defaults that will be overwritten by the device */
drv_data->num_profiles = 1;

View File

@@ -1532,7 +1532,7 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602) {
if (device->quirks & HIDPP20_QUIRK_G602) {
msg.msg.parameters[0] = 1;
i = 0;
}
@@ -1548,7 +1548,7 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = get_unaligned_be_u16(&msg.msg.parameters[i]);
if (device->quirk == HIDPP20_QUIRK_G602 && i == 2)
if ((device->quirks & HIDPP20_QUIRK_G602) && i == 2)
value += 0xe000;
if (value > 0xe000) {
@@ -1580,7 +1580,7 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
if (device->quirks & HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
rc = hidpp20_request_command(device, &msg);
@@ -1662,7 +1662,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
if (device->quirks & HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
@@ -2279,7 +2279,7 @@ hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
active_profile_index = rc;
/* Apply quirk for devices that return 1-indexed profile */
if (device->quirk == HIDPP20_QUIRK_INDEX_OFFSET && active_profile_index > 0)
if ((device->quirks & HIDPP20_QUIRK_INDEX_OFFSET) && active_profile_index > 0)
active_profile_index--;
profiles = zalloc(sizeof(struct hidpp20_profiles));
@@ -2802,7 +2802,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
profiles->sector_size,
data);
if (rc && device->quirk == HIDPP20_QUIRK_G305) {
if (rc && (device->quirks & HIDPP20_QUIRK_G305)) {
/* The G305 has a bug where it throws an ERR_INVALID_ARGUMENT
if the sector has not been written to yet. If this happens
we will read the ROM profiles.*/
@@ -2971,7 +2971,7 @@ hidpp20_onboard_profiles_write_profile(struct hidpp20_device *device,
// Mice like G502X can store custom animations onboard.
// ratbag does not support this, so we set it to disable it to ensure
// that the actual lighting matches the user's settings.
if (device->quirk == HIDPP20_QUIRK_G502X_PLUS)
if (device->quirks & HIDPP20_QUIRK_G502X_PLUS)
{
// Note(sewer56): There are two known mice which use this field;
// G502X PLUS and G705. [And I only own one of these.] But it's not known how

View File

@@ -51,12 +51,13 @@ struct hidpp20_feature {
uint8_t type;
};
/* Combinable flags so a device can have several at once (stored as a bitmask). */
enum hidpp20_quirk {
HIDPP20_QUIRK_NONE,
HIDPP20_QUIRK_G305,
HIDPP20_QUIRK_G602,
HIDPP20_QUIRK_G502X_PLUS, // G502X+ uses 2nd LED slot instead of 1st.
HIDPP20_QUIRK_INDEX_OFFSET, // Device returns 1-indexed profile, decrement by 1.
HIDPP20_QUIRK_NONE = 0,
HIDPP20_QUIRK_G305 = (1 << 0),
HIDPP20_QUIRK_G602 = (1 << 1),
HIDPP20_QUIRK_G502X_PLUS = (1 << 2), // G502X+ uses 2nd LED slot instead of 1st.
HIDPP20_QUIRK_INDEX_OFFSET = (1 << 3), // Device returns 1-indexed profile, decrement by 1.
};
struct hidpp20_device {
@@ -66,7 +67,7 @@ struct hidpp20_device {
unsigned proto_minor;
unsigned feature_count;
struct hidpp20_feature *feature_list;
enum hidpp20_quirk quirk;
uint32_t quirks; /* bitmask of enum hidpp20_quirk */
unsigned int led_ext_caps;
};

View File

@@ -67,7 +67,7 @@ enum driver {
struct data_hidpp20 {
int index;
enum hidpp20_quirk quirk;
uint32_t quirks; /* bitmask of enum hidpp20_quirk */
int led_count;
int report_rate;
int button_count;
@@ -176,6 +176,20 @@ init_data_hidpp10(struct ratbag *ratbag,
}
}
static uint32_t
hidpp20_quirk_from_string(const char *str)
{
if (streq(str, "G305"))
return HIDPP20_QUIRK_G305;
if (streq(str, "G602"))
return HIDPP20_QUIRK_G602;
if (streq(str, "G502X_PLUS"))
return HIDPP20_QUIRK_G502X_PLUS;
if (streq(str, "INDEX_OFFSET"))
return HIDPP20_QUIRK_INDEX_OFFSET;
return HIDPP20_QUIRK_NONE;
}
static void
init_data_hidpp20(struct ratbag *ratbag,
GKeyFile *keyfile,
@@ -184,7 +198,6 @@ init_data_hidpp20(struct ratbag *ratbag,
const char *group = "Driver/hidpp20";
GError *error = NULL;
int num;
char *str;
data->hidpp20.button_count = -1;
data->hidpp20.index = -1;
@@ -218,18 +231,13 @@ init_data_hidpp20(struct ratbag *ratbag,
if (error)
g_error_free(error);
str = g_key_file_get_string(keyfile, group, "Quirk", NULL);
data->hidpp20.quirk = HIDPP20_QUIRK_NONE;
if (str) {
if (streq(str, "G305"))
data->hidpp20.quirk = HIDPP20_QUIRK_G305;
else if(streq(str, "G602"))
data->hidpp20.quirk = HIDPP20_QUIRK_G602;
else if(streq(str, "G502X_PLUS"))
data->hidpp20.quirk = HIDPP20_QUIRK_G502X_PLUS;
else if(streq(str, "INDEX_OFFSET"))
data->hidpp20.quirk = HIDPP20_QUIRK_INDEX_OFFSET;
}
/* Quirks are a bitmask: "Quirk" takes a ;-separated list. */
data->hidpp20.quirks = HIDPP20_QUIRK_NONE;
gsize quirks_count = 0;
_cleanup_(g_strfreevp) char **quirks = g_key_file_get_string_list(keyfile, group, "Quirk", &quirks_count, NULL);
for (gsize i = 0; quirks && i < quirks_count; i++)
data->hidpp20.quirks |= hidpp20_quirk_from_string(quirks[i]);
}
static void
@@ -830,12 +838,12 @@ ratbag_device_data_hidpp20_get_report_rate(const struct ratbag_device_data *data
return data->hidpp20.report_rate;
}
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
uint32_t
ratbag_device_data_hidpp20_get_quirks(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.quirk;
return data->hidpp20.quirks;
}
/* SinoWealth */

View File

@@ -90,8 +90,8 @@ ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_report_rate(const struct ratbag_device_data *data);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
uint32_t
ratbag_device_data_hidpp20_get_quirks(const struct ratbag_device_data *data);
/* SinoWealth */