You need to sign in or sign up before continuing.
search.c 12.0 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 *	PCI searching functions.
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10
 *
 *	Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
 *					David Mosberger-Tang
 *	Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
 *	Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
 */

#include <linux/pci.h>
11
#include <linux/slab.h>
L
Linus Torvalds 已提交
12 13 14 15
#include <linux/module.h>
#include <linux/interrupt.h>
#include "pci.h"

16
DECLARE_RWSEM(pci_bus_sem);
17 18
EXPORT_SYMBOL_GPL(pci_bus_sem);

A
Alex Williamson 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * pci_for_each_dma_alias - Iterate over DMA aliases for a device
 * @pdev: starting downstream device
 * @fn: function to call for each alias
 * @data: opaque data to pass to @fn
 *
 * Starting @pdev, walk up the bus calling @fn for each possible alias
 * of @pdev at the root bus.
 */
int pci_for_each_dma_alias(struct pci_dev *pdev,
			   int (*fn)(struct pci_dev *pdev,
				     u16 alias, void *data), void *data)
{
	struct pci_bus *bus;
	int ret;

	ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);
	if (ret)
		return ret;

39 40 41 42
	/*
	 * If the device is broken and uses an alias requester ID for
	 * DMA, iterate over that too.
	 */
43 44 45 46 47 48 49 50 51
	if (unlikely(pdev->dma_alias_mask)) {
		u8 devfn;

		for_each_set_bit(devfn, pdev->dma_alias_mask, U8_MAX) {
			ret = fn(pdev, PCI_DEVID(pdev->bus->number, devfn),
				 data);
			if (ret)
				return ret;
		}
52 53
	}

A
Alex Williamson 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
		struct pci_dev *tmp;

		/* Skip virtual buses */
		if (!bus->self)
			continue;

		tmp = bus->self;

		/*
		 * PCIe-to-PCI/X bridges alias transactions from downstream
		 * devices using the subordinate bus number (PCI Express to
		 * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3).  For all cases
		 * where the upstream bus is PCI/X we alias to the bridge
		 * (there are various conditions in the previous reference
		 * where the bridge may take ownership of transactions, even
		 * when the secondary interface is PCI-X).
		 */
		if (pci_is_pcie(tmp)) {
			switch (pci_pcie_type(tmp)) {
			case PCI_EXP_TYPE_ROOT_PORT:
			case PCI_EXP_TYPE_UPSTREAM:
			case PCI_EXP_TYPE_DOWNSTREAM:
				continue;
			case PCI_EXP_TYPE_PCI_BRIDGE:
				ret = fn(tmp,
					 PCI_DEVID(tmp->subordinate->number,
						   PCI_DEVFN(0, 0)), data);
				if (ret)
					return ret;
				continue;
			case PCI_EXP_TYPE_PCIE_BRIDGE:
				ret = fn(tmp,
					 PCI_DEVID(tmp->bus->number,
						   tmp->devfn), data);
				if (ret)
					return ret;
				continue;
			}
		} else {
94 95 96 97 98 99 100 101
			if (tmp->dev_flags & PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS)
				ret = fn(tmp,
					 PCI_DEVID(tmp->subordinate->number,
						   PCI_DEVFN(0, 0)), data);
			else
				ret = fn(tmp,
					 PCI_DEVID(tmp->bus->number,
						   tmp->devfn), data);
A
Alex Williamson 已提交
102 103 104 105 106 107 108 109
			if (ret)
				return ret;
		}
	}

	return ret;
}

110
static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
L
Linus Torvalds 已提交
111
{
112 113
	struct pci_bus *child;
	struct pci_bus *tmp;
L
Linus Torvalds 已提交
114

R
Ryan Desfosses 已提交
115
	if (bus->number == busnr)
L
Linus Torvalds 已提交
116 117
		return bus;

118 119
	list_for_each_entry(tmp, &bus->children, node) {
		child = pci_do_find_bus(tmp, busnr);
R
Ryan Desfosses 已提交
120
		if (child)
L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134
			return child;
	}
	return NULL;
}

