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