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