摘自:

前几日读书会,谈到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总线设备和驱动的匹配策略则比较复杂。

摘自:

阅读(2861) | 评论(0) | 转发(0) |

linux 驱动没有设备id,linux不同总线的设备和驱动的匹配过程分析相关推荐

  1. linux下看pcie的设备id,linux lspci查看pci总线设备信息

    linux lspci查看pci总线设备信息 linux中lspci是一个用来显示系统中所有PCI总线设备或连接到该总线上的所有设备的工具,比如查看网卡.存储等信息. 参数 -v 使得 lspci 以 ...

  2. platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架

    platform框架 input. pinctrl. gpio 子系统都是 Linux 内核针对某一类设备而创建的框架, input子系统是管理输入的子系统 pinctrl 子系统重点是设置 PIN( ...

  3. Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)

    Android获取设备各项信息(设备id.ip地址.APP应用名称.设备名称.运行商.品牌.型号.分辨率.处理器.国家码.系统语言.网络类型.oaid.android版本.操作系统版本.mac地址.应 ...

  4. linux程序绑定硬件id,Linux:在系统上设置hostid?

    假设你所指的是传统的unix hostid.如果我想将软件绑定到一个系统,我会使用加密狗或一些更强大的方法来识别一个看起来像hostid的系统是非常随意的,但是.. 使用库调用"gethos ...

  5. linux 启动程序 绑定id,linux如何根据进程ID查找启动程序的路径

    昨天遇到一个问题,背景是这样的:我们工作环境不正常,使用ps命令查看,发现有程序A的两个进程状态一个是状态,一个是正常运行.由于该程序A是个通用服务程序,被拷贝成多份,分发到不同的目录中单独启动,只有 ...

  6. linux设备驱动之总线、设备、驱动

    文章转载至多个地方,网上拼凑的一篇文章,说的好听一些的话那就叫自己总结的文章,只 是多次引用啊,哈哈,哎,不管了,反正这个有利用学习进步就好,这是重要的,文章转载过来要经过一篇大脑才能成为自己的,以后 ...

  7. Linux 总线、设备、驱动模型的探究

    学习交流加 个人qq:1126137994 个人微信:liu1126137994 学习交流资源分享qq群:962535112 之前一直做项目,做项目的过程虽然也学习到了不少知识,但是,一直没有好好研究 ...

  8. Linux I2C核心、总线与设备驱动

    Linux I2C核心.总线与设备驱动 I2C总线仅仅使用SCL. SDA这两根信号线就实现了设备之间的数据交互,极大地简化了对硬件资源和PCB板布线空间的占用.因此, I2C总线非常广泛地应用在EE ...

  9. linux fb设备驱动,linux设备驱动归纳总结(八):1.总线、设备和驱动

    linux设备驱动归纳总结(八):1.总线.设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

最新文章

  1. 企业金融云存储建设之路
  2. 嵌入式MVN指令解析
  3. 黄金法则----比较法则
  4. POJ3468--A Simple Problem with Integers--线段树/树状数组 改段求段
  5. C语言中单目运算符的结合方向是,C语言运算符的结合性
  6. C语言基本运算符和表达式
  7. 第六章 统计量及其抽样分布
  8. append()与extend()
  9. c++ 如何确认网卡为无线网卡_笔记本无线网卡有哪几种 有必要升级吗
  10. C#使用Sockets操作FTP【转载】
  11. 云服务器如何选型?可以从这几个方面来考虑
  12. input type=text和textarea的区别
  13. HT1621B显示驱动LCD显示驱动芯片- SSOP48
  14. android多个app音量调节问题,「App 音量控制」依每个 App 的使用需求自动调整音量(Android)...
  15. android word编辑图片,Word转换成图片详细教程.doc
  16. 微信公众平台配置服务器之后实现自动回复
  17. 魔兽,星际,红警,完全对比表
  18. java-eclipse-tomcat配置运行发布网站
  19. 理解ASP.NET Core - 发送Http请求(HttpClient)
  20. rasp 系统_一类PHP RASP实现

热门文章

  1. (马世龙)Linux下CACTI完全搭建技术文档二
  2. BZOJ 4811 树链剖分+线段树
  3. UML该元素的行为为基础的元素
  4. Docker 入门(2)技术实现和核心组成
  5. JavaScript中的全局变量介绍
  6. 什么叫静态构建版本号码_为什么要使用GatsbyJS构建静态网站
  7. azure多功能成像好用吗_Azure持久功能简介:模式和最佳实践
  8. 校友邮箱_freeCodeCamp校友网络:FCC校友的自主指导网络
  9. 事件捕获(capture)和冒泡事件(Bubble)
  10. [RN] React Native 自定义导航栏随滚动渐变