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 50 #ifndef PCIIDS_PATH 51 # define PCIIDS_PATH "/usr/share/hwdata" 52 #endif 53 54 #define DO_MATCH(a,b) (((a) == PCI_MATCH_ANY) || ((a) == (b))) 55 56 /** 57 * Node for sorting vendor IDs. 58 * 59 * Each structure forms an internal node of an n-way tree. Each node selects 60 * \c pci_id_node::bits number of bits from the vendor ID. Starting from the 61 * root of the tree, a slice of the low-order bits of the vendor ID are 62 * selected and used as an index into the \c pci_id_node::children array. 63 * 64 * At the leaf nodes (i.e., the node entered when all 16 bits of the vendor ID 65 * have been used), the \c pci_id_node::children is actually an array of 66 * pointers to \c pci_id_leaf structures. 67 * 68 * \todo 69 * Determine if there is a cleaner way (in the source code) to have the 70 * \c children array change type based on whether the node is internal or 71 * a leaf. 72 * 73 * \todo 74 * Currently \c bits is always 4. Decide if this value can ever change 75 * (i.e., to pull-up levels of the n-way tree when all the children's children 76 * are full). If it can, rip it out and hard-code it to 4 everywhere. 77 */ 78 struct pci_id_node { 79 unsigned bits; 80 struct pci_id_node * children[16]; 81 }; 82 83 struct pci_id_leaf { 84 uint16_t vendor; 85 const char * vendor_name; 86 87 size_t num_devices; 88 struct pci_device_leaf * devices; 89 }; 90 91 struct pci_device_leaf { 92 struct pci_id_match id; 93 const char * device_name; 94 }; 95 96 /** 97 * Root of the PCI vendor ID search tree. 98 */ 99 struct pci_id_node * tree = NULL; 100 101 /** 102 * Name of the file containing the PCI ID information. 103 */ 104 static const char pci_id_file[] = PCIIDS_PATH "/pci.ids"; 105 106 107 /** 108 * Get a pointer to the leaf node for a vendor ID. 109 * 110 * If the vendor ID does not exist in the tree, it is added. 111 */ 112 static struct pci_id_leaf * 113 insert( uint16_t vendor ) 114 { 115 struct pci_id_node * n; 116 unsigned bits = 0; 117 118 if ( tree == NULL ) { 119 tree = calloc( 1, sizeof( struct pci_id_node ) ); 120 tree->bits = 4; 121 } 122 123 n = tree; 124 while ( n != NULL ) { 125 const unsigned used_bits = n->bits; 126 const unsigned mask = (1 << used_bits) - 1; 127 const unsigned idx = (vendor & (mask << bits)) >> bits; 128 129 130 if ( bits >= 16 ) { 131 break; 132 } 133 134 bits += used_bits; 135 136 if ( n->children[ idx ] == NULL ) { 137 if ( bits < 16 ) { 138 struct pci_id_node * child = 139 calloc( 1, sizeof( struct pci_id_node ) ); 140 141 child->bits = 4; 142 143 n->children[ idx ] = child; 144 } 145 else { 146 struct pci_id_leaf * leaf = 147 calloc( 1, sizeof( struct pci_id_leaf ) ); 148 149 leaf->vendor = vendor; 150 151 n->children[ idx ] = (struct pci_id_node *) leaf; 152 } 153 } 154 155 n = n->children[ idx ]; 156 } 157 158 return (struct pci_id_leaf *) n; 159 } 160 161 162 /** 163 * Populate a vendor node with all the devices associated with that vendor 164 * 165 * \param vend Vendor node that is to be filled from the pci.ids file. 166 * 167 * \todo 168 * The parsing in this function should be more rhobust. There are some error 169 * cases (i.e., a 0-tab line followed by a 2-tab line) that aren't handled 170 * correctly. I don't think there are any security problems with the code, 171 * but it's not impossible. 172 */ 173 static void 174 populate_vendor( struct pci_id_leaf * vend, int fill_device_data ) 175 { 176 FILE * f = fopen( pci_id_file, "r" ); 177 char buf[128]; 178 unsigned vendor = PCI_MATCH_ANY; 179 180 181 /* If the pci.ids file could not be opened, there's nothing we can do. 182 */ 183 if (f == NULL) { 184 return; 185 } 186 187 188 /* If the device tree for this vendor is already populated, don't do 189 * anything. This avoids wasted processing and potential memory leaks. 190 */ 191 if (vend->num_devices != 0) { 192 fclose(f); 193 return; 194 } 195 196 197 while( fgets( buf, sizeof( buf ), f ) != NULL ) { 198 unsigned num_tabs; 199 char * new_line; 200 size_t length; 201 202 /* Each line either starts with zero, one, or two tabs followed by 203 * a series of 4 hex digits. Any lines not matching that are ignored. 204 */ 205 206 for ( num_tabs = 0 ; num_tabs < 3 ; num_tabs++ ) { 207 if ( buf[ num_tabs ] != '\t' ) { 208 break; 209 } 210 } 211 212 if ( !isxdigit( buf[ num_tabs + 0 ] ) 213 || !isxdigit( buf[ num_tabs + 1 ] ) 214 || !isxdigit( buf[ num_tabs + 2 ] ) 215 || !isxdigit( buf[ num_tabs + 3 ] ) ) { 216 continue; 217 } 218 219 new_line = strchr( buf, '\n' ); 220 if ( new_line != NULL ) { 221 *new_line = '\0'; 222 } 223 224 length = strlen( buf ); 225 (void) memset( buf + length, 0, sizeof( buf ) - length ); 226 227 228 if ( num_tabs == 0 ) { 229 vendor = (unsigned) strtoul( & buf[ num_tabs ], NULL, 16 ); 230 if ( vend->vendor == vendor ) { 231 /* vendor_name may already be set from a previous invocation 232 * of this function with fill_device_data = 0. 233 */ 234 if (vend->vendor_name != NULL) { 235 vend->vendor_name = strdup( & buf[ num_tabs + 6 ] ); 236 } 237 238 /* If we're not going to fill in all of the device data as 239 * well, then bail out now. We have all the information that 240 * we need. 241 */ 242 if ( ! fill_device_data ) { 243 break; 244 } 245 } 246 } 247 else if ( vendor == vend->vendor ) { 248 struct pci_device_leaf * d; 249 struct pci_device_leaf * dev; 250 struct pci_device_leaf * last_dev; 251 252 253 254 d = realloc( vend->devices, (vend->num_devices + 1) 255 * sizeof( struct pci_device_leaf ) ); 256 if ( d == NULL ) { 257 return; 258 } 259 260 last_dev = & d[ vend->num_devices - 1 ]; 261 dev = & d[ vend->num_devices ]; 262 vend->num_devices++; 263 vend->devices = d; 264 265 if ( num_tabs == 1 ) { 266 dev->id.vendor_id = vend->vendor; 267 dev->id.device_id = (unsigned) strtoul( & buf[ num_tabs ], 268 NULL, 16 ); 269 dev->id.subvendor_id = PCI_MATCH_ANY; 270 dev->id.subdevice_id = PCI_MATCH_ANY; 271 272 dev->id.device_class = 0; 273 dev->id.device_class_mask = 0; 274 dev->id.match_data = 0; 275 276 dev->device_name = strdup( & buf[ num_tabs + 6 ] ); 277 } 278 else { 279 dev->id = last_dev->id; 280 281 dev->id.subvendor_id= (unsigned) strtoul( & buf[ num_tabs ], 282 NULL, 16 ); 283 dev->id.subdevice_id = (unsigned) strtoul( & buf[ num_tabs + 5 ], 284 NULL, 16 ); 285 dev->device_name = strdup( & buf[ num_tabs + 5 + 6 ] ); 286 } 287 } 288 } 289 290 fclose( f ); 291 } 292 293 294 /** 295 * Find the name of the specified device. 296 * 297 * Finds the actual product name of the specified device. If a subvendor ID 298 * and subdevice ID are specified in \c m, the returned name will be the name 299 * of the subdevice. 300 */ 301 static const char * 302 find_device_name( const struct pci_id_match * m ) 303 { 304 struct pci_id_leaf * vend; 305 unsigned i; 306 307 308 if ( m->vendor_id == PCI_MATCH_ANY ) { 309 return NULL; 310 } 311 312 313 vend = insert( m->vendor_id ); 314 if ( vend == NULL ) { 315 return NULL; 316 } 317 318 if ( vend->num_devices == 0 ) { 319 populate_vendor( vend, 1 ); 320 } 321 322 323 for ( i = 0 ; i < vend->num_devices ; i++ ) { 324 struct pci_device_leaf * d = & vend->devices[ i ]; 325 326 if ( DO_MATCH( m->vendor_id, d->id.vendor_id ) 327 && DO_MATCH( m->device_id, d->id.device_id ) 328 && DO_MATCH( m->subvendor_id, d->id.subvendor_id ) 329 && DO_MATCH( m->subdevice_id, d->id.subdevice_id ) ) { 330 return d->device_name; 331 } 332 } 333 334 return NULL; 335 } 336 337 338 /** 339 * Find the vendor name of the specified device. 340 * 341 * Finds the actual vendor name of the specified device. If a subvendor ID 342 * and subdevice ID are specified in \c m, the returned name will be the name 343 * associated with the subvendor. 344 */ 345 static const char * 346 find_vendor_name( const struct pci_id_match * m ) 347 { 348 struct pci_id_leaf * vend; 349 350 351 if ( m->vendor_id == PCI_MATCH_ANY ) { 352 return NULL; 353 } 354 355 356 vend = insert( m->vendor_id ); 357 if ( vend == NULL ) { 358 return NULL; 359 } 360 361 if ( vend->vendor_name == NULL ) { 362 populate_vendor( vend, 0 ); 363 } 364 365 366 return vend->vendor_name; 367 } 368 369 370 /** 371 * Get a name based on an arbitrary PCI search structure. 372 */ 373 void 374 pci_get_strings( const struct pci_id_match * m, 375 const char ** device_name, 376 const char ** vendor_name, 377 const char ** subdevice_name, 378 const char ** subvendor_name ) 379 { 380 struct pci_id_match temp; 381 382 383 temp = *m; 384 temp.subvendor_id = PCI_MATCH_ANY; 385 temp.subdevice_id = PCI_MATCH_ANY; 386 387 if ( device_name != NULL ) { 388 *device_name = find_device_name( & temp ); 389 } 390 391 if ( vendor_name != NULL ) { 392 *vendor_name = find_vendor_name( & temp ); 393 } 394 395 if ( subdevice_name != NULL ) { 396 *subdevice_name = find_device_name( m ); 397 } 398 399 if ( subvendor_name != NULL ) { 400 *subvendor_name = find_vendor_name( m ); 401 } 402 } 403 404 405 /** 406 * Get the name associated with the device's primary device ID. 407 */ 408 const char * 409 pci_device_get_device_name( const struct pci_device * dev ) 410 { 411 struct pci_id_match m; 412 413 414 m.vendor_id = dev->vendor_id; 415 m.device_id = dev->device_id; 416 m.subvendor_id = PCI_MATCH_ANY; 417 m.subdevice_id = PCI_MATCH_ANY; 418 m.device_class = 0; 419 m.device_class_mask = 0; 420 m.match_data = 0; 421 422 return find_device_name( & m ); 423 } 424 425 426 /** 427 * Get the name associated with the device's subdevice ID. 428 */ 429 const char * 430 pci_device_get_subdevice_name( const struct pci_device * dev ) 431 { 432 struct pci_id_match m; 433 434 435 if ( (dev->subvendor_id == 0) || (dev->subdevice_id == 0) ) { 436 return NULL; 437 } 438 439 m.vendor_id = dev->vendor_id; 440 m.device_id = dev->device_id; 441 m.subvendor_id = dev->subvendor_id; 442 m.subdevice_id = dev->subdevice_id; 443 m.device_class = 0; 444 m.device_class_mask = 0; 445 m.match_data = 0; 446 447 return find_device_name( & m ); 448 } 449 450 451 /** 452 * Get the name associated with the device's primary vendor ID. 453 */ 454 const char * 455 pci_device_get_vendor_name( const struct pci_device * dev ) 456 { 457 struct pci_id_match m; 458 459 460 m.vendor_id = dev->vendor_id; 461 m.device_id = PCI_MATCH_ANY; 462 m.subvendor_id = PCI_MATCH_ANY; 463 m.subdevice_id = PCI_MATCH_ANY; 464 m.device_class = 0; 465 m.device_class_mask = 0; 466 m.match_data = 0; 467 468 return find_vendor_name( & m ); 469 } 470 471 472 /** 473 * Get the name associated with the device's subvendor ID. 474 */ 475 const char * 476 pci_device_get_subvendor_name( const struct pci_device * dev ) 477 { 478 struct pci_id_match m; 479 480 481 if ( dev->subvendor_id == 0 ) { 482 return NULL; 483 } 484 485 486 m.vendor_id = dev->subvendor_id; 487 m.device_id = PCI_MATCH_ANY; 488 m.subvendor_id = PCI_MATCH_ANY; 489 m.subdevice_id = PCI_MATCH_ANY; 490 m.device_class = 0; 491 m.device_class_mask = 0; 492 m.match_data = 0; 493 494 return find_vendor_name( & m ); 495 } 496