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