]> mj.ucw.cz Git - pciutils.git/commitdiff
New names.c
authorMartin Mares <mj@ucw.cz>
Sat, 10 Sep 2005 12:09:51 +0000 (12:09 +0000)
committerMartin Mares <mj@ucw.cz>
Fri, 5 May 2006 12:18:31 +0000 (14:18 +0200)
git-archimport-id: mj@ucw.cz--public/pciutils--main--2.2--patch-79

ChangeLog
TODO
lib/names.c
lib/pci.h
lspci.c

index 970fc77099f79626893f0c549e30ea7031f3bf81..6eb55c8007ca7c1785b6ba2cf961285bfa620dd8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2005-09-10  Martin Mares  <mj@ucw.cz>
+
+       * lib/names.c: Rewritten the name database module almost from scratch.
+       Everything is much cleaner and there are hopefully no more memory leaks;
+       pci_lookup_name() now uses varargs in a backward compatible fashion.
+       Introduced PCI_LOOKUP_NO_NUMBERS.
+
+       The new code supports subsystem entries not tied to a specific device.
+       I had to extend the format of pci.ids in order to support it, so I have
+       extended the idea of the "C" (class) blocks and introduced "S" blocks
+       for subsystems. To avoid doing more incompatible changes in the future,
+       the parser skips unknown single-letter blocks without reporting errors.
+
 2005-08-23  Martin Mares  <mj@ucw.cz>
 
        * Released as 2.1.99-test9.
diff --git a/TODO b/TODO
index 9c2e89f8fcef38d1df98a0b9fe1badd061d44d61..b92f6c817603b7a9114cd0051eb037a01749f7e9 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,15 +1,14 @@
-- names.c: remove memory leaks
-- names.c: reading of separate subsystem entries
-
 - update the web page
 
+- kill "word"
+
 - some extended capabilities are currently only partially decoded
 - finish PCI-X 2.0 capabilities
 - finish PCI Express support
 - reading of VPD
 - change machine-readable output?
 - "Class %04x" -> "%04x"
-- merge pcimodules?
+- class 0805?
 
 PCIIDS:
 - another mirror at Atrey?
