| /* |
| * $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; |
| } |