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