index 01f141781dd0ba6ef6c5d2b9af0a829b6be4a5ef..0a88d62c0a91befe0caca91db065902975003bce 100644 (file)
 /*
  *     The PCI Library -- ID to Name Translation
  *
- *     Copyright (c) 1997--2002 Martin Mares <mj@ucw.cz>
+ *     Copyright (c) 1997--2005 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 <stdarg.h>
 #include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
 #include <errno.h>
 
 #include "internal.h"
 
-struct nl_entry {
-  struct nl_entry *next;
-  word id1, id2, id3, id4;
-  int cat;
-  byte *name;
+struct id_entry {
+  struct id_entry *next;
+  u32 id12, id34;
+  byte cat;
+  byte name[1];
 };
 
-#define NL_VENDOR 0
-#define NL_DEVICE 1
-#define NL_SUBSYSTEM 2
-#define NL_CLASS 3
-#define NL_SUBCLASS 4
-#define NL_PROGIF 5
+enum id_entry_type {
+  ID_UNKNOWN,
+  ID_VENDOR,
+  ID_DEVICE,
+  ID_SUBSYSTEM,
+  ID_GEN_SUBSYSTEM,
+  ID_CLASS,
+  ID_SUBCLASS,
+  ID_PROGIF
+};
+
+struct id_bucket {
+  struct id_bucket *next;
+  unsigned int full;
+};
 
-#define HASH_SIZE 1024
+#define MAX_LINE 1024
+#define BUCKET_SIZE 8192
+#define HASH_SIZE 4099
 
-static inline unsigned int nl_calc_hash(int cat, int id1, int id2, int id3, int id4)
+#ifdef __GNUC__FIXME
+#define BUCKET_ALIGNMENT __alignof__(struct id_bucket)
+#else
+union id_align {
+  struct id_bucket *next;
+  unsigned int full;
+};
+#define BUCKET_ALIGNMENT sizeof(union id_align)
+#endif
+#define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT)
+
+static void *id_alloc(struct pci_access *a, unsigned int size)
 {
-  unsigned int h;
+  struct id_bucket *buck = a->current_id_bucket;
+  unsigned int pos;
+  if (!buck || buck->full + size > BUCKET_SIZE)
+    {
+      buck = pci_malloc(a, BUCKET_SIZE);
+      buck->next = a->current_id_bucket;
+      a->current_id_bucket = buck;
+      buck->full = BUCKET_ALIGN(sizeof(struct id_bucket));
+    }
+  pos = buck->full;
+  buck->full = BUCKET_ALIGN(buck->full + size);
+  return (byte *)buck + pos;
+}
 
-  h = id1 ^ id2 ^ id3 ^ id4 ^ (cat << 5);
-  h += (h >> 6);
-  return h & (HASH_SIZE-1);
+static inline u32 id_pair(unsigned int x, unsigned int y)
+{
+  return ((x << 16) | y);
 }
 
-static struct nl_entry *nl_lookup(struct pci_access *a, int num, int cat, int id1, int id2, int id3, int id4)
+static inline unsigned int id_hash(int cat, u32 id12, u32 id34)
 {
   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))
+  h = id12 ^ (id34 << 3) ^ (cat << 5);
+  return h % HASH_SIZE;
+}
+
+static struct id_entry *id_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
+{
+  struct id_entry *n;
+  u32 id12 = id_pair(id1, id2);
+  u32 id34 = id_pair(id3, id4);
+
+  n = a->id_hash[id_hash(cat, id12, id34)];
+  while (n && (n->id12 != id12 || n->id34 != id34 || 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, byte *text)
+static int id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, byte *text)
 {
-  unsigned int h = nl_calc_hash(cat, id1, id2, id3, id4);
-  struct nl_entry *n = a->nl_hash[h];
+  u32 id12 = id_pair(id1, id2);
+  u32 id34 = id_pair(id3, id4);
+  unsigned int h = id_hash(cat, id12, id34);
+  struct id_entry *n = a->id_hash[h];
+  int len = strlen(text);
 
-  while (n && (n->id1 != id1 || n->id2 != id2 || n->id3 != id3 || n->id4 != id4 || n->cat != cat))
+  while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat))
     n = n->next;
   if (n)
     return 1;
-  n = pci_malloc(a, sizeof(struct nl_entry));
-  n->id1 = id1;
-  n->id2 = id2;
-  n->id3 = id3;
-  n->id4 = id4;
+  n = id_alloc(a, sizeof(struct id_entry) + len);
+  n->id12 = id12;
+  n->id34 = id34;
   n->cat = cat;
-  n->name = text;
-  n->next = a->nl_hash[h];
-  a->nl_hash[h] = n;
+  memcpy(n->name, text, len+1);
+  n->next = a->id_hash[h];
+  a->id_hash[h] = n;
   return 0;
 }
 
-static void
-err_name_list(struct pci_access *a, char *msg)
+static int id_hex(byte *p, int cnt)
 {
-  a->error("%s: %s: %s\n", a->id_file_name, msg, strerror(errno));
+  int x = 0;
+  while (cnt--)
+    {
+      x <<= 4;
+      if (*p >= '0' && *p <= '9')
+       x += (*p - '0');
+      else if (*p >= 'a' && *p <= 'f')
+       x += (*p - 'a' + 10);
+      else if (*p >= 'A' && *p <= 'F')
+       x += (*p - 'A' + 10);
+      else
+       return -1;
+      p++;
+    }
+  return x;
 }
 
-static void
-parse_name_list(struct pci_access *a)
+static inline int id_white_p(int c)
 {
-  byte *p = a->nl_list;
-  byte *q, *r;
-  int lino = 0;
-  unsigned int id1=0, id2=0, id3=0, id4=0;
+  return (c == ' ') || (c == '\t');
+}
+
+static const char *id_parse_list(struct pci_access *a, FILE *f, int *lino)
+{
+  byte line[MAX_LINE];
+  byte *p;
+  int id1=0, id2=0, id3=0, id4=0;
   int cat = -1;
+  int nest;
+  static const char parse_error[] = "Parse error";
 
-  while (*p)
+  *lino = 0;
+  while (fgets(line, sizeof(line), f))
     {
-      lino++;
-      q = p;
-      while (*p && *p != '\n')
+      (*lino)++;
+      p = line;
+      while (*p && *p != '\n' && *p != '\r')
        p++;
-      if (*p == '\n')
-       *p++ = 0;
-      if (!*q || *q == '#')
+      if (!*p && !feof(f))
+       return "Line too long";
+      *p = 0;
+      if (p > line && (p[-1] == ' ' || p[-1] == '\t'))
+       *--p = 0;
+
+      p = line;
+      while (id_white_p(*p))
+       p++;
+      if (!*p || *p == '#')
        continue;
-      r = p;
-      while (r > q && r[-1] == ' ')
-       *--r = 0;
-      r = q;
-      while (*q == '\t')
-       q++;
-      if (q == r)
+
+      p = line;
+      while (*p == '\t')
+       p++;
+      nest = p - line;
+
+      if (!nest)                                       /* Top-level entries */
        {
-         if (q[0] == 'C' && q[1] == ' ')
+         if (p[0] == 'C' && p[1] == ' ')               /* Class block */
            {
-             if (strlen(q+2) < 3 ||
-                 q[4] != ' ' ||
-                 sscanf(q+2, "%x", &id1) != 1)
-               goto parserr;
-             cat = NL_CLASS;
+             if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4]))
+               return parse_error;
+             cat = ID_CLASS;
+             p += 5;
+           }
+         else if (p[0] == 'S' && p[1] == ' ')
+           {                                           /* Generic subsystem block */
+             if ((id1 = id_hex(p+2, 4)) < 0 || p[6])
+               return parse_error;
+             if (!id_lookup(a, ID_VENDOR, id1, 0, 0, 0))
+               return "Vendor does not exist";
+             cat = ID_GEN_SUBSYSTEM;
+             continue;
+           }
+         else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ')
+           {                                           /* Unrecognized block (RFU) */
+             cat = ID_UNKNOWN;
+             continue;
            }
-         else
+         else                                          /* Vendor ID */
            {
-             if (strlen(q) < 5 ||
-                 q[4] != ' ' ||
-                 sscanf(q, "%x", &id1) != 1)
-               goto parserr;
-             cat = NL_VENDOR;
+             if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
+               return parse_error;
+             cat = ID_VENDOR;
+             p += 5;
            }
          id2 = id3 = id4 = 0;
-         q += 4;
        }
-      else if (q == r+1) 
+      else if (cat == ID_UNKNOWN)                      /* Nested entries in RFU blocks are skipped */
+       continue;
+      else if (nest == 1)                              /* Nesting level 1 */
        switch (cat)
          {
-         case NL_VENDOR:
-         case NL_DEVICE:
-         case NL_SUBSYSTEM:
-           if (sscanf(q, "%x", &id2) != 1 || q[4] != ' ')
-             goto parserr;
-           q += 5;
-           cat = NL_DEVICE;
+         case ID_VENDOR:
+         case ID_DEVICE:
+         case ID_SUBSYSTEM:
+           if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
+             return parse_error;
+           p += 5;
+           cat = ID_DEVICE;
+           id3 = id4 = 0;
+           break;
+         case ID_GEN_SUBSYSTEM:
+           if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
+             return parse_error;
+           p += 5;
            id3 = id4 = 0;
            break;
-         case NL_CLASS:
-         case NL_SUBCLASS:
-         case NL_PROGIF:
-           if (sscanf(q, "%x", &id2) != 1 || q[2] != ' ')
-             goto parserr;
-           q += 3;
-           cat = NL_SUBCLASS;
+         case ID_CLASS:
+         case ID_SUBCLASS:
+         case ID_PROGIF:
+           if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
+             return parse_error;
+           p += 3;
+           cat = ID_SUBCLASS;
            id3 = id4 = 0;
            break;
          default:
-           goto parserr;
+           return parse_error;
          }
