OSDN Git Service

Fix build on NetBSD-4.
[android-x86/external-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 #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>
59 typedef gzFile pci_id_file;
60
61 static pci_id_file
62 pci_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
78 typedef FILE * pci_id_file;
79
80 static pci_id_file
81 pci_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  */
121 struct pci_id_node {
122     unsigned bits;
123     struct pci_id_node * children[16];
124 };
125
126 struct 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
134 struct 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  */
149 static struct pci_id_leaf *
150 insert( 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  */
210 static void
211 populate_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  */
338 static const char *
339 find_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  */
382 static const char *
383 find_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  */
410 void
411 pci_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  */
445 const char *
446 pci_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  */
466 const char *
467 pci_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  */
491 const char *
492 pci_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  */
512 const char *
513 pci_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 }