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

摘自:

前几日读书会,谈到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) |

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397449.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

理解Flight框架核心

看到了这篇分析flight的文章还不错,就转过来了,地址:https://blog.csdn.net/sky_zhe/article/details/38906689 Flight框架(官网)是一个微型的PHP框架,它简单,快速,可扩展。借助Flig…

安装ISO系统(原版系统)系统终极方法

首先进入PE,在PE下找到你的系统ISO镜像,解压缩,然后将镜像里的boot文件夹、sources文件夹和bootmgr文件提取出来,然后复制到你要安装的分区(比如c盘),接下来拔下U盘,重新启动计算机&…

intel i218v千兆网卡 linux驱动,适用于英特尔® 千兆位以太网网络连接的 Linux* 基础驱动程序...

适用于英特尔 千兆位以太网网络连接的 Linux* igb* 基础驱动程序安装说明Linux* igb 驱动程序支持所有基于 82575、82576、82580,I350,I354 和 I210/I211 的英特尔 千兆位以太网网络连接。有关驱动程序配置的详细信息,请参阅下载中心中的自述…

Linux下如何抓取串口码流,linux alsa音频中采样率fs、比特率BCLK 、主时钟MCLK关系...

转:https://blog.csdn.net/lugandong/article/details/72468831一、拿512fs说话:看图知道采样的位深是32bit(位),左右声道各占了8*32BCLK,那一个完整的LRCLK一共8*32*2512BCLK。其实xxxfs就是这么算出来的,也是固定的&…

第 39 章 ThinkPHP--CURD 操作

学习ThinkPHP 模型中的 CURD 操作,也就是增删改查。通过 CURD, 我们可以方便快速的对数据库进行操作。 1.数据创建 2.数据写入 3.数据读取 4.数据更新 5.数据删除 一.数据创建 在数据库添加等操作之前,我们首先需要对数据进行创建…

洛谷 P1529 回家 Bessie Come Home Label:Dijkstra最短路 乱搞

题目描述 现在是晚餐时间,而母牛们在外面分散的牧场中。 农民约翰按响了电铃,所以她们开始向谷仓走去。 你的工作是要指出哪只母牛会最先到达谷仓(在给出的测试数据中,总会有且只有一只最快的母牛)。 在挤奶的时候(晚餐前),每只母牛都在她自己的牧场上,一些牧场上可能没有母牛。…

linux语言的说明顺序有哪些,(linux常用头文件详解.doc

(linux常用头文件详解linux常用头文件详解POSIX标准定义的头文件??????? 目录项???????? 文件控制??? 文件名匹配类型??? 路径名模式匹配类型??????? 组文件??? 网络数据库操作??????? 口令文件??? 正则表达式??????? TAR归档…

第 39 章 ThinkPHP--视图

学习要点: 1.模版定义 2.赋值和渲染 3.模版地址 4.获取内容 本节课,我们将要学习一下 ThinkPHP 视图,视图是 Web 的可见内容,一般是 HTML 结合 PHP 获取的数据提供给用户使用的部分,属于 MVC 中的 V。 一.模…

mysql日志(介绍 路径修改 备份)

2019独角兽企业重金招聘Python工程师标准>>> 环境:senos6 软件:mysql2.6.20 mysql日志: 错误日志 一般查询日志 慢查询日志 二进制日志 只记录DDL,DML等引起数据库改变的操作都会记录下来 复制&am…

Sort

<?xml version"1.0" encoding"utf-8"?> SortSort 1 Sort Select sort is the simplest sorting alogrithms. 1.1 IDEA 1.find the smallest element in the rest of array 2.exchange the element with with the i th entry. 3.repeat step1 and s…

a标签实现不跳转点击

<a class"tiao" href"./index.php"></a> JS实现无跳转a标签 <script type"text/javascript"> $(".tiao").click(function (){return false; }) </script> 转载于:https://www.cnblogs.com/wenhainan/p/…

linux下的c语言控制灯闪烁,C语言实现LED灯闪烁控制

原标题&#xff1a;C语言实现LED灯闪烁控制/********* 配套 **********/#include //包含 寄存器的头文件/****************************************函数功能&#xff1a;延时一段时间*****************************************/void delay(void) //两个void意思分别为无需返回…

VBA and Access

>>.用vba连接ACESS&#xff1a; Set Conn Server.CreateObject("ADODB.Connection") Conn.ConnectionString"ProviderMicrosoft.Jet.OLEDB.4.0;Data Source" & Server.MapPath("sample.mdb") Conn.Open>>.用vba连接EXCEL,打开EX…

温州大学c语言作业布置的网站,老师APP上布置作业 三年级娃为刷排名半夜做题_央广网...

在温州读小学三年级的皮皮(化名)&#xff0c;因为学习需要&#xff0c;在妈妈黄女士的手机里安装了5个APP学习软件。有数学速算的&#xff0c;英语配音的&#xff0c;还有语文复习的。这些软件&#xff0c;都是班上的老师推荐安装的。每天放学回家&#xff0c;皮皮就拿着黄女士…

Algorithm I assignment Collinear

这本来应该是第三周的作业&#xff0c;但是由于其他作业逼近deadline&#xff0c;暂时推后了一周完成。 这周的assignment大大提高了我对这门课的看法&#xff0c;不得不说&#xff0c;Algorithms这门课的assignment部分设计得很好。为什么好&#xff1f;个人认为有以下几点&am…

vc c语言坐标图,VC++6.0下C语言画图编程问题

复制内容到剪贴板代码:#include#includevoid CSinusoidView::OnDraw(CDC* pDC){CSinusoidDoc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for native data here//建立画笔CPen cpen,pen;pen.CreatePen(PS_SOLID,4,RGB(0,0,0));cpen.CreatePen(PS_SOLID,2…

Java BigDecimal详解

1.引言 float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算&#xff0c;这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而&#xff0c;它们没有提供完全精确的结果&#xff0c;所以不应该被用于要求精确结果的场合。但是…

Erlang库 -- 有意思的库汇总

抄自这里 首先&#xff0c;库存在的目的大致可分为&#xff1a;1、提供便利2、尽可能解决一些痛点首先&#xff0c;我们先明确一下Erlang编程语言的一些痛点&#xff08;伪痛点&#xff09;&#xff1a;1&#xff0c;单进程问题Erlang虚拟机属于抢占式调度&#xff0c;抢占式调…

windows 串口编程 c语言,windows下C语言版串口发送程序(基于VS2017)

#include "tchar.h"#include int main(){/*****************************打开串口*************************************/HANDLE hCom;//全局变量&#xff0c;串口句柄hCom CreateFile(_T("COM3"),//COM3口GENERIC_READ | GENERIC_WRITE,//允许读和写0,/…

scikit-learn决策树算法类库使用小结

之前对决策树的算法原理做了总结&#xff0c;包括决策树算法原理(上)和决策树算法原理(下)。今天就从实践的角度来介绍决策树算法&#xff0c;主要是讲解使用scikit-learn来跑决策树算法&#xff0c;结果的可视化以及一些参数调参的关键点。 1. scikit-learn决策树算法类库介绍…