]> mj.ucw.cz Git - pciutils.git/blob - lib/names.c
Added support for compressed pci.ids.
[pciutils.git] / lib / names.c
1 /*
2  *      The PCI Library -- ID to Name Translation
3  *
4  *      Copyright (c) 1997--2006 Martin Mares <mj@ucw.cz>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL.
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <errno.h>
14
15 #include "internal.h"
16
17 #ifdef PCI_COMPRESSED_IDS
18 #include <zlib.h>
19 typedef gzFile pci_file;
20 #define pci_gets(f, l, s)       gzgets(f, l, s)
21 #define pci_eof(f)              gzeof(f)
22
23 static pci_file pci_open(struct pci_access *a)
24 {
25   pci_file result;
26   size_t len;
27   char *new_name;
28
29   result = gzopen(a->id_file_name, "r");
30   if (result)
31     return result;
32   len = strlen(a->id_file_name);
33   if (len >= 3 && memcmp(a->id_file_name + len - 3, ".gz", 3) != 0)
34     return result;
35   new_name = malloc(len - 2);
36   memcpy(new_name, a->id_file_name, len - 3);
37   new_name[len - 3] = 0;
38   pci_set_name_list_path(a, new_name, 1);
39   return gzopen(a->id_file_name, "r");
40 }
41
42 #define pci_close(f)            gzclose(f)
43 #define PCI_ERROR(f, err)                                               \
44         if (!err) {                                                     \
45                 int errnum;                                             \
46                 err = gzerror(f, &errnum);                              \
47                 if (errnum == Z_ERRNO)  err = "I/O error";              \
48                 else if (errnum >= 0)   err = NULL;                     \
49         }
50 #else
51 typedef FILE * pci_file;
52 #define pci_gets(f, l, s)       fgets(l, s, f)
53 #define pci_eof(f)              feof(f)
54 #define pci_open(a)             fopen(a->id_file_name, "r")
55 #define pci_close(f)            fclose(f)
56 #define PCI_ERROR(f, err)       if (!err && ferror(f))  err = "I/O error";
57 #endif
58
59 struct id_entry {
60   struct id_entry *next;
61   u32 id12, id34;
62   byte cat;
63   char name[1];
64 };
65
66 enum id_entry_type {
67   ID_UNKNOWN,
68   ID_VENDOR,
69   ID_DEVICE,
70   ID_SUBSYSTEM,
71   ID_GEN_SUBSYSTEM,
72   ID_CLASS,
73   ID_SUBCLASS,
74   ID_PROGIF
75 };
76
77 struct id_bucket {
78   struct id_bucket *next;
79   unsigned int full;
80 };
81
82 #define MAX_LINE 1024
83 #define BUCKET_SIZE 8192
84 #define HASH_SIZE 4099
85
86 #ifdef __GNUC__
87 #define BUCKET_ALIGNMENT __alignof__(struct id_bucket)
88 #else
89 union id_align {
90   struct id_bucket *next;
91   unsigned int full;
92 };
93 #define BUCKET_ALIGNMENT sizeof(union id_align)
94 #endif
95 #define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT)
96
97 static void *id_alloc(struct pci_access *a, unsigned int size)
98 {
99   struct id_bucket *buck = a->current_id_bucket;
100   unsigned int pos;
101   if (!buck || buck->full + size > BUCKET_SIZE)
102     {
103       buck = pci_malloc(a, BUCKET_SIZE);
104       buck->next = a->current_id_bucket;
105       a->current_id_bucket = buck;
106       buck->full = BUCKET_ALIGN(sizeof(struct id_bucket));
107     }
108   pos = buck->full;
109   buck->full = BUCKET_ALIGN(buck->full + size);
110   return (byte *)buck + pos;
111 }
112
113 static inline u32 id_pair(unsigned int x, unsigned int y)
114 {
115   return ((x << 16) | y);
116 }
117
118 static inline unsigned int id_hash(int cat, u32 id12, u32 id34)
119 {
120   unsigned int h;
121
122   h = id12 ^ (id34 << 3) ^ (cat << 5);
123   return h % HASH_SIZE;
124 }
125
126 static char *id_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
127 {
128   struct id_entry *n;
129   u32 id12 = id_pair(id1, id2);
130   u32 id34 = id_pair(id3, id4);
131
132   if (!a->id_hash)
133     return NULL;
134   n = a->id_hash[id_hash(cat, id12, id34)];
135   while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat))
136     n = n->next;
137   return n ? n->name : NULL;
138 }
139
140 static int id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text)
141 {
142   u32 id12 = id_pair(id1, id2);
143   u32 id34 = id_pair(id3, id4);
144   unsigned int h = id_hash(cat, id12, id34);
145   struct id_entry *n = a->id_hash[h];
146   int len = strlen(text);
147
148   while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat))
149     n = n->next;
150   if (n)
151     return 1;
152   n = id_alloc(a, sizeof(struct id_entry) + len);
153   n->id12 = id12;
154   n->id34 = id34;
155   n->cat = cat;
156   memcpy(n->name, text, len+1);
157   n->next = a->id_hash[h];
158   a->id_hash[h] = n;
159   return 0;
160 }
161
162 static int id_hex(char *p, int cnt)
163 {
164   int x = 0;
165   while (cnt--)
166     {
167       x <<= 4;
168       if (*p >= '0' && *p <= '9')
169         x += (*p - '0');
170       else if (*p >= 'a' && *p <= 'f')
171         x += (*p - 'a' + 10);
172       else if (*p >= 'A' && *p <= 'F')
173         x += (*p - 'A' + 10);
174       else
175         return -1;
176       p++;
177     }
178   return x;
179 }
180
181 static inline int id_white_p(int c)
182 {
183   return (c == ' ') || (c == '\t');
184 }
185
186 static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino)
187 {
188   char line[MAX_LINE];
189   char *p;
190   int id1=0, id2=0, id3=0, id4=0;
191   int cat = -1;
192   int nest;
193   static const char parse_error[] = "Parse error";
194
195   *lino = 0;
196   while (pci_gets(f, line, sizeof(line)))
197     {
198       (*lino)++;
199       p = line;
200       while (*p && *p != '\n' && *p != '\r')
201         p++;
202       if (!*p && !pci_eof(f))
203         return "Line too long";
204       *p = 0;
205       if (p > line && (p[-1] == ' ' || p[-1] == '\t'))
206         *--p = 0;
207
208       p = line;
209       while (id_white_p(*p))
210         p++;
211       if (!*p || *p == '#')
212         continue;
213
214       p = line;
215       while (*p == '\t')
216         p++;
217       nest = p - line;
218
219       if (!nest)                                        /* Top-level entries */
220         {
221           if (p[0] == 'C' && p[1] == ' ')               /* Class block */
222             {
223               if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4]))
224                 return parse_error;
225               cat = ID_CLASS;
226               p += 5;
227             }
228           else if (p[0] == 'S' && p[1] == ' ')
229             {                                           /* Generic subsystem block */
230               if ((id1 = id_hex(p+2, 4)) < 0 || p[6])
231                 return parse_error;
232               if (!id_lookup(a, ID_VENDOR, id1, 0, 0, 0))
233                 return "Vendor does not exist";
234               cat = ID_GEN_SUBSYSTEM;
235               continue;
236             }
237           else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ')
238             {                                           /* Unrecognized block (RFU) */
239               cat = ID_UNKNOWN;
240               continue;
241             }
242           else                                          /* Vendor ID */
243             {
244               if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
245                 return parse_error;
246               cat = ID_VENDOR;
247               p += 5;
248             }
249           id2 = id3 = id4 = 0;
250         }
251       else if (cat == ID_UNKNOWN)                       /* Nested entries in RFU blocks are skipped */
252         continue;
253       else if (nest == 1)                               /* Nesting level 1 */
254         switch (cat)
255           {
256           case ID_VENDOR:
257           case ID_DEVICE:
258           case ID_SUBSYSTEM:
259             if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
260               return parse_error;
261             p += 5;
262             cat = ID_DEVICE;
263             id3 = id4 = 0;
264             break;
265           case ID_GEN_SUBSYSTEM:
266             if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
267               return parse_error;
268             p += 5;
269             id3 = id4 = 0;
270             break;
271           case ID_CLASS:
272           case ID_SUBCLASS:
273           case ID_PROGIF:
274             if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
275               return parse_error;
276             p += 3;
277             cat = ID_SUBCLASS;
278             id3 = id4 = 0;
279             break;
280           default:
281             return parse_error;
282           }
283       else if (nest == 2)                               /* Nesting level 2 */
284         switch (cat)
285           {
286           case ID_DEVICE:
287           case ID_SUBSYSTEM:
288             if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9]))
289               return parse_error;
290             p += 10;
291             cat = ID_SUBSYSTEM;
292             break;
293           case ID_CLASS:
294           case ID_SUBCLASS:
295           case ID_PROGIF:
296             if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
297               return parse_error;
298             p += 3;
299             cat = ID_PROGIF;
300             id4 = 0;
301             break;
302           default:
303             return parse_error;
304           }
305       else                                              /* Nesting level 3 or more */
306         return parse_error;
307       while (id_white_p(*p))
308         p++;
309       if (!*p)
310         return parse_error;
311       if (id_insert(a, cat, id1, id2, id3, id4, p))
312         return "Duplicate entry";
313     }
314   return NULL;
315 }
316
317 int
318 pci_load_name_list(struct pci_access *a)
319 {
320   pci_file f;
321   int lino;
322   const char *err;
323
324   pci_free_name_list(a);
325   a->hash_load_failed = 1;
326   if (!(f = pci_open(a)))
327     return 0;
328   a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE);
329   bzero(a->id_hash, sizeof(struct id_entry *) * HASH_SIZE);
330   err = id_parse_list(a, f, &lino);
331   PCI_ERROR(f, err);
332   pci_close(f);
333   if (err)
334     a->error("%s at %s, line %d\n", err, a->id_file_name, lino);
335   a->hash_load_failed = 0;
336   return 1;
337 }
338
339 void
340 pci_free_name_list(struct pci_access *a)
341 {
342   pci_mfree(a->id_hash);
343   a->id_hash = NULL;
344   while (a->current_id_bucket)
345     {
346       struct id_bucket *buck = a->current_id_bucket;
347       a->current_id_bucket = buck->next;
348       pci_mfree(buck);
349     }
350 }
351
352 static char *
353 id_lookup_subsys(struct pci_access *a, int iv, int id, int isv, int isd)
354 {
355   char *d = NULL;
356   if (iv > 0 && id > 0)                                         /* Per-device lookup */
357     d = id_lookup(a, ID_SUBSYSTEM, iv, id, isv, isd);
358   if (!d)                                                       /* Generic lookup */
359     d = id_lookup(a, ID_GEN_SUBSYSTEM, isv, isd, 0, 0);
360   if (!d && iv == isv && id == isd)                             /* Check for subsystem == device */
361     d = id_lookup(a, ID_DEVICE, iv, id, 0, 0);
362   return d;
363 }
364
365 static char *
366 format_name(char *buf, int size, int flags, char *name, char *num, char *unknown)
367 {
368   int res;
369   if ((flags & PCI_LOOKUP_NO_NUMBERS) && !name)
370     return NULL;
371   else if (flags & PCI_LOOKUP_NUMERIC)
372     res = snprintf(buf, size, "%s", num);
373   else if (!name)
374     res = snprintf(buf, size, ((flags & PCI_LOOKUP_MIXED) ? "%s [%s]" : "%s %s"), unknown, num);
375   else if (!(flags & PCI_LOOKUP_MIXED))
376     res = snprintf(buf, size, "%s", name);
377   else
378     res = snprintf(buf, size, "%s [%s]", name, num);
379   if (res < 0 || res >= size)
380     return "<pci_lookup_name: buffer too small>";
381   else
382     return buf;
383 }
384
385 static char *
386 format_name_pair(char *buf, int size, int flags, char *v, char *d, char *num)
387 {
388   int res;
389   if ((flags & PCI_LOOKUP_NO_NUMBERS) && (!v || !d))
390     return NULL;
391   if (flags & PCI_LOOKUP_NUMERIC)
392     res = snprintf(buf, size, "%s", num);
393   else if (flags & PCI_LOOKUP_MIXED)
394     {
395       if (v && d)
396         res = snprintf(buf, size, "%s %s [%s]", v, d, num);
397       else if (!v)
398         res = snprintf(buf, size, "Unknown device [%s]", num);
399       else /* v && !d */
400         res = snprintf(buf, size, "%s Unknown device [%s]", v, num);
401     }
402   else
403     {
404       if (v && d)
405         res = snprintf(buf, size, "%s %s", v, d);
406       else if (!v)
407         res = snprintf(buf, size, "Unknown device %s", num);
408       else /* v && !d */
409         res = snprintf(buf, size, "%s Unknown device %s", v, num+5);
410     }
411   if (res < 0 || res >= size)
412     return "<pci_lookup_name: buffer too small>";
413   else
414     return buf;
415 }
416
417 char *
418 pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...)
419 {
420   va_list args;
421   char *v, *d, *cls, *pif;
422   int iv, id, isv, isd, icls, ipif;
423   char numbuf[16], pifbuf[32];
424
425   va_start(args, flags);
426
427   if (!(flags & PCI_LOOKUP_NO_NUMBERS))
428     {
429       if (a->numeric_ids > 1)
430         flags |= PCI_LOOKUP_MIXED;
431       else if (a->numeric_ids)
432         flags |= PCI_LOOKUP_NUMERIC;
433     }
434   if (flags & PCI_LOOKUP_MIXED)
435     flags &= ~PCI_LOOKUP_NUMERIC;
436
437   if (!a->id_hash && !(flags & PCI_LOOKUP_NUMERIC) && !a->hash_load_failed)
438     pci_load_name_list(a);
439
440   switch (flags & 0xffff)
441     {
442     case PCI_LOOKUP_VENDOR:
443       iv = va_arg(args, int);
444       sprintf(numbuf, "%04x", iv);
445       return format_name(buf, size, flags, id_lookup(a, ID_VENDOR, iv, 0, 0, 0), numbuf, "Unknown vendor");
446     case PCI_LOOKUP_DEVICE:
447       iv = va_arg(args, int);
448       id = va_arg(args, int);
449       sprintf(numbuf, "%04x", id);
450       return format_name(buf, size, flags, id_lookup(a, ID_DEVICE, iv, id, 0, 0), numbuf, "Unknown device");
451     case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE:
452       iv = va_arg(args, int);
453       id = va_arg(args, int);
454       sprintf(numbuf, "%04x:%04x", iv, id);
455       v = id_lookup(a, ID_VENDOR, iv, 0, 0, 0);
456       d = id_lookup(a, ID_DEVICE, iv, id, 0, 0);
457       return format_name_pair(buf, size, flags, v, d, numbuf);
458     case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR:
459       isv = va_arg(args, int);
460       sprintf(numbuf, "%04x", isv);
461       v = id_lookup(a, ID_VENDOR, isv, 0, 0, 0);
462       return format_name(buf, size, flags, v, numbuf, "Unknown vendor");
463     case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE:
464       iv = va_arg(args, int);
465       id = va_arg(args, int);
466       isv = va_arg(args, int);
467       isd = va_arg(args, int);
468       sprintf(numbuf, "%04x", isd);
469       return format_name(buf, size, flags, id_lookup_subsys(a, iv, id, isv, isd), numbuf, "Unknown device");
470     case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM:
471       iv = va_arg(args, int);
472       id = va_arg(args, int);
473       isv = va_arg(args, int);
474       isd = va_arg(args, int);
475       v = id_lookup(a, ID_VENDOR, isv, 0, 0, 0);
476       d = id_lookup_subsys(a, iv, id, isv, isd);
477       sprintf(numbuf, "%04x:%04x", isv, isd);
478       return format_name_pair(buf, size, flags, v, d, numbuf);
479     case PCI_LOOKUP_CLASS:
480       icls = va_arg(args, int);
481       sprintf(numbuf, "%04x", icls);
482       cls = id_lookup(a, ID_SUBCLASS, icls >> 8, icls & 0xff, 0, 0);
483       if (!cls && (cls = id_lookup(a, ID_CLASS, icls >> 8, 0, 0, 0)))
484         {
485           if (!(flags & PCI_LOOKUP_NUMERIC)) /* Include full class number */
486             flags |= PCI_LOOKUP_MIXED;
487         }
488       return format_name(buf, size, flags, cls, numbuf, ((flags & PCI_LOOKUP_MIXED) ? "Unknown class" : "Class"));
489     case PCI_LOOKUP_PROGIF:
490       icls = va_arg(args, int);
491       ipif = va_arg(args, int);
492       sprintf(numbuf, "%02x", ipif);
493       pif = id_lookup(a, ID_PROGIF, icls >> 8, icls & 0xff, ipif, 0);
494       if (!pif && icls == 0x0101 && !(ipif & 0x70))
495         {
496           /* IDE controllers have complex prog-if semantics */
497           sprintf(pifbuf, "%s%s%s%s%s",
498                   (ipif & 0x80) ? " Master" : "",
499                   (ipif & 0x08) ? " SecP" : "",
500                   (ipif & 0x04) ? " SecO" : "",
501                   (ipif & 0x02) ? " PriP" : "",
502                   (ipif & 0x01) ? " PriO" : "");
503           pif = pifbuf;
504           if (*pif)
505             pif++;
506         }
507       return format_name(buf, size, flags, pif, numbuf, "ProgIf");
508     default:
509       return "<pci_lookup_name: invalid request>";
510     }
511 }
512
513 void pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed)
514 {
515   if (a->free_id_name)
516     free(a->id_file_name);
517   a->id_file_name = name;
518   a->free_id_name = to_be_freed;
519 }