-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for firmware versions #620
Conversation
Nice! Thanks for working on this. I was planning to work on it when I had some time but you already made great progress. I still need to get the firmware names for Gaomon so this PR actually fixes #609 and add the firmwares in the .tablet files. I'll try to test it ASAP. |
f85481e
to
6ad598d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Peter,
Sorry for the embarrassing long time it took me to actually run this PR.
This is a first review after running the code, even though I notice 2 main issues:
g_udev_device_get_sysfs_attr
always returnNULL
forfw_name
: I didn't have time to investigate if a different function is required or there is an issue in my kernel code- The fw name is not optional when matching a tablet. For example, because I wasn't able to get the
fw_name
, this doesn't matchusb|256c|006d|HUION Huion Tablet_H640P Pad|HUION_T203
.
I'll keep investigating how to fix these issues.
|
||
match->match = newmatch; | ||
match->name = g_strdup(name); | ||
match->fw = g_strdup(fw); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memory leak in libwacom_match_unref
:
WacomMatch*
libwacom_match_unref(WacomMatch *match)
{
if (match == NULL ||
!g_atomic_int_dec_and_test(&match->refcnt))
return NULL;
g_free (match->match);
g_free (match->name);
+ g_free (match->fw);
g_free (match);
return NULL;
}
libwacom/libwacom.c
Outdated
/* 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); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still not able to get the fw name, but there are some minor issues with this block of code:
/* Try getting the name/fw_name from the parent if that fails */
if (*name == NULL || *fw == NULL) {
GUdevDevice *prev_parent = 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 (parent, "fw_name"));
prev_parent = parent;
parent = g_udev_device_get_parent (prev_parent);
g_object_unref (prev_parent);
}
if (parent)
g_object_unref (parent);
if (*name == NULL)
goto out;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops. all comments addressed and rebased, thanks!
Still untested because, well, we don't have the test suite to test this... :(
This needs to be printed to stdout, otherwise meson won't show it.
Some device names include a colon which makes it hard to add extra optional components to the device matches.
No functional changes, this just preps the way for more optional elements in the match string.
libwacom/libwacom.c
Outdated
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")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is parent
:
*fw = g_strdup (g_udev_device_get_sysfs_attr (parent, "fw_name"));
With this change (and adding a missing S_IROTH
in the driver) libwacom is able to read the firmware name 🎉
Now I need to parse it to drop the version (HUION_T203_200720
-> HUION_T203
) and make the match to the fw name optional for backwards compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is
parent
:
thanks, fixed (and renamed tmp
to old_parent
for consistency with some other code).
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.
Moving this out from #620 (comment): if the version is encoded in the fw name then we might need a glob for the version match? That's possible but requires a bit of rework internally. |
Either we implement globs or we parse the well-known fw string. Since the fw names we are going to handle are going to be limited, I'm not sure if implementing globs is worth it. I'll have a look to the matching mechanism and see how complex it could be. |
For reference, this is how a very simple fw name parsing could look like: diffdiff --git a/data/huion-h640p.tablet b/data/huion-h640p.tablet
index fd01c22..d4135ae 100644
--- a/data/huion-h640p.tablet
+++ b/data/huion-h640p.tablet
@@ -16,7 +16,7 @@
[Device]
Name=Huion H640P
ModelName=
-DeviceMatch=usb|256c|006d|HUION Huion Tablet_H640P Pad;usb|256c|006d|HUION Huion Tablet_H640P Pen
+DeviceMatch=usb|256c|006d|HUION Huion Tablet_H640P Pad;usb|256c|006d|HUION Huion Tablet_H640P Pen;usb|256c|006d|HUION Huion Tablet_H640P Pad|HUION_T203;usb|256c|006d|HUION Huion Tablet_H640P Pen|HUION_T203
Class=Bamboo
Width=6
Height=4
diff --git a/libwacom/libwacom.c b/libwacom/libwacom.c
index 9442268..cf2574f 100644
--- a/libwacom/libwacom.c
+++ b/libwacom/libwacom.c
@@ -212,6 +212,28 @@ client_query_by_subsystem_and_device_file (GUdevClient *client,
return ret;
}
+static char *
+get_device_fw_name(GUdevDevice *device)
+{
+ GRegex *regex;
+ GMatchInfo *match_info;
+ const gchar *fw = g_udev_device_get_sysfs_attr (device, "fw_name");
+
+ if (!fw)
+ return g_strdup (fw);
+
+ regex = g_regex_new ("(HUION_.*)_.*$", G_REGEX_DEFAULT, G_REGEX_MATCH_DEFAULT, NULL);
+ g_regex_match (regex, fw, 0, &match_info);
+
+ if (g_match_info_matches (match_info)) {
+ fw = g_match_info_fetch (match_info, 1);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+ return fw;
+}
+
static gboolean
get_device_info (const char *path,
int *vendor_id,
@@ -285,7 +307,7 @@ get_device_info (const char *path,
}
*name = g_strdup (g_udev_device_get_sysfs_attr (device, "name"));
- *fw = g_strdup (g_udev_device_get_sysfs_attr (device, "fw_name"));
+ *fw = get_device_fw_name (device);
/* 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);
@@ -296,7 +318,7 @@ get_device_info (const char *path,
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"));
+ *fw = get_device_fw_name (parent);
tmp = parent;
parent = g_udev_device_get_parent (parent);
g_object_unref (tmp);
@@ -566,7 +588,7 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal
WacomDevice *ret = NULL;
WacomIntegrationFlags integration_flags;
char *name, *match_name;
- char *fw;
+ char *fw, *match_fw;
WacomMatch *match;
switch (fallback) {
@@ -592,10 +614,15 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal
return NULL;
match_name = name;
- device = libwacom_new (db, match_name, fw, vendor_id, product_id, bus, error);
+ match_fw = fw;
+ device = libwacom_new (db, match_name, match_fw, vendor_id, product_id, bus, error);
if (device == NULL) {
- match_name = NULL;
- device = libwacom_new (db, match_name, fw, vendor_id, product_id, bus, error);
+ match_fw = NULL;
+ device = libwacom_new (db, match_name, match_fw, vendor_id, product_id, bus, error);
+ if (device == NULL) {
+ match_name = NULL;
+ device = libwacom_new (db, match_name, match_fw, vendor_id, product_id, bus, error);
+ }
}
if (device == NULL) {
@@ -618,7 +645,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, fw, bus, vendor_id, product_id);
+ match = libwacom_match_new(match_name, match_fw, bus, vendor_id, product_id);
libwacom_set_default_match(ret, match);
libwacom_match_unref(match);
It is way simpler than modifying the |
A few comments:
+ if (!fw)
+ return g_strdup (fw);
+
We don't need anything long term right now, chances are that the vendors throw us a curveball anyway. All this is internal-only API so we can go with what works and fix it later where it doesn't. Side-note: I don't have any emotional attachment to this PR and since you're the one who can actually test it - feel free to push a PR that replaces this one and we close this one in favour of yours. Better then you having to give me diffs for my bugs :) |
I'm afraid we will need the regex to match different vendor names, see #610 (comment) for examples. Thankfully it is a very simple regex.
Then I guess it'd be better to stick with the simpler solution and less intrusive solution: parsing the firmware name.
Poor PR xD Let me share with you one more diff. If more changes are required, I promise I'll open another PR. With these changes, Also, it is possible to add the firmware version to the .tablet file:
To make the make it match taking into account the firmware name while keeping compatibility with older kernels. I think this is everything we need? diffdiff --git a/libwacom/libwacom.c b/libwacom/libwacom.c
index 6454a40..f97892d 100644
--- a/libwacom/libwacom.c
+++ b/libwacom/libwacom.c
@@ -212,6 +212,37 @@ client_query_by_subsystem_and_device_file (GUdevClient *client,
return ret;
}
+static char *
+get_device_fw_name(GUdevDevice *device)
+{
+ GRegex *regex;
+ GMatchInfo *match_info;
+ gchar *fw = g_strdup (g_udev_device_get_sysfs_attr (device, "fw_name"));
+
+ if (!fw)
+ return NULL;
+
+ /* The UCLogic kernel driver returns firmware names with form
+ * <vendor>_<model>_<version>. Remove the version from `fw` to avoid
+ * mismatches on firmware updates. */
+ regex = g_regex_new ("(.*_.*)_.*$",
+ G_REGEX_DEFAULT,
+ G_REGEX_MATCH_DEFAULT,
+ NULL);
+ g_regex_match (regex, fw, 0, &match_info);
+
+ if (g_match_info_matches (match_info)) {
+ gchar *tmp = fw;
+ fw = g_match_info_fetch (match_info, 1);
+ g_free (tmp);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+
+ return fw;
+}
+
static gboolean
get_device_info (const char *path,
int *vendor_id,
@@ -285,7 +316,7 @@ get_device_info (const char *path,
}
*name = g_strdup (g_udev_device_get_sysfs_attr (device, "name"));
- *fw = g_strdup (g_udev_device_get_sysfs_attr (device, "fw_name"));
+ *fw = get_device_fw_name (device);
/* 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);
@@ -296,7 +327,7 @@ get_device_info (const char *path,
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 (parent, "fw_name"));
+ *fw = get_device_fw_name (parent);
parent = g_udev_device_get_parent (parent);
g_object_unref (old_parent);
}
@@ -565,7 +596,7 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal
WacomDevice *ret = NULL;
WacomIntegrationFlags integration_flags;
char *name, *match_name;
- char *fw;
+ char *fw, *match_fw;
WacomMatch *match;
switch (fallback) {
@@ -591,10 +622,15 @@ libwacom_new_from_path(const WacomDeviceDatabase *db, const char *path, WacomFal
return NULL;
match_name = name;
- device = libwacom_new (db, match_name, fw, vendor_id, product_id, bus, error);
+ match_fw = fw;
+ device = libwacom_new (db, match_name, match_fw, vendor_id, product_id, bus, error);
if (device == NULL) {
- match_name = NULL;
- device = libwacom_new (db, match_name, fw, vendor_id, product_id, bus, error);
+ match_fw = NULL;
+ device = libwacom_new (db, match_name, match_fw, vendor_id, product_id, bus, error);
+ if (device == NULL) {
+ match_name = NULL;
+ device = libwacom_new (db, match_name, match_fw, vendor_id, product_id, bus, error);
+ }
}
if (device == NULL) {
@@ -617,7 +653,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, fw, bus, vendor_id, product_id);
+ match = libwacom_match_new(match_name, match_fw, bus, vendor_id, product_id);
libwacom_set_default_match(ret, match);
libwacom_match_unref(match);
I'll review one more time my kernel patches and submit them, I'll post the link. |
As promised, I opened my own PR: It includes a minimal change to get the firmware name from |
Closing in favour of #648 since that one is actually tested :) Thanks! |
Completely untested, this is just the first draft
Add support for specifying a firmware version string in the
DeviceMatch
which is filled in by reading the proposedfw_name
sysfs attribute.DeviceMatch
now uses|
as separator, we have a few devices with:
in the name and it's too painful to handle this otherwise.Fixes #610
cc @JoseExposito