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