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 (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 * 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 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 * 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 * 398 pcidb_vendor_name(pcidb_vendor_t *v) 399 { 400 return (v->pv_name); 401 } 402 403 uint16_t 404 pcidb_vendor_id(pcidb_vendor_t *v) 405 { 406 return (v->pv_id); 407 } 408 409 pcidb_vendor_t * 410 pcidb_vendor_iter(pcidb_hdl_t *h) 411 { 412 return (h->ph_vstart); 413 } 414 415 pcidb_vendor_t * 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 * 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 * 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 * 448 pcidb_device_iter(pcidb_vendor_t *v) 449 { 450 return (v->pv_dstart); 451 } 452 453 pcidb_device_t * 454 pcidb_device_iter_next(pcidb_device_t *d) 455 { 456 return (d->pd_next); 457 } 458 459 const char * 460 pcidb_device_name(pcidb_device_t *d) 461 { 462 return (d->pd_name); 463 } 464 465 uint16_t 466 pcidb_device_id(pcidb_device_t *d) 467 { 468 return (d->pd_id); 469 } 470 471 pcidb_vendor_t * 472 pcidb_device_vendor(pcidb_device_t *d) 473 { 474 return (d->pd_vend); 475 } 476 477 pcidb_subvd_t * 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 * 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 * 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 * 520 pcidb_subvd_iter(pcidb_device_t *d) 521 { 522 return (d->pd_sstart); 523 } 524 525 pcidb_subvd_t * 526 pcidb_subvd_iter_next(pcidb_subvd_t *s) 527 { 528 return (s->ps_next); 529 } 530 531 const char * 532 pcidb_subvd_name(pcidb_subvd_t *s) 533 { 534 return (s->ps_name); 535 } 536 537 uint16_t 538 pcidb_subvd_svid(pcidb_subvd_t *s) 539 { 540 return (s->ps_vid); 541 } 542 543 uint16_t 544 pcidb_subvd_sdid(pcidb_subvd_t *s) 545 { 546 return (s->ps_did); 547 } 548 549 pcidb_device_t * 550 pcidb_subvd_device(pcidb_subvd_t *s) 551 { 552 return (s->ps_dev); 553 } 554 555 pcidb_vendor_t * 556 pcidb_subvd_vendor(pcidb_subvd_t *s) 557 { 558 return (s->ps_vend); 559 } 560