pci-driver.c 4.25 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 * drivers/pci/pci-driver.c
Linus Torvalds's avatar
Linus Torvalds committed
3 4 5 6
 *
 */

#include <linux/pci.h>
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
#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;
}
Linus Torvalds's avatar
Linus Torvalds committed
136

137
static int pci_device_suspend(struct device * dev, u32 state, u32 level)
Linus Torvalds's avatar
Linus Torvalds committed
138 139 140 141 142
{
	struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev);
	int error = 0;

	if (pci_dev->driver) {
143
		if (level == SUSPEND_SAVE_STATE && pci_dev->driver->save_state)
Linus Torvalds's avatar
Linus Torvalds committed
144
			error = pci_dev->driver->save_state(pci_dev,state);
145
		else if (level == SUSPEND_POWER_DOWN && pci_dev->driver->suspend)
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149 150
			error = pci_dev->driver->suspend(pci_dev,state);
	}
	return error;
}

151
static int pci_device_resume(struct device * dev, u32 level)
Linus Torvalds's avatar
Linus Torvalds committed
152 153 154 155
{
	struct pci_dev * pci_dev = (struct pci_dev *)list_entry(dev,struct pci_dev,dev);

	if (pci_dev->driver) {
156
		if (level == RESUME_POWER_ON && pci_dev->driver->resume)
Linus Torvalds's avatar
Linus Torvalds committed
157 158 159 160 161 162 163 164 165
			pci_dev->driver->resume(pci_dev);
	}
	return 0;
}

struct device_driver pci_device_driver = {
	suspend:	pci_device_suspend,
	resume:		pci_device_resume,
};
166

167 168 169 170 171 172 173 174 175 176 177
struct bus_type pci_bus_type = {
	name:	"pci",
};

static int __init pci_driver_init(void)
{
	return bus_register(&pci_bus_type);
}

subsys_initcall(pci_driver_init);

178 179 180 181
EXPORT_SYMBOL(pci_match_device);
EXPORT_SYMBOL(pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver);
EXPORT_SYMBOL(pci_dev_driver);