libudev-zero: backport hwdb USB ID lookup from upstream master
authorAlexandru Ardelean <redacted>
Wed, 13 May 2026 06:49:49 +0000 (09:49 +0300)
committerAlexandru Ardelean <redacted>
Thu, 21 May 2026 17:28:51 +0000 (20:28 +0300)
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 <redacted>
libs/libudev-zero/Makefile
libs/libudev-zero/patches/0004-udev-implement-hwdb-USB-ID-lookup-from-usb.ids.patch [new file with mode: 0644]

index e8a5cb19ac22898ad4288fb510cf8cfe98099414..77e78573b3c3262ddfd3f762a3bbf5fae21049de 100644 (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)?
diff --git a/libs/libudev-zero/patches/0004-udev-implement-hwdb-USB-ID-lookup-from-usb.ids.patch b/libs/libudev-zero/patches/0004-udev-implement-hwdb-USB-ID-lookup-from-usb.ids.patch
new file mode 100644 (file)
index 0000000..890a00e
--- /dev/null
@@ -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;
++}
git clone https://git.99rst.org/PROJECT