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