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