// SPDX-License-Identifier: GPL-2.0-or-later /* * names.c -- USB name database manipulation routines * * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) * * Copyright (C) 2005 Takahiro Hirofuchi * - names_deinit() is added. */ #include <sys/types.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 "names.h" #include "usbip_common.h" struct vendor { struct vendor *next; u_int16_t vendorid; char name[1]; }; struct product { struct product *next; u_int16_t vendorid, productid; char name[1]; }; struct class { struct class *next; u_int8_t classid; char name[1]; }; struct subclass { struct subclass *next; u_int8_t classid, subclassid; char name[1]; }; struct protocol { struct protocol *next; u_int8_t classid, subclassid, protocolid; char name[1]; }; struct genericstrtable { struct genericstrtable *next; unsigned int num; char name[1]; }; #define HASH1 0x10 #define HASH2 0x02 #define HASHSZ 16 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 vendor *vendors[HASHSZ] = { NULL, }; static struct product *products[HASHSZ] = { NULL, }; static struct class *classes[HASHSZ] = { NULL, }; static struct subclass *subclasses[HASHSZ] = { NULL, }; static struct protocol *protocols[HASHSZ] = { NULL, }; const char *names_vendor(u_int16_t vendorid) { struct vendor *v; v = vendors[hashnum(vendorid)]; for (; v; v = v->next) if (v->vendorid == vendorid) return v->name; return NULL; } const char *names_product(u_int16_t vendorid, u_int16_t productid) { struct product *p; p = products[hashnum((vendorid << 16) | productid)]; for (; p; p = p->next) if (p->vendorid == vendorid && p->productid == productid) return p->name; return NULL; } const char *names_class(u_int8_t classid) { struct class *c; c = classes[hashnum(classid)]; for (; c; c = c->next) if (c->classid == classid) return c->name; return NULL; } const char *names_subclass(u_int8_t classid, u_int8_t subclassid) { struct subclass *s; s = subclasses[hashnum((classid << 8) | subclassid)]; for (; s; s = s->next) if (s->classid == classid && s->subclassid == subclassid) return s->name; return NULL; } const char *names_protocol(u_int8_t classid, u_int8_t subclassid, u_int8_t protocolid) { struct protocol *p; p = protocols[hashnum((classid << 16) | (subclassid << 8) | protocolid)]; for (; p; p = p->next) if (p->classid == classid && p->subclassid == subclassid && p->protocolid == protocolid) return p->name; return NULL; } /* add a cleanup function by takahiro */ struct pool { struct pool *next; void *mem; }; static struct pool *pool_head; static void *my_malloc(size_t size) { struct pool *p; p = calloc(1, sizeof(struct pool)); if (!p) return NULL; p->mem = calloc(1, size); if (!p->mem) { free(p); return NULL; } p->next = pool_head; pool_head = p; return p->mem; } void names_free(void) { struct pool *pool; if (!pool_head) return; for (pool = pool_head; pool != NULL; ) { struct pool *tmp; if (pool->mem) free(pool->mem); tmp = pool; pool = pool->next; free(tmp); } } static int new_vendor(const char *name, u_int16_t vendorid) { struct vendor *v; unsigned int h = hashnum(vendorid); v = vendors[h]; for (; v; v = v->next) if (v->vendorid == vendorid) return -1; v = my_malloc(sizeof(struct vendor) + strlen(name)); if (!v) return -1; strcpy(v->name, name); v->vendorid = vendorid; v->next = vendors[h]; vendors[h] = v; return 0; } static int new_product(const char *name, u_int16_t vendorid, u_int16_t productid) { struct product *p; unsigned int h = hashnum((vendorid << 16) | productid); p = products[h]; for (; p; p = p->next) if (p->vendorid == vendorid && p->productid == productid) return -1; p = my_malloc(sizeof(struct product) + strlen(name)); if (!p) return -1; strcpy(p->name, name); p->vendorid = vendorid; p->productid = productid; p->next = products[h]; products[h] = p; return 0; } static int new_class(const char *name, u_int8_t classid) { struct class *c; unsigned int h = hashnum(classid); c = classes[h]; for (; c; c = c->next) if (c->classid == classid) return -1; c = my_malloc(sizeof(struct class) + strlen(name)); if (!c) return -1; strcpy(c->name, name); c->classid = classid; c->next = classes[h]; classes[h] = c; return 0; } static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid) { struct subclass *s; unsigned int h = hashnum((classid << 8) | subclassid); s = subclasses[h]; for (; s; s = s->next) if (s->classid == classid && s->subclassid == subclassid) return -1; s = my_malloc(sizeof(struct subclass) + strlen(name)); if (!s) return -1; strcpy(s->name, name); s->classid = classid; s->subclassid = subclassid; s->next = subclasses[h]; subclasses[h] = s; return 0; } static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid, u_int8_t protocolid) { struct protocol *p; unsigned int h = hashnum((classid << 16) | (subclassid << 8) | protocolid); p = protocols[h]; for (; p; p = p->next) if (p->classid == classid && p->subclassid == subclassid && p->protocolid == protocolid) return -1; p = my_malloc(sizeof(struct protocol) + strlen(name)); if (!p) return -1; strcpy(p->name, name); p->classid = classid; p->subclassid = subclassid; p->protocolid = protocolid; p->next = protocols[h]; protocols[h] = p; return 0; } static void parse(FILE *f) { char buf[512], *cp; unsigned int linectr = 0; int lastvendor = -1; int lastclass = -1; int lastsubclass = -1; int lasthut = -1; int lastlang = -1; unsigned int u; while (fgets(buf, sizeof(buf), f)) { linectr++; /* remove line ends */ cp = strchr(buf, '\r'); if (cp) *cp = 0; cp = strchr(buf, '\n'); if (cp) *cp = 0; if (buf[0] == '#' || !buf[0]) continue; cp = buf; if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' && buf[3] == 'S' && buf[4] == 'D' && buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/ buf[7] == ' ') { continue; } if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') { continue; } if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' && buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') { continue; } if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') { lasthut = lastclass = lastvendor = lastsubclass = -1; /* * set 1 as pseudo-id to indicate that the parser is * in a `L' section. */ lastlang = 1; continue; } if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') { /* class spec */ cp = buf+2; while (isspace(*cp)) cp++; if (!isxdigit(*cp)) { err("Invalid class spec at line %u", linectr); continue; } u = strtoul(cp, &cp, 16); while (isspace(*cp)) cp++; if (!*cp) { err("Invalid class spec at line %u", linectr); continue; } if (new_class(cp, u)) err("Duplicate class spec at line %u class %04x %s", linectr, u, cp); dbg("line %5u class %02x %s", linectr, u, cp); lasthut = lastlang = lastvendor = lastsubclass = -1; lastclass = u; continue; } if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) { /* audio terminal type spec */ continue; } if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C' && isspace(buf[3])) { /* HID Descriptor bCountryCode */ continue; } if (isxdigit(*cp)) { /* vendor */ u = strtoul(cp, &cp, 16); while (isspace(*cp)) cp++; if (!*cp) { err("Invalid vendor spec at line %u", linectr); continue; } if (new_vendor(cp, u)) err("Duplicate vendor spec at line %u vendor %04x %s", linectr, u, cp); dbg("line %5u vendor %04x %s", linectr, u, cp); lastvendor = u; lasthut = lastlang = lastclass = lastsubclass = -1; continue; } if (buf[0] == '\t' && isxdigit(buf[1])) { /* product or subclass spec */ u = strtoul(buf+1, &cp, 16); while (isspace(*cp)) cp++; if (!*cp) { err("Invalid product/subclass spec at line %u", linectr); continue; } if (lastvendor != -1) { if (new_product(cp, lastvendor, u)) err("Duplicate product spec at line %u product %04x:%04x %s", linectr, lastvendor, u, cp); dbg("line %5u product %04x:%04x %s", linectr, lastvendor, u, cp); continue; } if (lastclass != -1) { if (new_subclass(cp, lastclass, u)) err("Duplicate subclass spec at line %u class %02x:%02x %s", linectr, lastclass, u, cp); dbg("line %5u subclass %02x:%02x %s", linectr, lastclass, u, cp); lastsubclass = u; continue; } if (lasthut != -1) { /* do not store hut */ continue; } if (lastlang != -1) { /* do not store langid */ continue; } err("Product/Subclass spec without prior Vendor/Class spec at line %u", linectr); continue; } if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) { /* protocol spec */ u = strtoul(buf+2, &cp, 16); while (isspace(*cp)) cp++; if (!*cp) { err("Invalid protocol spec at line %u", linectr); continue; } if (lastclass != -1 && lastsubclass != -1) { if (new_protocol(cp, lastclass, lastsubclass, u)) err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s", linectr, lastclass, lastsubclass, u, cp); dbg("line %5u protocol %02x:%02x:%02x %s", linectr, lastclass, lastsubclass, u, cp); continue; } err("Protocol spec without prior Class and Subclass spec at line %u", linectr); continue; } if (buf[0] == 'H' && buf[1] == 'I' && buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') { continue; } if (buf[0] == 'H' && buf[1] == 'U' && buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') { lastlang = lastclass = lastvendor = lastsubclass = -1; /* * set 1 as pseudo-id to indicate that the parser is * in a `HUT' section. */ lasthut = 1; continue; } if (buf[0] == 'R' && buf[1] == ' ') continue; if (buf[0] == 'V' && buf[1] == 'T') continue; err("Unknown line at line %u", linectr); } } int names_init(char *n) { FILE *f; f = fopen(n, "r"); if (!f) return errno; parse(f); fclose(f); return 0; }