diff options
Diffstat (limited to 'names.c')
-rw-r--r-- | names.c | 465 |
1 files changed, 465 insertions, 0 deletions
@@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB name database manipulation routines + * + * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 2013 Tom Gundersen (teg@jklm.no) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdint.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <stdbool.h> + +#include <libusb.h> +#include <libudev.h> + +#include "usb-spec.h" +#include "names.h" +#include "sysfs.h" + + +#define HASH1 0x10 +#define HASH2 0x02 +#define HASHSZ 512 + +static unsigned int hashnum(unsigned int num) +{ + unsigned int mask1 = (unsigned int)HASH1 << 27, mask2 = (unsigned int)HASH2 << 27; + + for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1) + if (num & mask1) + num ^= mask2; + return num & (HASHSZ-1); +} + +/* ---------------------------------------------------------------------- */ + +static struct udev *udev = NULL; +static struct udev_hwdb *hwdb = NULL; +static struct audioterminal *audioterminals_hash[HASHSZ] = { NULL, }; +static struct videoterminal *videoterminals_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *hiddescriptors_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *reports_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *huts_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *biass_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *physdess_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *hutus_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *langids_hash[HASHSZ] = { NULL, }; +static struct genericstrtable *countrycodes_hash[HASHSZ] = { NULL, }; + +/* ---------------------------------------------------------------------- */ + +static const char *names_genericstrtable(struct genericstrtable *t[HASHSZ], + unsigned int idx) +{ + struct genericstrtable *h; + + for (h = t[hashnum(idx)]; h; h = h->next) + if (h->num == idx) + return h->name; + return NULL; +} + +const char *names_hid(uint8_t hidd) +{ + return names_genericstrtable(hiddescriptors_hash, hidd); +} + +const char *names_reporttag(uint8_t rt) +{ + return names_genericstrtable(reports_hash, rt); +} + +const char *names_huts(unsigned int data) +{ + return names_genericstrtable(huts_hash, data); +} + +const char *names_hutus(unsigned int data) +{ + return names_genericstrtable(hutus_hash, data); +} + +const char *names_langid(uint16_t langid) +{ + return names_genericstrtable(langids_hash, langid); +} + +const char *names_physdes(uint8_t ph) +{ + return names_genericstrtable(physdess_hash, ph); +} + +const char *names_bias(uint8_t b) +{ + return names_genericstrtable(biass_hash, b); +} + +const char *names_countrycode(unsigned int countrycode) +{ + return names_genericstrtable(countrycodes_hash, countrycode); +} + +static const char *hwdb_get(const char *modalias, const char *key) +{ + struct udev_list_entry *entry; + + udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0)) + if (strcmp(udev_list_entry_get_name(entry), key) == 0) + return udev_list_entry_get_value(entry); + + return NULL; +} + +const char *names_vendor(uint16_t vendorid) +{ + char modalias[64]; + + sprintf(modalias, "usb:v%04X*", vendorid); + return hwdb_get(modalias, "ID_VENDOR_FROM_DATABASE"); +} + +const char *names_product(uint16_t vendorid, uint16_t productid) +{ + char modalias[64]; + + sprintf(modalias, "usb:v%04Xp%04X*", vendorid, productid); + return hwdb_get(modalias, "ID_MODEL_FROM_DATABASE"); +} + +const char *names_class(uint8_t classid) +{ + char modalias[64]; + + sprintf(modalias, "usb:v*p*d*dc%02X*", classid); + return hwdb_get(modalias, "ID_USB_CLASS_FROM_DATABASE"); +} + +const char *names_subclass(uint8_t classid, uint8_t subclassid) +{ + char modalias[64]; + + sprintf(modalias, "usb:v*p*d*dc%02Xdsc%02X*", classid, subclassid); + return hwdb_get(modalias, "ID_USB_SUBCLASS_FROM_DATABASE"); +} + +const char *names_protocol(uint8_t classid, uint8_t subclassid, uint8_t protocolid) +{ + char modalias[64]; + + sprintf(modalias, "usb:v*p*d*dc%02Xdsc%02Xdp%02X*", classid, subclassid, protocolid); + return hwdb_get(modalias, "ID_USB_PROTOCOL_FROM_DATABASE"); +} + +const char *names_audioterminal(uint16_t termt) +{ + struct audioterminal *at; + + at = audioterminals_hash[hashnum(termt)]; + for (; at; at = at->next) + if (at->termt == termt) + return at->name; + return NULL; +} + +const char *names_videoterminal(uint16_t termt) +{ + struct videoterminal *vt; + + vt = videoterminals_hash[hashnum(termt)]; + for (; vt; vt = vt->next) + if (vt->termt == termt) + return vt->name; + return NULL; +} + +/* ---------------------------------------------------------------------- */ + +int get_vendor_string(char *buf, size_t size, uint16_t vid) +{ + const char *cp; + + if (size < 1) + return 0; + *buf = 0; + if (!(cp = names_vendor(vid))) + return 0; + return snprintf(buf, size, "%s", cp); +} + +int get_product_string(char *buf, size_t size, uint16_t vid, uint16_t pid) +{ + const char *cp; + + if (size < 1) + return 0; + *buf = 0; + if (!(cp = names_product(vid, pid))) + return 0; + return snprintf(buf, size, "%s", cp); +} + +int get_class_string(char *buf, size_t size, uint8_t cls) +{ + const char *cp; + + if (size < 1) + return 0; + *buf = 0; + if (!(cp = names_class(cls))) + return snprintf(buf, size, "[unknown]"); + return snprintf(buf, size, "%s", cp); +} + +int get_subclass_string(char *buf, size_t size, uint8_t cls, uint8_t subcls) +{ + const char *cp; + + if (size < 1) + return 0; + *buf = 0; + if (!(cp = names_subclass(cls, subcls))) + return snprintf(buf, size, "[unknown]"); + return snprintf(buf, size, "%s", cp); +} + +/* + * Attempt to get friendly vendor and product names from the udev hwdb. If + * either or both are not present, instead populate those from the device's + * own string descriptors. + */ +void get_vendor_product_with_fallback(char *vendor, int vendor_len, + char *product, int product_len, + libusb_device *dev) +{ + struct libusb_device_descriptor desc; + char sysfs_name[PATH_MAX]; + bool have_vendor, have_product; + + libusb_get_device_descriptor(dev, &desc); + + /* set to "[unknown]" by default unless something below finds a string */ + strncpy(vendor, "[unknown]", vendor_len); + strncpy(product, "[unknown]", product_len); + + have_vendor = !!get_vendor_string(vendor, vendor_len, desc.idVendor); + have_product = !!get_product_string(product, product_len, + desc.idVendor, desc.idProduct); + + if (have_vendor && have_product) + return; + + if (get_sysfs_name(sysfs_name, sizeof(sysfs_name), dev) >= 0) { + if (!have_vendor) + read_sysfs_prop(vendor, vendor_len, sysfs_name, "manufacturer"); + if (!have_product) + read_sysfs_prop(product, product_len, sysfs_name, "product"); + } +} + +/* ---------------------------------------------------------------------- */ + +static int hash_audioterminal(struct audioterminal *at) +{ + struct audioterminal *at_old; + unsigned int h = hashnum(at->termt); + + for (at_old = audioterminals_hash[h]; at_old; at_old = at_old->next) + if (at_old->termt == at->termt) + return -1; + at->next = audioterminals_hash[h]; + audioterminals_hash[h] = at; + return 0; +} + +static int hash_audioterminals(void) +{ + int r = 0, i, k; + + for (i = 0; audioterminals[i].name; i++) + { + k = hash_audioterminal(&audioterminals[i]); + if (k < 0) + r = k; + } + + return r; +} + +static int hash_videoterminal(struct videoterminal *vt) +{ + struct videoterminal *vt_old; + unsigned int h = hashnum(vt->termt); + + for (vt_old = videoterminals_hash[h]; vt_old; vt_old = vt_old->next) + if (vt_old->termt == vt->termt) + return -1; + vt->next = videoterminals_hash[h]; + videoterminals_hash[h] = vt; + return 0; +} + +static int hash_videoterminals(void) +{ + int r = 0, i, k; + + for (i = 0; videoterminals[i].name; i++) + { + k = hash_videoterminal(&videoterminals[i]); + if (k < 0) + r = k; + } + + return r; +} + +static int hash_genericstrtable(struct genericstrtable *t[HASHSZ], + struct genericstrtable *g) +{ + struct genericstrtable *g_old; + unsigned int h = hashnum(g->num); + + for (g_old = t[h]; g_old; g_old = g_old->next) + if (g_old->num == g->num) + return -1; + g->next = t[h]; + t[h] = g; + return 0; +} + +#define HASH_EACH(array, hash) \ + for (i = 0; array[i].name; i++) { \ + k = hash_genericstrtable(hash, &array[i]); \ + if (k < 0) { \ + r = k; \ + }\ + } + +static int hash_tables(void) +{ + int r = 0, k, i; + + k = hash_audioterminals(); + if (k < 0) + r = k; + + k = hash_videoterminals(); + if (k < 0) + r = k; + + HASH_EACH(hiddescriptors, hiddescriptors_hash); + + HASH_EACH(reports, reports_hash); + + HASH_EACH(huts, huts_hash); + + HASH_EACH(hutus, hutus_hash); + + HASH_EACH(langids, langids_hash); + + HASH_EACH(physdess, physdess_hash); + + HASH_EACH(biass, biass_hash); + + HASH_EACH(countrycodes, countrycodes_hash); + + return r; +} + +/* ---------------------------------------------------------------------- */ + +/* +static void print_tables(void) +{ + int i; + struct audioterminal *at; + struct videoterminal *vt; + struct genericstrtable *li; + struct genericstrtable *hu; + + + printf("--------------------------------------------\n"); + printf("\t\t Audio Terminals\n"); + printf("--------------------------------------------\n"); + + for (i = 0; i < HASHSZ; i++) { + printf("hash: %d\n", i); + at = audioterminals_hash[i]; + for (; at; at = at->next) + printf("\tentry: %s\n", at->name); + } + + printf("--------------------------------------------\n"); + printf("\t\t Video Terminals\n"); + printf("--------------------------------------------\n"); + + for (i = 0; i < HASHSZ; i++) { + printf("hash: %d\n", i); + vt = videoterminals_hash[i]; + for (; vt; vt = vt->next) + printf("\tentry: %s\n", vt->name); + } + + printf("--------------------------------------------\n"); + printf("\t\t Languages\n"); + printf("--------------------------------------------\n"); + + for (i = 0; i < HASHSZ; i++) { + li = langids_hash[i]; + if (li) + printf("hash: %d\n", i); + for (; li; li = li->next) + printf("\tid: %x, entry: %s\n", li->num, li->name); + } + + printf("--------------------------------------------\n"); + printf("\t\t Conutry Codes\n"); + printf("--------------------------------------------\n"); + + for (i = 0; i < HASHSZ; i++) { + hu = countrycodes_hash[i]; + if (hu) + printf("hash: %d\n", i); + for (; hu; hu = hu->next) + printf("\tid: %x, entry: %s\n", hu->num, hu->name); + } + + printf("--------------------------------------------\n"); +} +*/ + +int names_init(void) +{ + int r; + + udev = udev_new(); + if (!udev) + r = -1; + else { + hwdb = udev_hwdb_new(udev); + if (!hwdb) + r = -1; + } + + r = hash_tables(); + + return r; +} + +void names_exit(void) +{ + hwdb = udev_hwdb_unref(hwdb); + udev = udev_unref(udev); +} |