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