/**
 * pci_find_bus - locate PCI bus from a given domain and bus number
 * @domain: number of PCI domain to search
 * @busnr: number of desired PCI bus
 *
 * Given a PCI bus number and domain number, the desired PCI bus is located
 * in the global list of PCI buses.  If the bus is found, a pointer to its
 * data structure is returned.  If no bus is found, %NULL is returned.
 */
R
Ryan Desfosses 已提交
135
struct pci_bus *pci_find_bus(int domain, int busnr)
L
Linus Torvalds 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148
{
	struct pci_bus *bus = NULL;
	struct pci_bus *tmp_bus;

	while ((bus = pci_find_next_bus(bus)) != NULL)  {
		if (pci_domain_nr(bus) != domain)
			continue;
		tmp_bus = pci_do_find_bus(bus, busnr);
		if (tmp_bus)
			return tmp_bus;
	}
	return NULL;
}
149
EXPORT_SYMBOL(pci_find_bus);
L
Linus Torvalds 已提交
150 151 152 153 154

/**
 * pci_find_next_bus - begin or continue searching for a PCI bus
 * @from: Previous PCI bus found, or %NULL for new search.
 *
155
 * Iterates through the list of known PCI buses.  A new search is
156
 * initiated by passing %NULL as the @from argument.  Otherwise if
L
Linus Torvalds 已提交
157 158 159
 * @from is not %NULL, searches continue from next device on the
 * global list.
 */
R
Ryan Desfosses 已提交
160
struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
L
Linus Torvalds 已提交
161 162 163 164 165
{
	struct list_head *n;
	struct pci_bus *b = NULL;

	WARN_ON(in_interrupt());
166
	down_read(&pci_bus_sem);
L
Linus Torvalds 已提交
167 168
	n = from ? from->node.next : pci_root_buses.next;
	if (n != &pci_root_buses)
169
		b = list_entry(n, struct pci_bus, node);
170
	up_read(&pci_bus_sem);
L
Linus Torvalds 已提交
171 172
	return b;
}
173
EXPORT_SYMBOL(pci_find_next_bus);
L
Linus Torvalds 已提交
174 175 176 177

/**
 * pci_get_slot - locate PCI device for a given PCI slot
 * @bus: PCI bus on which desired PCI device resides
178 179
 * @devfn: encodes number of PCI slot in which the desired PCI
 * device resides and the logical device number within that slot
L
Linus Torvalds 已提交
180 181
 * in case of multi-function devices.
 *
182
 * Given a PCI bus and slot/function number, the desired PCI device
L
Linus Torvalds 已提交
183 184 185 186 187 188
 * is located in the list of PCI devices.
 * If the device is found, its reference count is increased and this
 * function returns a pointer to its data structure.  The caller must
 * decrement the reference count by calling pci_dev_put().
 * If no device is found, %NULL is returned.
 */
189
struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)
L
Linus Torvalds 已提交
190 191 192 193
{
	struct pci_dev *dev;

	WARN_ON(in_interrupt());
194
	down_read(&pci_bus_sem);
L
Linus Torvalds 已提交
195

196
	list_for_each_entry(dev, &bus->devices, bus_list) {
L
Linus Torvalds 已提交
197 198 199 200 201 202 203
		if (dev->devfn == devfn)
			goto out;
	}

	dev = NULL;
 out:
	pci_dev_get(dev);
204
	up_read(&pci_bus_sem);
L
Linus Torvalds 已提交
205 206
	return dev;
}
207
EXPORT_SYMBOL(pci_get_slot);
L
Linus Torvalds 已提交
208

