2 * (C) Copyright IBM Corporation 2006
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:
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
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.
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.
39 #if defined(HAVE_STRING_H)
41 #elif defined(HAVE_STRINGS_H)
45 #if defined(HAVE_INTTYPES_H)
46 # include <inttypes.h>
47 #elif defined(HAVE_STDINT_H)
51 #include "pciaccess.h"
52 #include "pciaccess_private.h"
54 #define DO_MATCH(a,b) (((a) == PCI_MATCH_ANY) || ((a) == (b)))
59 typedef gzFile pci_id_file;
62 pci_id_file_open(void)
66 result = gzopen(PCIIDS_PATH "/pci.ids.gz", "rb");
70 return gzopen(PCIIDS_PATH "/pci.ids", "rb");
73 #define pci_id_file_gets(l, s, f) gzgets(f, l, s)
74 #define pci_id_file_close(f) gzclose(f)
78 typedef FILE * pci_id_file;
81 pci_id_file_open(void)
86 result = fopen(PCIIDS_PATH "/pci.ids", "re");
91 return fopen(PCIIDS_PATH "/pci.ids", "r");
94 #define pci_id_file_gets(l, s, f) fgets(l, s, f)
95 #define pci_id_file_close(f) fclose(f)
100 * Node for sorting vendor IDs.
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.
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.
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
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.
123 struct pci_id_node * children[16];
128 const char * vendor_name;
131 struct pci_device_leaf * devices;
134 struct pci_device_leaf {
135 struct pci_id_match id;
136 const char * device_name;
140 * Root of the PCI vendor ID search tree.
142 _pci_hidden struct pci_id_node * tree = NULL;
145 * Get a pointer to the leaf node for a vendor ID.
147 * If the vendor ID does not exist in the tree, it is added.
149 static struct pci_id_leaf *
150 insert( uint16_t vendor )
152 struct pci_id_node * n;
155 if ( tree == NULL ) {
156 tree = calloc( 1, sizeof( struct pci_id_node ) );
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;
173 if ( n->children[ idx ] == NULL ) {
175 struct pci_id_node * child =
176 calloc( 1, sizeof( struct pci_id_node ) );
180 n->children[ idx ] = child;
183 struct pci_id_leaf * leaf =
184 calloc( 1, sizeof( struct pci_id_leaf ) );
186 leaf->vendor = vendor;
188 n->children[ idx ] = (struct pci_id_node *) leaf;
192 n = n->children[ idx ];
195 return (struct pci_id_leaf *) n;
200 * Populate a vendor node with all the devices associated with that vendor
202 * \param vend Vendor node that is to be filled from the pci.ids file.
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.
211 populate_vendor( struct pci_id_leaf * vend, int fill_device_data )
215 unsigned vendor = PCI_MATCH_ANY;
218 /* If the device tree for this vendor is already populated, don't do
219 * anything. This avoids wasted processing and potential memory leaks.
221 if (vend->num_devices != 0) {
225 f = pci_id_file_open();
227 /* If the pci.ids file could not be opened, there's nothing we can do.
233 while( pci_id_file_gets( buf, sizeof( buf ), f ) != NULL ) {
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.
242 for ( num_tabs = 0 ; num_tabs < 3 ; num_tabs++ ) {
243 if ( buf[ num_tabs ] != '\t' ) {
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 ] ) ) {
255 new_line = strchr( buf, '\n' );
256 if ( new_line != NULL ) {
260 length = strlen( buf );
261 (void) memset( buf + length, 0, sizeof( buf ) - length );
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.
270 if (vend->vendor_name == NULL) {
271 vend->vendor_name = strdup( & buf[ num_tabs + 6 ] );
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
278 if ( ! fill_device_data ) {
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;
290 d = realloc( vend->devices, (vend->num_devices + 1)
291 * sizeof( struct pci_device_leaf ) );
296 last_dev = & d[ vend->num_devices - 1 ];
297 dev = & d[ vend->num_devices ];
301 if ( num_tabs == 1 ) {
302 dev->id.vendor_id = vend->vendor;
303 dev->id.device_id = (unsigned) strtoul( & buf[ num_tabs ],
305 dev->id.subvendor_id = PCI_MATCH_ANY;
306 dev->id.subdevice_id = PCI_MATCH_ANY;
308 dev->id.device_class = 0;
309 dev->id.device_class_mask = 0;
310 dev->id.match_data = 0;
312 dev->device_name = strdup( & buf[ num_tabs + 6 ] );
315 dev->id = last_dev->id;
317 dev->id.subvendor_id= (unsigned) strtoul( & buf[ num_tabs ],
319 dev->id.subdevice_id = (unsigned) strtoul( & buf[ num_tabs + 5 ],
321 dev->device_name = strdup( & buf[ num_tabs + 5 + 6 ] );
327 pci_id_file_close( f );
332 * Find the name of the specified device.
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
339 find_device_name( const struct pci_id_match * m )
341 struct pci_id_leaf * vend;
345 if ( m->vendor_id == PCI_MATCH_ANY ) {
350 vend = insert( m->vendor_id );
351 if ( vend == NULL ) {
355 if ( vend->num_devices == 0 ) {
356 populate_vendor( vend, 1 );
360 for ( i = 0 ; i < vend->num_devices ; i++ ) {
361 struct pci_device_leaf * d = & vend->devices[ i ];
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;
376 * Find the vendor name of the specified device.
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.
383 find_vendor_name( const struct pci_id_match * m )
385 struct pci_id_leaf * vend;
388 if ( m->vendor_id == PCI_MATCH_ANY ) {
393 vend = insert( m->vendor_id );
394 if ( vend == NULL ) {
398 if ( vend->vendor_name == NULL ) {
399 populate_vendor( vend, 0 );
403 return vend->vendor_name;
408 * Get a name based on an arbitrary PCI search structure.
411 pci_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 )
417 struct pci_id_match temp;
421 temp.subvendor_id = PCI_MATCH_ANY;
422 temp.subdevice_id = PCI_MATCH_ANY;
424 if ( device_name != NULL ) {
425 *device_name = find_device_name( & temp );
428 if ( vendor_name != NULL ) {
429 *vendor_name = find_vendor_name( & temp );
432 if ( subdevice_name != NULL ) {
433 *subdevice_name = find_device_name( m );
436 if ( subvendor_name != NULL ) {
437 *subvendor_name = find_vendor_name( m );
443 * Get the name associated with the device's primary device ID.
446 pci_device_get_device_name( const struct pci_device * dev )
448 struct pci_id_match m;
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;
456 m.device_class_mask = 0;
459 return find_device_name( & m );
464 * Get the name associated with the device's subdevice ID.
467 pci_device_get_subdevice_name( const struct pci_device * dev )
469 struct pci_id_match m;
472 if ( (dev->subvendor_id == 0) || (dev->subdevice_id == 0) ) {
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;
481 m.device_class_mask = 0;
484 return find_device_name( & m );
489 * Get the name associated with the device's primary vendor ID.
492 pci_device_get_vendor_name( const struct pci_device * dev )
494 struct pci_id_match m;
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;
502 m.device_class_mask = 0;
505 return find_vendor_name( & m );
510 * Get the name associated with the device's subvendor ID.
513 pci_device_get_subvendor_name( const struct pci_device * dev )
515 struct pci_id_match m;
518 if ( dev->subvendor_id == 0 ) {
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;
528 m.device_class_mask = 0;
531 return find_vendor_name( & m );