]>
Commit | Line | Data |
---|---|---|
727ce158 | 1 | /* |
727ce158 MM |
2 | * The PCI Library -- ID to Name Translation |
3 | * | |
a2322147 | 4 | * Copyright (c) 1997--2007 Martin Mares <mj@ucw.cz> |
727ce158 MM |
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> | |
aeaca5d3 | 11 | #include <stdarg.h> |
727ce158 | 12 | #include <string.h> |
727ce158 MM |
13 | #include <errno.h> |
14 | ||
15 | #include "internal.h" | |
16 | ||
cc062b4a MM |
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 | ||
a2322147 | 29 | result = gzopen(a->id_file_name, "rb"); |
cc062b4a MM |
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); | |
a2322147 | 39 | return gzopen(a->id_file_name, "rb"); |
cc062b4a MM |
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 | ||
aeaca5d3 MM |
59 | struct id_entry { |
60 | struct id_entry *next; | |
61 | u32 id12, id34; | |
62 | byte cat; | |
afebde01 | 63 | char name[1]; |
727ce158 MM |
64 | }; |
65 | ||
aeaca5d3 MM |
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 | }; | |
727ce158 | 81 | |
aeaca5d3 MM |
82 | #define MAX_LINE 1024 |
83 | #define BUCKET_SIZE 8192 | |
84 | #define HASH_SIZE 4099 | |
727ce158 | 85 | |
c07f9966 | 86 | #ifdef __GNUC__ |
aeaca5d3 MM |
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) | |
727ce158 | 98 | { |
aeaca5d3 MM |
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 | } | |
727ce158 | 112 | |
aeaca5d3 MM |
113 | static inline u32 id_pair(unsigned int x, unsigned int y) |
114 | { | |
115 | return ((x << 16) | y); | |
727ce158 MM |
116 | } |
117 | ||
aeaca5d3 | 118 | static inline unsigned int id_hash(int cat, u32 id12, u32 id34) |
727ce158 MM |
119 | { |
120 | unsigned int h; | |
727ce158 | 121 | |
aeaca5d3 MM |
122 | h = id12 ^ (id34 << 3) ^ (cat << 5); |
123 | return h % HASH_SIZE; | |
124 | } | |
125 | ||
afebde01 | 126 | static char *id_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4) |
aeaca5d3 MM |
127 | { |
128 | struct id_entry *n; | |
129 | u32 id12 = id_pair(id1, id2); | |
130 | u32 id34 = id_pair(id3, id4); | |
131 | ||
bc2eed2d MM |
132 | if (!a->id_hash) |
133 | return NULL; | |
aeaca5d3 MM |
134 | n = a->id_hash[id_hash(cat, id12, id34)]; |
135 | while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) | |
727ce158 | 136 | n = n->next; |
bc2eed2d | 137 | return n ? n->name : NULL; |
727ce158 MM |
138 | } |
139 | ||
afebde01 | 140 | static int id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text) |
727ce158 | 141 | { |
aeaca5d3 MM |
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); | |
727ce158 | 147 | |
aeaca5d3 | 148 | while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat)) |
727ce158 MM |
149 | n = n->next; |
150 | if (n) | |
151 | return 1; | |
aeaca5d3 MM |
152 | n = id_alloc(a, sizeof(struct id_entry) + len); |
153 | n->id12 = id12; | |
154 | n->id34 = id34; | |
727ce158 | 155 | n->cat = cat; |
aeaca5d3 MM |
156 | memcpy(n->name, text, len+1); |
157 | n->next = a->id_hash[h]; | |
158 | a->id_hash[h] = n; | |
727ce158 MM |
159 | return 0; |
160 | } | |
161 | ||
afebde01 | 162 | static int id_hex(char *p, int cnt) |
727ce158 | 163 | { |
aeaca5d3 MM |
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; | |
727ce158 MM |
179 | } |
180 | ||
aeaca5d3 | 181 | static inline int id_white_p(int c) |
727ce158 | 182 | { |
aeaca5d3 MM |
183 | return (c == ' ') || (c == '\t'); |
184 | } | |
185 | ||
cc062b4a | 186 | static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) |
aeaca5d3 | 187 | { |
afebde01 MM |
188 | char line[MAX_LINE]; |
189 | char *p; | |
aeaca5d3 | 190 | int id1=0, id2=0, id3=0, id4=0; |
d4798a32 | 191 | int cat = -1; |
aeaca5d3 MM |
192 | int nest; |
193 | static const char parse_error[] = "Parse error"; | |
727ce158 | 194 | |
aeaca5d3 | 195 | *lino = 0; |
cc062b4a | 196 | while (pci_gets(f, line, sizeof(line))) |
727ce158 | 197 | { |
aeaca5d3 MM |
198 | (*lino)++; |
199 | p = line; | |
200 | while (*p && *p != '\n' && *p != '\r') | |
d2f666f1 | 201 | p++; |
cc062b4a | 202 | if (!*p && !pci_eof(f)) |
aeaca5d3 MM |
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 == '#') | |
727ce158 | 212 | continue; |
aeaca5d3 MM |
213 | |
214 | p = line; | |
215 | while (*p == '\t') | |
216 | p++; | |
217 | nest = p - line; | |
218 | ||
219 | if (!nest) /* Top-level entries */ | |
727ce158 | 220 | { |
aeaca5d3 | 221 | if (p[0] == 'C' && p[1] == ' ') /* Class block */ |
727ce158 | 222 | { |
aeaca5d3 MM |
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; | |
727ce158 | 241 | } |
aeaca5d3 | 242 | else /* Vendor ID */ |
727ce158 | 243 | { |
aeaca5d3 MM |
244 | if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) |
245 | return parse_error; | |
246 | cat = ID_VENDOR; | |
247 | p += 5; | |
727ce158 | 248 | } |
d4798a32 | 249 | id2 = id3 = id4 = 0; |
727ce158 | 250 | } |
aeaca5d3 MM |
251 | else if (cat == ID_UNKNOWN) /* Nested entries in RFU blocks are skipped */ |
252 | continue; | |
253 | else if (nest == 1) /* Nesting level 1 */ | |
d4798a32 MM |
254 | switch (cat) |
255 | { | |
aeaca5d3 MM |
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; | |
d4798a32 MM |
269 | id3 = id4 = 0; |
270 | break; | |
aeaca5d3 MM |
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; | |
d4798a32 MM |
278 | id3 = id4 = 0; |
279 | break; | |
280 | default: | |
aeaca5d3 | 281 | return parse_error; |
d4798a32 | 282 | } |
aeaca5d3 | 283 | else if (nest == 2) /* Nesting level 2 */ |
d4798a32 MM |
284 | switch (cat) |
285 | { | |
aeaca5d3 MM |
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; | |
d4798a32 | 292 | break; |
aeaca5d3 MM |
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; | |
d4798a32 MM |
300 | id4 = 0; |
301 | break; | |
302 | default: | |
aeaca5d3 | 303 | return parse_error; |
d4798a32 | 304 | } |
aeaca5d3 MM |
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"; | |
727ce158 | 313 | } |
aeaca5d3 | 314 | return NULL; |
727ce158 MM |
315 | } |
316 | ||
aeaca5d3 MM |
317 | int |
318 | pci_load_name_list(struct pci_access *a) | |
727ce158 | 319 | { |
cc062b4a | 320 | pci_file f; |
aeaca5d3 MM |
321 | int lino; |
322 | const char *err; | |
727ce158 | 323 | |
aeaca5d3 | 324 | pci_free_name_list(a); |
bc2eed2d | 325 | a->hash_load_failed = 1; |
cc062b4a | 326 | if (!(f = pci_open(a))) |
aeaca5d3 MM |
327 | return 0; |
328 | a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE); | |
1ac3a99d | 329 | memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE); |
aeaca5d3 | 330 | err = id_parse_list(a, f, &lino); |
cc062b4a MM |
331 | PCI_ERROR(f, err); |
332 | pci_close(f); | |
aeaca5d3 MM |
333 | if (err) |
334 | a->error("%s at %s, line %d\n", err, a->id_file_name, lino); | |
bc2eed2d | 335 | a->hash_load_failed = 0; |
aeaca5d3 | 336 | return 1; |
727ce158 MM |
337 | } |
338 | ||
339 | void | |
340 | pci_free_name_list(struct pci_access *a) | |
341 | { | |
aeaca5d3 MM |
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 | ||
afebde01 | 352 | static char * |
bc2eed2d | 353 | id_lookup_subsys(struct pci_access *a, int iv, int id, int isv, int isd) |
aeaca5d3 | 354 | { |
afebde01 | 355 | char *d = NULL; |
aeaca5d3 MM |
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; | |
727ce158 MM |
363 | } |
364 | ||
afebde01 MM |
365 | static char * |
366 | format_name(char *buf, int size, int flags, char *name, char *num, char *unknown) | |
bc2eed2d MM |
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 | ||
afebde01 MM |
385 | static char * |
386 | format_name_pair(char *buf, int size, int flags, char *v, char *d, char *num) | |
bc2eed2d MM |
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 | ||
727ce158 | 417 | char * |
aeaca5d3 | 418 | pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...) |
727ce158 | 419 | { |
aeaca5d3 | 420 | va_list args; |
afebde01 | 421 | char *v, *d, *cls, *pif; |
aeaca5d3 | 422 | int iv, id, isv, isd, icls, ipif; |
afebde01 | 423 | char numbuf[16], pifbuf[32]; |
aeaca5d3 MM |
424 | |
425 | va_start(args, flags); | |
727ce158 | 426 | |
a42c0dad MM |
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 | } | |
bc2eed2d MM |
434 | if (flags & PCI_LOOKUP_MIXED) |
435 | flags &= ~PCI_LOOKUP_NUMERIC; | |
aeaca5d3 | 436 | |
bc2eed2d MM |
437 | if (!a->id_hash && !(flags & PCI_LOOKUP_NUMERIC) && !a->hash_load_failed) |
438 | pci_load_name_list(a); | |
aeaca5d3 | 439 | |
bc2eed2d | 440 | switch (flags & 0xffff) |
727ce158 MM |
441 | { |
442 | case PCI_LOOKUP_VENDOR: | |
aeaca5d3 | 443 | iv = va_arg(args, int); |
bc2eed2d MM |
444 | sprintf(numbuf, "%04x", iv); |
445 | return format_name(buf, size, flags, id_lookup(a, ID_VENDOR, iv, 0, 0, 0), numbuf, "Unknown vendor"); | |
727ce158 | 446 | case PCI_LOOKUP_DEVICE: |
aeaca5d3 MM |
447 | iv = va_arg(args, int); |
448 | id = va_arg(args, int); | |
bc2eed2d MM |
449 | sprintf(numbuf, "%04x", id); |
450 | return format_name(buf, size, flags, id_lookup(a, ID_DEVICE, iv, id, 0, 0), numbuf, "Unknown device"); | |
727ce158 | 451 | case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE: |
aeaca5d3 MM |
452 | iv = va_arg(args, int); |
453 | id = va_arg(args, int); | |
bc2eed2d MM |
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); | |
aeaca5d3 MM |
458 | case PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_VENDOR: |
459 | isv = va_arg(args, int); | |
bc2eed2d MM |
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"); | |
aeaca5d3 MM |
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); | |
bc2eed2d MM |
468 | sprintf(numbuf, "%04x", isd); |
469 | return format_name(buf, size, flags, id_lookup_subsys(a, iv, id, isv, isd), numbuf, "Unknown device"); | |
727ce158 | 470 | case PCI_LOOKUP_VENDOR | PCI_LOOKUP_DEVICE | PCI_LOOKUP_SUBSYSTEM: |
aeaca5d3 MM |
471 | iv = va_arg(args, int); |
472 | id = va_arg(args, int); | |
473 | isv = va_arg(args, int); | |
474 | isd = va_arg(args, int); | |
bc2eed2d MM |
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); | |
727ce158 | 479 | case PCI_LOOKUP_CLASS: |
aeaca5d3 | 480 | icls = va_arg(args, int); |
bc2eed2d MM |
481 | sprintf(numbuf, "%04x", icls); |
482 | cls = id_lookup(a, ID_SUBCLASS, icls >> 8, icls & 0xff, 0, 0); | |
c72eedde | 483 | if (!cls && (cls = id_lookup(a, ID_CLASS, icls >> 8, 0, 0, 0))) |
bc2eed2d MM |
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")); | |
d4798a32 | 489 | case PCI_LOOKUP_PROGIF: |
aeaca5d3 MM |
490 | icls = va_arg(args, int); |
491 | ipif = va_arg(args, int); | |
bc2eed2d MM |
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)) | |
d4798a32 MM |
495 | { |
496 | /* IDE controllers have complex prog-if semantics */ | |
bc2eed2d | 497 | sprintf(pifbuf, "%s%s%s%s%s", |
a42c0dad MM |
498 | (ipif & 0x80) ? " Master" : "", |
499 | (ipif & 0x08) ? " SecP" : "", | |
500 | (ipif & 0x04) ? " SecO" : "", | |
501 | (ipif & 0x02) ? " PriP" : "", | |
502 | (ipif & 0x01) ? " PriO" : ""); | |
bc2eed2d | 503 | pif = pifbuf; |
a42c0dad MM |
504 | if (*pif) |
505 | pif++; | |
d4798a32 | 506 | } |
bc2eed2d | 507 | return format_name(buf, size, flags, pif, numbuf, "ProgIf"); |
727ce158 MM |
508 | default: |
509 | return "<pci_lookup_name: invalid request>"; | |
510 | } | |
727ce158 | 511 | } |
cc062b4a MM |
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 | } |