Skip to content

Commit

Permalink
Add a firmware string entry to the matches
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
whot authored and jexposit committed Apr 15, 2024
1 parent 18f6811 commit f115bae
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 34 deletions.
6 changes: 5 additions & 1 deletion data/wacom.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 19 additions & 9 deletions libwacom/libwacom-database.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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:
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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;
}

Expand Down
112 changes: 92 additions & 20 deletions libwacom/libwacom.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
* <vendor>_<model>_<version>. 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)
Expand All @@ -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);
Expand Down Expand Up @@ -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) */
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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);

Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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, ";");
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions libwacom/libwacom.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions libwacom/libwacom.sym
Original file line number Diff line number Diff line change
Expand Up @@ -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;
6 changes: 4 additions & 2 deletions libwacom/libwacomint.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct _WacomMatch {
gint refcnt;
char *match;
char *name;
char *uniq;
WacomBusType bus;
uint32_t vendor_id;
uint32_t product_id;
Expand Down Expand Up @@ -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_ */

Expand Down
Loading

0 comments on commit f115bae

Please sign in to comment.