usbutils/names.c

453 lines
9.7 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* 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 <linux/limits.h>
#include <libudev.h>
#include "usb-spec.h"
#include "names.h"
#define HASH1 0x10
#define HASH2 0x02
#define HASHSZ 512
#define SYSFS_DEV_ATTR_PATH "/sys/bus/usb/devices/%d-%d/%s"
static unsigned int hashnum(unsigned int num)
{
unsigned int mask1 = HASH1 << 27, mask2 = 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 0;
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 0;
return snprintf(buf, size, "%s", cp);
}
/* ---------------------------------------------------------------------- */
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 read_sysfs_prop(char *buf, size_t size, uint8_t bnum, uint8_t pnum, char *propname)
{
int n, fd;
char path[PATH_MAX];
buf[0] = '\0';
snprintf(path, sizeof(path), SYSFS_DEV_ATTR_PATH, bnum, pnum, propname);
fd = open(path, O_RDONLY);
if (fd == -1)
return 0;
n = read(fd, buf, size);
if (n > 0)
buf[n-1] = '\0'; // Turn newline into null terminator
close(fd);
return 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);
}