-      else if (q == r+2)
+      else if (nest == 2)                              /* Nesting level 2 */
        switch (cat)
          {
-         case NL_DEVICE:
-         case NL_SUBSYSTEM:
-           if (sscanf(q, "%x%x", &id3, &id4) != 2 || q[9] != ' ')
-             goto parserr;
-           q += 10;
-           cat = NL_SUBSYSTEM;
+         case ID_DEVICE:
+         case ID_SUBSYSTEM:
+           if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9]))
+             return parse_error;
+           p += 10;
+           cat = ID_SUBSYSTEM;
            break;
-         case NL_CLASS:
-         case NL_SUBCLASS:
-         case NL_PROGIF:
-           if (sscanf(q, "%x", &id3) != 1 || q[2] != ' ')
-             goto parserr;
-           q += 3;
-           cat = NL_PROGIF;
+         case ID_CLASS:
+         case ID_SUBCLASS:
+         case ID_PROGIF:
+           if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
+             return parse_error;
+           p += 3;
+           cat = ID_PROGIF;
            id4 = 0;
            break;
          default:
-           goto parserr;
+           return parse_error;
          }
-      else
-       goto parserr;
-      while (*q == ' ')
-       q++;
-      if (!*q)
-       goto parserr;
-      if (nl_add(a, cat, id1, id2, id3, id4, q))
-       a->error("%s, line %d: duplicate entry", a->id_file_name, lino);
+      else                                             /* Nesting level 3 or more */
+       return parse_error;
+      while (id_white_p(*p))
+       p++;
+      if (!*p)
+       return parse_error;
+      if (id_insert(a, cat, id1, id2, id3, id4, p))
+       return "Duplicate entry";
     }
-  return;
-
-parserr:
-  a->error("%s, line %d: parse error", a->id_file_name, lino);
+  return NULL;
 }
 
-static void
-load_name_list(struct pci_access *a)
+int
+pci_load_name_list(struct pci_access *a)
 {
-  int fd;
-  struct stat st;
+  FILE *f;
+  int lino;
+  const char *err;
 
-  fd = open(a->id_file_name, O_RDONLY);
-  if (fd < 0)
-    {
-      a->numeric_ids = 1;
-      return;
-    }
-  if (fstat(fd, &st) < 0)
-    err_name_list(a, "stat");
-  a->nl_list = pci_malloc(a, st.st_size + 1);
-  if (read(fd, a->nl_list, st.st_size) != st.st_size)
-    err_name_list(a, "read");
-  a->nl_list[st.st_size] = 0;
-  a->nl_hash = pci_malloc(a, sizeof(struct nl_entry *) * HASH_SIZE);
-  bzero(a->nl_hash, sizeof(struct nl_entry *) * HASH_SIZE);
-  parse_name_list(a);
-  close(fd);
+  pci_free_name_list(a);
+  if (!(f = fopen(a->id_file_name, "r")))
+    return 0;
+  a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE);
+  bzero(a->id_hash, sizeof(struct id_entry *) * HASH_SIZE);
+  err = id_parse_list(a, f, &lino);
+  if (!err && ferror(f))
+    err = "I/O error";
+  fclose(f);
+  if (err)
+    a->error("%s at %s, line %d\n", err, a->id_file_name, lino);
+  return 1;
 }
 
 void
 pci_free_name_list(struct pci_access *a)
 {
-  pci_mfree(a->nl_list);
-  a->nl_list = NULL;
-  pci_mfree(a->nl_hash);
-  a->nl_hash = NULL;
+  pci_mfree(a->id_hash);
+  a->id_hash = NULL;
+  while (a->current_id_bucket)
+    {
+      struct id_bucket *buck = a->current_id_bucket;
+      a->current_id_bucket = buck->next;
+      pci_mfree(buck);
+    }
+}
+
+static struct id_entry *id_lookup_subsys(struct pci_access *a, int iv, int id, int isv, int isd)
+{
+  struct id_entry *d = NULL;
+  if (iv > 0 && id > 0)                                                /* Per-device lookup */
+    d = id_lookup(a, ID_SUBSYSTEM, iv, id, isv, isd);
+  if (!d)                                                      /* Generic lookup */
+    d = id_lookup(a, ID_GEN_SUBSYSTEM, isv, isd, 0, 0);
+  if (!d && iv == isv && id == isd)                            /* Check for subsystem == device */
+    d = id_lookup(a, ID_DEVICE, iv, id, 0, 0);
+  return d;
 }
 
 char *
-pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
+pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...)
 {
-  int num = a->numeric_ids;
-  int res;
-  struct nl_entry *n;
+  va_list args;
+  int num, res, synth;
+  struct id_entry *v, *d, *cls, *pif;
+  int iv, id, isv, isd, icls, ipif;
+
+  va_start(args, flags);
 
-  if (flags & PCI_LOOKUP_NUMERIC)
+  num = 0;
+  if ((flags & PCI_LOOKUP_NUMERIC) || a->numeric_ids)
     {
-      flags &= PCI_LOOKUP_NUMERIC;
+      flags &= ~PCI_LOOKUP_NUMERIC;
       num = 1;
     }
-  if (!a->nl_hash && !num)
+  else if (!a->id_hash)
     {
-      load_name_list(a);
-      num = a->numeric_ids;
+      if (!pci_load_name_list(a))
+       num = a->numeric_ids = 1;
     }
+
+  if (flags & PCI_LOOKUP_NO_NUMBERS)
+    {
+      flags &= ~PCI_LOOKUP_NO_NUMBERS;
+      synth = 0;
+      if (num)
+       return NULL;
+    }
+  else
+    synth = 1;
+
   switch (flags)
     {
     case PCI_LOOKUP_VENDOR:
-      if (n = nl_lookup(a, num, NL_VENDOR, arg1, 0, 0, 0))
-       return n->name;
+      iv = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%04x", iv);
+      else if (v = id_lookup(a, ID_VENDOR, iv, 0, 0, 0))
+       return v->name;
       else
-       res = snprintf(buf, size, "%04x", arg1);
+       res = snprintf(buf, size, "Unknown vendor %04x", iv);
       break;
     case PCI_LOOKUP_DEVICE:
-      if (n = nl_lookup(a, num, NL_DEVICE, arg1, arg2, 0, 0))
-       return n->name;
+      iv = va_arg(args, int);
+      id = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%04x", id);
+      else if (d = id_lookup(a, ID_DEVICE, iv, id, 0, 0))
+       return d->name;
+      else if (synth)
+       res = snprintf(buf, size, "Unknown device %04x", id);
       else
-       res = snprintf(buf, size, "%04x", arg2);
+       return NULL;
       break;
     case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE:
-      if (!num)
+      iv = va_arg(args, int);
+      id = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%04x:%04x", iv, id);
+      else
        {
-         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(buf, size, "Unknown device %04x:%04x", arg1, arg2);
-         else if (!e2)
-           res = snprintf(buf, size, "%s: Unknown device %04x", e->name, arg2);
-         else
-           res = snprintf(buf, size, "%s %s", e->name, e2->name);
+         v = id_lookup(a, ID_VENDOR, iv, 0, 0, 0);
+         d = id_lookup(a, ID_DEVICE, iv, id, 0, 0);
+         if (v && d)
+           res = snprintf(buf, size, "%s %s", v->name, d->name);
+         else if (!synth)
+           return NULL;
+         else if (!v)
+           res = snprintf(buf, size, "Unknown device %04x:%04x", iv, id);
+         else  /* !d */
+           res = snprintf(buf, size, "%s Unknown device %04x", v->name, id);
        }
-      else
-       res = snprintf(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;
+    case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR:
+      isv = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%04x", isv);
+      else if (v = id_lookup(a, ID_VENDOR, isv, 0, 0, 0))
+       return v->name;
+      else if (synth)
+       res = snprintf(buf, size, "Unknown vendor %04x", isv);
       else
-       res = snprintf(buf, size, "%04x", arg3);
+       return NULL;
       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;
+    case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE:
+      iv = va_arg(args, int);
+      id = va_arg(args, int);
+      isv = va_arg(args, int);
+      isd = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%04x", isd);
+      else if (d = id_lookup_subsys(a, iv, id, isv, isd))
+       return d->name;
+      else if (synth)
+       res = snprintf(buf, size, "Unknown device %04x", isd);
       else
-       res = snprintf(buf, size, "%04x", arg4);
+       return NULL;
       break;
     case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM:
-      if (!num)
+      iv = va_arg(args, int);
+      id = va_arg(args, int);
+      isv = va_arg(args, int);
+      isd = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%04x:%04x", isv, isd);
+      else
        {
-         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(buf, size, "Unknown device %04x:%04x", arg3, arg4);
-         else if (!e2)
-           res = snprintf(buf, size, "%s: Unknown device %04x", e->name, arg4);
-         else
-           res = snprintf(buf, size, "%s %s", e->name, e2->name);
+         v = id_lookup(a, ID_VENDOR, isv, 0, 0, 0);
+         d = id_lookup_subsys(a, iv, id, isv, isd);
+         if (v && d)
+           res = snprintf(buf, size, "%s %s", v->name, d->name);
+         else if (!synth)
+           return NULL;
+         else if (!v)
+           res = snprintf(buf, size, "Unknown device %04x:%04x", isv, isd);
+         else /* !d */
+           res = snprintf(buf, size, "%s Unknown device %04x", v->name, isd);
        }
-      else
-       res = snprintf(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(buf, size, "%s [%04x]", n->name, arg1);
+      icls = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%04x", icls);
+      else if (cls = id_lookup(a, ID_SUBCLASS, icls >> 8, icls & 0xff, 0, 0))
+       return cls->name;
+      else if (cls = id_lookup(a, ID_CLASS, icls, 0, 0, 0))
+       res = snprintf(buf, size, "%s [%04x]", cls->name, icls);
+      else if (synth)
+       res = snprintf(buf, size, "Class %04x", icls);
       else
-       res = snprintf(buf, size, "Class %04x", arg1);
+       return NULL;
       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)
+      icls = va_arg(args, int);
+      ipif = va_arg(args, int);
+      if (num)
+       res = snprintf(buf, size, "%02x", ipif);
+      else if (pif = id_lookup(a, ID_PROGIF, icls >> 8, icls & 0xff, ipif, 0))
+       return pif->name;
+      else if (icls == 0x0101 && !(ipif & 0x70))
        {
          /* IDE controllers have complex prog-if semantics */
-         if (arg2 & 0x70)
-           return NULL;
          res = snprintf(buf, size, "%s%s%s%s%s",
-                        (arg2 & 0x80) ? "Master " : "",
-                        (arg2 & 0x08) ? "SecP " : "",
-                        (arg2 & 0x04) ? "SecO " : "",
-                        (arg2 & 0x02) ? "PriP " : "",
-                        (arg2 & 0x01) ? "PriO " : "");
-         if (res)
+                        (ipif & 0x80) ? "Master " : "",
+                        (ipif & 0x08) ? "SecP " : "",
+                        (ipif & 0x04) ? "SecO " : "",
+                        (ipif & 0x02) ? "PriP " : "",
+                        (ipif & 0x01) ? "PriO " : "");
+         if (res > 0 && res < size)
            buf[--res] = 0;
-         break;
        }
-      return NULL;
+      else if (synth)
+       res = snprintf(buf, size, "ProgIf %02x", ipif);
+      else
+       return NULL;
+      break;
     default:
       return "<pci_lookup_name: invalid request>";
     }
-  return (res == size) ? "<too-large>" : buf;
+  if (res < 0 || res >= size)
+    return "<pci_lookup_name: buffer too small>";
+  else
+    return buf;
 }