A
Alan Cox 已提交
209
/**
210 211 212 213 214 215
 * pci_get_domain_bus_and_slot - locate PCI device for a given PCI domain (segment), bus, and slot
 * @domain: PCI domain/segment on which the PCI device resides.
 * @bus: PCI bus on which desired PCI device resides
 * @devfn: encodes number of PCI slot in which the desired PCI device
 * resides and the logical device number within that slot in case of
 * multi-function devices.
216
 *
217 218 219 220 221 222
 * Given a PCI domain, bus, and slot/function number, the desired PCI
 * device is located in the list of PCI devices. If the device is
 * found, its reference count is increased and this function returns a
 * pointer to its data structure.  The caller must decrement the
 * reference count by calling pci_dev_put().  If no device is found,
 * %NULL is returned.
A
Alan Cox 已提交
223
 */
224 225
struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
					    unsigned int devfn)
A
Alan Cox 已提交
226 227 228
{
	struct pci_dev *dev = NULL;

K
Kulikov Vasiliy 已提交
229
	for_each_pci_dev(dev) {
230 231
		if (pci_domain_nr(dev->bus) == domain &&
		    (dev->bus->number == bus && dev->devfn == devfn))
A
Alan Cox 已提交
232 233 234 235
			return dev;
	}
	return NULL;
}
236
EXPORT_SYMBOL(pci_get_domain_bus_and_slot);
A
Alan Cox 已提交
237

238
static int match_pci_dev_by_id(struct device *dev, void *data)
L
Linus Torvalds 已提交
239
{
240 241
	struct pci_dev *pdev = to_pci_dev(dev);
	struct pci_device_id *id = data;
L
Linus Torvalds 已提交
242

243 244 245
	if (pci_match_one_device(id, pdev))
		return 1;
	return 0;
L
Linus Torvalds 已提交
246 247
}

248 249 250
/*
 * pci_get_dev_by_id - begin or continue searching for a PCI device by id
 * @id: pointer to struct pci_device_id to match for the device
L
Linus Torvalds 已提交
251 252
 * @from: Previous PCI device found in search, or %NULL for new search.
 *
253
 * Iterates through the list of known PCI devices.  If a PCI device is found
254 255 256 257 258 259 260 261 262
 * with a matching id a pointer to its device structure is returned, and the
 * reference count to the device is incremented.  Otherwise, %NULL is returned.
 * A new search is initiated by passing %NULL as the @from argument.  Otherwise
 * if @from is not %NULL, searches continue from next device on the global
 * list.  The reference count for @from is always decremented if it is not
 * %NULL.
 *
 * This is an internal function for use by the other search functions in
 * this file.
L
Linus Torvalds 已提交
263
 */
264
static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
265
					 struct pci_dev *from)
L
Linus Torvalds 已提交
266
{
267 268 269 270 271
	struct device *dev;
	struct device *dev_start = NULL;
	struct pci_dev *pdev = NULL;

	WARN_ON(in_interrupt());
M
Matthew Wilcox 已提交
272 273
	if (from)
		dev_start = &from->dev;
274 275 276 277
	dev = bus_find_device(&pci_bus_type, dev_start, (void *)id,
			      match_pci_dev_by_id);
	if (dev)
		pdev = to_pci_dev(dev);
278
	pci_dev_put(from);
279
	return pdev;
L
Linus Torvalds 已提交
280 281 282 283 284 285 286 287 288 289
}

/**
 * pci_get_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
 * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
 * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
 * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids
 * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids
 * @from: Previous PCI device found in search, or %NULL for new search.
 *
290 291
 * Iterates through the list of known PCI devices.  If a PCI device is found
 * with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its
L
Linus Torvalds 已提交
292 293
 * device structure is returned, and the reference count to the device is
 * incremented.  Otherwise, %NULL is returned.  A new search is initiated by
294
 * passing %NULL as the @from argument.  Otherwise if @from is not %NULL,
L
Linus Torvalds 已提交
295 296 297
 * searches continue from next device on the global list.
 * The reference count for @from is always decremented if it is not %NULL.
 */
298 299
struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
			       unsigned int ss_vendor, unsigned int ss_device,
300
			       struct pci_dev *from)
