~vojtech-horky/helenos/numa

« back to all changes in this revision

Viewing changes to uspace/srv/pci/libpci/names.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

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