index 3d8f8658a015c3ef0100ad40e8be5ae0002d3e49..c92df1491f8d69227155dadf28ba9adc278203f6 100644 (file)
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -1,7 +1,7 @@
 /*
  *     The PCI Library
  *
- *     Copyright (c) 1997--2004 Martin Mares <mj@ucw.cz>
+ *     Copyright (c) 1997--2005 Martin Mares <mj@ucw.cz>
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -18,7 +18,6 @@
  */
 
 struct pci_methods;
-struct nl_entry;
 
 enum pci_access_type {
   /* Known access methods, remember to update access.c as well */
@@ -53,8 +52,8 @@ struct pci_access {
 
   /* Fields used internally: */
   struct pci_methods *methods;
-  char *nl_list;                       /* Name list cache */
-  struct nl_entry **nl_hash;
+  struct id_entry **id_hash;           /* names.c */
+  struct id_bucket *current_id_bucket;
   int fd;                              /* proc: fd */
   int fd_rw;                           /* proc: fd opened read-write */
   struct pci_dev *cached_dev;          /* proc: device the fd is for */
@@ -136,17 +135,34 @@ char *pci_filter_parse_id(struct pci_filter *, char *);
 int pci_filter_match(struct pci_filter *, struct pci_dev *);
 
 /*
- *     Device names
+ *     Conversion of PCI ID's to names (according to the pci.ids file)
+ *
+ *     Call pci_lookup_name() to identify different types of ID's:
+ *
+ *     VENDOR                          (vendorID) -> vendor
+ *     DEVICE                          (vendorID, deviceID) -> device
+ *     VENDOR | DEVICE                 (vendorID, deviceID) -> combined vendor and device
+ *     SUBSYSTEM | VENDOR              (subvendorID) -> subsystem vendor
+ *     SUBSYSTEM | DEVICE              (vendorID, deviceID, subvendorID, subdevID) -> subsystem device
+ *     SUBSYSTEM | VENDOR | DEVICE     (vendorID, deviceID, subvendorID, subdevID) -> combined subsystem v+d
+ *     SUBSYSTEM | ...                 (-1, -1, subvendorID, subdevID) -> generic subsystem
+ *     CLASS                           (classID) -> class
+ *     PROGIF                          (classID, progif) -> programming interface
  */
 
-char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, u32 arg1, u32 arg2, u32 arg3, u32 arg4);
-void pci_free_name_list(struct pci_access *a);
+char *pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...);
 
