Chinaunix首页 | 论坛 | 博客
  • 博客访问: 581986
  • 博文数量: 70
  • 博客积分: 3736
  • 博客等级: 中校
  • 技术积分: 1728
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-08 09:15
文章分类
文章存档

2014年(1)

2012年(21)

2011年(7)

2010年(28)

2009年(13)

分类: LINUX

2010-05-07 10:47:10

摘自:
前几日读书会,谈到linux中driver和device的匹配问题,我认为是通过设备名来匹配的,因为我之前看过platform的驱动,它就是通过设备name和驱动name来进行匹配,所以我确信linux里边所有的驱动和设备都是这样匹配的。但师兄A提出了反对意见,并举pci设备和pci驱动的匹配过程为例。我深信自己的理解是正确的,下来以后分秒必争的查看了内核源代码,结果发现:我们的理解都是正确的,但又都是不正确的!
先以pci设备和pci驱动的匹配过程为例:
static struct pci_driver serial_pci_driver = {
        .name           = "serial",
        .probe          = pciserial_init_one,
        .remove         = __devexit_p(pciserial_remove_one),
#ifdef CONFIG_PM
        .suspend        = pciserial_suspend_one,
        .resume         = pciserial_resume_one,
#endif
        .id_table       = serial_pci_tbl,
};

static int __init serial8250_pci_init(void)
{
        return pci_register_driver(&serial_pci_driver);
}

分析:
#define pci_register_driver(driver)             \
        __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

int __pci_register_driver(struct pci_driver *drv, struct module *owner,
                          const char *mod_name)
{
    ...
        drv->driver.bus = &pci_bus_type;
        error = driver_register(&drv->driver);
        ...
}
仅仅抓取两句关键代码,后面会用到
driver_register ->  bus_add_driver ->  driver_attach(&driver->driver);

int driver_attach(struct device_driver *drv)
{
    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
    //遍历总线上的所有设备,每遍历一个设备,就调用一次__driver_attach来和驱动进行匹配
}

static int __driver_attach(struct device *dev, void *data)
{
        struct device_driver *drv = data;

        /*
         * Lock device and try to bind to it. We drop the error
         * here and always return 0, because we need to keep trying
         * to bind to devices and some drivers will return an error
         * simply if it didn't support the device.
         *
         * driver_probe_device() will spit a warning if there
         * is an error.
         */

        if (drv->bus->match && !drv->bus->match(dev, drv))
                return 0;

        if (dev->parent)        /* Needed for USB */
                down(&dev->parent->sem);
        down(&dev->sem);
        if (!dev->driver)
                driver_probe_device(drv, dev);
        up(&dev->sem);  
        if (dev->parent)
                up(&dev->parent->sem);
        
        return 0;
}
我最关注的是这句代码:
if (drv->bus->match && !drv->bus->match(dev, drv))
                return 0;
这句代码揭露了匹配的过程。
这是一种面向对像的思想,你是pci设备,那就调用pci总线的match函数;你是其他总线的设备
那就调用相应的其他的match函数。由于我们是pci的驱动(我们注册驱动使用的函数是pci_register_driver)并且在前面有:
drv->driver.bus = &pci_bus_type;让我们看看pci_bus_type是和角色:
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,
        .pm             = PCI_PM_OPS_PTR,
};
很明显,我们将进入pci_bus_match
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 = to_pci_driver(drv);
        const struct pci_device_id *found_id;

        found_id = pci_match_device(pci_drv, pci_dev);//对pci驱动和设备进行匹配
        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;

        /* 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)) {
                        spin_unlock(&drv->dynids.lock);
                        return &dynid->id;
                }
        }
        spin_unlock(&drv->dynids.lock);
        return pci_match_id(drv->id_table, dev);
}
static inline const struct pci_device_id *
pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
{                                                  
        if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
            (id->device == PCI_ANY_ID || id->device == dev->device) &&
            (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
            (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
            !((id->class ^ dev->class) & id->class_mask))
                return id;
        return NULL;
}
这就是具体的匹配过程,这个过程大家看代码应该可以明白。
假如匹配,那么会返回一个pci_device_id类型的指针。
在__driver_attach函数里边继续往下看,有一句:
if (!dev->driver)
                driver_probe_device(drv, dev);
假如 这个设备还没有“名花有主”,那么调用driver_probe_device
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
        int ret = 0;

        if (!device_is_registered(dev))
                return -ENODEV;
        if (drv->bus->match && !drv->bus->match(dev, drv))
                goto done;

        pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
                 drv->bus->name, __func__, dev_name(dev), drv->name);

        ret = really_probe(dev, drv);

done:
        return ret;
}

static int really_probe(struct device *dev, struct device_driver *drv)
{
    ...
        if (dev->bus->probe) {
                ret = dev->bus->probe(dev);
                if (ret)
                        goto probe_failed;
        } else if (drv->probe) {
                ret = drv->probe(dev);          //终于进入了我们注册的驱动的probe函数
                if (ret)
                        goto probe_failed;
        }
    ...
}
总结一下,pci驱动和pci设备的匹配不是通过名字来进行匹配的,具体过程上面已经说清楚了。而且,通过上面的分析,大家可以看出来
内核使用的是面向对象的思想,不同的总线它的匹配策略是不一样的。



附:
1.platform总线的匹配策略:
static int platform_match(struct device *dev, struct device_driver *drv)
{
        struct platform_device *pdev;

        pdev = container_of(dev, struct platform_device, dev);
        return (strcmp(pdev->name, drv->name) == 0);
}
可见platform总线设备和驱动的匹配策略很简单,仅仅是比较name域是否相同
2.usb总线的匹配策略:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
        /* devices and interfaces are handled separately */
        if (is_usb_device(dev)) {

                /* interface drivers never match devices */
                if (!is_usb_device_driver(drv))
                        return 0;

                /* TODO: Add real matching code */
                return 1;

        } else {
                struct usb_interface *intf;
                struct usb_driver *usb_drv;
                const struct usb_device_id *id;

                /* device drivers never match interfaces */
                if (is_usb_device_driver(drv))
                        return 0;

                intf = to_usb_interface(dev);
                usb_drv = to_usb_driver(drv);

                id = usb_match_id(intf, usb_drv->id_table);
                if (id)
                        return 1;

                id = usb_match_dynamic_id(intf, usb_drv);
                if (id)
                        return 1;
        }

        return 0;
}
usb总线设备和驱动的匹配策略则比较复杂。

摘自:
阅读(3977) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~