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