/* * Copyright © 2017 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include #include #include #include #include "asus.h" #include "driver-sinowealth.h" #include "driver-steelseries.h" #include "libratbag.h" #include "libratbag-private.h" #include "libratbag-data.h" #include "hidpp20.h" #include "shared-macro.h" #include "usb-ids.h" #define GROUP_DEVICE "Device" DEFINE_TRIVIAL_CLEANUP_FUNC(GKeyFile *, g_key_file_free); DEFINE_TRIVIAL_CLEANUP_FUNC(GError *, g_error_free); DEFINE_TRIVIAL_CLEANUP_FUNC(char **, g_strfreev); enum driver { NONE = 0, HIDPP10, HIDPP20, ROCCAT, ROCCAT_KONE_PURE, ROCCAT_EMP, ETEKCITY, GSKILL, LOGITECH_G300, LOGITECH_G600, STEELSERIES, ASUS, SINOWEALTH, SINOWEALTH_NUBWO, OPENINPUT, MARSGAMING, }; struct data_hidpp20 { int index; uint32_t quirks; /* bitmask of enum hidpp20_quirk */ int led_count; int report_rate; int button_count; }; struct data_hidpp10 { int index; int profile_count; char *profile_type; struct dpi_list *dpi_list; struct dpi_range *dpi_range; int led_count; }; struct data_sinowealth { struct list supported_devices; }; struct data_steelseries { int device_version; int button_count; int led_count; struct dpi_list *dpi_list; struct dpi_range *dpi_range; int macro_length; enum steelseries_quirk quirk; }; struct data_asus { int profile_count; int button_count; int button_mapping[ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP]; int led_count; int dpi_count; int is_wireless; struct dpi_range *dpi_range; uint32_t quirks; int led_modes[ASUS_MAX_NUM_LED_MODES]; char *omni_signature; /* serial number prefix of mice behind an Omni receiver */ }; struct ratbag_device_data { int refcount; char *name; char *driver; enum driver drivertype; enum ratbag_device_type devicetype; union { struct data_hidpp20 hidpp20; struct data_hidpp10 hidpp10; struct data_sinowealth sinowealth; struct data_steelseries steelseries; struct data_asus asus; }; }; static void init_data_hidpp10(struct ratbag *ratbag, GKeyFile *keyfile, struct ratbag_device_data *data) { const char *group = "Driver/hidpp10"; char *profile_type; GError *error = NULL; _cleanup_(freep) char *str = NULL; int num; data->hidpp10.index = -1; data->hidpp10.profile_count = -1; data->hidpp10.profile_type = NULL; data->hidpp10.led_count = -1; num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error); if (num != 0 || !error) data->hidpp10.index = num; if (error) g_error_free(error); error = NULL; num = g_key_file_get_integer(keyfile, group, "Profiles", &error); if (num > 0 || !error) data->hidpp10.profile_count = num; if (error) g_error_free(error); error = NULL; num = g_key_file_get_integer(keyfile, group, "Leds", &error); if (num > 0 || !error) data->hidpp10.led_count = num; if (error) g_error_free(error); profile_type = g_key_file_get_string(keyfile, group, "ProfileType", NULL); if (profile_type) data->hidpp10.profile_type = profile_type; str = g_key_file_get_string(keyfile, group, "DpiRange", NULL); if (str) { data->hidpp10.dpi_range = dpi_range_from_string(str); } else { str = g_key_file_get_string(keyfile, group, "DpiList", NULL); if (str) data->hidpp10.dpi_list = dpi_list_from_string(str); } } 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, struct ratbag_device_data *data) { const char *group = "Driver/hidpp20"; GError *error = NULL; int num; data->hidpp20.button_count = -1; data->hidpp20.index = -1; data->hidpp20.led_count = -1; data->hidpp20.report_rate = -1; num = g_key_file_get_integer(keyfile, group, "Buttons", &error); if (num > 0 || !error) data->hidpp20.button_count = num; if (error) g_error_free(error); error = NULL; num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error); if (num != 0 || !error) data->hidpp20.index = num; if (error) g_error_free(error); error = NULL; num = g_key_file_get_integer(keyfile, group, "Leds", &error); if (!error) data->hidpp20.led_count = num; if (error) g_error_free(error); error = NULL; num = g_key_file_get_integer(keyfile, group, "ReportRate", &error); if (num > 0 || !error) data->hidpp20.report_rate = num; if (error) g_error_free(error); /* 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 init_data_sinowealth(struct ratbag *ratbag, GKeyFile *keyfile, struct ratbag_device_data *data) { const char *devices_group = "Driver/sinowealth/devices/"; GError *error = NULL; int rc; size_t group_count = 0; _cleanup_(g_strfreevp) char **groups = g_key_file_get_groups(keyfile, &group_count); list_init(&data->sinowealth.supported_devices); for (size_t i = 0; i < group_count; ++i) { const char *device_group = groups[i]; const char* fw_version = startswith(device_group, devices_group); if (fw_version == NULL) continue; rc = (int)strlen(fw_version); if (rc != SINOWEALTH_FW_VERSION_LEN) { log_error(ratbag, "Group '%s': incorrect firmware version string length `%d` (must be `%d`)\n", device_group, rc, SINOWEALTH_FW_VERSION_LEN); continue; } struct sinowealth_device_data *device = zalloc(sizeof(struct sinowealth_device_data)); device->fw_version = strdup_safe(fw_version); rc = g_key_file_get_integer(keyfile, device_group, "Buttons", &error); if (rc != 0 && !error) { device->button_count = rc; } else { device->button_count = -1; } g_clear_error(&error); device->device_name = g_key_file_get_string(keyfile, device_group, "DeviceName", &error); g_clear_error(&error); _cleanup_free_ char *led_type_str = g_key_file_get_string(keyfile, device_group, "LedType", &error); if (led_type_str) { if (streq(led_type_str, "RGB")) { device->led_type = SINOWEALTH_LED_TYPE_RGB; } else if (streq(led_type_str, "RBG")) { device->led_type = SINOWEALTH_LED_TYPE_RBG; } else if (streq(led_type_str, "None")) { device->led_type = SINOWEALTH_LED_TYPE_NONE; } else { log_error(ratbag, "Unknown LED type '%s' in group '%s'\n", led_type_str, device_group); device->led_type = SINOWEALTH_LED_TYPE_NONE; } } g_clear_error(&error); rc = g_key_file_get_integer(keyfile, device_group, "Profiles", &error); if (rc != 0 && !error) { device->profile_count = rc; } else { device->profile_count = -1; } list_insert(&data->sinowealth.supported_devices, &device->link); } } static void init_data_steelseries(struct ratbag *ratbag, GKeyFile *keyfile, struct ratbag_device_data *data) { const char *group = "Driver/steelseries"; GError *error = NULL; _cleanup_(freep) char *dpi_range = NULL; _cleanup_(freep) char *quirk = NULL; int num; data->steelseries.device_version = -1; data->steelseries.button_count = -1; data->steelseries.led_count = -1; data->steelseries.dpi_list = NULL; data->steelseries.dpi_range = NULL; data->steelseries.quirk = STEELSERIES_QURIK_NONE; num = g_key_file_get_integer(keyfile, group, "Buttons", &error); if (num != 0 || !error) data->steelseries.button_count = num; if (error) g_error_free(error); error = NULL; num = g_key_file_get_integer(keyfile, group, "Leds", &error); if (num > 0 || !error) data->steelseries.led_count = num; if (error) g_error_free(error); error = NULL; num = g_key_file_get_integer(keyfile, group, "DeviceVersion", &error); if (num > 0 || !error) data->steelseries.device_version = num; if (error) g_error_free(error); dpi_range = g_key_file_get_string(keyfile, group, "DpiRange", NULL); if (dpi_range) { data->steelseries.dpi_range = dpi_range_from_string(dpi_range); } else { dpi_range = g_key_file_get_string(keyfile, group, "DpiList", NULL); if (dpi_range) data->steelseries.dpi_list = dpi_list_from_string(dpi_range); } error = NULL; num = g_key_file_get_integer(keyfile, group, "MacroLength", &error); if (num > 0 || !error) data->steelseries.macro_length = num; if (error) g_error_free(error); quirk = g_key_file_get_string(keyfile, group, "Quirk", NULL); if (quirk) { if (streq(quirk, "Rival100")) data->steelseries.quirk = STEELSERIES_QUIRK_RIVAL100; if (streq(quirk, "SenseiRAW")) data->steelseries.quirk = STEELSERIES_QUIRK_SENSEIRAW; } } static void init_data_asus(struct ratbag *ratbag, GKeyFile *keyfile, struct ratbag_device_data *data) { const char *group = "Driver/asus"; GError *error = NULL; data->asus.profile_count = -1; data->asus.button_count = -1; data->asus.led_count = -1; data->asus.dpi_count = -1; data->asus.dpi_range = NULL; data->asus.is_wireless = -1; data->asus.quirks = 0; for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON * ASUS_MAX_NUM_BUTTON_GROUP; i++) data->asus.button_mapping[i] = -1; for (unsigned int i = 0; i < ASUS_MAX_NUM_LED_MODES; i++) data->asus.led_modes[i] = -1; int profiles = g_key_file_get_integer(keyfile, group, "Profiles", &error); if (!error && profiles >= 0) data->asus.profile_count = profiles; g_clear_error(&error); int buttons = g_key_file_get_integer(keyfile, group, "Buttons", &error); if (!error && buttons >= 0 && buttons <= ASUS_MAX_NUM_BUTTON) data->asus.button_count = buttons; g_clear_error(&error); gsize btn_map_count = 0; _cleanup_(g_strfreevp) char **btn_map = g_key_file_get_string_list(keyfile, group, "ButtonMapping", &btn_map_count, &error); if (!error && btn_map) { for (unsigned int i = 0; i < btn_map_count; i++) { data->asus.button_mapping[i] = strtoul(btn_map[i], NULL, 16); } } g_clear_error(&error); gsize btn_map_2nd_count = 0; _cleanup_(g_strfreevp) char **btn_map_2nd = g_key_file_get_string_list(keyfile, group, "ButtonMappingSecondary", &btn_map_2nd_count, &error); if (!error && btn_map_2nd) { for (unsigned int i = 0; i < btn_map_2nd_count; i++) { data->asus.button_mapping[i + ASUS_MAX_NUM_BUTTON] = strtoul(btn_map_2nd[i], NULL, 16); } } g_clear_error(&error); int leds = g_key_file_get_integer(keyfile, group, "Leds", &error); if (!error && leds >= 0 && leds <= ASUS_MAX_NUM_LED) data->asus.led_count = leds; g_clear_error(&error); int dpis = g_key_file_get_integer(keyfile, group, "Dpis", &error); if (!error && dpis >= 2 && dpis <= ASUS_MAX_NUM_DPI) data->asus.dpi_count = dpis; g_clear_error(&error); _cleanup_(freep) char *dpi_range = g_key_file_get_string(keyfile, group, "DpiRange", &error); if (!error && dpi_range) data->asus.dpi_range = dpi_range_from_string(dpi_range); g_clear_error(&error); int wireless = g_key_file_get_integer(keyfile, group, "Wireless", &error); if (!error && (wireless == 0 || wireless == 1)) data->asus.is_wireless = wireless; g_clear_error(&error); data->asus.omni_signature = g_key_file_get_string(keyfile, group, "OmniSignature", &error); g_clear_error(&error); gsize quirks_count = 0; _cleanup_(g_strfreevp) char **quirks = g_key_file_get_string_list(keyfile, group, "Quirks", &quirks_count, &error); if (!error && quirks) { for (unsigned int i = 0; i < quirks_count; i++) { if (streq(quirks[i], "DOUBLE_DPI")) { data->asus.quirks |= ASUS_QUIRK_DOUBLE_DPI; } else if (streq(quirks[i], "STRIX_PROFILE")) { data->asus.quirks |= ASUS_QUIRK_STRIX_PROFILE; } else if (streq(quirks[i], "RAW_BRIGHTNESS")) { data->asus.quirks |= ASUS_QUIRK_RAW_BRIGHTNESS; } else if (streq(quirks[i], "SEPARATE_XY_DPI")) { data->asus.quirks |= ASUS_QUIRK_SEPARATE_XY_DPI; } else if (streq(quirks[i], "SEPARATE_LEDS")) { data->asus.quirks |= ASUS_QUIRK_SEPARATE_LEDS; } else if (streq(quirks[i], "BUTTONS_SECONDARY")) { data->asus.quirks |= ASUS_QUIRK_BUTTONS_SECONDARY; } else if (streq(quirks[i], "OMNI_RECEIVER")) { data->asus.quirks |= ASUS_QUIRK_OMNI_RECEIVER; } else if (streq(quirks[i], "LED_V2")) { data->asus.quirks |= ASUS_QUIRK_LED_V2; } else if (streq(quirks[i], "SETTINGS_V2")) { data->asus.quirks |= ASUS_QUIRK_SETTINGS_V2; } else { log_error(ratbag, "%s is invalid quirk. Ignoring...\n", quirks[i]); } } } g_clear_error(&error); gsize led_modes_count = 0; _cleanup_(g_strfreevp) char **led_modes = g_key_file_get_string_list(keyfile, group, "LedModes", &led_modes_count, &error); if (!error && led_modes) { for (unsigned int i = 0; i < led_modes_count; i++) { if (streq(led_modes[i], "ON")) { data->asus.led_modes[i] = RATBAG_LED_ON; } else if (streq(led_modes[i], "BREATHING")) { data->asus.led_modes[i] = RATBAG_LED_BREATHING; } else if (streq(led_modes[i], "CYCLE")) { data->asus.led_modes[i] = RATBAG_LED_CYCLE; } else { log_error(ratbag, "%s is invalid LED mode. Ignoring...\n", led_modes[i]); } } } g_clear_error(&error); } static const struct driver_map { enum driver map; const char *driver; void (*init)(struct ratbag *ratbag, GKeyFile *keyfile, struct ratbag_device_data *data); } driver_map[] = { { HIDPP10, "hidpp10", init_data_hidpp10 }, { HIDPP20, "hidpp20", init_data_hidpp20 }, { ROCCAT, "roccat", NULL }, { ROCCAT_KONE_PURE, "roccat-kone-pure", NULL }, { ROCCAT_EMP, "roccat-kone-emp", NULL }, { ETEKCITY, "etekcity", NULL}, { GSKILL, "gskill", NULL }, { LOGITECH_G300, "logitech_g300", NULL}, { LOGITECH_G600, "logitech_g600", NULL}, { STEELSERIES, "steelseries", init_data_steelseries }, { ASUS, "asus", init_data_asus }, { SINOWEALTH, "sinowealth", init_data_sinowealth }, { SINOWEALTH_NUBWO, "sinowealth_nubwo", NULL}, { OPENINPUT, "openinput", NULL }, { MARSGAMING, "marsgaming", NULL }, }; const char * ratbag_device_data_get_driver(const struct ratbag_device_data *data) { return data->driver; } const char * ratbag_device_data_get_name(const struct ratbag_device_data *data) { return data->name; } enum ratbag_device_type ratbag_device_data_get_device_type(const struct ratbag_device_data *data) { return data->devicetype; } struct ratbag_device_data * ratbag_device_data_ref(struct ratbag_device_data *data) { data->refcount++; return data; } static void ratbag_device_data_destroy(struct ratbag_device_data *data) { switch (data->drivertype) { case HIDPP10: dpi_list_free(data->hidpp10.dpi_list); free(data->hidpp10.dpi_range); free(data->hidpp10.profile_type); break; case SINOWEALTH: { struct sinowealth_device_data *device_data = NULL; struct sinowealth_device_data *device_data_next = NULL; list_for_each_safe(device_data, device_data_next, &data->sinowealth.supported_devices, link) { free(device_data->device_name); free(device_data->fw_version); free(device_data); } break; } case STEELSERIES: dpi_list_free(data->steelseries.dpi_list); free(data->steelseries.dpi_range); break; default: break; } free(data->name); free(data->driver); free(data); } struct ratbag_device_data * ratbag_device_data_unref(struct ratbag_device_data *data) { if (data == NULL) return NULL; assert(data->refcount > 0); data->refcount--; if (data->refcount == 0) ratbag_device_data_destroy(data); return NULL; } DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbag_device_data *, ratbag_device_data_unref); static bool match(const struct input_id *id, char **strv) { const char *bus; char str[64]; int i = 0; switch (id->bustype) { case BUS_USB: bus = "usb"; break; case BUS_BLUETOOTH: bus = "bluetooth"; break; default: return false; } snprintf(str, sizeof(str), "%s:%04x:%04x", bus, id->vendor, id->product); while (strv[i]) { if (streq(strv[i], str)) return true; i++; } return false; } static bool file_data_matches(struct ratbag *ratbag, const char *path, const struct input_id *id, struct ratbag_device_data **data_out) { _cleanup_(g_key_file_freep) GKeyFile *keyfile = NULL; _cleanup_(g_error_freep) GError *error = NULL; _cleanup_(g_strfreevp) char **match_strv = NULL; _cleanup_(ratbag_device_data_unrefp) struct ratbag_device_data *data = NULL; _cleanup_free_ char *devicetype = NULL; int rc; keyfile = g_key_file_new(); rc = g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, &error); if (!rc) { log_error(ratbag, "Failed to parse keyfile %s: %s\n", path, error->message); return false; } match_strv = g_key_file_get_string_list(keyfile, GROUP_DEVICE, "DeviceMatch", NULL, NULL); if (!match_strv) { log_error(ratbag, "Missing DeviceMatch in %s\n", basename(path)); return false; } if (!match(id, match_strv)) return false; data = zalloc(sizeof(*data)); data->refcount = 1; data->name = g_key_file_get_string(keyfile, GROUP_DEVICE, "Name", NULL); if (!data->name) { return false; // ignore_clang_sa_mem_leak } data->driver = g_key_file_get_string(keyfile, GROUP_DEVICE, "Driver", NULL); if (!data->driver) { log_error(ratbag, "Missing Driver in %s\n", basename(path)); return false; } else { const struct driver_map *map; data->drivertype = NONE; ARRAY_FOR_EACH(driver_map, map) { if (streq(map->driver, data->driver)) { data->drivertype = map->map; if (map->init) map->init(ratbag, keyfile, data); break; } } if (data->drivertype == NONE) { log_error(ratbag, "Unknown driver %s in %s\n", data->driver, basename(path)); return false; } } devicetype = g_key_file_get_string(keyfile, GROUP_DEVICE, "DeviceType", NULL); if (devicetype == NULL) { log_error(ratbag, "No DeviceType found in '%s'\n", basename(path)); data->devicetype = TYPE_UNSPECIFIED; return false; } if (streq(devicetype, "keyboard")) { data->devicetype = TYPE_KEYBOARD; } else if (streq(devicetype, "mouse")) { data->devicetype = TYPE_MOUSE; } else if (streq(devicetype, "other")) { data->devicetype = TYPE_OTHER; } else { log_error(ratbag, "Invalid DeviceType '%s' in '%s'\n", devicetype, basename(path)); data->devicetype = TYPE_UNSPECIFIED; return false; } *data_out = data; data = NULL; return true; } static int filter_device_files(const struct dirent *entry) { const char *SUFFIX = ".device"; const char *name = entry->d_name; size_t len = 0; size_t slen = 0; if (!name || name[0] == '.') return 0; len = strlen(name); slen = strlen(SUFFIX); if (len <= slen) return 0; return streq(&name[len - slen], SUFFIX); } struct ratbag_device_data * ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id) { struct ratbag_device_data *data = NULL; struct dirent **files; int n, nfiles; const char *datadir; datadir = getenv("LIBRATBAG_DATA_DIR"); if (!datadir) datadir = LIBRATBAG_DATA_DIR; log_debug(ratbag, "Using data directory '%s'\n", datadir); n = scandir(datadir, &files, filter_device_files, alphasort); if (n <= 0) { log_error(ratbag, "Unable to locate device files in %s: %s\n", datadir, n == 0 ? "No files found" : strerror(errno)); return NULL; } nfiles = n; while(n--) { _cleanup_(freep) char *file = NULL; int rc; rc = xasprintf(&file, "%s/%s", datadir, files[n]->d_name); if (rc == -1) goto out; if (file_data_matches(ratbag, file, id, &data)) goto out; } if (id->vendor == USB_VENDOR_ID_LOGITECH && (id->product & 0xff00) == 0xc500) log_debug(ratbag, "%04x:%04x is a Logitech receiver, not a device. Ignoring...\n", id->vendor, id->product); else if (!data) log_debug(ratbag, "No data file found for %04x:%04x\n", id->vendor, id->product); out: while(nfiles--) free(files[nfiles]); free(files); return data; } int ratbag_device_data_omni_signature_match(struct ratbag *ratbag, const char *signature, struct input_id *id) { struct dirent **files; const char *datadir; int n, nfiles; size_t best_len = 0; int rc = -ENODEV; datadir = getenv("LIBRATBAG_DATA_DIR"); if (!datadir) datadir = LIBRATBAG_DATA_DIR; n = scandir(datadir, &files, filter_device_files, alphasort); if (n <= 0) { log_error(ratbag, "Unable to locate device files in %s: %s\n", datadir, n == 0 ? "No files found" : strerror(errno)); return rc; } nfiles = n; while (n--) { _cleanup_(g_key_file_freep) GKeyFile *keyfile = NULL; _cleanup_(g_error_freep) GError *error = NULL; _cleanup_(g_strfreevp) char **match_strv = NULL; _cleanup_free_ char *omni_signature = NULL; _cleanup_(freep) char *file = NULL; size_t len; unsigned int vendor, product; if (xasprintf(&file, "%s/%s", datadir, files[n]->d_name) == -1) goto out; keyfile = g_key_file_new(); if (!g_key_file_load_from_file(keyfile, file, G_KEY_FILE_NONE, &error)) { log_error(ratbag, "Failed to parse keyfile %s: %s\n", file, error->message); continue; } omni_signature = g_key_file_get_string(keyfile, "Driver/asus", "OmniSignature", NULL); if (!omni_signature) continue; len = strlen(omni_signature); if (len <= best_len || strncmp(signature, omni_signature, len) != 0) continue; match_strv = g_key_file_get_string_list(keyfile, GROUP_DEVICE, "DeviceMatch", NULL, NULL); if (!match_strv) continue; /* the device must have a usb id to be resolvable */ if (sscanf(match_strv[0], "usb:%x:%x", &vendor, &product) != 2) continue; id->vendor = vendor; id->product = product; best_len = len; rc = 0; } out: while (nfiles--) free(files[nfiles]); free(files); return rc; } /* HID++ 1.0 */ int ratbag_device_data_hidpp10_get_index(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP10); return data->hidpp10.index; } int ratbag_device_data_hidpp10_get_profile_count(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP10); return data->hidpp10.profile_count; } const char * ratbag_device_data_hidpp10_get_profile_type(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP10); return data->hidpp10.profile_type; } struct dpi_list * ratbag_device_data_hidpp10_get_dpi_list(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP10); return data->hidpp10.dpi_list; } struct dpi_range * ratbag_device_data_hidpp10_get_dpi_range(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP10); return data->hidpp10.dpi_range; } int ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP10); return data->hidpp10.led_count; } /* HID++ 2.0 */ int ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP20); return data->hidpp20.index; } int ratbag_device_data_hidpp20_get_button_count(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP20); return data->hidpp20.button_count; } int ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP20); return data->hidpp20.led_count; } int ratbag_device_data_hidpp20_get_report_rate(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP20); return data->hidpp20.report_rate; } uint32_t ratbag_device_data_hidpp20_get_quirks(const struct ratbag_device_data *data) { assert(data->drivertype == HIDPP20); return data->hidpp20.quirks; } /* SinoWealth */ const struct list * ratbag_device_data_sinowealth_get_supported_devices(const struct ratbag_device_data *data) { assert(data->drivertype == SINOWEALTH); return &data->sinowealth.supported_devices; } /* SteelSeries */ int ratbag_device_data_steelseries_get_device_version(const struct ratbag_device_data *data) { assert(data->drivertype == STEELSERIES); return data->steelseries.device_version; } int ratbag_device_data_steelseries_get_button_count(const struct ratbag_device_data *data) { assert(data->drivertype == STEELSERIES); return data->steelseries.button_count; } int ratbag_device_data_steelseries_get_led_count(const struct ratbag_device_data *data) { assert(data->drivertype == STEELSERIES); return data->steelseries.led_count; } struct dpi_list * ratbag_device_data_steelseries_get_dpi_list(const struct ratbag_device_data *data) { assert(data->drivertype == STEELSERIES); return data->steelseries.dpi_list; } struct dpi_range * ratbag_device_data_steelseries_get_dpi_range(const struct ratbag_device_data *data) { assert(data->drivertype == STEELSERIES); return data->steelseries.dpi_range; } int ratbag_device_data_steelseries_get_macro_length(const struct ratbag_device_data *data) { assert(data->drivertype == STEELSERIES); return data->steelseries.macro_length; } enum steelseries_quirk ratbag_device_data_steelseries_get_quirk(const struct ratbag_device_data *data) { assert(data->drivertype == STEELSERIES); return data->steelseries.quirk; } /* ASUS */ int ratbag_device_data_asus_get_profile_count(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.profile_count; } int ratbag_device_data_asus_get_button_count(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.button_count; } const int * ratbag_device_data_asus_get_button_mapping(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.button_mapping; } int ratbag_device_data_asus_get_led_count(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.led_count; } const int * ratbag_device_data_asus_get_led_modes(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.led_modes; } int ratbag_device_data_asus_get_dpi_count(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.dpi_count; } struct dpi_range * ratbag_device_data_asus_get_dpi_range(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.dpi_range; } int ratbag_device_data_asus_is_wireless(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.is_wireless; } uint32_t ratbag_device_data_asus_get_quirks(const struct ratbag_device_data *data) { assert(data->drivertype == ASUS); return data->asus.quirks; }