L
Linus Torvalds 已提交
301
{
302 303 304 305 306 307
	struct pci_device_id id = {
		.vendor = vendor,
		.device = device,
		.subvendor = ss_vendor,
		.subdevice = ss_device,
	};
308

309
	return pci_get_dev_by_id(&id, from);
L
Linus Torvalds 已提交
310
}
311
EXPORT_SYMBOL(pci_get_subsys);
L
Linus Torvalds 已提交
312 313 314 315 316 317 318 319 320 321 322

/**
 * pci_get_device - begin or continue searching for a PCI device by vendor/device id
 * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
 * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
 * @from: Previous PCI device found in search, or %NULL for new search.
 *
 * Iterates through the list of known PCI devices.  If a PCI device is
 * found with a matching @vendor and @device, the reference count to the
 * device is incremented and a pointer to its device structure is returned.
 * Otherwise, %NULL is returned.  A new search is initiated by passing %NULL
323
 * as the @from argument.  Otherwise if @from is not %NULL, searches continue
L
Linus Torvalds 已提交
324 325 326
 * from next device on the global list.  The reference count for @from is
 * always decremented if it is not %NULL.
 */
R
Ryan Desfosses 已提交
327 328
struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
			       struct pci_dev *from)
L
Linus Torvalds 已提交
329 330 331
{
	return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
332
EXPORT_SYMBOL(pci_get_device);
L
Linus Torvalds 已提交
333 334 335 336 337 338 339 340 341 342

/**
 * pci_get_class - begin or continue searching for a PCI device by class
 * @class: search for a PCI device with this class designation
 * @from: Previous PCI device found in search, or %NULL for new search.
 *
 * Iterates through the list of known PCI devices.  If a PCI device is
 * found with a matching @class, the reference count to the device is
 * incremented and a pointer to its device structure is returned.
 * Otherwise, %NULL is returned.
343
 * A new search is initiated by passing %NULL as the @from argument.
L
Linus Torvalds 已提交
344 345 346 347 348 349
 * Otherwise if @from is not %NULL, searches continue from next device
 * on the global list.  The reference count for @from is always decremented
 * if it is not %NULL.
 */
struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
{
350 351 352 353 354 355 356 357 358 359
	struct pci_device_id id = {
		.vendor = PCI_ANY_ID,
		.device = PCI_ANY_ID,
		.subvendor = PCI_ANY_ID,
		.subdevice = PCI_ANY_ID,
		.class_mask = PCI_ANY_ID,
		.class = class,
	};

	return pci_get_dev_by_id(&id, from);
L
Linus Torvalds 已提交
360
}
361
EXPORT_SYMBOL(pci_get_class);
L
Linus Torvalds 已提交
362

363 364 365 366 367 368 369 370 371 372 373 374
/**
 * pci_dev_present - Returns 1 if device matching the device list is present, 0 if not.
 * @ids: A pointer to a null terminated list of struct pci_device_id structures
 * that describe the type of PCI device the caller is trying to find.
 *
 * Obvious fact: You do not have a reference to any device that might be found
 * by this function, so if that device is removed from the system right after
 * this function is finished, the value will be stale.  Use this function to
 * find devices that are usually built into a system, or for a general hint as
 * to if another device happens to be present at this specific moment in time.
 */
int pci_dev_present(const struct pci_device_id *ids)
A
Alan Cox 已提交
375
{
376
	struct pci_dev *found = NULL;
A
Alan Cox 已提交
377 378 379

	WARN_ON(in_interrupt());
	while (ids->vendor || ids->subvendor || ids->class_mask) {
380
		found = pci_get_dev_by_id(ids, NULL);
381 382 383 384
		if (found) {
			pci_dev_put(found);
			return 1;
		}
A
Alan Cox 已提交
385 386
		ids++;
	}
387

388
	return 0;
L
Linus Torvalds 已提交
389 390
}
EXPORT_SYMBOL(pci_dev_present);