487 lines
10 KiB
C
487 lines
10 KiB
C
|
// 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;
|
||
|
}
|