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