diff options
Diffstat (limited to 'drivers/pci/search.c')
-rw-r--r-- | drivers/pci/search.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/drivers/pci/search.c b/drivers/pci/search.c new file mode 100644 index 000000000000..a90a533eba0f --- /dev/null +++ b/drivers/pci/search.c | |||
@@ -0,0 +1,388 @@ | |||
1 | /* | ||
2 | * PCI searching functions. | ||
3 | * | ||
4 | * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter, | ||
5 | * David Mosberger-Tang | ||
6 | * Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz> | ||
7 | * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <linux/pci.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include "pci.h" | ||
15 | |||
16 | DEFINE_SPINLOCK(pci_bus_lock); | ||
17 | |||
18 | static struct pci_bus * __devinit | ||
19 | pci_do_find_bus(struct pci_bus* bus, unsigned char busnr) | ||
20 | { | ||
21 | struct pci_bus* child; | ||
22 | struct list_head *tmp; | ||
23 | |||
24 | if(bus->number == busnr) | ||
25 | return bus; | ||
26 | |||
27 | list_for_each(tmp, &bus->children) { | ||
28 | child = pci_do_find_bus(pci_bus_b(tmp), busnr); | ||
29 | if(child) | ||
30 | return child; | ||
31 | } | ||
32 | return NULL; | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * pci_find_bus - locate PCI bus from a given domain and bus number | ||
37 | * @domain: number of PCI domain to search | ||
38 | * @busnr: number of desired PCI bus | ||
39 | * | ||
40 | * Given a PCI bus number and domain number, the desired PCI bus is located | ||
41 | * in the global list of PCI buses. If the bus is found, a pointer to its | ||
42 | * data structure is returned. If no bus is found, %NULL is returned. | ||
43 | */ | ||
44 | struct pci_bus * __devinit pci_find_bus(int domain, int busnr) | ||
45 | { | ||
46 | struct pci_bus *bus = NULL; | ||
47 | struct pci_bus *tmp_bus; | ||
48 | |||
49 | while ((bus = pci_find_next_bus(bus)) != NULL) { | ||
50 | if (pci_domain_nr(bus) != domain) | ||
51 | continue; | ||
52 | tmp_bus = pci_do_find_bus(bus, busnr); | ||
53 | if (tmp_bus) | ||
54 | return tmp_bus; | ||
55 | } | ||
56 | return NULL; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * pci_find_next_bus - begin or continue searching for a PCI bus | ||
61 | * @from: Previous PCI bus found, or %NULL for new search. | ||
62 | * | ||
63 | * Iterates through the list of known PCI busses. A new search is | ||
64 | * initiated by passing %NULL to the @from argument. Otherwise if | ||
65 | * @from is not %NULL, searches continue from next device on the | ||
66 | * global list. | ||
67 | */ | ||
68 | struct pci_bus * | ||
69 | pci_find_next_bus(const struct pci_bus *from) | ||
70 | { | ||
71 | struct list_head *n; | ||
72 | struct pci_bus *b = NULL; | ||
73 | |||
74 | WARN_ON(in_interrupt()); | ||
75 | spin_lock(&pci_bus_lock); | ||
76 | n = from ? from->node.next : pci_root_buses.next; | ||
77 | if (n != &pci_root_buses) | ||
78 | b = pci_bus_b(n); | ||
79 | spin_unlock(&pci_bus_lock); | ||
80 | return b; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * pci_find_slot - locate PCI device from a given PCI slot | ||
85 | * @bus: number of PCI bus on which desired PCI device resides | ||
86 | * @devfn: encodes number of PCI slot in which the desired PCI | ||
87 | * device resides and the logical device number within that slot | ||
88 | * in case of multi-function devices. | ||
89 | * | ||
90 | * Given a PCI bus and slot/function number, the desired PCI device | ||
91 | * is located in system global list of PCI devices. If the device | ||
92 | * is found, a pointer to its data structure is returned. If no | ||
93 | * device is found, %NULL is returned. | ||
94 | */ | ||
95 | struct pci_dev * | ||
96 | pci_find_slot(unsigned int bus, unsigned int devfn) | ||
97 | { | ||
98 | struct pci_dev *dev = NULL; | ||
99 | |||
100 | while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | ||
101 | if (dev->bus->number == bus && dev->devfn == devfn) | ||
102 | return dev; | ||
103 | } | ||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * pci_get_slot - locate PCI device for a given PCI slot | ||
109 | * @bus: PCI bus on which desired PCI device resides | ||
110 | * @devfn: encodes number of PCI slot in which the desired PCI | ||
111 | * device resides and the logical device number within that slot | ||
112 | * in case of multi-function devices. | ||
113 | * | ||
114 | * Given a PCI bus and slot/function number, the desired PCI device | ||
115 | * is located in the list of PCI devices. | ||
116 | * If the device is found, its reference count is increased and this | ||
117 | * function returns a pointer to its data structure. The caller must | ||
118 | * decrement the reference count by calling pci_dev_put(). | ||
119 | * If no device is found, %NULL is returned. | ||
120 | */ | ||
121 | struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) | ||
122 | { | ||
123 | struct list_head *tmp; | ||
124 | struct pci_dev *dev; | ||
125 | |||
126 | WARN_ON(in_interrupt()); | ||
127 | spin_lock(&pci_bus_lock); | ||
128 | |||
129 | list_for_each(tmp, &bus->devices) { | ||
130 | dev = pci_dev_b(tmp); | ||
131 | if (dev->devfn == devfn) | ||
132 | goto out; | ||
133 | } | ||
134 | |||
135 | dev = NULL; | ||
136 | out: | ||
137 | pci_dev_get(dev); | ||
138 | spin_unlock(&pci_bus_lock); | ||
139 | return dev; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id | ||
144 | * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids | ||
145 | * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids | ||
146 | * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids | ||
147 | * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids | ||
148 | * @from: Previous PCI device found in search, or %NULL for new search. | ||
149 | * | ||
150 | * Iterates through the list of known PCI devices. If a PCI device is | ||
151 | * found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its | ||
152 | * device structure is returned. Otherwise, %NULL is returned. | ||
153 | * A new search is initiated by passing %NULL to the @from argument. | ||
154 | * Otherwise if @from is not %NULL, searches continue from next device on the global list. | ||
155 | * | ||
156 | * NOTE: Do not use this function anymore, use pci_get_subsys() instead, as | ||
157 | * the pci device returned by this function can disappear at any moment in | ||
158 | * time. | ||
159 | */ | ||
160 | static struct pci_dev * pci_find_subsys(unsigned int vendor, | ||
161 | unsigned int device, | ||
162 | unsigned int ss_vendor, | ||
163 | unsigned int ss_device, | ||
164 | const struct pci_dev *from) | ||
165 | { | ||
166 | struct list_head *n; | ||
167 | struct pci_dev *dev; | ||
168 | |||
169 | WARN_ON(in_interrupt()); | ||
170 | spin_lock(&pci_bus_lock); | ||
171 | n = from ? from->global_list.next : pci_devices.next; | ||
172 | |||
173 | while (n && (n != &pci_devices)) { | ||
174 | dev = pci_dev_g(n); | ||
175 | if ((vendor == PCI_ANY_ID || dev->vendor == vendor) && | ||
176 | (device == PCI_ANY_ID || dev->device == device) && | ||
177 | (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) && | ||
178 | (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)) | ||
179 | goto exit; | ||
180 | n = n->next; | ||
181 | } | ||
182 | dev = NULL; | ||
183 | exit: | ||
184 | spin_unlock(&pci_bus_lock); | ||
185 | return dev; | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * pci_find_device - begin or continue searching for a PCI device by vendor/device id | ||
190 | * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids | ||
191 | * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids | ||
192 | * @from: Previous PCI device found in search, or %NULL for new search. | ||
193 | * | ||
194 | * Iterates through the list of known PCI devices. If a PCI device is | ||
195 | * found with a matching @vendor and @device, a pointer to its device structure is | ||
196 | * returned. Otherwise, %NULL is returned. | ||
197 | * A new search is initiated by passing %NULL to the @from argument. | ||
198 | * Otherwise if @from is not %NULL, searches continue from next device on the global list. | ||
199 | * | ||
200 | * NOTE: Do not use this function anymore, use pci_get_device() instead, as | ||
201 | * the pci device returned by this function can disappear at any moment in | ||
202 | * time. | ||
203 | */ | ||
204 | struct pci_dev * | ||
205 | pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from) | ||
206 | { | ||
207 | return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * pci_get_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id | ||
212 | * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids | ||
213 | * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids | ||
214 | * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids | ||
215 | * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids | ||
216 | * @from: Previous PCI device found in search, or %NULL for new search. | ||
217 | * | ||
218 | * Iterates through the list of known PCI devices. If a PCI device is | ||
219 | * found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its | ||
220 | * device structure is returned, and the reference count to the device is | ||
221 | * incremented. Otherwise, %NULL is returned. A new search is initiated by | ||
222 | * passing %NULL to the @from argument. Otherwise if @from is not %NULL, | ||
223 | * searches continue from next device on the global list. | ||
224 | * The reference count for @from is always decremented if it is not %NULL. | ||
225 | */ | ||
226 | struct pci_dev * | ||
227 | pci_get_subsys(unsigned int vendor, unsigned int device, | ||
228 | unsigned int ss_vendor, unsigned int ss_device, | ||
229 | struct pci_dev *from) | ||
230 | { | ||
231 | struct list_head *n; | ||
232 | struct pci_dev *dev; | ||
233 | |||
234 | WARN_ON(in_interrupt()); | ||
235 | spin_lock(&pci_bus_lock); | ||
236 | n = from ? from->global_list.next : pci_devices.next; | ||
237 | |||
238 | while (n && (n != &pci_devices)) { | ||
239 | dev = pci_dev_g(n); | ||
240 | if ((vendor == PCI_ANY_ID || dev->vendor == vendor) && | ||
241 | (device == PCI_ANY_ID || dev->device == device) && | ||
242 | (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) && | ||
243 | (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)) | ||
244 | goto exit; | ||
245 | n = n->next; | ||
246 | } | ||
247 | dev = NULL; | ||
248 | exit: | ||
249 | pci_dev_put(from); | ||
250 | dev = pci_dev_get(dev); | ||
251 | spin_unlock(&pci_bus_lock); | ||
252 | return dev; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * pci_get_device - begin or continue searching for a PCI device by vendor/device id | ||
257 | * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids | ||
258 | * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids | ||
259 | * @from: Previous PCI device found in search, or %NULL for new search. | ||
260 | * | ||
261 | * Iterates through the list of known PCI devices. If a PCI device is | ||
262 | * found with a matching @vendor and @device, the reference count to the | ||
263 | * device is incremented and a pointer to its device structure is returned. | ||
264 | * Otherwise, %NULL is returned. A new search is initiated by passing %NULL | ||
265 | * to the @from argument. Otherwise if @from is not %NULL, searches continue | ||
266 | * from next device on the global list. The reference count for @from is | ||
267 | * always decremented if it is not %NULL. | ||
268 | */ | ||
269 | struct pci_dev * | ||
270 | pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from) | ||
271 | { | ||
272 | return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); | ||
273 | } | ||
274 | |||
275 | |||
276 | /** | ||
277 | * pci_find_device_reverse - begin or continue searching for a PCI device by vendor/device id | ||
278 | * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids | ||
279 | * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids | ||
280 | * @from: Previous PCI device found in search, or %NULL for new search. | ||
281 | * | ||
282 | * Iterates through the list of known PCI devices in the reverse order of pci_find_device(). | ||
283 | * If a PCI device is found with a matching @vendor and @device, a pointer to | ||
284 | * its device structure is returned. Otherwise, %NULL is returned. | ||
285 | * A new search is initiated by passing %NULL to the @from argument. | ||
286 | * Otherwise if @from is not %NULL, searches continue from previous device on the global list. | ||
287 | */ | ||
288 | struct pci_dev * | ||
289 | pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct pci_dev *from) | ||
290 | { | ||
291 | struct list_head *n; | ||
292 | struct pci_dev *dev; | ||
293 | |||
294 | WARN_ON(in_interrupt()); | ||
295 | spin_lock(&pci_bus_lock); | ||
296 | n = from ? from->global_list.prev : pci_devices.prev; | ||
297 | |||
298 | while (n && (n != &pci_devices)) { | ||
299 | dev = pci_dev_g(n); | ||
300 | if ((vendor == PCI_ANY_ID || dev->vendor == vendor) && | ||
301 | (device == PCI_ANY_ID || dev->device == device)) | ||
302 | goto exit; | ||
303 | n = n->prev; | ||
304 | } | ||
305 | dev = NULL; | ||
306 | exit: | ||
307 | spin_unlock(&pci_bus_lock); | ||
308 | return dev; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * pci_get_class - begin or continue searching for a PCI device by class | ||
313 | * @class: search for a PCI device with this class designation | ||
314 | * @from: Previous PCI device found in search, or %NULL for new search. | ||
315 | * | ||
316 | * Iterates through the list of known PCI devices. If a PCI device is | ||
317 | * found with a matching @class, the reference count to the device is | ||
318 | * incremented and a pointer to its device structure is returned. | ||
319 | * Otherwise, %NULL is returned. | ||
320 | * A new search is initiated by passing %NULL to the @from argument. | ||
321 | * Otherwise if @from is not %NULL, searches continue from next device | ||
322 | * on the global list. The reference count for @from is always decremented | ||
323 | * if it is not %NULL. | ||
324 | */ | ||
325 | struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) | ||
326 | { | ||
327 | struct list_head *n; | ||
328 | struct pci_dev *dev; | ||
329 | |||
330 | WARN_ON(in_interrupt()); | ||
331 | spin_lock(&pci_bus_lock); | ||
332 | n = from ? from->global_list.next : pci_devices.next; | ||
333 | |||
334 | while (n && (n != &pci_devices)) { | ||
335 | dev = pci_dev_g(n); | ||
336 | if (dev->class == class) | ||
337 | goto exit; | ||
338 | n = n->next; | ||
339 | } | ||
340 | dev = NULL; | ||
341 | exit: | ||
342 | pci_dev_put(from); | ||
343 | dev = pci_dev_get(dev); | ||
344 | spin_unlock(&pci_bus_lock); | ||
345 | return dev; | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * pci_dev_present - Returns 1 if device matching the device list is present, 0 if not. | ||
350 | * @ids: A pointer to a null terminated list of struct pci_device_id structures | ||
351 | * that describe the type of PCI device the caller is trying to find. | ||
352 | * | ||
353 | * Obvious fact: You do not have a reference to any device that might be found | ||
354 | * by this function, so if that device is removed from the system right after | ||
355 | * this function is finished, the value will be stale. Use this function to | ||
356 | * find devices that are usually built into a system, or for a general hint as | ||
357 | * to if another device happens to be present at this specific moment in time. | ||
358 | */ | ||
359 | int pci_dev_present(const struct pci_device_id *ids) | ||
360 | { | ||
361 | struct pci_dev *dev; | ||
362 | int found = 0; | ||
363 | |||
364 | WARN_ON(in_interrupt()); | ||
365 | spin_lock(&pci_bus_lock); | ||
366 | while (ids->vendor || ids->subvendor || ids->class_mask) { | ||
367 | list_for_each_entry(dev, &pci_devices, global_list) { | ||
368 | if (pci_match_one_device(ids, dev)) { | ||
369 | found = 1; | ||
370 | goto exit; | ||
371 | } | ||
372 | } | ||
373 | ids++; | ||
374 | } | ||
375 | exit: | ||
376 | spin_unlock(&pci_bus_lock); | ||
377 | return found; | ||
378 | } | ||
379 | EXPORT_SYMBOL(pci_dev_present); | ||
380 | |||
381 | EXPORT_SYMBOL(pci_find_bus); | ||
382 | EXPORT_SYMBOL(pci_find_device); | ||
383 | EXPORT_SYMBOL(pci_find_device_reverse); | ||
384 | EXPORT_SYMBOL(pci_find_slot); | ||
385 | EXPORT_SYMBOL(pci_get_device); | ||
386 | EXPORT_SYMBOL(pci_get_subsys); | ||
387 | EXPORT_SYMBOL(pci_get_slot); | ||
388 | EXPORT_SYMBOL(pci_get_class); | ||