From 53ae250fd1683342c0f8be6a01ba28909aaa6fb8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 20 Dec 2023 10:32:21 +1000 Subject: [PATCH] Add a firmware string entry to the matches Some device manufacturers re-use the VID/PID but we can still get to the firmware versions if the kernel driver exports them. Add those to the match string as fifth component after the (possibly empty) name. A DeviceMatch=usb|1234|abcd|Foo Tablet|ABCD1234 now matches against a device with that firmware version. --- data/wacom.example | 6 +++- libwacom/libwacom-database.c | 23 ++++++++----- libwacom/libwacom.c | 62 +++++++++++++++++++++++++----------- libwacom/libwacom.h | 1 + libwacom/libwacom.sym | 4 +++ libwacom/libwacomint.h | 6 ++-- 6 files changed, 73 insertions(+), 29 deletions(-) diff --git a/data/wacom.example b/data/wacom.example index 3aa7ca1e6..fab8cab85 100644 --- a/data/wacom.example +++ b/data/wacom.example @@ -81,7 +81,11 @@ DeviceMatch=usb|056a|00bc;bluetooth|056a|00bc; # (common for HUION, GAOMON, UCLogic, XP-Pen), use the device name # as fourth element in the match string. # DeviceMatch=usb|1234|abcd:SomeDevice Name; - +# +# Optionally after the device name (which may be empty) the +# the remainer of that substring is the firmware version to match against. +# DeviceMatch=usb|1234|abcd||ABC_1234; +# DeviceMatch=usb|1234|abcd|SomeDevice Name|ABC_1234; # Paired PID includes the match line of any device that share the same # physical device but has different product or vendor ids (e.g. the touch diff --git a/libwacom/libwacom-database.c b/libwacom/libwacom-database.c index baf934bdb..e7f3dc9a2 100644 --- a/libwacom/libwacom-database.c +++ b/libwacom/libwacom-database.c @@ -153,17 +153,19 @@ bus_to_str (WacomBusType bus) } char * -make_match_string (const char *name, WacomBusType bus, int vendor_id, int product_id) +make_match_string (const char *name, const char *fw, WacomBusType bus, int vendor_id, int product_id) { - return g_strdup_printf("%s|%04x|%04x%s%s", + return g_strdup_printf("%s|%04x|%04x%s%s%s%s", bus_to_str (bus), vendor_id, product_id, name ? "|" : "", - name ? name : ""); + name ? name : "", + fw ? "|" : "", + fw ? fw : ""); } static gboolean -match_from_string(const char *str_in, WacomBusType *bus, int *vendor_id, int *product_id, char **name) +match_from_string(const char *str_in, WacomBusType *bus, int *vendor_id, int *product_id, char **name, char **fw) { gboolean rc = FALSE; guint64 num; @@ -188,6 +190,8 @@ match_from_string(const char *str_in, WacomBusType *bus, int *vendor_id, int *pr if (components[3]) *name = g_strdup(components[3]); + if (components[4]) + *fw = g_strdup(components[3]); rc = TRUE; out: @@ -200,6 +204,7 @@ static WacomMatch * libwacom_match_from_string(const char *matchstr) { char *name = NULL; + char *fw = NULL; int vendor_id, product_id; WacomBusType bus; WacomMatch *match; @@ -209,15 +214,16 @@ libwacom_match_from_string(const char *matchstr) if (g_str_equal(matchstr, GENERIC_DEVICE_MATCH)) { name = NULL; + fw = NULL; bus = WBUSTYPE_UNKNOWN; vendor_id = 0; product_id = 0; - } else if (!match_from_string(matchstr, &bus, &vendor_id, &product_id, &name)) { + } else if (!match_from_string(matchstr, &bus, &vendor_id, &product_id, &name, &fw)) { DBG("failed to match '%s' for product/vendor IDs. Skipping.\n", matchstr); return NULL; } - match = libwacom_match_new(name, bus, vendor_id, product_id); + match = libwacom_match_new(name, fw, bus, vendor_id, product_id); free(name); return match; @@ -227,17 +233,18 @@ static gboolean libwacom_matchstr_to_paired(WacomDevice *device, const char *matchstr) { char *name = NULL; + char *fw = NULL; int vendor_id, product_id; WacomBusType bus; g_return_val_if_fail(device->paired == NULL, FALSE); - if (!match_from_string(matchstr, &bus, &vendor_id, &product_id, &name)) { + if (!match_from_string(matchstr, &bus, &vendor_id, &product_id, &name, &fw)) { DBG("failed to match '%s' for product/vendor IDs. Ignoring.\n", matchstr); return FALSE; } - device->paired = libwacom_match_new(name, bus, vendor_id, product_id); + device->paired = libwacom_match_new(name, fw, bus, vendor_id, product_id); free(name); return TRUE; diff --git a/libwacom/libwacom.c b/libwacom/libwacom.c index bde7c9d9b..0c4f140fb 100644 --- a/libwacom/libwacom.c +++ b/libwacom/libwacom.c @@ -217,6 +217,7 @@ get_device_info (const char *path, int *vendor_id, int *product_id, char **name, + char **fw, WacomBusType *bus, WacomIntegrationFlags *integration_flags, WacomError *error) @@ -232,6 +233,7 @@ get_device_info (const char *path, /* The integration flags from device info are unset by default */ *integration_flags = WACOM_DEVICE_INTEGRATED_UNSET; *name = NULL; + *fw = NULL; bus_str = NULL; client = g_udev_client_new (subsystems); device = client_query_by_subsystem_and_device_file (client, subsystems[0], path); @@ -283,15 +285,24 @@ get_device_info (const char *path, } *name = g_strdup (g_udev_device_get_sysfs_attr (device, "name")); - /* Try getting the name from the parent if that fails */ - if (*name == NULL) { + *fw = g_strdup (g_udev_device_get_sysfs_attr (device, "fw_name")); + /* Try getting the name/fw_name from the parent if that fails */ + if (*name == NULL || *fw == NULL) { GUdevDevice *parent; - parent = g_udev_device_get_parent (device); - if (!parent) - goto out; - *name = g_strdup (g_udev_device_get_sysfs_attr (parent, "name")); + + while (parent && *name == NULL && *fw == NULL) { + if (*name == NULL) + *name = g_strdup (g_udev_device_get_sysfs_attr (parent, "name")); + if (*fw == NULL) + *fw = g_strdup (g_udev_device_get_sysfs_attr (device, "fw_name")); + g_object_unref (parent); + parent = g_udev_device_get_parent (device); + } g_object_unref (parent); + + if (*name == NULL) + goto out; } /* Parse the PRODUCT attribute (for Bluetooth, USB, I2C) */ @@ -317,8 +328,10 @@ get_device_info (const char *path, out: if (bus_str != NULL) g_free (bus_str); - if (retval == FALSE) + if (retval == FALSE) { g_free (*name); + g_free (*fw); + } if (device != NULL) g_object_unref (device); if (client != NULL) @@ -523,7 +536,7 @@ libwacom_compare(const WacomDevice *a, const WacomDevice *b, WacomCompareFlags f } static const WacomDevice * -libwacom_new (const WacomDeviceDatabase *db, const char *name, int vendor_id, int product_id, WacomBusType bus, WacomError *error) +libwacom_new (const WacomDeviceDatabase *db, const char *name, const char *fw, int vendor_id, int product_id, WacomBusType bus, WacomError *error) { const WacomDevice *device; char *match; @@ -533,7 +546,7 @@ libwacom_new (const WacomDeviceDatabase *db, const char *name, int vendor_id, in return NULL; } - match = make_match_string(name, bus, vendor_id, product_id); + match = make_match_string(name, fw, bus, vendor_id, product_id); device = libwacom_get_device(db, match); g_free (match); @@ -549,6 +562,7 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal WacomDevice *ret = NULL; WacomIntegrationFlags integration_flags; char *name, *match_name; + char *fw; WacomMatch *match; switch (fallback) { @@ -570,14 +584,14 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal return NULL; } - if (!get_device_info (path, &vendor_id, &product_id, &name, &bus, &integration_flags, error)) + if (!get_device_info (path, &vendor_id, &product_id, &name, &fw, &bus, &integration_flags, error)) return NULL; match_name = name; - device = libwacom_new (db, match_name, vendor_id, product_id, bus, error); + device = libwacom_new (db, match_name, fw, vendor_id, product_id, bus, error); if (device == NULL) { match_name = NULL; - device = libwacom_new (db, match_name, vendor_id, product_id, bus, error); + device = libwacom_new (db, match_name, fw, vendor_id, product_id, bus, error); } if (device == NULL) { @@ -600,7 +614,7 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal } /* for multiple-match devices, set to the one we requested */ - match = libwacom_match_new(match_name, bus, vendor_id, product_id); + match = libwacom_match_new(match_name, fw, bus, vendor_id, product_id); libwacom_set_default_match(ret, match); libwacom_match_unref(match); @@ -625,11 +639,11 @@ libwacom_new_from_usbid(const WacomDeviceDatabase *db, int vendor_id, int produc return NULL; } - device = libwacom_new(db, NULL, vendor_id, product_id, WBUSTYPE_USB, error); + device = libwacom_new(db, NULL, NULL, vendor_id, product_id, WBUSTYPE_USB, error); if (!device) - device = libwacom_new(db, NULL, vendor_id, product_id, WBUSTYPE_I2C, error); + device = libwacom_new(db, NULL, NULL, vendor_id, product_id, WBUSTYPE_I2C, error); if (!device) - device = libwacom_new(db, NULL, vendor_id, product_id, WBUSTYPE_BLUETOOTH, error); + device = libwacom_new(db, NULL, NULL, vendor_id, product_id, WBUSTYPE_BLUETOOTH, error); if (device) return libwacom_copy(device); @@ -816,6 +830,7 @@ static void print_integrated_flags_for_device (int fd, const WacomDevice *device static void print_match(int fd, const WacomMatch *match) { const char *name = libwacom_match_get_name(match); + const char *fw = libwacom_match_get_firmware_string(match); WacomBusType type = libwacom_match_get_bustype(match); int vendor = libwacom_match_get_vendor_id(match); int product = libwacom_match_get_product_id(match); @@ -832,6 +847,8 @@ static void print_match(int fd, const WacomMatch *match) dprintf(fd, "%s|%04x|%04x", bus_name, vendor, product); if (name) dprintf(fd, "|%s", name); + if (fw) + dprintf(fd, "|%s", fw); dprintf(fd, ";"); } @@ -962,7 +979,9 @@ libwacom_match_unref(WacomMatch *match) } WacomMatch* -libwacom_match_new(const char *name, WacomBusType bus, int vendor_id, int product_id) +libwacom_match_new(const char *name, const char *fw, + WacomBusType bus, + int vendor_id, int product_id) { WacomMatch *match; char *newmatch; @@ -972,10 +991,11 @@ libwacom_match_new(const char *name, WacomBusType bus, int vendor_id, int produc if (name == NULL && bus == WBUSTYPE_UNKNOWN && vendor_id == 0 && product_id == 0) newmatch = g_strdup("generic"); else - newmatch = make_match_string(name, bus, vendor_id, product_id); + newmatch = make_match_string(name, fw, bus, vendor_id, product_id); match->match = newmatch; match->name = g_strdup(name); + match->fw = g_strdup(fw); match->bus = bus; match->vendor_id = vendor_id; match->product_id = product_id; @@ -1440,6 +1460,12 @@ libwacom_match_get_name(const WacomMatch *match) return match->name; } +LIBWACOM_EXPORT const char * +libwacom_match_get_firmware_string(const WacomMatch *match) +{ + return match->fw; +} + LIBWACOM_EXPORT WacomBusType libwacom_match_get_bustype(const WacomMatch *match) { diff --git a/libwacom/libwacom.h b/libwacom/libwacom.h index 389681d2b..1963ce01d 100644 --- a/libwacom/libwacom.h +++ b/libwacom/libwacom.h @@ -876,6 +876,7 @@ void libwacom_print_stylus_description (int fd, const WacomStylus *stylus); /** @addtogroup devices * @{ */ const char *libwacom_match_get_name(const WacomMatch *match); +const char *libwacom_match_get_firmware_string(const WacomMatch *match); WacomBusType libwacom_match_get_bustype(const WacomMatch *match); uint32_t libwacom_match_get_product_id(const WacomMatch *match); uint32_t libwacom_match_get_vendor_id(const WacomMatch *match); diff --git a/libwacom/libwacom.sym b/libwacom/libwacom.sym index 76ed06921..583b545b4 100644 --- a/libwacom/libwacom.sym +++ b/libwacom/libwacom.sym @@ -72,3 +72,7 @@ local: LIBWACOM_2.9 { libwacom_get_num_keys; } LIBWACOM_2.0; + +LIBWACOM_2.10 { + libwacom_match_get_firmware_string; +} LIBWACOM_2.9; diff --git a/libwacom/libwacomint.h b/libwacom/libwacomint.h index 537afdb48..7a3526cc1 100644 --- a/libwacom/libwacomint.h +++ b/libwacom/libwacomint.h @@ -56,6 +56,7 @@ struct _WacomMatch { gint refcnt; char *match; char *name; + char *fw; WacomBusType bus; uint32_t vendor_id; uint32_t product_id; @@ -143,12 +144,13 @@ void libwacom_error_set(WacomError *error, enum WacomErrorCode code, const char void libwacom_add_match(WacomDevice *device, WacomMatch *newmatch); void libwacom_set_default_match(WacomDevice *device, WacomMatch *newmatch); void libwacom_remove_match(WacomDevice *device, WacomMatch *newmatch); -WacomMatch* libwacom_match_new(const char *name, WacomBusType bus, +WacomMatch* libwacom_match_new(const char *name, const char *fw, + WacomBusType bus, int vendor_id, int product_id); WacomBusType bus_from_str (const char *str); const char *bus_to_str (WacomBusType bus); -char *make_match_string(const char *name, WacomBusType bus, int vendor_id, int product_id); +char *make_match_string(const char *name, const char *fw, WacomBusType bus, int vendor_id, int product_id); #endif /* _LIBWACOMINT_H_ */