xref: /libpciaccess/src/common_iterator.c (revision 5a04522a)
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_iterator.c
27  * Platform independent iterator support routines.
28  *
29  * \author Ian Romanick <[email protected]>
30  */
31 
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <regex.h>
36 
37 #include "pciaccess.h"
38 #include "pciaccess_private.h"
39 
40 /**
41  * Track device iteration state
42  *
43  * \private
44  */
45 struct pci_device_iterator {
46     unsigned next_index;
47     regex_t   reg;
48     int no_regex;
49 };
50 
51 
52 
53 /**
54  * Create an iterator based on a regular expression.
55  *
56  * The set of devices to be iterated is selected by the regular expression
57  * passed in \c regex.  The expression matches against an extended PCI bus
58  * identifier string. The format of this string is
59  * "domain:bus:slot.function:vendor:device_id:subvendor:subdevice_id:class".
60  * Unlike classic X bus IDs, all values in the extened bus identifier string
61  * are in hexadecimal.  To simplify the required regular expressions, all hex
62  * digits greater than 9 will be lower-case.
63  *
64  * To match all devices in domain 0, the expression "0:.+" would be used.  To
65  * match all devices by ATI, the expression ".+:1002:.+". To match all devices
66  * with a class of display, a class of multimedia and a subclass of video, or
67  * a class of processor and a subclass of coprocessor, the expression
68  * ".+:(03[[:hex:]]2|0400|0b40|0001)$" would be used.  Since this is a fully
69  * function regular expression, arbitrarilly complex matches can be requested.
70  *
71  * \param pci_sys  Handle for the PCI subsystem.
72  * \param regex    Pointer to the regular expression to match against.  If
73  *                 \c NULL is passed, all devices will be matched.
74  *
75  * \return
76  * A pointer to a fully initialized \c pci_device_iterator structure on
77  * success, or \c NULL on failure.
78  *
79  * \sa pci_device_next, pci_iterator_destroy
80  */
81 struct pci_device_iterator *
82 pci_iterator_create( const char * re )
83 {
84     struct pci_device_iterator * iter;
85 
86     if ( pci_sys == NULL ) {
87 	return NULL;
88     }
89 
90     iter = malloc( sizeof( *iter ) );
91     if ( iter != NULL ) {
92 	iter->next_index = 0;
93 
94 	/* If the caller passed a NULL or empty expression, then we don't try
95 	 * to compile the expression.  Instead we set a flag that tells the
96 	 * iterator routine to iterate every device in the list.
97 	 */
98 	if ( (re != NULL) && (strlen( re ) > 0) ) {
99 	    int err = regcomp( & iter->reg, re, REG_EXTENDED | REG_NOSUB );
100 	    if ( err != 0 ) {
101 		free( iter );
102 		iter = NULL;
103 	    }
104 
105 	    iter->no_regex = 0;
106 	}
107 	else {
108 	    iter->no_regex = 1;
109 	}
110     }
111 
112     return iter;
113 }
114 
115 
116 /**
117  * Destroy an iterator previously created with \c pci_iterator_create.
118  *
119  * \param iter  Iterator to be destroyed.
120  *
121  * \sa pci_device_next, pci_iterator_create
122  */
123 void
124 pci_iterator_destroy( struct pci_device_iterator * iter )
125 {
126     if ( iter != NULL ) {
127 	if ( ! iter->no_regex ) {
128 	    regfree( & iter->reg );
129 	}
130 
131 	free( iter );
132     }
133 }
134 
135 
136 static void
137 fill_device_string( struct pci_device_private * d )
138 {
139 
140     if ( d->device_string == NULL ) {
141 	char * const string = malloc( 40 );
142 	if ( string != NULL ) {
143 	    pci_device_probe( (struct pci_device *) d );
144 	    sprintf( string, "%04x:%02x:%02x.%u:%04x:%04x:%04x:%04x:%06x",
145 		     d->base.domain,
146 		     d->base.bus,
147 		     d->base.dev,
148 		     d->base.func,
149 		     d->base.vendor_id,
150 		     d->base.device_id,
151 		     d->base.subvendor_id,
152 		     d->base.subdevice_id,
153 		     d->base.device_class );
154 
155 	    d->device_string = string;
156 	}
157     }
158 }
159 
160 
161 /**
162  * Iterate to the next PCI device.
163  *
164  * \param iter  Device iterator returned by \c pci_device_iterate.
165  *
166  * \return
167  * A pointer to a \c pci_device, or \c NULL when all devices have been
168  * iterated.
169  *
170  * \bug
171  * The only time this routine should be able to return \c NULL is when the
172  * end of the list is hit.  However, there is a memory allocation (via
173  * \c fill_device_string) that can fail.  If this allocation fails, \c NULL
174  * will be erroneously returned.  What should be done here?  Pre-fill the
175  * device strings in \c pci_iterator_create?
176  */
177 struct pci_device *
178 pci_device_next( struct pci_device_iterator * iter )
179 {
180     struct pci_device_private * d = NULL;
181 
182     if ( iter->no_regex ) {
183 	if ( iter->next_index < pci_sys->num_devices ) {
184 	    d = & pci_sys->devices[ iter->next_index ];
185 	    iter->next_index++;
186 	}
187     }
188     else {
189 	while ( iter->next_index < pci_sys->num_devices ) {
190 	    struct pci_device_private * const temp =
191 	      & pci_sys->devices[ iter->next_index ];
192 
193 	    if ( temp->device_string == NULL ) {
194 		fill_device_string( temp );
195 		if ( temp->device_string == NULL ) {
196 		    break;
197 		}
198 	    }
199 
200 	    iter->next_index++;
201 
202 	    if ( regexec( & iter->reg, temp->device_string, 0, NULL, 0 ) == 0 ) {
203 		d = temp;
204 		break;
205 	    }
206 	}
207     }
208 
209     return (struct pci_device *) d;
210 }
211