c
pci_register_driver() (include/linux/pci.h)
-->pci_register_driver() (drivers/pci/pci-driver.c)
-->driver_register() (drivers/base/driver.c)
-->bus_add_driver (drivers/base/driver.c)
-->driver_attach() (drivers/base/dd.c)
-->__driver_attach() (drivers/base/dd.c)
-->driver_match_device()
pci_bus_match
driver_match_device()
-->pci_bus_match (drivers/pci/pci-driver.c)
-->pci_match_device() (drivers/pci/pci-driver.c)
-->pci_match_id() (drivers/pci/pci-driver.c)
-->pci_match_one_device (drivers/pci/pci.h)
整个驱动的注册后触发使能的过程如上。这里核心位于bus_for_each_dev
c
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
c
static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *pci_drv;
const struct pci_device_id *found_id;
if (!pci_dev->match_driver)
return 0;
pci_drv = to_pci_driver(drv);
found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;
return 0;
}
static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
struct pci_dev *dev)
{
struct pci_dynid *dynid;
const struct pci_device_id *found_id = NULL;
/* When driver_override is set, only bind to the matching driver */
if (dev->driver_override && strcmp(dev->driver_override, drv->name))
return NULL;
/* Look at the dynamic ids first, before the static ones */
spin_lock(&drv->dynids.lock);
list_for_each_entry(dynid, &drv->dynids.list, node) {
if (pci_match_one_device(&dynid->id, dev)) {
found_id = &dynid->id;
break;
}
}
spin_unlock(&drv->dynids.lock);
if (!found_id)
found_id = pci_match_id(drv->id_table, dev);
/* driver_override will always match, send a dummy id */
if (!found_id && dev->driver_override)
found_id = &pci_device_id_any;
return found_id;
}
为了查看为什么驱动匹配不到设备,我写了如下测试代码。当然必须重构部分数据结构,因为没有导出。
c
int test_bus_for_each_dev( void )
{
struct klist_iter i;
struct device *dev;
int error = 0;
struct device *start = NULL;
struct bus_type *bus = &pci_bus_type;
printk("pci_bus_type %lx \n", pci_bus_type);
klist_iter_init_node(&bus->p->klist_devices, &i,
( NULL));
while (!error && (dev = next_device(&i))){
struct pci_dev *pci_dev = to_pci_dev(dev);
printk("pci_dev %x %x match driver %x \n",
pci_dev->vendor, pci_dev->device, pci_dev->match_driver);
}
klist_iter_exit(&i);
return error;
}
最后发现pci_dev->match_driver没有使能。
c
struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops,
void *sysdata)
{
LIST_HEAD(resources);
struct pci_bus *b;
pci_add_resource(&resources, &ioport_resource);
pci_add_resource(&resources, &iomem_resource);
pci_add_resource(&resources, &busn_resource);
b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources);
if (b) {
pci_scan_child_bus(b);
} else {
pci_free_resource_list(&resources);
}
return b;
}
EXPORT_SYMBOL(pci_scan_bus);
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
struct pci_ops *ops, void *sysdata, struct list_head *resources)
{
int error;
struct pci_host_bridge *bridge;
bridge = pci_alloc_host_bridge(0);
if (!bridge)
return NULL;
bridge->dev.parent = parent;
list_splice_init(resources, &bridge->windows);
bridge->sysdata = sysdata;
bridge->busnr = bus;
bridge->ops = ops;
error = pci_register_host_bridge(bridge);
if (error < 0)
goto err_out;
return bridge->bus;
err_out:
put_device(&bridge->dev);
return NULL;
}
EXPORT_SYMBOL_GPL(pci_create_root_bus);
总线设备其实对于总线驱动来看,仅仅是配置空间和bar空间。只要过滤插槽号和func就能过滤任意设别,或者虚拟一个设备出来。