blob: 9c1ab262866d84da4180ce569fcd6069724ac4b6 [file] [log] [blame]
/*
* $Id: names.c,v 1.2.2.1 2005/04/06 23:18:11 stekloff Exp $
*
* The PCI Library -- ID to Name Translation
*
* Copyright (c) 1997--2002 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include "names.h"
struct nl_entry {
struct nl_entry *next;
unsigned short id1, id2, id3, id4;
int cat;
unsigned char *name;
};
#define NL_VENDOR 0
#define NL_DEVICE 1
#define NL_SUBSYSTEM 2
#define NL_CLASS 3
#define NL_SUBCLASS 4
#define NL_PROGIF 5
#define HASH_SIZE 1024
static inline unsigned int nl_calc_hash(int cat, int id1, int id2, int id3, int id4)
{
unsigned int h;
h = id1 ^ id2 ^ id3 ^ id4 ^ (cat << 5);
h += (h >> 6);
return h & (HASH_SIZE-1);
}
static struct nl_entry *nl_lookup(struct pci_access *a, int num, int cat, int id1, int id2, int id3, int id4)
{
unsigned int h;
struct nl_entry *n;
if (num)
return NULL;
h = nl_calc_hash(cat, id1, id2, id3, id4);
n = a->nl_hash[h];
while (n && (n->id1 != id1 || n->id2 != id2 || n->id3 != id3 || n->id4 != id4 || n->cat != cat))
n = n->next;
return n;
}
static int nl_add(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, unsigned char *text)
{
unsigned int h = nl_calc_hash(cat, id1, id2, id3, id4);
struct nl_entry *n = a->nl_hash[h];
while (n && (n->id1 != id1 || n->id2 != id2 || n->id3 != id3 || n->id4 != id4 || n->cat != cat))
n = n->next;
if (n)
return 1;
n = malloc(sizeof(struct nl_entry));
bzero(n, sizeof(struct nl_entry));
n->id1 = id1;
n->id2 = id2;
n->id3 = id3;
n->id4 = id4;
n->cat = cat;
n->name = text;
n->next = a->nl_hash[h];
a->nl_hash[h] = n;
return 0;
}
static void
err_name_list(struct pci_access *a, unsigned char *msg)
{
fprintf(stderr, "%s: %s: %s\n", a->pci_id_file_name, msg, strerror(errno));
}
static void
parse_name_list(struct pci_access *a)
{
unsigned char *p = a->nl_list;
unsigned char *q, *r;
int lino = 0;
unsigned int id1=0, id2=0, id3=0, id4=0;
int cat = -1;
while (*p)
{
lino++;
q = p;
while (*p && *p != '\n')
p++;
if (*p == '\n')
*p++ = 0;
if (!*q || *q == '#')
continue;
r = p;
while (r > q && r[-1] == ' ')
*--r = 0;
r = q;
while (*q == '\t')
q++;
if (q == r)
{
if (q[0] == 'C' && q[1] == ' ')
{
if (strlen((char *)q+2) < 3 ||
q[4] != ' ' ||
sscanf((char *)q+2, "%x", &id1) != 1)
goto parserr;
cat = NL_CLASS;
}
else
{
if (strlen((char *)q) < 5 ||
q[4] != ' ' ||
sscanf((char *)q, "%x", &id1) != 1)
goto parserr;
cat = NL_VENDOR;
}
id2 = id3 = id4 = 0;
q += 4;
}
else if (q == r+1)
switch (cat)
{
case NL_VENDOR:
case NL_DEVICE:
case NL_SUBSYSTEM:
if (sscanf((char *)q, "%x", &id2) != 1 || q[4] != ' ')
goto parserr;
q += 5;
cat = NL_DEVICE;
id3 = id4 = 0;
break;
case NL_CLASS:
case NL_SUBCLASS:
case NL_PROGIF:
if (sscanf((char *)q, "%x", &id2) != 1 || q[2] != ' ')
goto parserr;
q += 3;
cat = NL_SUBCLASS;
id3 = id4 = 0;
break;
default:
goto parserr;
}
else if (q == r+2)
switch (cat)
{
case NL_DEVICE:
case NL_SUBSYSTEM:
if (sscanf((char *)q, "%x%x", &id3, &id4) != 2 || q[9] != ' ')
goto parserr;
q += 10;
cat = NL_SUBSYSTEM;
break;
case NL_CLASS:
case NL_SUBCLASS:
case NL_PROGIF:
if (sscanf((char *)q, "%x", &id3) != 1 || q[2] != ' ')
goto parserr;
q += 3;
cat = NL_PROGIF;
id4 = 0;
break;
default:
goto parserr;
}
else
goto parserr;
while (*q == ' ')
q++;
if (!*q)
goto parserr;
if (nl_add(a, cat, id1, id2, id3, id4, q))
fprintf(stderr, "%s, line %d: duplicate entry", a->pci_id_file_name, lino);
}
return;
parserr:
fprintf(stderr, "%s, line %d: parse error", a->pci_id_file_name, lino);
}
static void
load_name_list(struct pci_access *a)
{
int fd;
struct stat st;
fd = open((char *)a->pci_id_file_name, O_RDONLY);
if (fd < 0)
{
a->numeric_ids = 1;
return;
}
if (fstat(fd, &st) < 0)
err_name_list(a, (unsigned char *)"stat");
a->nl_list = malloc(st.st_size + 1);
if (read(fd, a->nl_list, st.st_size) != st.st_size)
err_name_list(a, (unsigned char *)"read");
a->nl_list[st.st_size] = 0;
a->nl_hash = malloc(sizeof(struct nl_entry *) * HASH_SIZE);
bzero(a->nl_hash, sizeof(struct nl_entry *) * HASH_SIZE);
parse_name_list(a);
close(fd);
}
void
pci_free_name_list(struct pci_access *a)
{
int i = 0;
struct nl_entry *n = NULL, *temp = NULL;
free(a->nl_list);
a->nl_list = NULL;
if (a->nl_hash != NULL) {
for (i = 0; i < HASH_SIZE; i++) {
if (a->nl_hash[i] != NULL) {
n = a->nl_hash[i];
do {
temp = n->next;
free (n);
n = temp;
} while (temp != NULL);
}
}
}
free(a->nl_hash);
a->nl_hash = NULL;
}
unsigned char *
pci_lookup_name(struct pci_access *a, unsigned char *buf, int size, int flags, unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4)
{
int num = a->numeric_ids;
int res;
struct nl_entry *n;
if (flags & PCI_LOOKUP_NUMERIC)
{
flags &= PCI_LOOKUP_NUMERIC;
num = 1;
}
if (!a->nl_hash && !num)
{
load_name_list(a);
num = a->numeric_ids;
}
switch (flags)
{
case PCI_LOOKUP_VENDOR:
if ((n = nl_lookup(a, num, NL_VENDOR, arg1, 0, 0, 0)))
return n->name;
else
res = snprintf((char *)buf, size, "%04x", arg1);
break;
case PCI_LOOKUP_DEVICE:
if ((n = nl_lookup(a, num, NL_DEVICE, arg1, arg2, 0, 0)))
return n->name;
else
res = snprintf((char *)buf, size, "%04x", arg2);
break;
case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE:
if (!num)
{
struct nl_entry *e, *e2;
e = nl_lookup(a, 0, NL_VENDOR, arg1, 0, 0, 0);
e2 = nl_lookup(a, 0, NL_DEVICE, arg1, arg2, 0, 0);
if (!e)
res = snprintf((char *)buf, size, "Unknown device %04x:%04x", arg1, arg2);
else if (!e2)
res = snprintf((char *)buf, size, "%s: Unknown device %04x", e->name, arg2);
else
res = snprintf((char *)buf, size, "%s %s", e->name, e2->name);
}
else
res = snprintf((char *)buf, size, "%04x:%04x", arg1, arg2);
break;
case PCI_LOOKUP_VENDOR | PCI_LOOKUP_SUBSYSTEM:
if ((n = nl_lookup(a, num, NL_VENDOR, arg3, 0, 0, 0)))
return n->name;
else
res = snprintf((char *)buf, size, "%04x", arg2);
break;
case PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM:
if ((n = nl_lookup(a, num, NL_SUBSYSTEM, arg1, arg2, arg3, arg4)))
return n->name;
else if (arg1 == arg3 && arg2 == arg4 && (n = nl_lookup(a, num, NL_DEVICE, arg1, arg2, 0, 0)))
return n->name;
else
res = snprintf((char *)buf, size, "%04x", arg4);
break;
case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM:
if (!num)
{
struct nl_entry *e, *e2;
e = nl_lookup(a, 0, NL_VENDOR, arg3, 0, 0, 0);
e2 = nl_lookup(a, 0, NL_SUBSYSTEM, arg1, arg2, arg3, arg4);
if (!e2 && arg1 == arg3 && arg2 == arg4)
/* Cheat for vendors blindly setting subsystem ID same as device ID */
e2 = nl_lookup(a, 0, NL_DEVICE, arg1, arg2, 0, 0);
if (!e)
res = snprintf((char *)buf, size, "Unknown device %04x:%04x", arg3, arg4);
else if (!e2)
res = snprintf((char *)buf, size, "%s: Unknown device %04x", e->name, arg4);
else
res = snprintf((char *)buf, size, "%s %s", e->name, e2->name);
}
else
res = snprintf((char *)buf, size, "%04x:%04x", arg3, arg4);
break;
case PCI_LOOKUP_CLASS:
if ((n = nl_lookup(a, num, NL_SUBCLASS, arg1 >> 8, arg1 & 0xff, 0, 0)))
return n->name;
else if ((n = nl_lookup(a, num, NL_CLASS, arg1, 0, 0, 0)))
res = snprintf((char *)buf, size, "%s [%04x]", n->name, arg1);
else
res = snprintf((char *)buf, size, "Class %04x", arg1);
break;
case PCI_LOOKUP_PROGIF:
if ((n = nl_lookup(a, num, NL_PROGIF, arg1 >> 8, arg1 & 0xff, arg2, 0)))
return n->name;
if (arg1 == 0x0101)
{
/* IDE controllers have complex prog-if semantics */
if (arg2 & 0x70)
return NULL;
res = snprintf((char *)buf, size, "%s%s%s%s%s",
(arg2 & 0x80) ? "Master " : "",
(arg2 & 0x08) ? "SecP " : "",
(arg2 & 0x04) ? "SecO " : "",
(arg2 & 0x02) ? "PriP " : "",
(arg2 & 0x01) ? "PriO " : "");
if (res)
buf[--res] = 0;
break;
}
return NULL;
default:
return (unsigned char *)"<pci_lookup_name: invalid request>";
}
if (res == size)
return (unsigned char *)"<too-large>";
else
return buf;
}