-#define PCI_LOOKUP_VENDOR 1
-#define PCI_LOOKUP_DEVICE 2
-#define PCI_LOOKUP_CLASS 4
-#define PCI_LOOKUP_SUBSYSTEM 8
-#define PCI_LOOKUP_PROGIF 16
-#define PCI_LOOKUP_NUMERIC 0x10000
+int pci_load_name_list(struct pci_access *a);  /* Called automatically by pci_lookup_*() when needed; returns success */
+void pci_free_name_list(struct pci_access *a); /* Called automatically by pci_cleanup() */
+
+enum pci_lookup_mode {
+  PCI_LOOKUP_VENDOR = 1,               /* Vendor name (args: vendorID) */
+  PCI_LOOKUP_DEVICE = 2,               /* Device name (args: vendorID, deviceID) */
+  PCI_LOOKUP_CLASS = 4,                        /* Device class (args: classID) */
+  PCI_LOOKUP_SUBSYSTEM = 8,
+  PCI_LOOKUP_PROGIF = 16,              /* Programming interface (args: classID, prog_if) */
+  PCI_LOOKUP_NUMERIC = 0x10000,                /* Want only formatted numbers; default if access->numeric_ids is set */
+  PCI_LOOKUP_NO_NUMBERS = 0x20000      /* Return NULL if not found in the database; default is to print numerically */
+};
 
 #endif
diff --git a/lspci.c b/lspci.c
index b00ea48ad745259cdc55ac79894737df7e624ecc..7295bdd8a3d5eaa1fa9b3fca33002ee382ec2299 100644 (file)
--- a/lspci.c
+++ b/lspci.c
@@ -266,7 +266,7 @@ show_terse(struct device *d)
       char *x;
       c = get_conf_byte(d, PCI_CLASS_PROG);
       x = pci_lookup_name(pacc, devbuf, sizeof(devbuf),
-                         PCI_LOOKUP_PROGIF,
+                         PCI_LOOKUP_PROGIF | PCI_LOOKUP_NO_NUMBERS,
                          get_conf_word(d, PCI_CLASS_DEVICE), c, 0, 0);
       if (c || x)
        {