diff --git a/data/wacom.example b/data/wacom.example index 3aa7ca1e..f1e163f0 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 remainder of that substring is the uniq ID 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 baf934bd..186ec8ad 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 *uniq, 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 : "", + uniq ? "|" : "", + uniq ? uniq : ""); } 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 **uniq) { gboolean rc = FALSE; guint64 num; @@ -186,8 +188,11 @@ match_from_string(const char *str_in, WacomBusType *bus, int *vendor_id, int *pr goto out; *product_id = (int)num; - if (components[3]) + if (components[3]) { *name = g_strdup(components[3]); + if (components[4]) + *uniq = g_strdup(components[4]); + } rc = TRUE; out: @@ -200,6 +205,7 @@ static WacomMatch * libwacom_match_from_string(const char *matchstr) { char *name = NULL; + char *uniq = NULL; int vendor_id, product_id; WacomBusType bus; WacomMatch *match; @@ -209,16 +215,18 @@ libwacom_match_from_string(const char *matchstr) if (g_str_equal(matchstr, GENERIC_DEVICE_MATCH)) { name = NULL; + uniq = 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, &uniq)) { 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, uniq, bus, vendor_id, product_id); free(name); + free(uniq); return match; } @@ -227,19 +235,21 @@ static gboolean libwacom_matchstr_to_paired(WacomDevice *device, const char *matchstr) { char *name = NULL; + char *uniq = 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, &uniq)) { 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, uniq, bus, vendor_id, product_id); free(name); + free(uniq); return TRUE; } diff --git a/libwacom/libwacom.c b/libwacom/libwacom.c index eb4cde08..0223ee72 100644 --- a/libwacom/libwacom.c +++ b/libwacom/libwacom.c @@ -212,11 +212,48 @@ client_query_by_subsystem_and_device_file (GUdevClient *client, return ret; } +static char * +get_device_uniq(GUdevDevice *device) +{ + GRegex *regex; + GMatchInfo *match_info; + gchar *uniq = g_strdup (g_udev_device_get_sysfs_attr (device, "uniq")); + + if (!uniq) + return NULL; + + if (strlen(uniq) == 0) { + g_free (uniq); + return NULL; + } + + /* The UCLogic kernel driver returns firmware names with form + * __. Remove the version from `uniq` to avoid + * mismatches on firmware updates. */ + regex = g_regex_new ("(.*_.*)_.*$", + G_REGEX_DEFAULT, + G_REGEX_MATCH_DEFAULT, + NULL); + g_regex_match (regex, uniq, 0, &match_info); + + if (g_match_info_matches (match_info)) { + gchar *tmp = uniq; + uniq = g_match_info_fetch (match_info, 1); + g_free (tmp); + } + + g_match_info_free (match_info); + g_regex_unref (regex); + + return uniq; +} + static gboolean get_device_info (const char *path, int *vendor_id, int *product_id, char **name, + char **uniq, WacomBusType *bus, WacomIntegrationFlags *integration_flags, WacomError *error) @@ -232,6 +269,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; + *uniq = NULL; bus_str = NULL; client = g_udev_client_new (subsystems); device = client_query_by_subsystem_and_device_file (client, subsystems[0], path); @@ -284,15 +322,27 @@ 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) { - GUdevDevice *parent; + *uniq = get_device_uniq (device); + /* Try getting the name/uniq from the parent if that fails */ + if (*name == NULL || *uniq == NULL) { + GUdevDevice *parent = g_udev_device_get_parent (device); + + while (parent && (*name == NULL || *uniq == NULL)) { + GUdevDevice *old_parent = parent; + + if (*name == NULL) + *name = g_strdup (g_udev_device_get_sysfs_attr (parent, "name")); + if (*uniq == NULL) + *uniq = get_device_uniq (parent); + parent = g_udev_device_get_parent (parent); + g_object_unref (old_parent); + } - parent = g_udev_device_get_parent (device); - if (!parent) + if (parent) + g_object_unref (parent); + + if (*name == NULL) goto out; - *name = g_strdup (g_udev_device_get_sysfs_attr (parent, "name")); - g_object_unref (parent); } /* Parse the PRODUCT attribute (for Bluetooth, USB, I2C) */ @@ -318,8 +368,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 (*uniq); + } if (device != NULL) g_object_unref (device); if (client != NULL) @@ -524,7 +576,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 *uniq, int vendor_id, int product_id, WacomBusType bus, WacomError *error) { const WacomDevice *device; char *match; @@ -534,7 +586,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, uniq, bus, vendor_id, product_id); device = libwacom_get_device(db, match); g_free (match); @@ -550,6 +602,7 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal WacomDevice *ret = NULL; WacomIntegrationFlags integration_flags; char *name, *match_name; + char *uniq, *match_uniq; WacomMatch *match; switch (fallback) { @@ -571,14 +624,19 @@ 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, &uniq, &bus, &integration_flags, error)) return NULL; match_name = name; - device = libwacom_new (db, match_name, vendor_id, product_id, bus, error); + match_uniq = uniq; + device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, bus, error); if (device == NULL) { - match_name = NULL; - device = libwacom_new (db, match_name, vendor_id, product_id, bus, error); + match_uniq = NULL; + device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, bus, error); + if (device == NULL) { + match_name = NULL; + device = libwacom_new (db, match_name, match_uniq, vendor_id, product_id, bus, error); + } } if (device == NULL) { @@ -601,7 +659,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, match_uniq, bus, vendor_id, product_id); libwacom_set_default_match(ret, match); libwacom_match_unref(match); @@ -611,6 +669,7 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal out: g_free (name); + g_free (uniq); if (ret == NULL) libwacom_error_set(error, WERROR_UNKNOWN_MODEL, "unknown model"); return ret; @@ -626,11 +685,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); @@ -817,6 +876,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 *uniq = libwacom_match_get_uniq(match); WacomBusType type = libwacom_match_get_bustype(match); int vendor = libwacom_match_get_vendor_id(match); int product = libwacom_match_get_product_id(match); @@ -833,6 +893,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 (uniq) + dprintf(fd, "|%s", uniq); dprintf(fd, ";"); } @@ -957,13 +1019,16 @@ libwacom_match_unref(WacomMatch *match) g_free (match->match); g_free (match->name); + g_free (match->uniq); g_free (match); return NULL; } WacomMatch* -libwacom_match_new(const char *name, WacomBusType bus, int vendor_id, int product_id) +libwacom_match_new(const char *name, const char *uniq, + WacomBusType bus, + int vendor_id, int product_id) { WacomMatch *match; char *newmatch; @@ -973,10 +1038,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, uniq, bus, vendor_id, product_id); match->match = newmatch; match->name = g_strdup(name); + match->uniq = g_strdup(uniq); match->bus = bus; match->vendor_id = vendor_id; match->product_id = product_id; @@ -1441,6 +1507,12 @@ libwacom_match_get_name(const WacomMatch *match) return match->name; } +LIBWACOM_EXPORT const char * +libwacom_match_get_uniq(const WacomMatch *match) +{ + return match->uniq; +} + LIBWACOM_EXPORT WacomBusType libwacom_match_get_bustype(const WacomMatch *match) { diff --git a/libwacom/libwacom.h b/libwacom/libwacom.h index 389681d2..21400810 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_uniq(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 76ed0692..e856904a 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.12 { + libwacom_match_get_uniq; +} LIBWACOM_2.9; diff --git a/libwacom/libwacomint.h b/libwacom/libwacomint.h index 537afdb4..f2d7c1cb 100644 --- a/libwacom/libwacomint.h +++ b/libwacom/libwacomint.h @@ -56,6 +56,7 @@ struct _WacomMatch { gint refcnt; char *match; char *name; + char *uniq; 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 *uniq, + 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 *uniq, WacomBusType bus, int vendor_id, int product_id); #endif /* _LIBWACOMINT_H_ */ diff --git a/tools/list-devices.c b/tools/list-devices.c index 3dd75175..e3193813 100644 --- a/tools/list-devices.c +++ b/tools/list-devices.c @@ -60,6 +60,7 @@ static void print_device_info (WacomDevice *device, WacomBusType bus_type_filter const char *bus = "unknown"; int vid = libwacom_match_get_vendor_id(*match); int pid = libwacom_match_get_product_id(*match); + const char *uniq = libwacom_match_get_uniq(*match); switch (type) { case WBUSTYPE_USB: bus = "usb"; break; @@ -72,9 +73,9 @@ static void print_device_info (WacomDevice *device, WacomBusType bus_type_filter /* We don't need to print the generic device */ if (vid != 0 || pid != 0 || bus != 0) - printf("- { bus: '%s',%*svid: '0x%04x', pid: '0x%04x', name: '%s' }\n", + printf("- { bus: '%s',%*svid: '0x%04x', pid: '0x%04x', name: '%s', uniq: '%s' }\n", bus, (int)(10 - strlen(bus)), " ", - vid, pid, name); + vid, pid, name, uniq); } } }