libudev-zero: backport hwdb USB ID lookup from upstream master

Replace the four stub udev_hwdb_*() functions with a working
implementation that looks up vendor and product names from
/usr/share/hwdata/usb.ids, so callers using the standard libudev
hwdb API benefit without needing package-specific patches.

The patch is a clean backport of upstream commit 2bebebc9e0444
("udev: implement hwdb USB ID lookup from usb.ids (#80)") merged
to illiliti/libudev-zero master on 2026-05-19, post-1.0.3. Drop
when the package is bumped to the next libudev-zero release.

Upstream now defaults USB_IDS_PATH to ${SHAREDIR}/hwdata/usb.ids
with SHAREDIR=${PREFIX}/share, so the explicit
USB_IDS_PATH=/usr/share/hwdata/usb.ids in MAKE_FLAGS is no longer
needed and is dropped.

Fixes: https://github.com/openwrt/packages/issues/29386

Signed-off-by: Alexandru Ardelean <alex@shruggie.ro>
(cherry picked from commit 93759026fd)
This commit is contained in:
Alexandru Ardelean
2026-05-13 09:49:49 +03:00
committed by Josef Schlehofer
parent df8535e73f
commit 5db7c46b19
2 changed files with 217 additions and 1 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=libudev-zero
PKG_VERSION:=1.0.3
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/illiliti/libudev-zero/tar.gz/$(PKG_VERSION)?
@@ -0,0 +1,216 @@
From 2bebebc9e0444ec53afd7f1f37aa80ff6b95f5f7 Mon Sep 17 00:00:00 2001
From: Alexandru Ardelean <ardeleanalex@gmail.com>
Date: Tue, 19 May 2026 17:52:47 +0300
Subject: [PATCH] udev: implement hwdb USB ID lookup from usb.ids (#80)
Replace the four stub udev_hwdb_*() functions with a working
implementation that looks up vendor and product names from a
usb.ids database file (e.g. provided by the hwdata package).
udev_hwdb_get_properties_list_entry() parses the file for USB
modalias strings of the form "usb:vVVVVpPPPP" (product lookup)
or "usb:vVVVV" (vendor lookup), and populates the entry list with
ID_MODEL_FROM_DATABASE or ID_VENDOR_FROM_DATABASE properties.
Lines are read via POSIX getline() so the buffer grows as needed
on long usb.ids entries.
The path to the usb.ids file is set at build time via the
USB_IDS_PATH Makefile variable, which defaults to
${SHAREDIR}/hwdata/usb.ids.
---
Makefile | 3 ++
udev.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 144 insertions(+), 7 deletions(-)
--- a/Makefile
+++ b/Makefile
@@ -3,9 +3,12 @@
PREFIX = /usr/local
LIBDIR = ${PREFIX}/lib
+SHAREDIR = ${PREFIX}/share
INCLUDEDIR = ${PREFIX}/include
PKGCONFIGDIR = ${LIBDIR}/pkgconfig
+USB_IDS_PATH = ${SHAREDIR}/hwdata/usb.ids
XCFLAGS = ${CPPFLAGS} ${CFLAGS} -std=c99 -fPIC -D_XOPEN_SOURCE=700 \
+ -DUSB_IDS_PATH=\"${USB_IDS_PATH}\" \
-Wall -Wextra -Wpedantic -Wmissing-prototypes -Wstrict-prototypes \
-Wno-unused-parameter
XLDFLAGS = ${LDFLAGS} -shared -Wl,-soname,libudev.so.1
--- a/udev.c
+++ b/udev.c
@@ -15,9 +15,13 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <stdio.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include "udev.h"
+#include "udev_list.h"
struct udev {
int refcount;
@@ -76,22 +80,152 @@ int udev_get_log_priority(struct udev *u
return 0;
}
-/* XXX NOT IMPLEMENTED */ struct udev_hwdb *udev_hwdb_new(struct udev *udev)
+struct udev_hwdb {
+ struct udev_list_entry head;
+ int refcount;
+};
+
+static int usb_ids_lookup_vendor(uint16_t vendorid, char *buf, size_t buflen)
{
- return NULL;
+ char *line = NULL, *name;
+ size_t cap = 0, len;
+ unsigned int id;
+ FILE *f;
+
+ f = fopen(USB_IDS_PATH, "r");
+ if (!f) {
+ return 0;
+ }
+ while (getline(&line, &cap, f) != -1) {
+ if (line[0] == '#' || line[0] == '\t' || line[0] == '\n') {
+ continue;
+ }
+ if (sscanf(line, "%04x", &id) != 1 || id != vendorid) {
+ continue;
+ }
+ name = strchr(line, ' ');
+ if (name) {
+ while (*name == ' ') {
+ name++;
+ }
+ len = strlen(name);
+ while (len > 0 && (name[len - 1] == '\n' || name[len - 1] == '\r')) {
+ name[--len] = '\0';
+ }
+ snprintf(buf, buflen, "%s", name);
+ }
+ break;
+ }
+ free(line);
+ fclose(f);
+ return buf[0] != '\0';
+}
+
+static int usb_ids_lookup_product(uint16_t vendorid, uint16_t productid, char *buf, size_t buflen)
+{
+ char *line = NULL, *name;
+ size_t cap = 0, len;
+ int in_vendor = 0;
+ unsigned int id;
+ FILE *f;
+
+ f = fopen(USB_IDS_PATH, "r");
+ if (!f) {
+ return 0;
+ }
+ while (getline(&line, &cap, f) != -1) {
+ if (line[0] == '#' || line[0] == '\n') {
+ continue;
+ }
+ if (in_vendor) {
+ if (line[0] != '\t') {
+ break;
+ }
+ if (line[1] == '\t') {
+ continue;
+ }
+ if (sscanf(line + 1, "%04x", &id) != 1 || id != productid) {
+ continue;
+ }
+ name = strchr(line + 1, ' ');
+ if (name) {
+ while (*name == ' ') {
+ name++;
+ }
+ len = strlen(name);
+ while (len > 0 && (name[len - 1] == '\n' || name[len - 1] == '\r')) {
+ name[--len] = '\0';
+ }
+ snprintf(buf, buflen, "%s", name);
+ }
+ break;
+ }
+ if (line[0] == '\t') {
+ continue;
+ }
+ if (sscanf(line, "%04x", &id) != 1 || id != vendorid) {
+ continue;
+ }
+ in_vendor = 1;
+ }
+ free(line);
+ fclose(f);
+ return buf[0] != '\0';
}
-/* XXX NOT IMPLEMENTED */ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb)
+struct udev_hwdb *udev_hwdb_new(struct udev *udev)
{
- return NULL;
+ struct udev_hwdb *hwdb;
+
+ hwdb = calloc(1, sizeof(*hwdb));
+ if (!hwdb) {
+ return NULL;
+ }
+ hwdb->refcount = 1;
+ return hwdb;
}
-/* XXX NOT IMPLEMENTED */ struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb)
+struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb)
{
- return NULL;
+ if (!hwdb) {
+ return NULL;
+ }
+ hwdb->refcount++;
+ return hwdb;
}
-/* XXX NOT IMPLEMENTED */ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags)
+struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb)
{
+ if (!hwdb) {
+ return NULL;
+ }
+ if (--hwdb->refcount > 0) {
+ return NULL;
+ }
+ udev_list_entry_free_all(&hwdb->head);
+ free(hwdb);
return NULL;
}
+
+struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags)
+{
+ unsigned int vendorid, productid;
+ char name[64];
+
+ if (!hwdb || !modalias) {
+ return NULL;
+ }
+ udev_list_entry_free_all(&hwdb->head);
+ hwdb->head.next = NULL;
+ name[0] = '\0';
+ if (sscanf(modalias, "usb:v%4xp%4x", &vendorid, &productid) == 2) {
+ if (usb_ids_lookup_product((uint16_t)vendorid, (uint16_t)productid, name, sizeof(name))) {
+ udev_list_entry_add(&hwdb->head, "ID_MODEL_FROM_DATABASE", name, 0);
+ }
+ } else if (sscanf(modalias, "usb:v%4x", &vendorid) == 1) {
+ if (usb_ids_lookup_vendor((uint16_t)vendorid, name, sizeof(name))) {
+ udev_list_entry_add(&hwdb->head, "ID_VENDOR_FROM_DATABASE", name, 0);
+ }
+ }
+ return hwdb->head.next;
+}