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