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