--- /dev/null
+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;
++}