全部博文(51)
分类: LINUX
2016-09-12 17:57:35
以Atheros wifi驱动加载流程为例来说明PCI设备驱动加载流程
static struct pci_driver ath_pci_driver = { .name = "ath_pci", .id_table = ath_pci_id_table, //列出支持的pci类型; .probe = ath_pci_probe, .remove = ath_pci_remove, #ifdef ATH_BUS_PM .suspend = ath_pci_suspend, .resume = ath_pci_resume, #endif /* ATH_BUS_PM */ /* Linux 2.4.6 has save_state and enable_wake that are not used here */ }; |
struct bus_type pci_bus_type = { .name = "pci", .match = pci_bus_match, .uevent = pci_uevent, .probe = pci_device_probe, .remove = pci_device_remove, .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, .bus_attrs = pci_bus_attrs, .pm = PCI_PM_OPS_PTR, }; |
pci_register_driver
-->__pci_register_driver //register a new pci driver;Adds the driver structure to the list of registered drivers.
int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name) { int error;
/* initialize common driver fields */ pci设备驱动统一信息; drv->driver.name = drv->name; drv->driver.bus = &pci_bus_type; drv->driver.owner = owner; drv->driver.mod_name = mod_name;
spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */ error = driver_register(&drv->driver); //register driver with bus if (error) goto out;
error = pci_create_newid_files(drv); if (error) goto out_newid; out: return error;
out_newid: driver_unregister(&drv->driver); goto out; } |
driver_register-->bus_add_driver //Add a driver to the bus.
-->初始化struct driver_private, kobject相关;
-->driver_attach
提取出drv_bus上的所有设备,依次调用__driver_attach
-->__driver_attach
-->driver_match_device
调用pci_bus_match,通过比对id_table表项与设备信息来确认驱动是否支持该设备;
static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data;
if (!driver_match_device(drv, dev)) return 0;
if (dev->parent) /* Needed for USB */ device_lock(dev->parent); device_lock(dev); if (!dev->driver) //设备已经绑定驱动,则跳过probe流程; driver_probe_device(drv, dev); // attempt to bind device & driver together device_unlock(dev); if (dev->parent) device_unlock(dev->parent);
return 0; } |
--> really_probe
static int really_probe(struct device *dev, struct device_driver *drv) { int ret = 0;
atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv; //dev绑定driver if (driver_sysfs_add(dev)) { //新建sysfs printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); goto probe_failed; }
if (dev->bus->probe) { ret = dev->bus->probe(dev); // pci_device_probe if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; }
driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); goto done;
probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL;
if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ dev_info(dev, "Driver %s requests probe deferral\n", drv->name); driver_deferred_probe_add(dev); } else if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev_name(dev), ret); } else { pr_debug("%s: probe of %s rejects match %d\n", drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0; done: atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; } |
static int pci_device_probe(struct device * dev) { int error = 0; struct pci_driver *drv; struct pci_dev *pci_dev;
drv = to_pci_driver(dev->driver); pci_dev = to_pci_dev(dev); pci_dev_get(pci_dev); error = __pci_device_probe(drv, pci_dev); if (error) pci_dev_put(pci_dev);
return error; } |
static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) { const struct pci_device_id *id; int error = 0;
if (!pci_dev->driver && drv->probe) { error = -ENODEV;
id = pci_match_device(drv, pci_dev); //检查pci设备与驱动是否匹配; if (id) error = pci_call_probe(drv, pci_dev, id); //调用local_pci_probe if (error >= 0) { pci_dev->driver = drv; error = 0; } } return error; } |
static long local_pci_probe(void *_ddi) { struct drv_dev_and_id *ddi = _ddi; struct device *dev = &ddi->dev->dev; int rc;
/* Unbound PCI devices are always set to disabled and suspended. * During probe, the device is set to enabled and active and the * usage count is incremented. If the driver supports runtime PM, * it should call pm_runtime_put_noidle() in its probe routine and * pm_runtime_get_noresume() in its remove routine. */ pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev);
rc = ddi->drv->probe(ddi->dev, ddi->id); //此处的probe为Atheros的ath_pci_probe if (rc) { pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_put_noidle(dev); } return rc; } |