2005-04-17 02:20:36 +04:00
|
|
|
/*
|
2013-11-14 22:28:18 +04:00
|
|
|
* PCI searching functions.
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
* 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>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 11:04:11 +03:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include "pci.h"
|
|
|
|
|
2006-06-02 08:35:43 +04:00
|
|
|
DECLARE_RWSEM(pci_bus_sem);
|
2012-05-23 20:20:35 +04:00
|
|
|
EXPORT_SYMBOL_GPL(pci_bus_sem);
|
|
|
|
|
2014-05-23 03:07:30 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2014-05-23 03:07:43 +04:00
|
|
|
/*
|
|
|
|
* If the device is broken and uses an alias requester ID for
|
|
|
|
* DMA, iterate over that too.
|
|
|
|
*/
|
|
|
|
if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) {
|
|
|
|
ret = fn(pdev, PCI_DEVID(pdev->bus->number,
|
|
|
|
pdev->dma_alias_devfn), data);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-05-23 03:07:30 +04:00
|
|
|
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 {
|
2014-05-29 00:57:02 +04:00
|
|
|
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);
|
2014-05-23 03:07:30 +04:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-10-22 03:41:46 +04:00
|
|
|
/*
|
2009-12-03 14:49:24 +03:00
|
|
|
* find the upstream PCIe-to-PCI bridge of a PCI device
|
2007-10-22 03:41:46 +04:00
|
|
|
* if the device is PCIE, return NULL
|
2009-12-03 14:49:24 +03:00
|
|
|
* if the device isn't connected to a PCIe bridge (that is its parent is a
|
2007-10-22 03:41:46 +04:00
|
|
|
* legacy PCI bridge and the bridge is directly connected to bus 0), return its
|
|
|
|
* parent
|
|
|
|
*/
|
2014-04-19 04:13:49 +04:00
|
|
|
struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
|
2007-10-22 03:41:46 +04:00
|
|
|
{
|
|
|
|
struct pci_dev *tmp = NULL;
|
|
|
|
|
2009-11-11 08:36:17 +03:00
|
|
|
if (pci_is_pcie(pdev))
|
2007-10-22 03:41:46 +04:00
|
|
|
return NULL;
|
|
|
|
while (1) {
|
2009-05-26 11:06:10 +04:00
|
|
|
if (pci_is_root_bus(pdev->bus))
|
2007-10-22 03:41:46 +04:00
|
|
|
break;
|
|
|
|
pdev = pdev->bus->self;
|
|
|
|
/* a p2p bridge */
|
2009-11-11 08:36:17 +03:00
|
|
|
if (!pci_is_pcie(pdev)) {
|
2007-10-22 03:41:46 +04:00
|
|
|
tmp = pdev;
|
|
|
|
continue;
|
|
|
|
}
|
2009-12-03 14:49:24 +03:00
|
|
|
/* PCI device should connect to a PCIe bridge */
|
2012-07-24 13:20:03 +04:00
|
|
|
if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {
|
2007-10-22 03:41:46 +04:00
|
|
|
/* Busted hardware? */
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return pdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-03-27 09:53:30 +04:00
|
|
|
static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2014-02-13 17:14:03 +04:00
|
|
|
struct pci_bus *child;
|
|
|
|
struct pci_bus *tmp;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-04-19 04:13:49 +04:00
|
|
|
if (bus->number == busnr)
|
2005-04-17 02:20:36 +04:00
|
|
|
return bus;
|
|
|
|
|
2014-02-13 17:14:03 +04:00
|
|
|
list_for_each_entry(tmp, &bus->children, node) {
|
|
|
|
child = pci_do_find_bus(tmp, busnr);
|
2014-04-19 04:13:49 +04:00
|
|
|
if (child)
|
2005-04-17 02:20:36 +04:00
|
|
|
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.
|
|
|
|
*/
|
2014-04-19 04:13:49 +04:00
|
|
|
struct pci_bus *pci_find_bus(int domain, int busnr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2014-04-26 00:32:25 +04:00
|
|
|
EXPORT_SYMBOL(pci_find_bus);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* pci_find_next_bus - begin or continue searching for a PCI bus
|
|
|
|
* @from: Previous PCI bus found, or %NULL for new search.
|
|
|
|
*
|
2013-11-14 22:28:18 +04:00
|
|
|
* Iterates through the list of known PCI buses. A new search is
|
2006-07-30 14:03:41 +04:00
|
|
|
* initiated by passing %NULL as the @from argument. Otherwise if
|
2005-04-17 02:20:36 +04:00
|
|
|
* @from is not %NULL, searches continue from next device on the
|
|
|
|
* global list.
|
|
|
|
*/
|
2014-04-19 04:13:49 +04:00
|
|
|
struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct list_head *n;
|
|
|
|
struct pci_bus *b = NULL;
|
|
|
|
|
|
|
|
WARN_ON(in_interrupt());
|
2006-06-02 08:35:43 +04:00
|
|
|
down_read(&pci_bus_sem);
|
2005-04-17 02:20:36 +04:00
|
|
|
n = from ? from->node.next : pci_root_buses.next;
|
|
|
|
if (n != &pci_root_buses)
|
2014-02-13 17:14:03 +04:00
|
|
|
b = list_entry(n, struct pci_bus, node);
|
2006-06-02 08:35:43 +04:00
|
|
|
up_read(&pci_bus_sem);
|
2005-04-17 02:20:36 +04:00
|
|
|
return b;
|
|
|
|
}
|
2014-04-26 00:32:25 +04:00
|
|
|
EXPORT_SYMBOL(pci_find_next_bus);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* pci_get_slot - locate PCI device for a given PCI slot
|
|
|
|
* @bus: PCI bus on which desired PCI device resides
|
2013-11-14 22:28:18 +04:00
|
|
|
* @devfn: encodes number of PCI slot in which the desired PCI
|
|
|
|
* device resides and the logical device number within that slot
|
2005-04-17 02:20:36 +04:00
|
|
|
* in case of multi-function devices.
|
|
|
|
*
|
2013-11-14 22:28:18 +04:00
|
|
|
* Given a PCI bus and slot/function number, the desired PCI device
|
2005-04-17 02:20:36 +04:00
|
|
|
* 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.
|
|
|
|
*/
|
2012-08-18 01:53:27 +04:00
|
|
|
struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct pci_dev *dev;
|
|
|
|
|
|
|
|
WARN_ON(in_interrupt());
|
2006-06-02 08:35:43 +04:00
|
|
|
down_read(&pci_bus_sem);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-08-18 01:53:27 +04:00
|
|
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
2005-04-17 02:20:36 +04:00
|
|
|
if (dev->devfn == devfn)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = NULL;
|
|
|
|
out:
|
|
|
|
pci_dev_get(dev);
|
2006-06-02 08:35:43 +04:00
|
|
|
up_read(&pci_bus_sem);
|
2005-04-17 02:20:36 +04:00
|
|
|
return dev;
|
|
|
|
}
|
2014-04-26 00:32:25 +04:00
|
|
|
EXPORT_SYMBOL(pci_get_slot);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-10-17 03:20:21 +04:00
|
|
|
/**
|
2009-10-12 23:14:00 +04:00
|
|
|
* 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.
|
2007-06-29 03:04:21 +04:00
|
|
|
*
|
2009-10-12 23:14:00 +04:00
|
|
|
* 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.
|
2006-10-17 03:20:21 +04:00
|
|
|
*/
|
2009-10-12 23:14:00 +04:00
|
|
|
struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
|
|
|
|
unsigned int devfn)
|
2006-10-17 03:20:21 +04:00
|
|
|
{
|
|
|
|
struct pci_dev *dev = NULL;
|
|
|
|
|
2010-07-03 20:04:39 +04:00
|
|
|
for_each_pci_dev(dev) {
|
2009-10-12 23:14:00 +04:00
|
|
|
if (pci_domain_nr(dev->bus) == domain &&
|
|
|
|
(dev->bus->number == bus && dev->devfn == devfn))
|
2006-10-17 03:20:21 +04:00
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-10-12 23:14:00 +04:00
|
|
|
EXPORT_SYMBOL(pci_get_domain_bus_and_slot);
|
2006-10-17 03:20:21 +04:00
|
|
|
|
2008-02-13 22:03:58 +03:00
|
|
|
static int match_pci_dev_by_id(struct device *dev, void *data)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-02-13 22:03:58 +03:00
|
|
|
struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
|
struct pci_device_id *id = data;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-02-13 22:03:58 +03:00
|
|
|
if (pci_match_one_device(id, pdev))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2008-02-13 22:03:58 +03:00
|
|
|
/*
|
|
|
|
* 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
|
2005-04-17 02:20:36 +04:00
|
|
|
* @from: Previous PCI device found in search, or %NULL for new search.
|
|
|
|
*
|
2006-07-30 14:03:41 +04:00
|
|
|
* Iterates through the list of known PCI devices. If a PCI device is found
|
2008-02-13 22:03:58 +03:00
|
|
|
* 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.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2008-02-13 22:03:58 +03:00
|
|
|
static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
|
2008-08-26 19:20:34 +04:00
|
|
|
struct pci_dev *from)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-02-13 22:03:58 +03:00
|
|
|
struct device *dev;
|
|
|
|
struct device *dev_start = NULL;
|
|
|
|
struct pci_dev *pdev = NULL;
|
|
|
|
|
|
|
|
WARN_ON(in_interrupt());
|
2008-10-21 05:13:08 +04:00
|
|
|
if (from)
|
|
|
|
dev_start = &from->dev;
|
2008-02-13 22:03:58 +03:00
|
|
|
dev = bus_find_device(&pci_bus_type, dev_start, (void *)id,
|
|
|
|
match_pci_dev_by_id);
|
|
|
|
if (dev)
|
|
|
|
pdev = to_pci_dev(dev);
|
2008-08-22 00:47:58 +04:00
|
|
|
if (from)
|
|
|
|
pci_dev_put(from);
|
2008-02-13 22:03:58 +03:00
|
|
|
return pdev;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
2006-07-30 14:03:41 +04:00
|
|
|
* 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
|
2005-04-17 02:20:36 +04:00
|
|
|
* device structure is returned, and the reference count to the device is
|
|
|
|
* incremented. Otherwise, %NULL is returned. A new search is initiated by
|
2006-07-30 14:03:41 +04:00
|
|
|
* passing %NULL as the @from argument. Otherwise if @from is not %NULL,
|
2005-04-17 02:20:36 +04:00
|
|
|
* searches continue from next device on the global list.
|
|
|
|
* The reference count for @from is always decremented if it is not %NULL.
|
|
|
|
*/
|
2008-02-13 22:03:58 +03:00
|
|
|
struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
|
|
|
|
unsigned int ss_vendor, unsigned int ss_device,
|
2008-08-26 19:20:34 +04:00
|
|
|
struct pci_dev *from)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2012-08-23 11:45:03 +04:00
|
|
|
struct pci_device_id id = {
|
|
|
|
.vendor = vendor,
|
|
|
|
.device = device,
|
|
|
|
.subvendor = ss_vendor,
|
|
|
|
.subdevice = ss_device,
|
|
|
|
};
|
2007-01-06 03:36:21 +03:00
|
|
|
|
2012-08-23 11:45:03 +04:00
|
|
|
return pci_get_dev_by_id(&id, from);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2014-04-26 00:32:25 +04:00
|
|
|
EXPORT_SYMBOL(pci_get_subsys);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2006-07-30 14:03:41 +04:00
|
|
|
* as the @from argument. Otherwise if @from is not %NULL, searches continue
|
2005-04-17 02:20:36 +04:00
|
|
|
* from next device on the global list. The reference count for @from is
|
|
|
|
* always decremented if it is not %NULL.
|
|
|
|
*/
|
2014-04-19 04:13:49 +04:00
|
|
|
struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
|
|
|
|
struct pci_dev *from)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
|
|
|
|
}
|
2014-04-26 00:32:25 +04:00
|
|
|
EXPORT_SYMBOL(pci_get_device);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2006-07-30 14:03:41 +04:00
|
|
|
* A new search is initiated by passing %NULL as the @from argument.
|
2005-04-17 02:20:36 +04:00
|
|
|
* 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)
|
|
|
|
{
|
2012-08-23 11:45:03 +04:00
|
|
|
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);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2014-04-26 00:32:25 +04:00
|
|
|
EXPORT_SYMBOL(pci_get_class);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-02-13 00:36:20 +03:00
|
|
|
/**
|
|
|
|
* 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)
|
2006-12-05 02:14:44 +03:00
|
|
|
{
|
2008-02-13 22:03:58 +03:00
|
|
|
struct pci_dev *found = NULL;
|
2006-12-05 02:14:44 +03:00
|
|
|
|
|
|
|
WARN_ON(in_interrupt());
|
|
|
|
while (ids->vendor || ids->subvendor || ids->class_mask) {
|
2008-02-13 22:03:58 +03:00
|
|
|
found = pci_get_dev_by_id(ids, NULL);
|
2013-01-22 01:20:45 +04:00
|
|
|
if (found) {
|
|
|
|
pci_dev_put(found);
|
|
|
|
return 1;
|
|
|
|
}
|
2006-12-05 02:14:44 +03:00
|
|
|
ids++;
|
|
|
|
}
|
2013-01-22 01:20:45 +04:00
|
|
|
|
2008-02-13 00:36:20 +03:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(pci_dev_present);
|