1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
23 */
24
25 /*
26 * This library exists to understand and parse the pci.ids database that is
27 * maintained at http://pci-ids.ucw.cz/ and in the gate at cmd/hwdata. This
28 * database provides a way to map the PCI device, vendor, and subsystem ids to
29 * a human understandable name.
30 *
31 * This library exports this data in a similar way to a tree. The handle that
32 * is returned from pcidb_open is the root of the tree. The next level are the
33 * vendors. Each vendor has a unique set of devices and each device has a unique
34 * set of subvendor and subdevice pairs.
35 *
36 * Parsing information:
37 *
38 * The database is formatted in the following basic format:
39 * vendor_id<two spaces>vendor_name
40 * <tab>device_id<two spaces>device_name
41 * <tab><tab>subvendor<space>subdevice<two spaces>subsystem_name
42 *
43 * For any given vendor, there can be multiple devices. And for any given device
44 * there will be multiple subsystems. In addition, there can be comments that
45 * start a line which use the '#' character.
46 *
47 * At the end of the file, there are a series of PCI classes. Those will start
48 * with a single C<space>. Once we hit those, we stop all parsing. We currently
49 * don't care about consuming or presenting those.
50 */
51
52 #include <sys/types.h>
53 #include <stdint.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <errno.h>
57 #include <string.h>
58 #include <assert.h>
59 #include <unistd.h>
60
61 #include "pcidb.h"
62
63 #define PCI_NAME_MAX 256
64 #define PCI_READLINE 1024
65
66 /* Forward declarations */
67 struct pcidb_vendor;
68 struct pcidb_device;
69 struct pcidb_subvd;
70
71 struct pcidb_subvd {
72 uint16_t ps_vid;
73 uint16_t ps_did;
74 char ps_name[PCI_NAME_MAX];
75 struct pcidb_subvd *ps_prev;
76 struct pcidb_subvd *ps_next;
77 struct pcidb_device *ps_dev;
78 struct pcidb_vendor *ps_vend;
79 };
80
81 struct pcidb_device {
82 uint16_t pd_id;
83 char pd_name[PCI_NAME_MAX];
84 struct pcidb_subvd *pd_sstart;
85 struct pcidb_subvd *pd_send;
86 struct pcidb_device *pd_next;
87 struct pcidb_device *pd_prev;
88 struct pcidb_vendor *pd_vend;
89 };
90
91 struct pcidb_vendor {
92 uint16_t pv_id;
93 char pv_name[PCI_NAME_MAX];
94 struct pcidb_device *pv_dstart;
95 struct pcidb_device *pv_dend;
96 struct pcidb_vendor *pv_prev;
97 struct pcidb_vendor *pv_next;
98 };
99
100 struct pcidb_hdl {
101 pcidb_vendor_t *ph_vstart;
102 pcidb_vendor_t *ph_vend;
103 };
104
105 typedef enum pcidb_parse {
106 PDB_VENDOR,
107 PDB_DEVICE,
108 PDB_SUBDEV
109 } pcidb_parse_t;
110
111 static const char *pci_db = "/usr/share/hwdata/pci.ids";
112
113 static void
pcihdl_add_vendor(pcidb_hdl_t * hdl,pcidb_vendor_t * v)114 pcihdl_add_vendor(pcidb_hdl_t *hdl, pcidb_vendor_t *v)
115 {
116 if (hdl->ph_vstart == NULL && hdl->ph_vend == NULL) {
117 hdl->ph_vstart = v;
118 hdl->ph_vend = v;
119 v->pv_prev = NULL;
120 v->pv_next = NULL;
121 } else {
122 v->pv_prev = hdl->ph_vend;
123 v->pv_next = NULL;
124 hdl->ph_vend->pv_next = v;
125 hdl->ph_vend = v;
126 }
127 }
128
129 static pcidb_vendor_t *
parse_vendor(char * buf,pcidb_hdl_t * hdl)130 parse_vendor(char *buf, pcidb_hdl_t *hdl)
131 {
132 pcidb_vendor_t *v;
133 size_t len;
134
135 v = malloc(sizeof (pcidb_vendor_t));
136 if (v == NULL)
137 return (NULL);
138
139 pcihdl_add_vendor(hdl, v);
140 v->pv_dstart = NULL;
141 v->pv_dend = NULL;
142
143 buf[4] = '\0';
144 v->pv_id = strtol(buf, NULL, 16);
145 buf += 6;
146 len = strlen(buf);
147 if (buf[len-1] == '\n')
148 buf[len-1] = '\0';
149
150 (void) strlcpy(v->pv_name, buf, PCI_NAME_MAX);
151
152 return (v);
153 }
154
155 static void
insert_device(pcidb_vendor_t * v,pcidb_device_t * d)156 insert_device(pcidb_vendor_t *v, pcidb_device_t *d)
157 {
158 d->pd_vend = v;
159 if (v->pv_dstart == NULL && v->pv_dend == NULL) {
160 v->pv_dstart = d;
161 v->pv_dend = d;
162 d->pd_next = NULL;
163 d->pd_prev = NULL;
164 } else {
165 d->pd_prev = v->pv_dend;
166 d->pd_next = NULL;
167 v->pv_dend->pd_next = d;
168 v->pv_dend = d;
169 }
170 }
171
172 static pcidb_device_t *
parse_device(char * buf,pcidb_vendor_t * v)173 parse_device(char *buf, pcidb_vendor_t *v)
174 {
175 pcidb_device_t *d;
176 size_t len;
177
178 d = malloc(sizeof (pcidb_device_t));
179 if (d == NULL)
180 return (d);
181
182 d->pd_sstart = NULL;
183 d->pd_send = NULL;
184 insert_device(v, d);
185
186 buf++;
187 buf[4] = '\0';
188 d->pd_id = strtol(buf, NULL, 16);
189 buf += 6;
190 len = strlen(buf);
191 if (buf[len-1] == '\n')
192 buf[len-1] = '\0';
193
194 (void) strlcpy(d->pd_name, buf, PCI_NAME_MAX);
195 return (d);
196 }
197
198 static void
insert_subdev(pcidb_device_t * d,pcidb_subvd_t * s)199 insert_subdev(pcidb_device_t *d, pcidb_subvd_t *s)
200 {
201 s->ps_dev = d;
202 s->ps_vend = d->pd_vend;
203 if (d->pd_sstart == NULL) {
204 d->pd_sstart = s;
205 d->pd_send = s;
206 s->ps_prev = NULL;
207 s->ps_next = NULL;
208 } else {
209 s->ps_prev = d->pd_send;
210 s->ps_next = NULL;
211 d->pd_send->ps_next = s;
212 d->pd_send = s;
213 }
214 }
215
216 static pcidb_subvd_t *
parse_subdev(char * buf,pcidb_device_t * d)217 parse_subdev(char *buf, pcidb_device_t *d)
218 {
219 pcidb_subvd_t *s;
220 size_t len;
221
222 s = malloc(sizeof (pcidb_subvd_t));
223 if (s == NULL)
224 return (NULL);
225 insert_subdev(d, s);
226
227 buf += 2;
228 buf[4] = '\0';
229 s->ps_vid = strtol(buf, NULL, 16);
230 buf += 5;
231 buf[4] = '\0';
232 s->ps_did = strtol(buf, NULL, 16);
233 buf += 6;
234
235 len = strlen(buf);
236 if (buf[len-1] == '\n')
237 buf[len-1] = '\0';
238
239 (void) strlcpy(s->ps_name, buf, PCI_NAME_MAX);
240
241 return (s);
242 }
243
244 static int
readline(FILE * f,char * buf,size_t len)245 readline(FILE *f, char *buf, size_t len)
246 {
247 for (;;) {
248 if (fgets(buf, len, f) == NULL)
249 return (-1);
250
251 if (buf[0] == 'C')
252 return (-1);
253
254 if (buf[0] != '#' && buf[0] != '\n')
255 return (0);
256 }
257 }
258
259 static int
parse_db(FILE * f,pcidb_hdl_t * hdl)260 parse_db(FILE *f, pcidb_hdl_t *hdl)
261 {
262 char buf[1024];
263 pcidb_vendor_t *v = NULL;
264 pcidb_device_t *d = NULL;
265 pcidb_parse_t state = PDB_VENDOR;
266
267 for (;;) {
268 errno = 0;
269 if (readline(f, buf, sizeof (buf)) != 0) {
270 if (errno != 0)
271 return (-1);
272 else
273 return (0);
274 }
275
276 newstate:
277 switch (state) {
278 case PDB_VENDOR:
279 v = parse_vendor(buf, hdl);
280 if (v == NULL)
281 return (NULL);
282 state = PDB_DEVICE;
283 continue;
284 case PDB_DEVICE:
285 if (buf[0] != '\t') {
286 state = PDB_VENDOR;
287 goto newstate;
288 }
289
290 if (buf[1] == '\t') {
291 state = PDB_SUBDEV;
292 goto newstate;
293 }
294
295 assert(v != NULL);
296 d = parse_device(buf, v);
297 if (d == NULL)
298 return (NULL);
299 continue;
300 case PDB_SUBDEV:
301 if (buf[0] != '\t') {
302 state = PDB_VENDOR;
303 goto newstate;
304 }
305
306 if (buf[0] == '\t' && buf[1] != '\t') {
307 state = PDB_DEVICE;
308 goto newstate;
309 }
310
311 assert(buf[0] == '\t' && buf[1] == '\t');
312 assert(d != NULL);
313 (void) parse_subdev(buf, d);
314 }
315 }
316 }
317
318 pcidb_hdl_t *
pcidb_open(int version)319 pcidb_open(int version)
320 {
321 pcidb_hdl_t *h;
322 FILE *f;
323
324 if (version != PCIDB_VERSION) {
325 errno = EINVAL;
326 return (NULL);
327 }
328
329 h = malloc(sizeof (pcidb_hdl_t));
330 if (h == NULL)
331 return (NULL);
332
333 h->ph_vstart = NULL;
334 h->ph_vend = NULL;
335
336 f = fopen(pci_db, "rF");
337 if (f == NULL) {
338 free(h);
339 return (NULL);
340 }
341
342 if (parse_db(f, h) < 0) {
343 pcidb_close(h);
344 free(h);
345 return (NULL);
346 }
347
348 return (h);
349 }
350
351 void
pcidb_close(pcidb_hdl_t * h)352 pcidb_close(pcidb_hdl_t *h)
353 {
354 pcidb_vendor_t *v, *tv;
355
356 pcidb_device_t *d, *td;
357 pcidb_subvd_t *s, *ts;
358
359 if (h == NULL)
360 return;
361
362 v = h->ph_vstart;
363 while (v != NULL) {
364 d = v->pv_dstart;
365 while (d != NULL) {
366 s = d->pd_sstart;
367 while (s != NULL) {
368 ts = s;
369 s = s->ps_next;
370 free(ts);
371 }
372 td = d;
373 d = d->pd_next;
374 free(td);
375 }
376 tv = v;
377 v = v->pv_next;
378 free(tv);
379 }
380
381 free(h);
382 }
383
384 pcidb_vendor_t *
pcidb_lookup_vendor(pcidb_hdl_t * hdl,uint16_t id)385 pcidb_lookup_vendor(pcidb_hdl_t *hdl, uint16_t id)
386 {
387 pcidb_vendor_t *v;
388
389 for (v = hdl->ph_vstart; v != NULL; v = v->pv_next) {
390 if (v->pv_id == id)
391 return (v);
392 }
393
394 return (NULL);
395 }
396
397 const char *
pcidb_vendor_name(pcidb_vendor_t * v)398 pcidb_vendor_name(pcidb_vendor_t *v)
399 {
400 return (v->pv_name);
401 }
402
403 uint16_t
pcidb_vendor_id(pcidb_vendor_t * v)404 pcidb_vendor_id(pcidb_vendor_t *v)
405 {
406 return (v->pv_id);
407 }
408
409 pcidb_vendor_t *
pcidb_vendor_iter(pcidb_hdl_t * h)410 pcidb_vendor_iter(pcidb_hdl_t *h)
411 {
412 return (h->ph_vstart);
413 }
414
415 pcidb_vendor_t *
pcidb_vendor_iter_next(pcidb_vendor_t * v)416 pcidb_vendor_iter_next(pcidb_vendor_t *v)
417 {
418 assert(v != NULL);
419 return (v->pv_next);
420 }
421
422 pcidb_device_t *
pcidb_lookup_device_by_vendor(pcidb_vendor_t * v,uint16_t id)423 pcidb_lookup_device_by_vendor(pcidb_vendor_t *v, uint16_t id)
424 {
425 pcidb_device_t *d;
426 assert(v != NULL);
427
428 for (d = v->pv_dstart; d != NULL; d = d->pd_next)
429 if (d->pd_id == id)
430 return (d);
431
432 return (NULL);
433 }
434
435 pcidb_device_t *
pcidb_lookup_device(pcidb_hdl_t * h,uint16_t vid,uint16_t did)436 pcidb_lookup_device(pcidb_hdl_t *h, uint16_t vid, uint16_t did)
437 {
438 pcidb_vendor_t *v;
439
440 v = pcidb_lookup_vendor(h, vid);
441 if (v == NULL)
442 return (NULL);
443
444 return (pcidb_lookup_device_by_vendor(v, did));
445 }
446
447 pcidb_device_t *
pcidb_device_iter(pcidb_vendor_t * v)448 pcidb_device_iter(pcidb_vendor_t *v)
449 {
450 return (v->pv_dstart);
451 }
452
453 pcidb_device_t *
pcidb_device_iter_next(pcidb_device_t * d)454 pcidb_device_iter_next(pcidb_device_t *d)
455 {
456 return (d->pd_next);
457 }
458
459 const char *
pcidb_device_name(pcidb_device_t * d)460 pcidb_device_name(pcidb_device_t *d)
461 {
462 return (d->pd_name);
463 }
464
465 uint16_t
pcidb_device_id(pcidb_device_t * d)466 pcidb_device_id(pcidb_device_t *d)
467 {
468 return (d->pd_id);
469 }
470
471 pcidb_vendor_t *
pcidb_device_vendor(pcidb_device_t * d)472 pcidb_device_vendor(pcidb_device_t *d)
473 {
474 return (d->pd_vend);
475 }
476
477 pcidb_subvd_t *
pcidb_lookup_subvd_by_device(pcidb_device_t * d,uint16_t svid,uint16_t sdid)478 pcidb_lookup_subvd_by_device(pcidb_device_t *d, uint16_t svid, uint16_t sdid)
479 {
480 pcidb_subvd_t *s;
481
482 assert(d != NULL);
483
484 for (s = d->pd_sstart; s != NULL; s = s->ps_next)
485 if (s->ps_vid == svid && s->ps_did == sdid)
486 return (s);
487
488 return (NULL);
489 }
490
491 pcidb_subvd_t *
pcidb_lookup_subvd_by_vendor(pcidb_vendor_t * v,uint16_t devid,uint16_t svid,uint16_t sdid)492 pcidb_lookup_subvd_by_vendor(pcidb_vendor_t *v, uint16_t devid, uint16_t svid,
493 uint16_t sdid)
494 {
495 pcidb_device_t *d;
496
497 assert(v != NULL);
498 d = pcidb_lookup_device_by_vendor(v, devid);
499 if (d == NULL)
500 return (NULL);
501
502 return (pcidb_lookup_subvd_by_device(d, svid, sdid));
503 }
504
505 pcidb_subvd_t *
pcidb_lookup_subvd(pcidb_hdl_t * h,uint16_t vid,uint16_t did,uint16_t svid,uint16_t sdid)506 pcidb_lookup_subvd(pcidb_hdl_t *h, uint16_t vid, uint16_t did, uint16_t svid,
507 uint16_t sdid)
508 {
509 pcidb_device_t *d;
510
511 assert(h != NULL);
512 d = pcidb_lookup_device(h, vid, did);
513 if (d == NULL)
514 return (NULL);
515
516 return (pcidb_lookup_subvd_by_device(d, svid, sdid));
517 }
518
519 pcidb_subvd_t *
pcidb_subvd_iter(pcidb_device_t * d)520 pcidb_subvd_iter(pcidb_device_t *d)
521 {
522 return (d->pd_sstart);
523 }
524
525 pcidb_subvd_t *
pcidb_subvd_iter_next(pcidb_subvd_t * s)526 pcidb_subvd_iter_next(pcidb_subvd_t *s)
527 {
528 return (s->ps_next);
529 }
530
531 const char *
pcidb_subvd_name(pcidb_subvd_t * s)532 pcidb_subvd_name(pcidb_subvd_t *s)
533 {
534 return (s->ps_name);
535 }
536
537 uint16_t
pcidb_subvd_svid(pcidb_subvd_t * s)538 pcidb_subvd_svid(pcidb_subvd_t *s)
539 {
540 return (s->ps_vid);
541 }
542
543 uint16_t
pcidb_subvd_sdid(pcidb_subvd_t * s)544 pcidb_subvd_sdid(pcidb_subvd_t *s)
545 {
546 return (s->ps_did);
547 }
548
549 pcidb_device_t *
pcidb_subvd_device(pcidb_subvd_t * s)550 pcidb_subvd_device(pcidb_subvd_t *s)
551 {
552 return (s->ps_dev);
553 }
554
555 pcidb_vendor_t *
pcidb_subvd_vendor(pcidb_subvd_t * s)556 pcidb_subvd_vendor(pcidb_subvd_t *s)
557 {
558 return (s->ps_vend);
559 }
560