dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libpcidb / common / pcidb.c
blobedc66f33e1caa38e024155a7b35c2c8129f70eeb
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
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.
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.
36 * Parsing information:
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
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.
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.
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>
61 #include "pcidb.h"
63 #define PCI_NAME_MAX 256
64 #define PCI_READLINE 1024
66 /* Forward declarations */
67 struct pcidb_vendor;
68 struct pcidb_device;
69 struct pcidb_subvd;
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;
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;
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;
100 struct pcidb_hdl {
101 pcidb_vendor_t *ph_vstart;
102 pcidb_vendor_t *ph_vend;
105 typedef enum pcidb_parse {
106 PDB_VENDOR,
107 PDB_DEVICE,
108 PDB_SUBDEV
109 } pcidb_parse_t;
111 static const char *pci_db = "/usr/share/hwdata/pci.ids";
113 static void
114 pcihdl_add_vendor(pcidb_hdl_t *hdl, pcidb_vendor_t *v)
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;
129 static pcidb_vendor_t *
130 parse_vendor(char *buf, pcidb_hdl_t *hdl)
132 pcidb_vendor_t *v;
133 size_t len;
135 v = malloc(sizeof (pcidb_vendor_t));
136 if (v == NULL)
137 return (NULL);
139 pcihdl_add_vendor(hdl, v);
140 v->pv_dstart = NULL;
141 v->pv_dend = NULL;
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';
150 (void) strlcpy(v->pv_name, buf, PCI_NAME_MAX);
152 return (v);
155 static void
156 insert_device(pcidb_vendor_t *v, pcidb_device_t *d)
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;
172 static pcidb_device_t *
173 parse_device(char *buf, pcidb_vendor_t *v)
175 pcidb_device_t *d;
176 size_t len;
178 d = malloc(sizeof (pcidb_device_t));
179 if (d == NULL)
180 return (d);
182 d->pd_sstart = NULL;
183 d->pd_send = NULL;
184 insert_device(v, d);
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';
194 (void) strlcpy(d->pd_name, buf, PCI_NAME_MAX);
195 return (d);
198 static void
199 insert_subdev(pcidb_device_t *d, pcidb_subvd_t *s)
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;
216 static pcidb_subvd_t *
217 parse_subdev(char *buf, pcidb_device_t *d)
219 pcidb_subvd_t *s;
220 size_t len;
222 s = malloc(sizeof (pcidb_subvd_t));
223 if (s == NULL)
224 return (NULL);
225 insert_subdev(d, s);
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;
235 len = strlen(buf);
236 if (buf[len-1] == '\n')
237 buf[len-1] = '\0';
239 (void) strlcpy(s->ps_name, buf, PCI_NAME_MAX);
241 return (s);
244 static int
245 readline(FILE *f, char *buf, size_t len)
247 for (;;) {
248 if (fgets(buf, len, f) == NULL)
249 return (-1);
251 if (buf[0] == 'C')
252 return (-1);
254 if (buf[0] != '#' && buf[0] != '\n')
255 return (0);
259 static int
260 parse_db(FILE *f, pcidb_hdl_t *hdl)
262 char buf[1024];
263 pcidb_vendor_t *v = NULL;
264 pcidb_device_t *d = NULL;
265 pcidb_parse_t state = PDB_VENDOR;
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);
276 newstate:
277 switch (state) {
278 case PDB_VENDOR:
279 v = parse_vendor(buf, hdl);
280 if (v == NULL)
281 return (0);
282 state = PDB_DEVICE;
283 continue;
284 case PDB_DEVICE:
285 if (buf[0] != '\t') {
286 state = PDB_VENDOR;
287 goto newstate;
290 if (buf[1] == '\t') {
291 state = PDB_SUBDEV;
292 goto newstate;
295 assert(v != NULL);
296 d = parse_device(buf, v);
297 if (d == NULL)
298 return (0);
299 continue;
300 case PDB_SUBDEV:
301 if (buf[0] != '\t') {
302 state = PDB_VENDOR;
303 goto newstate;
306 if (buf[0] == '\t' && buf[1] != '\t') {
307 state = PDB_DEVICE;
308 goto newstate;
311 assert(buf[0] == '\t' && buf[1] == '\t');
312 assert(d != NULL);
313 (void) parse_subdev(buf, d);
318 pcidb_hdl_t *
319 pcidb_open(int version)
321 pcidb_hdl_t *h;
322 FILE *f;
324 if (version != PCIDB_VERSION) {
325 errno = EINVAL;
326 return (NULL);
329 h = malloc(sizeof (pcidb_hdl_t));
330 if (h == NULL)
331 return (NULL);
333 h->ph_vstart = NULL;
334 h->ph_vend = NULL;
336 f = fopen(pci_db, "rF");
337 if (f == NULL) {
338 free(h);
339 return (NULL);
342 if (parse_db(f, h) < 0) {
343 pcidb_close(h);
344 free(h);
345 return (NULL);
348 return (h);
351 void
352 pcidb_close(pcidb_hdl_t *h)
354 pcidb_vendor_t *v, *tv;
356 pcidb_device_t *d, *td;
357 pcidb_subvd_t *s, *ts;
359 if (h == NULL)
360 return;
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);
372 td = d;
373 d = d->pd_next;
374 free(td);
376 tv = v;
377 v = v->pv_next;
378 free(tv);
381 free(h);
384 pcidb_vendor_t *
385 pcidb_lookup_vendor(pcidb_hdl_t *hdl, uint16_t id)
387 pcidb_vendor_t *v;
389 for (v = hdl->ph_vstart; v != NULL; v = v->pv_next) {
390 if (v->pv_id == id)
391 return (v);
394 return (NULL);
397 const char *
398 pcidb_vendor_name(pcidb_vendor_t *v)
400 return (v->pv_name);
403 uint16_t
404 pcidb_vendor_id(pcidb_vendor_t *v)
406 return (v->pv_id);
409 pcidb_vendor_t *
410 pcidb_vendor_iter(pcidb_hdl_t *h)
412 return (h->ph_vstart);
415 pcidb_vendor_t *
416 pcidb_vendor_iter_next(pcidb_vendor_t *v)
418 assert(v != NULL);
419 return (v->pv_next);
422 pcidb_device_t *
423 pcidb_lookup_device_by_vendor(pcidb_vendor_t *v, uint16_t id)
425 pcidb_device_t *d;
426 assert(v != NULL);
428 for (d = v->pv_dstart; d != NULL; d = d->pd_next)
429 if (d->pd_id == id)
430 return (d);
432 return (NULL);
435 pcidb_device_t *
436 pcidb_lookup_device(pcidb_hdl_t *h, uint16_t vid, uint16_t did)
438 pcidb_vendor_t *v;
440 v = pcidb_lookup_vendor(h, vid);
441 if (v == NULL)
442 return (NULL);
444 return (pcidb_lookup_device_by_vendor(v, did));
447 pcidb_device_t *
448 pcidb_device_iter(pcidb_vendor_t *v)
450 return (v->pv_dstart);
453 pcidb_device_t *
454 pcidb_device_iter_next(pcidb_device_t *d)
456 return (d->pd_next);
459 const char *
460 pcidb_device_name(pcidb_device_t *d)
462 return (d->pd_name);
465 uint16_t
466 pcidb_device_id(pcidb_device_t *d)
468 return (d->pd_id);
471 pcidb_vendor_t *
472 pcidb_device_vendor(pcidb_device_t *d)
474 return (d->pd_vend);
477 pcidb_subvd_t *
478 pcidb_lookup_subvd_by_device(pcidb_device_t *d, uint16_t svid, uint16_t sdid)
480 pcidb_subvd_t *s;
482 assert(d != NULL);
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);
488 return (NULL);
491 pcidb_subvd_t *
492 pcidb_lookup_subvd_by_vendor(pcidb_vendor_t *v, uint16_t devid, uint16_t svid,
493 uint16_t sdid)
495 pcidb_device_t *d;
497 assert(v != NULL);
498 d = pcidb_lookup_device_by_vendor(v, devid);
499 if (d == NULL)
500 return (NULL);
502 return (pcidb_lookup_subvd_by_device(d, svid, sdid));
505 pcidb_subvd_t *
506 pcidb_lookup_subvd(pcidb_hdl_t *h, uint16_t vid, uint16_t did, uint16_t svid,
507 uint16_t sdid)
509 pcidb_device_t *d;
511 assert(h != NULL);
512 d = pcidb_lookup_device(h, vid, did);
513 if (d == NULL)
514 return (NULL);
516 return (pcidb_lookup_subvd_by_device(d, svid, sdid));
519 pcidb_subvd_t *
520 pcidb_subvd_iter(pcidb_device_t *d)
522 return (d->pd_sstart);
525 pcidb_subvd_t *
526 pcidb_subvd_iter_next(pcidb_subvd_t *s)
528 return (s->ps_next);
531 const char *
532 pcidb_subvd_name(pcidb_subvd_t *s)
534 return (s->ps_name);
537 uint16_t
538 pcidb_subvd_svid(pcidb_subvd_t *s)
540 return (s->ps_vid);
543 uint16_t
544 pcidb_subvd_sdid(pcidb_subvd_t *s)
546 return (s->ps_did);
549 pcidb_device_t *
550 pcidb_subvd_device(pcidb_subvd_t *s)
552 return (s->ps_dev);
555 pcidb_vendor_t *
556 pcidb_subvd_vendor(pcidb_subvd_t *s)
558 return (s->ps_vend);