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 committed Dec 20, 2023
1 parent 07f9e72 commit 6ad598d
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 31 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 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
Expand Down
26 changes: 17 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 *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;
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])
*fw = g_strdup(components[3]);
}

rc = TRUE;
out:
Expand All @@ -200,6 +205,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;
Expand All @@ -209,15 +215,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;
Expand All @@ -227,17 +234,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;
Expand Down
65 changes: 46 additions & 19 deletions libwacom/libwacom.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -283,15 +285,25 @@ 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;
*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 = g_udev_device_get_parent (device);

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);
}

if (parent)
g_object_unref (parent);

parent = g_udev_device_get_parent (device);
if (!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 @@ -317,8 +329,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)
Expand Down Expand Up @@ -523,7 +537,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;
Expand All @@ -533,7 +547,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);

Expand All @@ -549,6 +563,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) {
Expand All @@ -570,14 +585,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) {
Expand All @@ -600,7 +615,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);

Expand All @@ -625,11 +640,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 @@ -816,6 +831,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);
Expand All @@ -832,6 +848,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, ";");
}

Expand Down Expand Up @@ -962,7 +980,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;
Expand All @@ -972,10 +992,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;
Expand Down Expand Up @@ -1440,6 +1461,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)
{
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_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);
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.10 {
libwacom_match_get_firmware_string;
} 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 *fw;
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 *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_ */

Expand Down

0 comments on commit 6ad598d

Please sign in to comment.