xref: /illumos-gate/usr/src/lib/libpcidb/common/pcidb.c (revision dd72704bd9e794056c558153663c739e2012d721)
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  * Copyright 2021 Oxide Computer Company
24  */
25 
26 /*
27  * This library exists to understand and parse the pci.ids database that is
28  * maintained at http://pci-ids.ucw.cz/ and in the gate at cmd/hwdata. This
29  * database provides a way to map the PCI device, vendor, and subsystem ids to
30  * a human understandable name.
31  *
32  * This library exports this data in a similar way to a tree. The handle that
33  * is returned from pcidb_open is the root of the tree. The next level are the
34  * vendors. Each vendor has a unique set of devices and each device has a unique
35  * set of subvendor and subdevice pairs.
36  *
37  * Parsing information:
38  *
39  * The database is formatted in the following basic format:
40  * vendor_id<two spaces>vendor_name
41  * <tab>device_id<two spaces>device_name
42  * <tab><tab>subvendor<space>subdevice<two spaces>subsystem_name
43  *
44  * For any given vendor, there can be multiple devices. And for any given device
45  * there will be multiple subsystems. In addition, there can be comments that
46  * start a line which use the '#' character.
47  *
48  * At the end of the file, there are a series of PCI classes. Those will start
49  * with a single C<space>. Once we hit those, we stop all parsing. We currently
50  * don't care about consuming or presenting those.
51  */
52 
53 #include <sys/types.h>
54 #include <stdint.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <errno.h>
58 #include <string.h>
59 #include <assert.h>
60 #include <unistd.h>
61 #include <stddef.h>
62 #include <sys/list.h>
63 
64 #include "pcidb.h"
65 
66 #define	PCI_NAME_MAX	256
67 #define	PCI_READLINE	1024
68 
69 struct pcidb_progif {
70 	char		pp_name[PCI_NAME_MAX];
71 	list_node_t	pp_link;
72 	pcidb_subclass_t *pp_subclass;
73 	uint8_t		pp_code;
74 };
75 
76 struct pcidb_subclass {
77 	char		psc_name[PCI_NAME_MAX];
78 	list_node_t	psc_link;
79 	list_t		psc_progifs;
80 	pcidb_class_t	*psc_class;
81 	uint8_t		psc_code;
82 };
83 
84 struct pcidb_class {
85 	char		pc_name[PCI_NAME_MAX];
86 	list_node_t	pc_link;
87 	list_t		pc_subclass;
88 	pcidb_hdl_t	*pc_hdl;
89 	uint8_t		pc_code;
90 };
91 
92 struct pcidb_subvd {
93 	uint16_t	ps_vid;
94 	uint16_t	ps_did;
95 	char		ps_name[PCI_NAME_MAX];
96 	list_node_t	ps_link;
97 	pcidb_device_t	*ps_dev;
98 	pcidb_vendor_t	*ps_vend;
99 };
100 
101 struct pcidb_device {
102 	uint16_t	pd_id;
103 	char		pd_name[PCI_NAME_MAX];
104 	list_t		pd_subs;
105 	list_node_t	pd_link;
106 	pcidb_vendor_t	*pd_vend;
107 };
108 
109 struct pcidb_vendor {
110 	uint16_t	pv_id;
111 	char		pv_name[PCI_NAME_MAX];
112 	list_t		pv_devs;
113 	list_node_t	pv_link;
114 	pcidb_hdl_t	*pv_hdl;
115 };
116 
117 struct pcidb_hdl {
118 	list_t ph_vendors;
119 	list_t ph_classes;
120 };
121 
122 typedef enum pcidb_parse {
123 	PDB_INIT,
124 	PDB_VENDOR,
125 	PDB_DEVICE,
126 	PDB_SUBDEV,
127 	PDB_CLASS,
128 	PDB_SUBCLASS,
129 	PDB_PROGIF
130 } pcidb_parse_t;
131 
132 static const char *pci_db = "/usr/share/hwdata/pci.ids";
133 
134 static pcidb_vendor_t *
135 parse_vendor(char *buf, pcidb_hdl_t *hdl)
136 {
137 	pcidb_vendor_t *vend;
138 
139 	vend = malloc(sizeof (pcidb_vendor_t));
140 	if (vend == NULL)
141 		return (NULL);
142 
143 	list_create(&vend->pv_devs, sizeof (pcidb_device_t),
144 	    offsetof(pcidb_device_t, pd_link));
145 	vend->pv_hdl = hdl;
146 	list_insert_tail(&hdl->ph_vendors, vend);
147 
148 	buf[4] = '\0';
149 	vend->pv_id = strtol(buf, NULL, 16);
150 	buf += 6;
151 
152 	(void) strlcpy(vend->pv_name, buf, PCI_NAME_MAX);
153 
154 	return (vend);
155 }
156 
157 static pcidb_device_t *
158 parse_device(char *buf, pcidb_vendor_t *vend)
159 {
160 	pcidb_device_t *dev;
161 
162 	dev = malloc(sizeof (pcidb_device_t));
163 	if (dev == NULL)
164 		return (dev);
165 
166 	list_create(&dev->pd_subs, sizeof (pcidb_subvd_t),
167 	    offsetof(pcidb_subvd_t, ps_link));
168 	dev->pd_vend = vend;
169 	list_insert_tail(&vend->pv_devs, dev);
170 
171 	buf++;
172 	buf[4] = '\0';
173 	dev->pd_id = strtol(buf, NULL, 16);
174 	buf += 6;
175 
176 	(void) strlcpy(dev->pd_name, buf, PCI_NAME_MAX);
177 	return (dev);
178 }
179 
180 static pcidb_subvd_t *
181 parse_subdev(char *buf, pcidb_device_t *dev)
182 {
183 	pcidb_subvd_t *sub;
184 
185 	sub = malloc(sizeof (pcidb_subvd_t));
186 	if (sub == NULL)
187 		return (NULL);
188 
189 	sub->ps_dev = dev;
190 	sub->ps_vend = dev->pd_vend;
191 	list_insert_tail(&dev->pd_subs, sub);
192 
193 	buf += 2;
194 	buf[4] = '\0';
195 	sub->ps_vid = strtol(buf, NULL, 16);
196 	buf += 5;
197 	buf[4] = '\0';
198 	sub->ps_did = strtol(buf, NULL, 16);
199 	buf += 6;
200 
201 	(void) strlcpy(sub->ps_name, buf, PCI_NAME_MAX);
202 
203 	return (sub);
204 }
205 
206 static pcidb_class_t *
207 pcidb_parse_class(char *buf, pcidb_hdl_t *hdl)
208 {
209 	pcidb_class_t *class;
210 
211 	class = malloc(sizeof (pcidb_class_t));
212 	if (class == NULL)
213 		return (NULL);
214 
215 	list_create(&class->pc_subclass, sizeof (pcidb_subclass_t),
216 	    offsetof(pcidb_subclass_t, psc_link));
217 	class->pc_hdl = hdl;
218 	list_insert_tail(&hdl->ph_classes, class);
219 
220 	buf += 2;
221 	buf[3] = '\0';
222 	class->pc_code = strtol(buf, NULL, 16);
223 	buf += 4;
224 	(void) strlcpy(class->pc_name, buf, PCI_NAME_MAX);
225 
226 	return (class);
227 }
228 
229 static pcidb_subclass_t *
230 pcidb_parse_subclass(char *buf, pcidb_class_t *class)
231 {
232 	pcidb_subclass_t *sub;
233 
234 	sub = malloc(sizeof (pcidb_subclass_t));
235 	if (sub == NULL)
236 		return (NULL);
237 
238 	list_create(&sub->psc_progifs, sizeof (pcidb_progif_t),
239 	    offsetof(pcidb_progif_t, pp_link));
240 	sub->psc_class = class;
241 	list_insert_tail(&class->pc_subclass, sub);
242 
243 	buf++;
244 	buf[3] = '\0';
245 	sub->psc_code = strtol(buf, NULL, 16);
246 	buf += 4;
247 	(void) strlcpy(sub->psc_name, buf, PCI_NAME_MAX);
248 
249 	return (sub);
250 }
251 
252 static pcidb_progif_t *
253 pcidb_parse_progif(char *buf, pcidb_subclass_t *sub)
254 {
255 	pcidb_progif_t *prog;
256 
257 	prog = malloc(sizeof (pcidb_progif_t));
258 	if (prog == NULL) {
259 		return (NULL);
260 	}
261 
262 	prog->pp_subclass = sub;
263 	list_insert_tail(&sub->psc_progifs, prog);
264 
265 	buf += 2;
266 	buf[3] = '\0';
267 	prog->pp_code = strtol(buf, NULL, 16);
268 	buf += 4;
269 	(void) strlcpy(prog->pp_name, buf, PCI_NAME_MAX);
270 
271 	return (prog);
272 }
273 
274 static int
275 readline(FILE *f, char *buf, size_t len)
276 {
277 	for (;;) {
278 		char *c;
279 
280 		if (fgets(buf, len, f) == NULL)
281 			return (-1);
282 
283 		if ((c = strchr(buf, '\n')) != NULL)
284 			*c = '\0';
285 
286 		if (buf[0] != '#' && buf[0] != '\0')
287 			return (0);
288 	}
289 }
290 
291 static int
292 parse_db(FILE *f, pcidb_hdl_t *hdl)
293 {
294 	pcidb_vendor_t *vend = NULL;
295 	pcidb_device_t *dev = NULL;
296 	pcidb_class_t *class = NULL;
297 	pcidb_subclass_t *sub = NULL;
298 	pcidb_parse_t state = PDB_INIT;
299 
300 	for (;;) {
301 		char buf[1024];
302 
303 		errno = 0;
304 		if (readline(f, buf, sizeof (buf)) != 0) {
305 			if (errno != 0)
306 				return (-1);
307 			else
308 				return (0);
309 		}
310 
311 newstate:
312 		switch (state) {
313 		case PDB_INIT:
314 			vend = NULL;
315 			dev = NULL;
316 			class = NULL;
317 			sub = NULL;
318 			if (buf[0] == 'C') {
319 				state = PDB_CLASS;
320 			} else {
321 				state = PDB_VENDOR;
322 			}
323 			goto newstate;
324 		case PDB_VENDOR:
325 			vend = parse_vendor(buf, hdl);
326 			if (vend == NULL)
327 				return (-1);
328 			state = PDB_DEVICE;
329 			break;
330 		case PDB_DEVICE:
331 			if (buf[0] != '\t') {
332 				state = PDB_INIT;
333 				goto newstate;
334 			}
335 
336 			if (buf[1] == '\t') {
337 				state = PDB_SUBDEV;
338 				goto newstate;
339 			}
340 
341 			assert(vend != NULL);
342 			dev = parse_device(buf, vend);
343 			if (dev == NULL)
344 				return (0);
345 			break;
346 		case PDB_SUBDEV:
347 			if (buf[0] != '\t') {
348 				state = PDB_INIT;
349 				goto newstate;
350 			}
351 
352 			if (buf[0] == '\t' && buf[1] != '\t') {
353 				state = PDB_DEVICE;
354 				goto newstate;
355 			}
356 
357 			assert(buf[0] == '\t' && buf[1] == '\t');
358 			assert(dev != NULL);
359 			if (parse_subdev(buf, dev) == NULL) {
360 				return (-1);
361 			}
362 			break;
363 		case PDB_CLASS:
364 			class = pcidb_parse_class(buf, hdl);
365 			state = PDB_SUBCLASS;
366 			break;
367 		case PDB_SUBCLASS:
368 			if (buf[0] != '\t') {
369 				state = PDB_INIT;
370 				goto newstate;
371 			}
372 
373 			if (buf[1] == '\t') {
374 				state = PDB_PROGIF;
375 				goto newstate;
376 			}
377 
378 			assert(class != NULL);
379 			sub = pcidb_parse_subclass(buf, class);
380 			if (sub == NULL) {
381 				return (-1);
382 			}
383 			break;
384 		case PDB_PROGIF:
385 			if (buf[0] != '\t') {
386 				state = PDB_INIT;
387 				goto newstate;
388 			}
389 
390 			if (buf[0] == '\t' && buf[1] != '\t') {
391 				state = PDB_SUBCLASS;
392 				goto newstate;
393 			}
394 
395 			assert(sub != NULL);
396 			if (pcidb_parse_progif(buf, sub) == NULL) {
397 				return (-1);
398 			}
399 			break;
400 		}
401 	}
402 }
403 
404 pcidb_hdl_t *
405 pcidb_open(int version)
406 {
407 	pcidb_hdl_t *h;
408 	FILE *f;
409 
410 	if (version != PCIDB_VERSION) {
411 		errno = EINVAL;
412 		return (NULL);
413 	}
414 
415 	h = malloc(sizeof (pcidb_hdl_t));
416 	if (h == NULL)
417 		return (NULL);
418 
419 	list_create(&h->ph_vendors, sizeof (pcidb_vendor_t),
420 	    offsetof(pcidb_vendor_t, pv_link));
421 	list_create(&h->ph_classes, sizeof (pcidb_class_t),
422 	    offsetof(pcidb_class_t, pc_link));
423 
424 	f = fopen(pci_db, "rF");
425 	if (f == NULL) {
426 		free(h);
427 		return (NULL);
428 	}
429 
430 	if (parse_db(f, h) < 0) {
431 		(void) fclose(f);
432 		pcidb_close(h);
433 		return (NULL);
434 	}
435 
436 	(void) fclose(f);
437 
438 	return (h);
439 }
440 
441 void
442 pcidb_close(pcidb_hdl_t *hdl)
443 {
444 	pcidb_vendor_t *vend;
445 	pcidb_class_t *class;
446 
447 	if (hdl == NULL)
448 		return;
449 
450 	while ((vend = list_remove_head(&hdl->ph_vendors)) != NULL) {
451 		pcidb_device_t *dev;
452 
453 		while ((dev = list_remove_head(&vend->pv_devs)) != NULL) {
454 			pcidb_subvd_t *sub;
455 
456 			while ((sub = list_remove_head(&dev->pd_subs)) !=
457 			    NULL) {
458 				free(sub);
459 			}
460 			list_destroy(&dev->pd_subs);
461 			free(dev);
462 		}
463 		list_destroy(&vend->pv_devs);
464 		free(vend);
465 	}
466 	list_destroy(&hdl->ph_vendors);
467 
468 	while ((class = list_remove_head(&hdl->ph_classes)) != NULL) {
469 		pcidb_subclass_t *sub;
470 
471 		while ((sub = list_remove_head(&class->pc_subclass)) != NULL) {
472 			pcidb_progif_t *prog;
473 
474 			while ((prog = list_remove_head(&sub->psc_progifs)) !=
475 			    NULL) {
476 				free(prog);
477 			}
478 			list_destroy(&sub->psc_progifs);
479 			free(sub);
480 		}
481 		list_destroy(&class->pc_subclass);
482 		free(class);
483 	}
484 	list_destroy(&hdl->ph_classes);
485 
486 	free(hdl);
487 }
488 
489 pcidb_vendor_t *
490 pcidb_lookup_vendor(pcidb_hdl_t *hdl, uint16_t id)
491 {
492 	pcidb_vendor_t *v;
493 
494 	for (v = list_head(&hdl->ph_vendors); v != NULL;
495 	    v = list_next(&hdl->ph_vendors, v)) {
496 		if (v->pv_id == id)
497 			return (v);
498 	}
499 
500 	return (NULL);
501 }
502 
503 const char *
504 pcidb_vendor_name(pcidb_vendor_t *vend)
505 {
506 	return (vend->pv_name);
507 }
508 
509 uint16_t
510 pcidb_vendor_id(pcidb_vendor_t *vend)
511 {
512 	return (vend->pv_id);
513 }
514 
515 pcidb_vendor_t *
516 pcidb_vendor_iter(pcidb_hdl_t *hdl)
517 {
518 	return (list_head(&hdl->ph_vendors));
519 }
520 
521 pcidb_vendor_t *
522 pcidb_vendor_iter_next(pcidb_vendor_t *vend)
523 {
524 	assert(vend != NULL);
525 	return (list_next(&vend->pv_hdl->ph_vendors, vend));
526 }
527 
528 pcidb_device_t *
529 pcidb_lookup_device_by_vendor(pcidb_vendor_t *vend, uint16_t id)
530 {
531 	assert(vend != NULL);
532 
533 	for (pcidb_device_t *dev = list_head(&vend->pv_devs); dev != NULL;
534 	    dev = list_next(&vend->pv_devs, dev)) {
535 		if (dev->pd_id == id)
536 			return (dev);
537 	}
538 
539 	return (NULL);
540 }
541 
542 pcidb_device_t *
543 pcidb_lookup_device(pcidb_hdl_t *hdl, uint16_t vid, uint16_t did)
544 {
545 	pcidb_vendor_t *vend;
546 
547 	vend = pcidb_lookup_vendor(hdl, vid);
548 	if (vend == NULL)
549 		return (NULL);
550 
551 	return (pcidb_lookup_device_by_vendor(vend, did));
552 }
553 
554 pcidb_device_t *
555 pcidb_device_iter(pcidb_vendor_t *vend)
556 {
557 	return (list_head(&vend->pv_devs));
558 }
559 
560 pcidb_device_t *
561 pcidb_device_iter_next(pcidb_device_t *dev)
562 {
563 	return (list_next(&dev->pd_vend->pv_devs, dev));
564 }
565 
566 const char *
567 pcidb_device_name(pcidb_device_t *dev)
568 {
569 	return (dev->pd_name);
570 }
571 
572 uint16_t
573 pcidb_device_id(pcidb_device_t *dev)
574 {
575 	return (dev->pd_id);
576 }
577 
578 pcidb_vendor_t *
579 pcidb_device_vendor(pcidb_device_t *dev)
580 {
581 	return (dev->pd_vend);
582 }
583 
584 pcidb_subvd_t *
585 pcidb_lookup_subvd_by_device(pcidb_device_t *dev, uint16_t svid, uint16_t sdid)
586 {
587 	pcidb_subvd_t *sub;
588 
589 	assert(dev != NULL);
590 
591 	for (sub = list_head(&dev->pd_subs); sub != NULL;
592 	    sub = list_next(&dev->pd_subs, sub)) {
593 		if (sub->ps_vid == svid && sub->ps_did == sdid)
594 			return (sub);
595 	}
596 
597 	return (NULL);
598 }
599 
600 pcidb_subvd_t *
601 pcidb_lookup_subvd_by_vendor(pcidb_vendor_t *vend, uint16_t devid,
602     uint16_t svid, uint16_t sdid)
603 {
604 	pcidb_device_t *dev;
605 
606 	assert(vend != NULL);
607 	dev = pcidb_lookup_device_by_vendor(vend, devid);
608 	if (dev == NULL)
609 		return (NULL);
610 
611 	return (pcidb_lookup_subvd_by_device(dev, svid, sdid));
612 }
613 
614 pcidb_subvd_t *
615 pcidb_lookup_subvd(pcidb_hdl_t *hdl, uint16_t vid, uint16_t did, uint16_t svid,
616     uint16_t sdid)
617 {
618 	pcidb_device_t *dev;
619 
620 	assert(hdl != NULL);
621 	dev = pcidb_lookup_device(hdl, vid, did);
622 	if (dev == NULL)
623 		return (NULL);
624 
625 	return (pcidb_lookup_subvd_by_device(dev, svid, sdid));
626 }
627 
628 pcidb_subvd_t *
629 pcidb_subvd_iter(pcidb_device_t *dev)
630 {
631 	return (list_head(&dev->pd_subs));
632 }
633 
634 pcidb_subvd_t *
635 pcidb_subvd_iter_next(pcidb_subvd_t *sub)
636 {
637 	return (list_next(&sub->ps_dev->pd_subs, sub));
638 }
639 
640 const char *
641 pcidb_subvd_name(pcidb_subvd_t *sub)
642 {
643 	return (sub->ps_name);
644 }
645 
646 uint16_t
647 pcidb_subvd_svid(pcidb_subvd_t *sub)
648 {
649 	return (sub->ps_vid);
650 }
651 
652 uint16_t
653 pcidb_subvd_sdid(pcidb_subvd_t *sub)
654 {
655 	return (sub->ps_did);
656 }
657 
658 pcidb_device_t *
659 pcidb_subvd_device(pcidb_subvd_t *sub)
660 {
661 	return (sub->ps_dev);
662 }
663 
664 pcidb_vendor_t *
665 pcidb_subvd_vendor(pcidb_subvd_t *sub)
666 {
667 	return (sub->ps_vend);
668 }
669 
670 
671 pcidb_class_t *
672 pcidb_lookup_class(pcidb_hdl_t *hdl, uint8_t code)
673 {
674 	for (pcidb_class_t *class = list_head(&hdl->ph_classes); class != NULL;
675 	    class = list_next(&hdl->ph_classes, class)) {
676 		if (class->pc_code == code) {
677 			return (class);
678 		}
679 	}
680 
681 	return (NULL);
682 }
683 
684 pcidb_class_t *
685 pcidb_class_iter(pcidb_hdl_t *hdl)
686 {
687 	return (list_head(&hdl->ph_classes));
688 }
689 
690 pcidb_class_t *
691 pcidb_class_iter_next(pcidb_class_t *class)
692 {
693 	return (list_next(&class->pc_hdl->ph_classes, class));
694 }
695 
696 const char *
697 pcidb_class_name(pcidb_class_t *class)
698 {
699 	return (class->pc_name);
700 }
701 
702 uint8_t
703 pcidb_class_code(pcidb_class_t *class)
704 {
705 	return (class->pc_code);
706 }
707 
708 pcidb_subclass_t *
709 pcidb_lookup_subclass(pcidb_hdl_t *hdl, uint8_t ccode, uint8_t subcode)
710 {
711 	pcidb_class_t *class;
712 
713 	class = pcidb_lookup_class(hdl, ccode);
714 	if (class == NULL) {
715 		return (NULL);
716 	}
717 
718 	return (pcidb_lookup_subclass_by_class(class, subcode));
719 }
720 
721 pcidb_subclass_t *
722 pcidb_lookup_subclass_by_class(pcidb_class_t *class, uint8_t code)
723 {
724 	for (pcidb_subclass_t *sub = list_head(&class->pc_subclass);
725 	    sub != NULL; sub = list_next(&class->pc_subclass, sub)) {
726 		if (sub->psc_code == code) {
727 			return (sub);
728 		}
729 	}
730 
731 	return (NULL);
732 }
733 
734 pcidb_subclass_t *
735 pcidb_subclass_iter(pcidb_class_t *class)
736 {
737 	return (list_head(&class->pc_subclass));
738 }
739 
740 pcidb_subclass_t *
741 pcidb_subclass_iter_next(pcidb_subclass_t *sub)
742 {
743 	return (list_next(&sub->psc_class->pc_subclass, sub));
744 }
745 
746 const char *
747 pcidb_subclass_name(pcidb_subclass_t *sub)
748 {
749 	return (sub->psc_name);
750 }
751 
752 uint8_t
753 pcidb_subclass_code(pcidb_subclass_t *sub)
754 {
755 	return (sub->psc_code);
756 }
757 
758 pcidb_progif_t *
759 pcidb_lookup_progif(pcidb_hdl_t *hdl, uint8_t ccode, uint8_t scode,
760     uint8_t pcode)
761 {
762 	pcidb_subclass_t *sub;
763 
764 	sub = pcidb_lookup_subclass(hdl, ccode, scode);
765 	if (sub == NULL) {
766 		return (NULL);
767 	}
768 
769 	return (pcidb_lookup_progif_by_subclass(sub, pcode));
770 }
771 
772 pcidb_progif_t *
773 pcidb_lookup_progif_by_subclass(pcidb_subclass_t *sub, uint8_t code)
774 {
775 	for (pcidb_progif_t *prog = list_head(&sub->psc_progifs); prog != NULL;
776 	    prog = list_next(&sub->psc_progifs, prog)) {
777 		if (prog->pp_code == code) {
778 			return (prog);
779 		}
780 	}
781 
782 	return (NULL);
783 }
784 
785 pcidb_progif_t *
786 pcidb_progif_iter(pcidb_subclass_t *sub)
787 {
788 	return (list_head(&sub->psc_progifs));
789 }
790 
791 pcidb_progif_t *
792 pcidb_progif_iter_next(pcidb_progif_t *prog)
793 {
794 	return (list_next(&prog->pp_subclass->psc_progifs, prog));
795 }
796 
797 const char *
798 pcidb_progif_name(pcidb_progif_t *prog)
799 {
800 	return (prog->pp_name);
801 }
802 
803 uint8_t
804 pcidb_progif_code(pcidb_progif_t *prog)
805 {
806 	return (prog->pp_code);
807 }
808