common_device_name.c revision 9266c31d
14f5e7dd7Smrg/*
24f5e7dd7Smrg * (C) Copyright IBM Corporation 2006
34f5e7dd7Smrg * All Rights Reserved.
44f5e7dd7Smrg *
54f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a
64f5e7dd7Smrg * copy of this software and associated documentation files (the "Software"),
74f5e7dd7Smrg * to deal in the Software without restriction, including without limitation
84f5e7dd7Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
94f5e7dd7Smrg * license, and/or sell copies of the Software, and to permit persons to whom
104f5e7dd7Smrg * the Software is furnished to do so, subject to the following conditions:
114f5e7dd7Smrg *
124f5e7dd7Smrg * The above copyright notice and this permission notice (including the next
134f5e7dd7Smrg * paragraph) shall be included in all copies or substantial portions of the
144f5e7dd7Smrg * Software.
154f5e7dd7Smrg *
164f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
174f5e7dd7Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
184f5e7dd7Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
194f5e7dd7Smrg * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
204f5e7dd7Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
214f5e7dd7Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
224f5e7dd7Smrg * DEALINGS IN THE SOFTWARE.
234f5e7dd7Smrg */
244f5e7dd7Smrg
254f5e7dd7Smrg/**
264f5e7dd7Smrg * \file common_device_name.c
274f5e7dd7Smrg * Support routines used to determine the vendor or device names associated
284f5e7dd7Smrg * with a particular device or vendor.
294f5e7dd7Smrg */
304f5e7dd7Smrg
319266c31dSmrg#ifdef HAVE_CONFIG_H
324f5e7dd7Smrg#include "config.h"
3347fd271eSmrg#endif
3447fd271eSmrg
354f5e7dd7Smrg#include <stdio.h>
364f5e7dd7Smrg#include <stdlib.h>
374f5e7dd7Smrg#include <ctype.h>
384f5e7dd7Smrg
394f5e7dd7Smrg#if defined(HAVE_STRING_H)
404f5e7dd7Smrg# include <string.h>
414f5e7dd7Smrg#elif defined(HAVE_STRINGS_H)
424f5e7dd7Smrg# include <strings.h>
434f5e7dd7Smrg#endif
444f5e7dd7Smrg
454f5e7dd7Smrg#if defined(HAVE_INTTYPES_H)
464f5e7dd7Smrg# include <inttypes.h>
474f5e7dd7Smrg#elif defined(HAVE_STDINT_H)
484f5e7dd7Smrg# include <stdint.h>
494f5e7dd7Smrg#endif
504f5e7dd7Smrg
514f5e7dd7Smrg#include "pciaccess.h"
524f5e7dd7Smrg#include "pciaccess_private.h"
534f5e7dd7Smrg
544f5e7dd7Smrg#define DO_MATCH(a,b)  (((a) == PCI_MATCH_ANY) || ((a) == (b)))
554f5e7dd7Smrg
564f5e7dd7Smrg#ifdef HAVE_ZLIB
572e46f441Smrg
584f5e7dd7Smrg#include <zlib.h>
594f5e7dd7Smrgtypedef gzFile pci_id_file;
604f5e7dd7Smrg
614f5e7dd7Smrgstatic pci_id_file
6269bf0a65Smrgpci_id_file_open(void)
634f5e7dd7Smrg{
644f5e7dd7Smrg    pci_id_file result;
654f5e7dd7Smrg
664f5e7dd7Smrg    result = gzopen(PCIIDS_PATH "/pci.ids.gz", "rb");
674f5e7dd7Smrg    if (result)
684f5e7dd7Smrg        return result;
694f5e7dd7Smrg
704f5e7dd7Smrg    return gzopen(PCIIDS_PATH "/pci.ids", "rb");
714f5e7dd7Smrg}
724f5e7dd7Smrg
734f5e7dd7Smrg#define pci_id_file_gets(l, s, f)	gzgets(f, l, s)
744f5e7dd7Smrg#define pci_id_file_close(f)		gzclose(f)
752e46f441Smrg
762e46f441Smrg#else /* not zlib */
772e46f441Smrg
782e46f441Smrgtypedef FILE * pci_id_file;
792e46f441Smrg
802e46f441Smrgstatic pci_id_file
812e46f441Smrgpci_id_file_open(void)
822e46f441Smrg{
839266c31dSmrg#ifndef __sun
842e46f441Smrg    pci_id_file result;
852e46f441Smrg
862e46f441Smrg    result = fopen(PCIIDS_PATH "/pci.ids", "re");
872e46f441Smrg    if (result)
882e46f441Smrg        return result;
892e46f441Smrg#endif
902e46f441Smrg
912e46f441Smrg    return fopen(PCIIDS_PATH "/pci.ids", "r");
922e46f441Smrg}
932e46f441Smrg
944f5e7dd7Smrg#define pci_id_file_gets(l, s, f)	fgets(l, s, f)
954f5e7dd7Smrg#define pci_id_file_close(f)		fclose(f)
962e46f441Smrg
974f5e7dd7Smrg#endif
984f5e7dd7Smrg
994f5e7dd7Smrg/**
1004f5e7dd7Smrg * Node for sorting vendor IDs.
1012e46f441Smrg *
1024f5e7dd7Smrg * Each structure forms an internal node of an n-way tree.  Each node selects
1034f5e7dd7Smrg * \c pci_id_node::bits number of bits from the vendor ID.  Starting from the
1044f5e7dd7Smrg * root of the tree, a slice of the low-order bits of the vendor ID are
1054f5e7dd7Smrg * selected and used as an index into the \c pci_id_node::children array.
1064f5e7dd7Smrg *
1074f5e7dd7Smrg * At the leaf nodes (i.e., the node entered when all 16 bits of the vendor ID
1084f5e7dd7Smrg * have been used), the \c pci_id_node::children is actually an array of
1094f5e7dd7Smrg * pointers to \c pci_id_leaf structures.
1102e46f441Smrg *
1114f5e7dd7Smrg * \todo
1124f5e7dd7Smrg * Determine if there is a cleaner way (in the source code) to have the
1134f5e7dd7Smrg * \c children array change type based on whether the node is internal or
1144f5e7dd7Smrg * a leaf.
1154f5e7dd7Smrg *
1164f5e7dd7Smrg * \todo
1174f5e7dd7Smrg * Currently \c bits is always 4.  Decide if this value can ever change
1184f5e7dd7Smrg * (i.e., to pull-up levels of the n-way tree when all the children's children
1194f5e7dd7Smrg * are full).  If it can, rip it out and hard-code it to 4 everywhere.
1204f5e7dd7Smrg */
1214f5e7dd7Smrgstruct pci_id_node {
1224f5e7dd7Smrg    unsigned bits;
1234f5e7dd7Smrg    struct pci_id_node * children[16];
1244f5e7dd7Smrg};
1254f5e7dd7Smrg
1264f5e7dd7Smrgstruct pci_id_leaf {
1274f5e7dd7Smrg    uint16_t     vendor;
1284f5e7dd7Smrg    const char * vendor_name;
1292e46f441Smrg
1304f5e7dd7Smrg    size_t num_devices;
1314f5e7dd7Smrg    struct pci_device_leaf * devices;
1324f5e7dd7Smrg};
1334f5e7dd7Smrg
1344f5e7dd7Smrgstruct pci_device_leaf {
1354f5e7dd7Smrg    struct pci_id_match   id;
1364f5e7dd7Smrg    const char * device_name;
1374f5e7dd7Smrg};
1384f5e7dd7Smrg
1394f5e7dd7Smrg/**
1404f5e7dd7Smrg * Root of the PCI vendor ID search tree.
1414f5e7dd7Smrg */
1424f5e7dd7Smrg_pci_hidden struct pci_id_node * tree = NULL;
1434f5e7dd7Smrg
1444f5e7dd7Smrg/**
1454f5e7dd7Smrg * Get a pointer to the leaf node for a vendor ID.
1462e46f441Smrg *
1474f5e7dd7Smrg * If the vendor ID does not exist in the tree, it is added.
1484f5e7dd7Smrg */
1494f5e7dd7Smrgstatic struct pci_id_leaf *
1504f5e7dd7Smrginsert( uint16_t vendor )
1514f5e7dd7Smrg{
1524f5e7dd7Smrg    struct pci_id_node * n;
1534f5e7dd7Smrg    unsigned bits = 0;
1544f5e7dd7Smrg
1554f5e7dd7Smrg    if ( tree == NULL ) {
1564f5e7dd7Smrg	tree = calloc( 1, sizeof( struct pci_id_node ) );
1574f5e7dd7Smrg	tree->bits = 4;
1584f5e7dd7Smrg    }
1594f5e7dd7Smrg
1604f5e7dd7Smrg    n = tree;
1614f5e7dd7Smrg    while ( n != NULL ) {
1624f5e7dd7Smrg	const unsigned used_bits = n->bits;
1634f5e7dd7Smrg	const unsigned mask = (1 << used_bits) - 1;
1644f5e7dd7Smrg	const unsigned idx = (vendor & (mask << bits)) >> bits;
1654f5e7dd7Smrg
1664f5e7dd7Smrg
1674f5e7dd7Smrg	if ( bits >= 16 ) {
1684f5e7dd7Smrg	    break;
1694f5e7dd7Smrg	}
1704f5e7dd7Smrg
1714f5e7dd7Smrg	bits += used_bits;
1724f5e7dd7Smrg
1734f5e7dd7Smrg	if ( n->children[ idx ] == NULL ) {
1744f5e7dd7Smrg	    if ( bits < 16 ) {
1754f5e7dd7Smrg		struct pci_id_node * child =
1764f5e7dd7Smrg		    calloc( 1, sizeof( struct pci_id_node ) );
1774f5e7dd7Smrg
1784f5e7dd7Smrg		child->bits = 4;
1794f5e7dd7Smrg
1804f5e7dd7Smrg		n->children[ idx ] = child;
1814f5e7dd7Smrg	    }
1824f5e7dd7Smrg	    else {
1832e46f441Smrg		struct pci_id_leaf * leaf =
1844f5e7dd7Smrg		    calloc( 1, sizeof( struct pci_id_leaf ) );
1854f5e7dd7Smrg
1864f5e7dd7Smrg		leaf->vendor = vendor;
1874f5e7dd7Smrg
1884f5e7dd7Smrg		n->children[ idx ] = (struct pci_id_node *) leaf;
1894f5e7dd7Smrg	    }
1904f5e7dd7Smrg	}
1914f5e7dd7Smrg
1924f5e7dd7Smrg	n = n->children[ idx ];
1934f5e7dd7Smrg    }
1944f5e7dd7Smrg
1954f5e7dd7Smrg    return (struct pci_id_leaf *) n;
1964f5e7dd7Smrg}
1974f5e7dd7Smrg
1984f5e7dd7Smrg
1994f5e7dd7Smrg/**
2004f5e7dd7Smrg * Populate a vendor node with all the devices associated with that vendor
2012e46f441Smrg *
2024f5e7dd7Smrg * \param vend  Vendor node that is to be filled from the pci.ids file.
2032e46f441Smrg *
2044f5e7dd7Smrg * \todo
2054f5e7dd7Smrg * The parsing in this function should be more rhobust.  There are some error
2064f5e7dd7Smrg * cases (i.e., a 0-tab line followed by a 2-tab line) that aren't handled
2074f5e7dd7Smrg * correctly.  I don't think there are any security problems with the code,
2084f5e7dd7Smrg * but it's not impossible.
2094f5e7dd7Smrg */
2104f5e7dd7Smrgstatic void
2114f5e7dd7Smrgpopulate_vendor( struct pci_id_leaf * vend, int fill_device_data )
2124f5e7dd7Smrg{
2132e46f441Smrg    pci_id_file f;
2144f5e7dd7Smrg    char buf[128];
2154f5e7dd7Smrg    unsigned vendor = PCI_MATCH_ANY;
2164f5e7dd7Smrg
2174f5e7dd7Smrg
2184f5e7dd7Smrg    /* If the device tree for this vendor is already populated, don't do
2194f5e7dd7Smrg     * anything.  This avoids wasted processing and potential memory leaks.
2204f5e7dd7Smrg     */
2214f5e7dd7Smrg    if (vend->num_devices != 0) {
2224f5e7dd7Smrg	return;
2234f5e7dd7Smrg    }
2244f5e7dd7Smrg
2254f5e7dd7Smrg    f = pci_id_file_open();
2262e46f441Smrg
2274f5e7dd7Smrg    /* If the pci.ids file could not be opened, there's nothing we can do.
2284f5e7dd7Smrg     */
2294f5e7dd7Smrg    if (f == NULL) {
2304f5e7dd7Smrg	return;
2314f5e7dd7Smrg    }
2324f5e7dd7Smrg
2334f5e7dd7Smrg    while( pci_id_file_gets( buf, sizeof( buf ), f ) != NULL ) {
2344f5e7dd7Smrg	unsigned num_tabs;
2354f5e7dd7Smrg	char * new_line;
2364f5e7dd7Smrg	size_t length;
2374f5e7dd7Smrg
2384f5e7dd7Smrg	/* Each line either starts with zero, one, or two tabs followed by
2394f5e7dd7Smrg	 * a series of 4 hex digits.  Any lines not matching that are ignored.
2404f5e7dd7Smrg	 */
2414f5e7dd7Smrg
2424f5e7dd7Smrg	for ( num_tabs = 0 ; num_tabs < 3 ; num_tabs++ ) {
2434f5e7dd7Smrg	    if ( buf[ num_tabs ] != '\t' ) {
2444f5e7dd7Smrg		break;
2454f5e7dd7Smrg	    }
2464f5e7dd7Smrg	}
2472e46f441Smrg
2484f5e7dd7Smrg	if ( !isxdigit( buf[ num_tabs + 0 ] )
2494f5e7dd7Smrg	     || !isxdigit( buf[ num_tabs + 1 ] )
2504f5e7dd7Smrg	     || !isxdigit( buf[ num_tabs + 2 ] )
2514f5e7dd7Smrg	     || !isxdigit( buf[ num_tabs + 3 ] ) ) {
2524f5e7dd7Smrg	    continue;
2534f5e7dd7Smrg	}
2542e46f441Smrg
2554f5e7dd7Smrg	new_line = strchr( buf, '\n' );
2564f5e7dd7Smrg	if ( new_line != NULL ) {
2574f5e7dd7Smrg	    *new_line = '\0';
2584f5e7dd7Smrg	}
2594f5e7dd7Smrg
2604f5e7dd7Smrg	length = strlen( buf );
2614f5e7dd7Smrg	(void) memset( buf + length, 0, sizeof( buf ) - length );
2624f5e7dd7Smrg
2634f5e7dd7Smrg
2644f5e7dd7Smrg	if ( num_tabs == 0 ) {
2654f5e7dd7Smrg	    vendor = (unsigned) strtoul( & buf[ num_tabs ], NULL, 16 );
2664f5e7dd7Smrg	    if ( vend->vendor == vendor ) {
2674f5e7dd7Smrg		/* vendor_name may already be set from a previous invocation
2684f5e7dd7Smrg		 * of this function with fill_device_data = 0.
2694f5e7dd7Smrg		 */
2704f5e7dd7Smrg		if (vend->vendor_name == NULL) {
2714f5e7dd7Smrg		    vend->vendor_name = strdup( & buf[ num_tabs + 6 ] );
2724f5e7dd7Smrg		}
2734f5e7dd7Smrg
2744f5e7dd7Smrg		/* If we're not going to fill in all of the device data as
2754f5e7dd7Smrg		 * well, then bail out now.  We have all the information that
2764f5e7dd7Smrg		 * we need.
2774f5e7dd7Smrg		 */
2784f5e7dd7Smrg		if ( ! fill_device_data ) {
2794f5e7dd7Smrg		    break;
2804f5e7dd7Smrg		}
2814f5e7dd7Smrg	    }
2824f5e7dd7Smrg	}
2834f5e7dd7Smrg	else if ( vendor == vend->vendor ) {
2844f5e7dd7Smrg	    struct pci_device_leaf * d;
2854f5e7dd7Smrg	    struct pci_device_leaf * dev;
2864f5e7dd7Smrg	    struct pci_device_leaf * last_dev;
2872e46f441Smrg
2884f5e7dd7Smrg
2894f5e7dd7Smrg
2904f5e7dd7Smrg	    d = realloc( vend->devices, (vend->num_devices + 1)
2914f5e7dd7Smrg			 * sizeof( struct pci_device_leaf ) );
2924f5e7dd7Smrg	    if ( d == NULL ) {
2932e46f441Smrg		goto cleanup;
2944f5e7dd7Smrg	    }
2954f5e7dd7Smrg
2964f5e7dd7Smrg	    last_dev = & d[ vend->num_devices - 1 ];
2974f5e7dd7Smrg	    dev = & d[ vend->num_devices ];
2984f5e7dd7Smrg	    vend->num_devices++;
2994f5e7dd7Smrg	    vend->devices = d;
3004f5e7dd7Smrg
3014f5e7dd7Smrg	    if ( num_tabs == 1 ) {
3024f5e7dd7Smrg		dev->id.vendor_id = vend->vendor;
3032e46f441Smrg		dev->id.device_id = (unsigned) strtoul( & buf[ num_tabs ],
3044f5e7dd7Smrg							NULL, 16 );
3054f5e7dd7Smrg		dev->id.subvendor_id = PCI_MATCH_ANY;
3064f5e7dd7Smrg		dev->id.subdevice_id = PCI_MATCH_ANY;
3074f5e7dd7Smrg
3084f5e7dd7Smrg		dev->id.device_class = 0;
3094f5e7dd7Smrg		dev->id.device_class_mask = 0;
3104f5e7dd7Smrg		dev->id.match_data = 0;
3114f5e7dd7Smrg
3124f5e7dd7Smrg		dev->device_name = strdup( & buf[ num_tabs + 6 ] );
3134f5e7dd7Smrg	    }
3144f5e7dd7Smrg	    else {
3154f5e7dd7Smrg		dev->id = last_dev->id;
3164f5e7dd7Smrg
3174f5e7dd7Smrg		dev->id.subvendor_id= (unsigned) strtoul( & buf[ num_tabs ],
3184f5e7dd7Smrg							  NULL, 16 );
3192e46f441Smrg		dev->id.subdevice_id = (unsigned) strtoul( & buf[ num_tabs + 5 ],
3204f5e7dd7Smrg							   NULL, 16 );
3214f5e7dd7Smrg		dev->device_name = strdup( & buf[ num_tabs + 5 + 6 ] );
3224f5e7dd7Smrg	    }
3234f5e7dd7Smrg	}
3244f5e7dd7Smrg    }
3252e46f441Smrg
3262e46f441Smrg  cleanup:
3274f5e7dd7Smrg    pci_id_file_close( f );
3284f5e7dd7Smrg}
3294f5e7dd7Smrg
3304f5e7dd7Smrg
3314f5e7dd7Smrg/**
3324f5e7dd7Smrg * Find the name of the specified device.
3334f5e7dd7Smrg *
3344f5e7dd7Smrg * Finds the actual product name of the specified device.  If a subvendor ID
3354f5e7dd7Smrg * and subdevice ID are specified in \c m, the returned name will be the name
3364f5e7dd7Smrg * of the subdevice.
3374f5e7dd7Smrg */
3384f5e7dd7Smrgstatic const char *
3394f5e7dd7Smrgfind_device_name( const struct pci_id_match * m )
3404f5e7dd7Smrg{
3414f5e7dd7Smrg    struct pci_id_leaf * vend;
3424f5e7dd7Smrg    unsigned i;
3434f5e7dd7Smrg
3444f5e7dd7Smrg
3454f5e7dd7Smrg    if ( m->vendor_id == PCI_MATCH_ANY ) {
3464f5e7dd7Smrg	return NULL;
3474f5e7dd7Smrg    }
3484f5e7dd7Smrg
3494f5e7dd7Smrg
3504f5e7dd7Smrg    vend = insert( m->vendor_id );
3514f5e7dd7Smrg    if ( vend == NULL ) {
3524f5e7dd7Smrg	return NULL;
3534f5e7dd7Smrg    }
3544f5e7dd7Smrg
3554f5e7dd7Smrg    if ( vend->num_devices == 0 ) {
3564f5e7dd7Smrg	populate_vendor( vend, 1 );
3574f5e7dd7Smrg    }
3584f5e7dd7Smrg
3594f5e7dd7Smrg
3604f5e7dd7Smrg    for ( i = 0 ; i < vend->num_devices ; i++ ) {
3614f5e7dd7Smrg	struct pci_device_leaf * d = & vend->devices[ i ];
3624f5e7dd7Smrg
3634f5e7dd7Smrg	if ( DO_MATCH( m->vendor_id, d->id.vendor_id )
3644f5e7dd7Smrg	     && DO_MATCH( m->device_id, d->id.device_id )
3654f5e7dd7Smrg	     && DO_MATCH( m->subvendor_id, d->id.subvendor_id )
3664f5e7dd7Smrg	     && DO_MATCH( m->subdevice_id, d->id.subdevice_id ) ) {
3674f5e7dd7Smrg	    return d->device_name;
3684f5e7dd7Smrg	}
3694f5e7dd7Smrg    }
3704f5e7dd7Smrg
3714f5e7dd7Smrg    return NULL;
3724f5e7dd7Smrg}
3734f5e7dd7Smrg
3744f5e7dd7Smrg
3754f5e7dd7Smrg/**
3764f5e7dd7Smrg * Find the vendor name of the specified device.
3774f5e7dd7Smrg *
3784f5e7dd7Smrg * Finds the actual vendor name of the specified device.  If a subvendor ID
3794f5e7dd7Smrg * and subdevice ID are specified in \c m, the returned name will be the name
3804f5e7dd7Smrg * associated with the subvendor.
3814f5e7dd7Smrg */
3824f5e7dd7Smrgstatic const char *
3834f5e7dd7Smrgfind_vendor_name( const struct pci_id_match * m )
3844f5e7dd7Smrg{
3854f5e7dd7Smrg    struct pci_id_leaf * vend;
3864f5e7dd7Smrg
3874f5e7dd7Smrg
3884f5e7dd7Smrg    if ( m->vendor_id == PCI_MATCH_ANY ) {
3894f5e7dd7Smrg	return NULL;
3904f5e7dd7Smrg    }
3914f5e7dd7Smrg
3924f5e7dd7Smrg
3934f5e7dd7Smrg    vend = insert( m->vendor_id );
3944f5e7dd7Smrg    if ( vend == NULL ) {
3954f5e7dd7Smrg	return NULL;
3964f5e7dd7Smrg    }
3974f5e7dd7Smrg
3984f5e7dd7Smrg    if ( vend->vendor_name == NULL ) {
3994f5e7dd7Smrg	populate_vendor( vend, 0 );
4004f5e7dd7Smrg    }
4014f5e7dd7Smrg
4024f5e7dd7Smrg
4034f5e7dd7Smrg    return vend->vendor_name;
4044f5e7dd7Smrg}
4054f5e7dd7Smrg
4064f5e7dd7Smrg
4074f5e7dd7Smrg/**
4084f5e7dd7Smrg * Get a name based on an arbitrary PCI search structure.
4094f5e7dd7Smrg */
4104f5e7dd7Smrgvoid
4114f5e7dd7Smrgpci_get_strings( const struct pci_id_match * m,
4124f5e7dd7Smrg		 const char ** device_name,
4134f5e7dd7Smrg		 const char ** vendor_name,
4144f5e7dd7Smrg		 const char ** subdevice_name,
4154f5e7dd7Smrg		 const char ** subvendor_name )
4164f5e7dd7Smrg{
4174f5e7dd7Smrg    struct pci_id_match  temp;
4184f5e7dd7Smrg
4194f5e7dd7Smrg
4204f5e7dd7Smrg    temp = *m;
4214f5e7dd7Smrg    temp.subvendor_id = PCI_MATCH_ANY;
4224f5e7dd7Smrg    temp.subdevice_id = PCI_MATCH_ANY;
4234f5e7dd7Smrg
4244f5e7dd7Smrg    if ( device_name != NULL ) {
4254f5e7dd7Smrg	*device_name = find_device_name( & temp );
4264f5e7dd7Smrg    }
4274f5e7dd7Smrg
4284f5e7dd7Smrg    if ( vendor_name != NULL ) {
4294f5e7dd7Smrg	*vendor_name = find_vendor_name( & temp );
4304f5e7dd7Smrg    }
4314f5e7dd7Smrg
4324f5e7dd7Smrg    if ( subdevice_name != NULL ) {
4334f5e7dd7Smrg	*subdevice_name = find_device_name( m );
4344f5e7dd7Smrg    }
4354f5e7dd7Smrg
4364f5e7dd7Smrg    if ( subvendor_name != NULL ) {
4374f5e7dd7Smrg	*subvendor_name = find_vendor_name( m );
4384f5e7dd7Smrg    }
4394f5e7dd7Smrg}
4404f5e7dd7Smrg
4414f5e7dd7Smrg
4424f5e7dd7Smrg/**
4434f5e7dd7Smrg * Get the name associated with the device's primary device ID.
4444f5e7dd7Smrg */
4454f5e7dd7Smrgconst char *
4464f5e7dd7Smrgpci_device_get_device_name( const struct pci_device * dev )
4474f5e7dd7Smrg{
4484f5e7dd7Smrg    struct pci_id_match m;
4494f5e7dd7Smrg
4504f5e7dd7Smrg
4514f5e7dd7Smrg    m.vendor_id = dev->vendor_id;
4524f5e7dd7Smrg    m.device_id = dev->device_id;
4534f5e7dd7Smrg    m.subvendor_id = PCI_MATCH_ANY;
4544f5e7dd7Smrg    m.subdevice_id = PCI_MATCH_ANY;
4554f5e7dd7Smrg    m.device_class = 0;
4564f5e7dd7Smrg    m.device_class_mask = 0;
4574f5e7dd7Smrg    m.match_data = 0;
4584f5e7dd7Smrg
4594f5e7dd7Smrg    return find_device_name( & m );
4604f5e7dd7Smrg}
4614f5e7dd7Smrg
4624f5e7dd7Smrg
4634f5e7dd7Smrg/**
4644f5e7dd7Smrg * Get the name associated with the device's subdevice ID.
4654f5e7dd7Smrg */
4664f5e7dd7Smrgconst char *
4674f5e7dd7Smrgpci_device_get_subdevice_name( const struct pci_device * dev )
4684f5e7dd7Smrg{
4694f5e7dd7Smrg    struct pci_id_match m;
4704f5e7dd7Smrg
4714f5e7dd7Smrg
4724f5e7dd7Smrg    if ( (dev->subvendor_id == 0) || (dev->subdevice_id == 0) ) {
4734f5e7dd7Smrg	return NULL;
4744f5e7dd7Smrg    }
4754f5e7dd7Smrg
4764f5e7dd7Smrg    m.vendor_id = dev->vendor_id;
4774f5e7dd7Smrg    m.device_id = dev->device_id;
4784f5e7dd7Smrg    m.subvendor_id = dev->subvendor_id;
4794f5e7dd7Smrg    m.subdevice_id = dev->subdevice_id;
4804f5e7dd7Smrg    m.device_class = 0;
4814f5e7dd7Smrg    m.device_class_mask = 0;
4824f5e7dd7Smrg    m.match_data = 0;
4834f5e7dd7Smrg
4844f5e7dd7Smrg    return find_device_name( & m );
4854f5e7dd7Smrg}
4864f5e7dd7Smrg
4874f5e7dd7Smrg
4884f5e7dd7Smrg/**
4894f5e7dd7Smrg * Get the name associated with the device's primary vendor ID.
4904f5e7dd7Smrg */
4914f5e7dd7Smrgconst char *
4924f5e7dd7Smrgpci_device_get_vendor_name( const struct pci_device * dev )
4934f5e7dd7Smrg{
4944f5e7dd7Smrg    struct pci_id_match m;
4954f5e7dd7Smrg
4964f5e7dd7Smrg
4974f5e7dd7Smrg    m.vendor_id = dev->vendor_id;
4984f5e7dd7Smrg    m.device_id = PCI_MATCH_ANY;
4994f5e7dd7Smrg    m.subvendor_id = PCI_MATCH_ANY;
5004f5e7dd7Smrg    m.subdevice_id = PCI_MATCH_ANY;
5014f5e7dd7Smrg    m.device_class = 0;
5024f5e7dd7Smrg    m.device_class_mask = 0;
5034f5e7dd7Smrg    m.match_data = 0;
5044f5e7dd7Smrg
5054f5e7dd7Smrg    return find_vendor_name( & m );
5064f5e7dd7Smrg}
5074f5e7dd7Smrg
5084f5e7dd7Smrg
5094f5e7dd7Smrg/**
5104f5e7dd7Smrg * Get the name associated with the device's subvendor ID.
5114f5e7dd7Smrg */
5124f5e7dd7Smrgconst char *
5134f5e7dd7Smrgpci_device_get_subvendor_name( const struct pci_device * dev )
5144f5e7dd7Smrg{
5154f5e7dd7Smrg    struct pci_id_match m;
5164f5e7dd7Smrg
5174f5e7dd7Smrg
5184f5e7dd7Smrg    if ( dev->subvendor_id == 0 ) {
5194f5e7dd7Smrg	return NULL;
5204f5e7dd7Smrg    }
5214f5e7dd7Smrg
5224f5e7dd7Smrg
5234f5e7dd7Smrg    m.vendor_id = dev->subvendor_id;
5244f5e7dd7Smrg    m.device_id = PCI_MATCH_ANY;
5254f5e7dd7Smrg    m.subvendor_id = PCI_MATCH_ANY;
5264f5e7dd7Smrg    m.subdevice_id = PCI_MATCH_ANY;
5274f5e7dd7Smrg    m.device_class = 0;
5284f5e7dd7Smrg    m.device_class_mask = 0;
5294f5e7dd7Smrg    m.match_data = 0;
5304f5e7dd7Smrg
5314f5e7dd7Smrg    return find_vendor_name( & m );
5324f5e7dd7Smrg}
533