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