1 /* 2 * (C) Copyright IBM Corporation 2006 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * on the rights to use, copy, modify, merge, publish, distribute, sub 9 * license, and/or sell copies of the Software, and to permit persons to whom 10 * the Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 19 * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 */ 24 25 /** 26 * \file common_device_name.c 27 * Support routines used to determine the vendor or device names associated 28 * with a particular device or vendor. 29 */ 30 31 #include "config.h" 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <ctype.h> 35 36 #if defined(HAVE_STRING_H) 37 # include <string.h> 38 #elif defined(HAVE_STRINGS_H) 39 # include <strings.h> 40 #endif 41 42 #if defined(HAVE_INTTYPES_H) 43 # include <inttypes.h> 44 #elif defined(HAVE_STDINT_H) 45 # include <stdint.h> 46 #endif 47 48 #include "pciaccess.h" 49 #include "pciaccess_private.h" 50 51 #define DO_MATCH(a,b) (((a) == PCI_MATCH_ANY) || ((a) == (b))) 52 53 #ifdef HAVE_ZLIB 54 #include <zlib.h> 55 typedef gzFile pci_id_file; 56 57 static pci_id_file 58 pci_id_file_open(void) 59 { 60 pci_id_file result; 61 62 result = gzopen(PCIIDS_PATH "/pci.ids.gz", "rb"); 63 if (result) 64 return result; 65 66 return gzopen(PCIIDS_PATH "/pci.ids", "rb"); 67 } 68 69 #define pci_id_file_gets(l, s, f) gzgets(f, l, s) 70 #define pci_id_file_close(f) gzclose(f) 71 #else 72 typedef FILE pci_id_file; 73 #define pci_id_file_open() fopen(PCIIDS_PATH "/pci.ids", "r") 74 #define pci_id_file_gets(l, s, f) fgets(l, s, f) 75 #define pci_id_file_close(f) fclose(f) 76 #endif 77 78 /** 79 * Node for sorting vendor IDs. 80 * 81 * Each structure forms an internal node of an n-way tree. Each node selects 82 * \c pci_id_node::bits number of bits from the vendor ID. Starting from the 83 * root of the tree, a slice of the low-order bits of the vendor ID are 84 * selected and used as an index into the \c pci_id_node::children array. 85 * 86 * At the leaf nodes (i.e., the node entered when all 16 bits of the vendor ID 87 * have been used), the \c pci_id_node::children is actually an array of 88 * pointers to \c pci_id_leaf structures. 89 * 90 * \todo 91 * Determine if there is a cleaner way (in the source code) to have the 92 * \c children array change type based on whether the node is internal or 93 * a leaf. 94 * 95 * \todo 96 * Currently \c bits is always 4. Decide if this value can ever change 97 * (i.e., to pull-up levels of the n-way tree when all the children's children 98 * are full). If it can, rip it out and hard-code it to 4 everywhere. 99 */ 100 struct pci_id_node { 101 unsigned bits; 102 struct pci_id_node * children[16]; 103 }; 104 105 struct pci_id_leaf { 106 uint16_t vendor; 107 const char * vendor_name; 108 109 size_t num_devices; 110 struct pci_device_leaf * devices; 111 }; 112 113 struct pci_device_leaf { 114 struct pci_id_match id; 115 const char * device_name; 116 }; 117 118 /** 119 * Root of the PCI vendor ID search tree. 120 */ 121 _pci_hidden struct pci_id_node * tree = NULL; 122 123 /** 124 * Get a pointer to the leaf node for a vendor ID. 125 * 126 * If the vendor ID does not exist in the tree, it is added. 127 */ 128 static struct pci_id_leaf * 129 insert( uint16_t vendor ) 130 { 131 struct pci_id_node * n; 132 unsigned bits = 0; 133 134 if ( tree == NULL ) { 135 tree = calloc( 1, sizeof( struct pci_id_node ) ); 136 tree->bits = 4; 137 } 138 139 n = tree; 140 while ( n != NULL ) { 141 const unsigned used_bits = n->bits; 142 const unsigned mask = (1 << used_bits) - 1; 143 const unsigned idx = (vendor & (mask << bits)) >> bits; 144 145 146 if ( bits >= 16 ) { 147 break; 148 } 149 150 bits += used_bits; 151 152 if ( n->children[ idx ] == NULL ) { 153 if ( bits < 16 ) { 154 struct pci_id_node * child = 155 calloc( 1, sizeof( struct pci_id_node ) ); 156 157 child->bits = 4; 158 159 n->children[ idx ] = child; 160 } 161 else { 162 struct pci_id_leaf * leaf = 163 calloc( 1, sizeof( struct pci_id_leaf ) ); 164 165 leaf->vendor = vendor; 166 167 n->children[ idx ] = (struct pci_id_node *) leaf; 168 } 169 } 170 171 n = n->children[ idx ]; 172 } 173 174 return (struct pci_id_leaf *) n; 175 } 176 177 178 /** 179 * Populate a vendor node with all the devices associated with that vendor 180 * 181 * \param vend Vendor node that is to be filled from the pci.ids file. 182 * 183 * \todo 184 * The parsing in this function should be more rhobust. There are some error 185 * cases (i.e., a 0-tab line followed by a 2-tab line) that aren't handled 186 * correctly. I don't think there are any security problems with the code, 187 * but it's not impossible. 188 */ 189 static void 190 populate_vendor( struct pci_id_leaf * vend, int fill_device_data ) 191 { 192 pci_id_file * f; 193 char buf[128]; 194 unsigned vendor = PCI_MATCH_ANY; 195 196 197 /* If the device tree for this vendor is already populated, don't do 198 * anything. This avoids wasted processing and potential memory leaks. 199 */ 200 if (vend->num_devices != 0) { 201 return; 202 } 203 204 f = pci_id_file_open(); 205 206 /* If the pci.ids file could not be opened, there's nothing we can do. 207 */ 208 if (f == NULL) { 209 return; 210 } 211 212 while( pci_id_file_gets( buf, sizeof( buf ), f ) != NULL ) { 213 unsigned num_tabs; 214 char * new_line; 215 size_t length; 216 217 /* Each line either starts with zero, one, or two tabs followed by 218 * a series of 4 hex digits. Any lines not matching that are ignored. 219 */ 220 221 for ( num_tabs = 0 ; num_tabs < 3 ; num_tabs++ ) { 222 if ( buf[ num_tabs ] != '\t' ) { 223 break; 224 } 225 } 226 227 if ( !isxdigit( buf[ num_tabs + 0 ] ) 228 || !isxdigit( buf[ num_tabs + 1 ] ) 229 || !isxdigit( buf[ num_tabs + 2 ] ) 230 || !isxdigit( buf[ num_tabs + 3 ] ) ) { 231 continue; 232 } 233 234 new_line = strchr( buf, '\n' ); 235 if ( new_line != NULL ) { 236 *new_line = '\0'; 237 } 238 239 length = strlen( buf ); 240 (void) memset( buf + length, 0, sizeof( buf ) - length ); 241 242 243 if ( num_tabs == 0 ) { 244 vendor = (unsigned) strtoul( & buf[ num_tabs ], NULL, 16 ); 245 if ( vend->vendor == vendor ) { 246 /* vendor_name may already be set from a previous invocation 247 * of this function with fill_device_data = 0. 248 */ 249 if (vend->vendor_name == NULL) { 250 vend->vendor_name = strdup( & buf[ num_tabs + 6 ] ); 251 } 252 253 /* If we're not going to fill in all of the device data as 254 * well, then bail out now. We have all the information that 255 * we need. 256 */ 257 if ( ! fill_device_data ) { 258 break; 259 } 260 } 261 } 262 else if ( vendor == vend->vendor ) { 263 struct pci_device_leaf * d; 264 struct pci_device_leaf * dev; 265 struct pci_device_leaf * last_dev; 266 267 268 269 d = realloc( vend->devices, (vend->num_devices + 1) 270 * sizeof( struct pci_device_leaf ) ); 271 if ( d == NULL ) { 272 return; 273 } 274 275 last_dev = & d[ vend->num_devices - 1 ]; 276 dev = & d[ vend->num_devices ]; 277 vend->num_devices++; 278 vend->devices = d; 279 280 if ( num_tabs == 1 ) { 281 dev->id.vendor_id = vend->vendor; 282 dev->id.device_id = (unsigned) strtoul( & buf[ num_tabs ], 283 NULL, 16 ); 284 dev->id.subvendor_id = PCI_MATCH_ANY; 285 dev->id.subdevice_id = PCI_MATCH_ANY; 286 287 dev->id.device_class = 0; 288 dev->id.device_class_mask = 0; 289 dev->id.match_data = 0; 290 291 dev->device_name = strdup( & buf[ num_tabs + 6 ] ); 292 } 293 else { 294 dev->id = last_dev->id; 295 296 dev->id.subvendor_id= (unsigned) strtoul( & buf[ num_tabs ], 297 NULL, 16 ); 298 dev->id.subdevice_id = (unsigned) strtoul( & buf[ num_tabs + 5 ], 299 NULL, 16 ); 300 dev->device_name = strdup( & buf[ num_tabs + 5 + 6 ] ); 301 } 302 } 303 } 304 305 pci_id_file_close( f ); 306 } 307 308 309 /** 310 * Find the name of the specified device. 311 * 312 * Finds the actual product name of the specified device. If a subvendor ID 313 * and subdevice ID are specified in \c m, the returned name will be the name 314 * of the subdevice. 315 */ 316 static const char * 317 find_device_name( const struct pci_id_match * m ) 318 { 319 struct pci_id_leaf * vend; 320 unsigned i; 321 322 323 if ( m->vendor_id == PCI_MATCH_ANY ) { 324 return NULL; 325 } 326 327 328 vend = insert( m->vendor_id ); 329 if ( vend == NULL ) { 330 return NULL; 331 } 332 333 if ( vend->num_devices == 0 ) { 334 populate_vendor( vend, 1 ); 335 } 336 337 338 for ( i = 0 ; i < vend->num_devices ; i++ ) { 339 struct pci_device_leaf * d = & vend->devices[ i ]; 340 341 if ( DO_MATCH( m->vendor_id, d->id.vendor_id ) 342 && DO_MATCH( m->device_id, d->id.device_id ) 343 && DO_MATCH( m->subvendor_id, d->id.subvendor_id ) 344 && DO_MATCH( m->subdevice_id, d->id.subdevice_id ) ) { 345 return d->device_name; 346 } 347 } 348 349 return NULL; 350 } 351 352 353 /** 354 * Find the vendor name of the specified device. 355 * 356 * Finds the actual vendor name of the specified device. If a subvendor ID 357 * and subdevice ID are specified in \c m, the returned name will be the name 358 * associated with the subvendor. 359 */ 360 static const char * 361 find_vendor_name( const struct pci_id_match * m ) 362 { 363 struct pci_id_leaf * vend; 364 365 366 if ( m->vendor_id == PCI_MATCH_ANY ) { 367 return NULL; 368 } 369 370 371 vend = insert( m->vendor_id ); 372 if ( vend == NULL ) { 373 return NULL; 374 } 375 376 if ( vend->vendor_name == NULL ) { 377 populate_vendor( vend, 0 ); 378 } 379 380 381 return vend->vendor_name; 382 } 383 384 385 /** 386 * Get a name based on an arbitrary PCI search structure. 387 */ 388 void 389 pci_get_strings( const struct pci_id_match * m, 390 const char ** device_name, 391 const char ** vendor_name, 392 const char ** subdevice_name, 393 const char ** subvendor_name ) 394 { 395 struct pci_id_match temp; 396 397 398 temp = *m; 399 temp.subvendor_id = PCI_MATCH_ANY; 400 temp.subdevice_id = PCI_MATCH_ANY; 401 402 if ( device_name != NULL ) { 403 *device_name = find_device_name( & temp ); 404 } 405 406 if ( vendor_name != NULL ) { 407 *vendor_name = find_vendor_name( & temp ); 408 } 409 410 if ( subdevice_name != NULL ) { 411 *subdevice_name = find_device_name( m ); 412 } 413 414 if ( subvendor_name != NULL ) { 415 *subvendor_name = find_vendor_name( m ); 416 } 417 } 418 419 420 /** 421 * Get the name associated with the device's primary device ID. 422 */ 423 const char * 424 pci_device_get_device_name( const struct pci_device * dev ) 425 { 426 struct pci_id_match m; 427 428 429 m.vendor_id = dev->vendor_id; 430 m.device_id = dev->device_id; 431 m.subvendor_id = PCI_MATCH_ANY; 432 m.subdevice_id = PCI_MATCH_ANY; 433 m.device_class = 0; 434 m.device_class_mask = 0; 435 m.match_data = 0; 436 437 return find_device_name( & m ); 438 } 439 440 441 /** 442 * Get the name associated with the device's subdevice ID. 443 */ 444 const char * 445 pci_device_get_subdevice_name( const struct pci_device * dev ) 446 { 447 struct pci_id_match m; 448 449 450 if ( (dev->subvendor_id == 0) || (dev->subdevice_id == 0) ) { 451 return NULL; 452 } 453 454 m.vendor_id = dev->vendor_id; 455 m.device_id = dev->device_id; 456 m.subvendor_id = dev->subvendor_id; 457 m.subdevice_id = dev->subdevice_id; 458 m.device_class = 0; 459 m.device_class_mask = 0; 460 m.match_data = 0; 461 462 return find_device_name( & m ); 463 } 464 465 466 /** 467 * Get the name associated with the device's primary vendor ID. 468 */ 469 const char * 470 pci_device_get_vendor_name( const struct pci_device * dev ) 471 { 472 struct pci_id_match m; 473 474 475 m.vendor_id = dev->vendor_id; 476 m.device_id = PCI_MATCH_ANY; 477 m.subvendor_id = PCI_MATCH_ANY; 478 m.subdevice_id = PCI_MATCH_ANY; 479 m.device_class = 0; 480 m.device_class_mask = 0; 481 m.match_data = 0; 482 483 return find_vendor_name( & m ); 484 } 485 486 487 /** 488 * Get the name associated with the device's subvendor ID. 489 */ 490 const char * 491 pci_device_get_subvendor_name( const struct pci_device * dev ) 492 { 493 struct pci_id_match m; 494 495 496 if ( dev->subvendor_id == 0 ) { 497 return NULL; 498 } 499 500 501 m.vendor_id = dev->subvendor_id; 502 m.device_id = PCI_MATCH_ANY; 503 m.subvendor_id = PCI_MATCH_ANY; 504 m.subdevice_id = PCI_MATCH_ANY; 505 m.device_class = 0; 506 m.device_class_mask = 0; 507 m.match_data = 0; 508 509 return find_vendor_name( & m ); 510 } 511