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 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 * 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 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 * 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 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 * 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 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 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 (0); 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 (0); 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 * 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 (void) fclose(f); 344 pcidb_close(h); 345 free(h); 346 return (NULL); 347 } 348 349 (void) fclose(f); 350 351 return (h); 352 } 353 354 void 355 pcidb_close(pcidb_hdl_t *h) 356 { 357 pcidb_vendor_t *v, *tv; 358 359 pcidb_device_t *d, *td; 360 pcidb_subvd_t *s, *ts; 361 362 if (h == NULL) 363 return; 364 365 v = h->ph_vstart; 366 while (v != NULL) { 367 d = v->pv_dstart; 368 while (d != NULL) { 369 s = d->pd_sstart; 370 while (s != NULL) { 371 ts = s; 372 s = s->ps_next; 373 free(ts); 374 } 375 td = d; 376 d = d->pd_next; 377 free(td); 378 } 379 tv = v; 380 v = v->pv_next; 381 free(tv); 382 } 383 384 free(h); 385 } 386 387 pcidb_vendor_t * 388 pcidb_lookup_vendor(pcidb_hdl_t *hdl, uint16_t id) 389 { 390 pcidb_vendor_t *v; 391 392 for (v = hdl->ph_vstart; v != NULL; v = v->pv_next) { 393 if (v->pv_id == id) 394 return (v); 395 } 396 397 return (NULL); 398 } 399 400 const char * 401 pcidb_vendor_name(pcidb_vendor_t *v) 402 { 403 return (v->pv_name); 404 } 405 406 uint16_t 407 pcidb_vendor_id(pcidb_vendor_t *v) 408 { 409 return (v->pv_id); 410 } 411 412 pcidb_vendor_t * 413 pcidb_vendor_iter(pcidb_hdl_t *h) 414 { 415 return (h->ph_vstart); 416 } 417 418 pcidb_vendor_t * 419 pcidb_vendor_iter_next(pcidb_vendor_t *v) 420 { 421 assert(v != NULL); 422 return (v->pv_next); 423 } 424 425 pcidb_device_t * 426 pcidb_lookup_device_by_vendor(pcidb_vendor_t *v, uint16_t id) 427 { 428 pcidb_device_t *d; 429 assert(v != NULL); 430 431 for (d = v->pv_dstart; d != NULL; d = d->pd_next) 432 if (d->pd_id == id) 433 return (d); 434 435 return (NULL); 436 } 437 438 pcidb_device_t * 439 pcidb_lookup_device(pcidb_hdl_t *h, uint16_t vid, uint16_t did) 440 { 441 pcidb_vendor_t *v; 442 443 v = pcidb_lookup_vendor(h, vid); 444 if (v == NULL) 445 return (NULL); 446 447 return (pcidb_lookup_device_by_vendor(v, did)); 448 } 449 450 pcidb_device_t * 451 pcidb_device_iter(pcidb_vendor_t *v) 452 { 453 return (v->pv_dstart); 454 } 455 456 pcidb_device_t * 457 pcidb_device_iter_next(pcidb_device_t *d) 458 { 459 return (d->pd_next); 460 } 461 462 const char * 463 pcidb_device_name(pcidb_device_t *d) 464 { 465 return (d->pd_name); 466 } 467 468 uint16_t 469 pcidb_device_id(pcidb_device_t *d) 470 { 471 return (d->pd_id); 472 } 473 474 pcidb_vendor_t * 475 pcidb_device_vendor(pcidb_device_t *d) 476 { 477 return (d->pd_vend); 478 } 479 480 pcidb_subvd_t * 481 pcidb_lookup_subvd_by_device(pcidb_device_t *d, uint16_t svid, uint16_t sdid) 482 { 483 pcidb_subvd_t *s; 484 485 assert(d != NULL); 486 487 for (s = d->pd_sstart; s != NULL; s = s->ps_next) 488 if (s->ps_vid == svid && s->ps_did == sdid) 489 return (s); 490 491 return (NULL); 492 } 493 494 pcidb_subvd_t * 495 pcidb_lookup_subvd_by_vendor(pcidb_vendor_t *v, uint16_t devid, uint16_t svid, 496 uint16_t sdid) 497 { 498 pcidb_device_t *d; 499 500 assert(v != NULL); 501 d = pcidb_lookup_device_by_vendor(v, devid); 502 if (d == NULL) 503 return (NULL); 504 505 return (pcidb_lookup_subvd_by_device(d, svid, sdid)); 506 } 507 508 pcidb_subvd_t * 509 pcidb_lookup_subvd(pcidb_hdl_t *h, uint16_t vid, uint16_t did, uint16_t svid, 510 uint16_t sdid) 511 { 512 pcidb_device_t *d; 513 514 assert(h != NULL); 515 d = pcidb_lookup_device(h, vid, did); 516 if (d == NULL) 517 return (NULL); 518 519 return (pcidb_lookup_subvd_by_device(d, svid, sdid)); 520 } 521 522 pcidb_subvd_t * 523 pcidb_subvd_iter(pcidb_device_t *d) 524 { 525 return (d->pd_sstart); 526 } 527 528 pcidb_subvd_t * 529 pcidb_subvd_iter_next(pcidb_subvd_t *s) 530 { 531 return (s->ps_next); 532 } 533 534 const char * 535 pcidb_subvd_name(pcidb_subvd_t *s) 536 { 537 return (s->ps_name); 538 } 539 540 uint16_t 541 pcidb_subvd_svid(pcidb_subvd_t *s) 542 { 543 return (s->ps_vid); 544 } 545 546 uint16_t 547 pcidb_subvd_sdid(pcidb_subvd_t *s) 548 { 549 return (s->ps_did); 550 } 551 552 pcidb_device_t * 553 pcidb_subvd_device(pcidb_subvd_t *s) 554 { 555 return (s->ps_dev); 556 } 557 558 pcidb_vendor_t * 559 pcidb_subvd_vendor(pcidb_subvd_t *s) 560 { 561 return (s->ps_vend); 562 } 563