Commit 9036d433 authored by Patrick Mochel's avatar Patrick Mochel Committed by Linus Torvalds

Split drivers/pci/pci.c into smaller, bite-size chunks

parent 8da5f1d4
......@@ -11,9 +11,11 @@
O_TARGET := driver.o
export-objs := pci.o
obj-y += access.o probe.o pci.o pool.o quirks.o \
compat.o names.o pci-driver.o search.o
obj-$(CONFIG_PM) += power.o
obj-$(CONFIG_HOTPLUG) += hotplug.o
obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o pci-driver.o
obj-$(CONFIG_PROC_FS) += proc.o
ifndef CONFIG_SPARC64
......@@ -35,6 +37,8 @@ ifndef CONFIG_X86
obj-y += syscall.o
endif
export-objs := $(obj-y)
include $(TOPDIR)/Rules.make
names.o: names.c devlist.h classlist.h
......
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/ioport.h>
/*
* This interrupt-safe spinlock protects all accesses to PCI
* configuration space.
*/
static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;
/*
* Wrappers for all PCI configuration access functions. They just check
* alignment, do locking and call the low-level functions pointed to
* by pci_dev->ops.
*/
#define PCI_byte_BAD 0
#define PCI_word_BAD (pos & 1)
#define PCI_dword_BAD (pos & 3)
#define PCI_OP(rw,size,type) \
int pci_##rw##_config_##size (struct pci_dev *dev, int pos, type value) \
{ \
int res; \
unsigned long flags; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irqsave(&pci_lock, flags); \
res = dev->bus->ops->rw##_##size(dev, pos, value); \
spin_unlock_irqrestore(&pci_lock, flags); \
return res; \
}
PCI_OP(read, byte, u8 *)
PCI_OP(read, word, u16 *)
PCI_OP(read, dword, u32 *)
PCI_OP(write, byte, u8)
PCI_OP(write, word, u16)
PCI_OP(write, dword, u32)
EXPORT_SYMBOL(pci_read_config_byte);
EXPORT_SYMBOL(pci_read_config_word);
EXPORT_SYMBOL(pci_read_config_dword);
EXPORT_SYMBOL(pci_write_config_byte);
EXPORT_SYMBOL(pci_write_config_word);
EXPORT_SYMBOL(pci_write_config_dword);
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/kmod.h> /* for hotplug_path */
extern struct list_head pci_drivers;
extern int pci_announce_device(struct pci_driver *drv, struct pci_dev *dev);
#ifndef FALSE
#define FALSE (0)
#define TRUE (!FALSE)
#endif
static void
run_sbin_hotplug(struct pci_dev *pdev, int insert)
{
int i;
char *argv[3], *envp[8];
char id[20], sub_id[24], bus_id[24], class_id[20];
if (!hotplug_path[0])
return;
sprintf(class_id, "PCI_CLASS=%04X", pdev->class);
sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device);
sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device);
sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name);
i = 0;
argv[i++] = hotplug_path;
argv[i++] = "pci";
argv[i] = 0;
i = 0;
/* minimal command environment */
envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
/* other stuff we want to pass to /sbin/hotplug */
envp[i++] = class_id;
envp[i++] = id;
envp[i++] = sub_id;
envp[i++] = bus_id;
if (insert)
envp[i++] = "ACTION=add";
else
envp[i++] = "ACTION=remove";
envp[i] = 0;
call_usermodehelper (argv [0], argv, envp);
}
/**
* pci_announce_device_to_drivers - tell the drivers a new device has appeared
* @dev: the device that has shown up
*
* Notifys the drivers that a new device has appeared, and also notifys
* userspace through /sbin/hotplug.
*/
void
pci_announce_device_to_drivers(struct pci_dev *dev)
{
struct list_head *ln;
for(ln=pci_drivers.next; ln != &pci_drivers; ln=ln->next) {
struct pci_driver *drv = list_entry(ln, struct pci_driver, node);
if (drv->remove && pci_announce_device(drv, dev))
break;
}
/* notify userspace of new hotplug device */
run_sbin_hotplug(dev, TRUE);
}
/**
* pci_insert_device - insert a hotplug device
* @dev: the device to insert
* @bus: where to insert it
*
* Add a new device to the device lists and notify userspace (/sbin/hotplug).
*/
void
pci_insert_device(struct pci_dev *dev, struct pci_bus *bus)
{
list_add_tail(&dev->bus_list, &bus->devices);
list_add_tail(&dev->global_list, &pci_devices);
#ifdef CONFIG_PROC_FS
pci_proc_attach_device(dev);
#endif
pci_announce_device_to_drivers(dev);
}
static void
pci_free_resources(struct pci_dev *dev)
{
int i;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *res = dev->resource + i;
if (res->parent)
release_resource(res);
}
}
/**
* pci_remove_device - remove a hotplug device
* @dev: the device to remove
*
* Delete the device structure from the device lists and
* notify userspace (/sbin/hotplug).
*/
void
pci_remove_device(struct pci_dev *dev)
{
if (dev->driver) {
if (dev->driver->remove)
dev->driver->remove(dev);
dev->driver = NULL;
}
list_del(&dev->bus_list);
list_del(&dev->global_list);
pci_free_resources(dev);
#ifdef CONFIG_PROC_FS
pci_proc_detach_device(dev);
#endif
/* notify userspace of hotplug device removal */
run_sbin_hotplug(dev, FALSE);
}
EXPORT_SYMBOL(pci_insert_device);
EXPORT_SYMBOL(pci_remove_device);
EXPORT_SYMBOL(pci_announce_device_to_drivers);
/*
* drivers/pci/pci-driver.c - default PCI driver.
* drivers/pci/pci-driver.c
*
*/
#include <linux/pci.h>
#include <linux/module.h>
/*
* Registration of PCI drivers and handling of hot-pluggable devices.
*/
LIST_HEAD(pci_drivers);
/**
* pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
* @ids: array of PCI device id structures to search in
* @dev: the PCI device structure to match against
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices.Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
const struct pci_device_id *
pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev)
{
while (ids->vendor || ids->subvendor || ids->class_mask) {
if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) &&
(ids->device == PCI_ANY_ID || ids->device == dev->device) &&
(ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) &&
(ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) &&
!((ids->class ^ dev->class) & ids->class_mask))
return ids;
ids++;
}
return NULL;
}
int
pci_announce_device(struct pci_driver *drv, struct pci_dev *dev)
{
const struct pci_device_id *id;
int ret = 0;
if (drv->id_table) {
id = pci_match_device(drv->id_table, dev);
if (!id) {
ret = 0;
goto out;
}
} else
id = NULL;
dev_probe_lock();
if (drv->probe(dev, id) >= 0) {
dev->driver = drv;
ret = 1;
}
dev_probe_unlock();
out:
return ret;
}
/**
* pci_register_driver - register a new pci driver
* @drv: the driver structure to register
*
* Adds the driver structure to the list of registered drivers
* Returns the number of pci devices which were claimed by the driver
* during registration. The driver remains registered even if the
* return value is zero.
*/
int
pci_register_driver(struct pci_driver *drv)
{
struct pci_dev *dev;
int count = 0;
list_add_tail(&drv->node, &pci_drivers);
pci_for_each_dev(dev) {
if (!pci_dev_driver(dev))
count += pci_announce_device(drv, dev);
}
return count;
}
/**
* pci_unregister_driver - unregister a pci driver
* @drv: the driver structure to unregister
*
* Deletes the driver structure from the list of registered PCI drivers,
* gives it a chance to clean up by calling its remove() function for
* each device it was responsible for, and marks those devices as
* driverless.
*/
void
pci_unregister_driver(struct pci_driver *drv)
{
struct pci_dev *dev;
list_del(&drv->node);
pci_for_each_dev(dev) {
if (dev->driver == drv) {
if (drv->remove)
drv->remove(dev);
dev->driver = NULL;
}
}
}
static struct pci_driver pci_compat_driver = {
name: "compat"
};
/**
* pci_dev_driver - get the pci_driver of a device
* @dev: the device to query
*
* Returns the appropriate pci_driver structure or %NULL if there is no
* registered driver for the device.
*/
struct pci_driver *
pci_dev_driver(const struct pci_dev *dev)
{
if (dev->driver)
return dev->driver;
else {
int i;
for(i=0; i<=PCI_ROM_RESOURCE; i++)
if (dev->resource[i].flags & IORESOURCE_BUSY)
return &pci_compat_driver;
}
return NULL;
}
static int pci_device_suspend(struct device * dev, u32 state, u32 level)
{
......@@ -34,3 +163,8 @@ struct device_driver pci_device_driver = {
suspend: pci_device_suspend,
resume: pci_device_resume,
};
EXPORT_SYMBOL(pci_match_device);
EXPORT_SYMBOL(pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver);
EXPORT_SYMBOL(pci_dev_driver);
This diff is collapsed.
#include <linux/pci.h>
#include <linux/module.h>
/*
* Pool allocator ... wraps the pci_alloc_consistent page allocator, so
* small blocks are easily used by drivers for bus mastering controllers.
* This should probably be sharing the guts of the slab allocator.
*/
struct pci_pool { /* the pool */
struct list_head page_list;
spinlock_t lock;
size_t blocks_per_page;
size_t size;
struct pci_dev *dev;
size_t allocation;
char name [32];
wait_queue_head_t waitq;
};
struct pci_page { /* cacheable header for 'allocation' bytes */
struct list_head page_list;
void *vaddr;
dma_addr_t dma;
unsigned long bitmap [0];
};
#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000)
#define POOL_POISON_BYTE 0xa7
/**
* pci_pool_create - Creates a pool of pci consistent memory blocks, for dma.
* @name: name of pool, for diagnostics
* @pdev: pci device that will be doing the DMA
* @size: size of the blocks in this pool.
* @align: alignment requirement for blocks; must be a power of two
* @allocation: returned blocks won't cross this boundary (or zero)
* @mem_flags: SLAB_* flags.
*
* Returns a pci allocation pool with the requested characteristics, or
* null if one can't be created. Given one of these pools, pci_pool_alloc()
* may be used to allocate memory. Such memory will all have "consistent"
* DMA mappings, accessible by the device and its driver without using
* cache flushing primitives. The actual size of blocks allocated may be
* larger than requested because of alignment.
*
* If allocation is nonzero, objects returned from pci_pool_alloc() won't
* cross that size boundary. This is useful for devices which have
* addressing restrictions on individual DMA transfers, such as not crossing
* boundaries of 4KBytes.
*/
struct pci_pool *
pci_pool_create (const char *name, struct pci_dev *pdev,
size_t size, size_t align, size_t allocation, int mem_flags)
{
struct pci_pool *retval;
if (align == 0)
align = 1;
if (size == 0)
return 0;
else if (size < align)
size = align;
else if ((size % align) != 0) {
size += align + 1;
size &= ~(align - 1);
}
if (allocation == 0) {
if (PAGE_SIZE < size)
allocation = size;
else
allocation = PAGE_SIZE;
// FIXME: round up for less fragmentation
} else if (allocation < size)
return 0;
if (!(retval = kmalloc (sizeof *retval, mem_flags)))
return retval;
strncpy (retval->name, name, sizeof retval->name);
retval->name [sizeof retval->name - 1] = 0;
retval->dev = pdev;
INIT_LIST_HEAD (&retval->page_list);
spin_lock_init (&retval->lock);
retval->size = size;
retval->allocation = allocation;
retval->blocks_per_page = allocation / size;
init_waitqueue_head (&retval->waitq);
return retval;
}
static struct pci_page *
pool_alloc_page (struct pci_pool *pool, int mem_flags)
{
struct pci_page *page;
int mapsize;
mapsize = pool->blocks_per_page;
mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG;
mapsize *= sizeof (long);
page = (struct pci_page *) kmalloc (mapsize + sizeof *page, mem_flags);
if (!page)
return 0;
page->vaddr = pci_alloc_consistent (pool->dev,
pool->allocation,
&page->dma);
if (page->vaddr) {
memset (page->bitmap, 0xff, mapsize); // bit set == free
#ifdef CONFIG_DEBUG_SLAB
memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
#endif
list_add (&page->page_list, &pool->page_list);
} else {
kfree (page);
page = 0;
}
return page;
}
static inline int
is_page_busy (int blocks, unsigned long *bitmap)
{
while (blocks > 0) {
if (*bitmap++ != ~0UL)
return 1;
blocks -= BITS_PER_LONG;
}
return 0;
}
static void
pool_free_page (struct pci_pool *pool, struct pci_page *page)
{
dma_addr_t dma = page->dma;
#ifdef CONFIG_DEBUG_SLAB
memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
#endif
pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma);
list_del (&page->page_list);
kfree (page);
}
/**
* pci_pool_destroy - destroys a pool of pci memory blocks.
* @pool: pci pool that will be destroyed
*
* Caller guarantees that no more memory from the pool is in use,
* and that nothing will try to use the pool after this call.
*/
void
pci_pool_destroy (struct pci_pool *pool)
{
unsigned long flags;
spin_lock_irqsave (&pool->lock, flags);
while (!list_empty (&pool->page_list)) {
struct pci_page *page;
page = list_entry (pool->page_list.next,
struct pci_page, page_list);
if (is_page_busy (pool->blocks_per_page, page->bitmap)) {
printk (KERN_ERR "pci_pool_destroy %s/%s, %p busy\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, page->vaddr);
/* leak the still-in-use consistent memory */
list_del (&page->page_list);
kfree (page);
} else
pool_free_page (pool, page);
}
spin_unlock_irqrestore (&pool->lock, flags);
kfree (pool);
}
/**
* pci_pool_alloc - get a block of consistent memory
* @pool: pci pool that will produce the block
* @mem_flags: SLAB_KERNEL or SLAB_ATOMIC
* @handle: pointer to dma address of block
*
* This returns the kernel virtual address of a currently unused block,
* and reports its dma address through the handle.
* If such a memory block can't be allocated, null is returned.
*/
void *
pci_pool_alloc (struct pci_pool *pool, int mem_flags, dma_addr_t *handle)
{
unsigned long flags;
struct list_head *entry;
struct pci_page *page;
int map, block;
size_t offset;
void *retval;
restart:
spin_lock_irqsave (&pool->lock, flags);
list_for_each (entry, &pool->page_list) {
int i;
page = list_entry (entry, struct pci_page, page_list);
/* only cachable accesses here ... */
for (map = 0, i = 0;
i < pool->blocks_per_page;
i += BITS_PER_LONG, map++) {
if (page->bitmap [map] == 0)
continue;
block = ffz (~ page->bitmap [map]);
if ((i + block) < pool->blocks_per_page) {
clear_bit (block, &page->bitmap [map]);
offset = (BITS_PER_LONG * map) + block;
offset *= pool->size;
goto ready;
}
}
}
if (!(page = pool_alloc_page (pool, mem_flags))) {
if (mem_flags == SLAB_KERNEL) {
DECLARE_WAITQUEUE (wait, current);
current->state = TASK_INTERRUPTIBLE;
add_wait_queue (&pool->waitq, &wait);
spin_unlock_irqrestore (&pool->lock, flags);
schedule_timeout (POOL_TIMEOUT_JIFFIES);
current->state = TASK_RUNNING;
remove_wait_queue (&pool->waitq, &wait);
goto restart;
}
retval = 0;
goto done;
}
clear_bit (0, &page->bitmap [0]);
offset = 0;
ready:
retval = offset + page->vaddr;
*handle = offset + page->dma;
done:
spin_unlock_irqrestore (&pool->lock, flags);
return retval;
}
static struct pci_page *
pool_find_page (struct pci_pool *pool, dma_addr_t dma)
{
unsigned long flags;
struct list_head *entry;
struct pci_page *page;
spin_lock_irqsave (&pool->lock, flags);
list_for_each (entry, &pool->page_list) {
page = list_entry (entry, struct pci_page, page_list);
if (dma < page->dma)
continue;
if (dma < (page->dma + pool->allocation))
goto done;
}
page = 0;
done:
spin_unlock_irqrestore (&pool->lock, flags);
return page;
}
/**
* pci_pool_free - put block back into pci pool
* @pool: the pci pool holding the block
* @vaddr: virtual address of block
* @dma: dma address of block
*
* Caller promises neither device nor driver will again touch this block
* unless it is first re-allocated.
*/
void
pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
{
struct pci_page *page;
unsigned long flags;
int map, block;
if ((page = pool_find_page (pool, dma)) == 0) {
printk (KERN_ERR "pci_pool_free %s/%s, %p/%lx (bad dma)\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, vaddr, (unsigned long) dma);
return;
}
block = dma - page->dma;
block /= pool->size;
map = block / BITS_PER_LONG;
block %= BITS_PER_LONG;
#ifdef CONFIG_DEBUG_SLAB
if (((dma - page->dma) + (void *)page->vaddr) != vaddr) {
printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%lx\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, vaddr, (unsigned long) dma);
return;
}
if (page->bitmap [map] & (1UL << block)) {
printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n",
pool->dev ? pool->dev->slot_name : NULL,
pool->name, dma);
return;
}
memset (vaddr, POOL_POISON_BYTE, pool->size);
#endif
spin_lock_irqsave (&pool->lock, flags);
set_bit (block, &page->bitmap [map]);
if (waitqueue_active (&pool->waitq))
wake_up (&pool->waitq);
/*
* Resist a temptation to do
* if (!is_page_busy(bpp, page->bitmap)) pool_free_page(pool, page);
* it is not interrupt safe. Better have empty pages hang around.
*/
spin_unlock_irqrestore (&pool->lock, flags);
}
EXPORT_SYMBOL (pci_pool_create);
EXPORT_SYMBOL (pci_pool_destroy);
EXPORT_SYMBOL (pci_pool_alloc);
EXPORT_SYMBOL (pci_pool_free);
#include <linux/pci.h>
#include <linux/pm.h>
/*
* PCI Power management..
*
* This needs to be done centralized, so that we power manage PCI
* devices in the right order: we should not shut down PCI bridges
* before we've shut down the devices behind them, and we should
* not wake up devices before we've woken up the bridge to the
* device.. Eh?
*
* We do not touch devices that don't have a driver that exports
* a suspend/resume function. That is just too dangerous. If the default
* PCI suspend/resume functions work for a device, the driver can
* easily implement them (ie just have a suspend function that calls
* the pci_set_power_state() function).
*/
static int pci_pm_save_state_device(struct pci_dev *dev, u32 state)
{
int error = 0;
if (dev) {
struct pci_driver *driver = dev->driver;
if (driver && driver->save_state)
error = driver->save_state(dev,state);
}
return error;
}
static int pci_pm_suspend_device(struct pci_dev *dev, u32 state)
{
int error = 0;
if (dev) {
struct pci_driver *driver = dev->driver;
if (driver && driver->suspend)
error = driver->suspend(dev,state);
}
return error;
}
static int pci_pm_resume_device(struct pci_dev *dev)
{
int error = 0;
if (dev) {
struct pci_driver *driver = dev->driver;
if (driver && driver->resume)
error = driver->resume(dev);
}
return error;
}
static int pci_pm_save_state_bus(struct pci_bus *bus, u32 state)
{
struct list_head *list;
int error = 0;
list_for_each(list, &bus->children) {
error = pci_pm_save_state_bus(pci_bus_b(list),state);
if (error) return error;
}
list_for_each(list, &bus->devices) {
error = pci_pm_save_state_device(pci_dev_b(list),state);
if (error) return error;
}
return 0;
}
static int pci_pm_suspend_bus(struct pci_bus *bus, u32 state)
{
struct list_head *list;
/* Walk the bus children list */
list_for_each(list, &bus->children)
pci_pm_suspend_bus(pci_bus_b(list),state);
/* Walk the device children list */
list_for_each(list, &bus->devices)
pci_pm_suspend_device(pci_dev_b(list),state);
return 0;
}
static int pci_pm_resume_bus(struct pci_bus *bus)
{
struct list_head *list;
/* Walk the device children list */
list_for_each(list, &bus->devices)
pci_pm_resume_device(pci_dev_b(list));
/* And then walk the bus children */
list_for_each(list, &bus->children)
pci_pm_resume_bus(pci_bus_b(list));
return 0;
}
static int pci_pm_save_state(u32 state)
{
struct list_head *list;
struct pci_bus *bus;
int error = 0;
list_for_each(list, &pci_root_buses) {
bus = pci_bus_b(list);
error = pci_pm_save_state_bus(bus,state);
if (!error)
error = pci_pm_save_state_device(bus->self,state);
}
return error;
}
static int pci_pm_suspend(u32 state)
{
struct list_head *list;
struct pci_bus *bus;
list_for_each(list, &pci_root_buses) {
bus = pci_bus_b(list);
pci_pm_suspend_bus(bus,state);
pci_pm_suspend_device(bus->self,state);
}
return 0;
}
static int pci_pm_resume(void)
{
struct list_head *list;
struct pci_bus *bus;
list_for_each(list, &pci_root_buses) {
bus = pci_bus_b(list);
pci_pm_resume_device(bus->self);
pci_pm_resume_bus(bus);
}
return 0;
}
static int
pci_pm_callback(struct pm_dev *pm_device, pm_request_t rqst, void *data)
{
int error = 0;
switch (rqst) {
case PM_SAVE_STATE:
error = pci_pm_save_state((unsigned long)data);
break;
case PM_SUSPEND:
error = pci_pm_suspend((unsigned long)data);
break;
case PM_RESUME:
error = pci_pm_resume();
break;
default: break;
}
return error;
}
static int __init pci_pm_init(void)
{
pm_register(PM_PCI_DEV, 0, pci_pm_callback);
return 0;
}
subsys_initcall(pci_pm_init);
This diff is collapsed.
......@@ -6,11 +6,9 @@
* Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/smp_lock.h>
......@@ -615,3 +613,11 @@ static int __init pci_proc_init(void)
}
__initcall(pci_proc_init);
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pci_proc_attach_device);
EXPORT_SYMBOL(pci_proc_detach_device);
EXPORT_SYMBOL(pci_proc_attach_bus);
EXPORT_SYMBOL(pci_proc_detach_bus);
#endif
#include <linux/pci.h>
#include <linux/module.h>
/**
* pci_find_slot - locate PCI device from a given PCI slot
* @bus: number of 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.
*
* Given a PCI bus and slot/function number, the desired PCI device
* is located in system global list of PCI devices. If the device
* is found, a pointer to its data structure is returned. If no
* device is found, %NULL is returned.
*/
struct pci_dev *
pci_find_slot(unsigned int bus, unsigned int devfn)
{
struct pci_dev *dev;
pci_for_each_dev(dev) {
if (dev->bus->number == bus && dev->devfn == devfn)
return dev;
}
return NULL;
}
/**
* pci_find_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.
*
* 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
* device structure is returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device on the global list.
*/
struct pci_dev *
pci_find_subsys(unsigned int vendor, unsigned int device,
unsigned int ss_vendor, unsigned int ss_device,
const struct pci_dev *from)
{
struct list_head *n = from ? from->global_list.next : pci_devices.next;
while (n != &pci_devices) {
struct pci_dev *dev = pci_dev_g(n);
if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
(device == PCI_ANY_ID || dev->device == device) &&
(ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
(ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
return dev;
n = n->next;
}
return NULL;
}
/**
* pci_find_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, a pointer to its device structure is
* returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device on the global list.
*/
struct pci_dev *
pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from)
{
return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
/**
* pci_find_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, a pointer to its device structure is
* returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device
* on the global list.
*/
struct pci_dev *
pci_find_class(unsigned int class, const struct pci_dev *from)
{
struct list_head *n = from ? from->global_list.next : pci_devices.next;
while (n != &pci_devices) {
struct pci_dev *dev = pci_dev_g(n);
if (dev->class == class)
return dev;
n = n->next;
}
return NULL;
}
EXPORT_SYMBOL(pci_find_class);
EXPORT_SYMBOL(pci_find_device);
EXPORT_SYMBOL(pci_find_slot);
EXPORT_SYMBOL(pci_find_subsys);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment