0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux设备与驱动的手动解绑与手动绑定

Linux阅码场 来源:Linuxer 2020-08-03 16:25 次阅读

众所周知,Linux靠设备与驱动之间的match,来完成设备与驱动的bind,从而触发驱动的probe()成员函数被执行。每个bus都有相应的match方法,完成match的总的入口函数是:

static inline int driver_match_device(struct device_driver *drv, struct device *dev){ return drv->bus->match ? drv->bus->match(dev, drv) : 1;}

而这个总的入口函数又会调用到各自不同总线的match函数,对于platform bus而言,它的match函数就是platform_match()

static int platform_match(struct device *dev, struct device_driver *drv){ struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* When driver_override is set, only bind to the matching driver */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0);}

从代码可以看出,platform的driver和device之间的match有很多方法成立,比如设备的name和驱动的name相同:

strcmp(pdev->name, drv->name) == 0

比如,设备的名字出现在驱动的ID表中:

if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL;

比如device tree里面的compatible字段与驱动的dt兼容性字段匹配:

if (of_driver_match_device(dev, drv)) return 1;

只要符合其中任意一种,driver和device都可以匹配上。

这种自动匹配非常简单,实施起来也非常容易。

但是有时候,这种自动匹配并不一定是我们想要的。比如我们有时候就是希望XXX设备用YYY驱动,而不是用XXX驱动。工程中有手动匹配的需求,最典型的场景是VFIO的场景,想让设备与内核空间原本绑定的驱动解绑,转而采用内核空间的通用VFIO驱动,而VFIO驱动又提供了userspace驾驭设备的能力。

下面我们来从原理和实践上演示这种手动的unbind和bind是怎么进行的。在《Linux设备驱动开发详解》一书中,我们给出了一个简单的globalfifo设备和globalfifo驱动:

globalfifo-dev.ko(增加platform_device的模块):

static int __init globalfifodev_init(void){ int ret; globalfifo_pdev=platform_device_alloc("globalfifo",-1); ret = platform_device_add(globalfifo_pdev);... return 0; }module_init(globalfifodev_init);

globalfifo.ko(增加platform_driver的模块):

static struct platform_driver globalfifo_driver = { .driver = { .name = "globalfifo", .owner = THIS_MODULE, }, .probe = globalfifo_probe, .remove = globalfifo_remove,}; module_platform_driver(globalfifo_driver);

由于其中的platform_driver和platform_device的name都是“globalfifo”,符合此行的匹配规则:

strcmp(pdev->name, drv->name) == 0

设备和驱动匹配成功,从sysfs也可以看出:

globalfifo的device和driver各自找到了对方。

现在我们来写一个第三者driver,名字叫做globalxxx,然后我们想把globalfifo device的driver指向globalxxx。因此我们要完成2步:

unbind:解除globalfifo driver与globalfifo device的绑定

bind: 进行globalxxxdriver与globalfifo device的绑定

第三者globalxxx驱动代码类似:

globalxxx.ko(增加platform_driver的模块):

static struct platform_driver globalxxx_driver = { .driver = { .name = "globalxxx", .owner = THIS_MODULE, }, .probe = globalxxx_probe, .remove = globalxxx_remove,}; module_platform_driver(globalxxx_driver);

下面我们来完成第一步的unbind,这一步很简单,跑到/sys/bus/platform/drivers/globalfifo目录,把设备globalfifo的名字写进去unbind文件:

当然我们也可以来回折腾着unbind,bind着玩:

这样我们看到一堆的probe(每次设备和驱动bind成功,驱动probe都会执行),remove(每次设备和驱动unbind成功,驱动remove都会执行),最后处于unbind状态。

现在我们来把globalfifo设备bind到globalxxx驱动:

绑定的时候提示错误!

绑定的时候提示错误!!

绑定的时候提示错误!!!

前面我们用globalfifo的driver去bind globalfifo的device的时候,是想怎么绑就怎么绑的,想绑多少次就绑多少次的!为什么换了globalxxx来绑就不行了呢?

爱情不是你想卖想买就能卖

让我挣开 让我明白

放手你的爱

我们来看看这个bind sysfs入口工作的函数bind_store():

static ssize_t bind_store(struct device_driver *drv, const char *buf, size_t count){ ... dev = bus_find_device_by_name(bus, NULL, buf); if (dev && dev->driver == NULL && driver_match_device(drv, dev)) { err = device_driver_attach(drv, dev); if (err > 0) { /* success */ err = count; } else if (err == 0) { /* driver didn't accept device */ err = -ENODEV; } } ...}

看起来,如果要强行bind,仍然需要device_driver_attach()成立,否则内核会返回-ENODEV错误:

} else if (err == 0) { /* driver didn't accept device */ err = -ENODEV; }

根据前文对platform_match()的代码分析,globalxxx driver和globalfifo device确实八竿子都打不着!!没有任何匹配因子。

下面我们来把globalxxx的代码稍微改一下,通过ID表来增加一个匹配因子:

static const struct platform_device_id globalxxx_ids[] = { { .name = "globalfifo", }, {}};MODULE_DEVICE_TABLE(platform, globalxxx_ids); static struct platform_driver globalxxx_driver = { .driver = { .name = "globalxxx", .owner = THIS_MODULE, }, .id_table = globalxxx_ids, .probe = globalxxx_probe, .remove = globalxxx_remove,}; module_platform_driver(globalxxx_driver);

rmmod和insmod globalxxx.ko

然后重新bind:

现在globalfifo device可以在globalxxx和globalfifo这2个driver里面进行自由地bind和unbind!

看到这里,客官们一定觉得这太特么狗血了!不是说可以自由地绑定第三者吗?为嘛还要求这个第三者驱动与这个原先的设备匹配呢?这有嘛意思呢?

别忘了,在platform_match中还有这么一行:

if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name);

设备完全可以自由地宣布她喜欢的第三者driver,哪怕这个第三者driver和她本身完全没有任何的匹配因子,操作的入口就是driver_override sysfs文件。

我们完全可以保留globalxxx驱动的原样

static struct platform_driver globalxxx_driver = { .driver = { .name = "globalxxx", .owner = THIS_MODULE, }, .probe = globalxxx_probe, .remove = globalxxx_remove,};

不去增加任何的id_table,而换做到globalfifo device里面去写driver_override文件,宣布globalxxx driver可以匹配globalfifo device。

这样之后,哪怕globalxxx driver和globalfifo device八竿子打不着,也是可以驱动globalfifo device的。工程里面如果我们想用VFIO的方式来驱动一个设备,就可以这样做:

echo vfio-platform > driver_override

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 驱动
    +关注

    关注

    12

    文章

    1824

    浏览量

    85169
  • Linux
    +关注

    关注

    87

    文章

    11219

    浏览量

    208879
  • 函数
    +关注

    关注

    3

    文章

    4303

    浏览量

    62411

原文标题:宋宝华:Linux设备与驱动的手动解绑与手动绑定

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux设备驱动程序分类有哪些

    Linux设备驱动程序是操作系统与硬件设备之间的桥梁,负责实现硬件设备与操作系统之间的通信和控制。Lin
    的头像 发表于 08-30 15:11 424次阅读

    linux驱动程序的编译方法有哪两种

    Linux驱动程序的编译方法主要可以归纳为两种: 手动编译 和 使用内核构建系统(Makefile)自动编译 。 1. 手动编译 手动编译
    的头像 发表于 08-30 14:39 502次阅读

    延时开关改手动开关怎么接线

    :延时开关是一种具有延时功能的开关,当按下开关按钮时,电路会在设定的延时时间后自动断开或闭合。延时开关通常用于控制照明、通风、加热等设备,以实现节能和安全的目的。 手动开关:手动开关是一种简单的开关,按下开关按
    的头像 发表于 08-19 15:49 481次阅读

    万能式断路器怎么手动合闸

    万能式断路器是一种广泛应用于电力系统中的保护和控制设备。它具有多种功能,包括过载保护、短路保护和远程控制等。手动合闸是断路器操作的一种方式,通常在自动控制失效或需要手动干预的情况下使用。 1.
    的头像 发表于 08-14 15:52 1545次阅读

    TE Connectivity高级手动工具解决方案

    CERTI-CRIMP手动工具是业界顶级的优质手动操作工具,用于压接各种端子、接触件和专用布线器件。这些工具根据实现始终如一的高品质端接所需的具体规范设计而成。使用寿命有可能达到 50,000 次以上,具体取决于操作员的维护。
    的头像 发表于 07-19 16:23 237次阅读
    TE Connectivity高级<b class='flag-5'>手动</b>工具解决方案

    电力电容器手动投切和自动投切区别在哪

    电力电容器的手动投切和自动投切有以下几点区别: 一、操作方式: 1、手动投切:需要操作人员手动控制开关或按钮来进行电容器的投切(连接)和切除(断开)操作。 2、自动投切:通过自动化控制系统或
    的头像 发表于 06-26 14:16 687次阅读
    电力电容器<b class='flag-5'>手动</b>投切和自动投切区别在哪

    abb工业机器人手动操作有哪三种模式?

    ABB工业机器人是一种广泛应用于制造业、物流、医疗等领域的自动化设备。它具有高度的灵活性和精确性,可以完成各种复杂的任务。在操作ABB工业机器人时,通常有三种手动操作模式:手动单轴移动模式、
    的头像 发表于 06-16 16:44 3986次阅读

    如何手动往esp32 arp列表中添加自定义的arp绑定信息?

    我目前需要手动往esp32 arp列表中添加自定义的arp绑定信息,而非arp请求获得,因为另一方设备无法完成arp应答 如IP为:192.168.88.2 MAC为:00:0A:35:01:FE:C0 arp
    发表于 06-07 08:14

    数据点修改后未生效的解决方案

    在使用机智云App时,有时候修改了数据点后却发现没有任何变化。这可能是由于设备绑定或App代码更新等原因导致的。针对这一问题,我们可以采取以下解决方案:官网通用调试App1、如果是虚拟设备,需
    的头像 发表于 05-08 08:10 287次阅读
    数据点修改后未生效的解决方案

    浅析非集中控制型消防应急照明和疏散指示系统手动控制的设计与应用

    浅析非集中控制型消防应急照明和疏散指示系统手动控制的设计与应用 张颖姣 摘要:针对非集中控制型消防应急照明和疏散指示系统在火灾确认后如何手动控制系统的应急启动存在的实际问题:在哪里手动控制?由谁来
    的头像 发表于 04-15 17:06 1021次阅读
    浅析非集中控制型消防应急照明和疏散指示系统<b class='flag-5'>手动</b>控制的设计与应用

    手动光纤检偏器产品介绍

    手动光纤检偏器用于光纤及光器件的偏振特性测试,配合光功率计或光电探测器可实现输入光偏振消光比的精确测量。手动光纤检偏器模块内部采用平凸透镜与高性能偏振片,仅需将光纤连接器旋入标准 FC 型适配器
    发表于 01-23 09:20 0次下载

    手动光纤起偏器产品介绍

    手动光纤起偏器是基于康宁Polarcor™ 全玻璃偏振片与六自由度光纤/空间光耦合装置构成的精密手动调节光纤型起偏器,专门用于保偏光纤、无源偏振器件的性能测试。光路设计采用外置一体化空间光路,六
    发表于 01-23 09:19 0次下载

    innovus中如何手动拉线及常用快捷键

    route之后已经用了相当多的办法(包括调整floorplan)之后,仍然有drc或者antenna,且violation数量不多时,就需要手动拉线了。本文介绍了innouvs里手动拉线常用的工具和快捷键。
    的头像 发表于 01-08 10:05 3887次阅读
    innovus中如何<b class='flag-5'>手动</b>拉线及常用快捷键

    手动和时空开关控制电路原理图

    这是通过一个转换开关切换手动与时控开关控制电机的电路。
    的头像 发表于 12-14 13:30 1385次阅读
    <b class='flag-5'>手动</b>和时空开关控制电路原理图

    linux手动设置网络参数

    Linux 是一种广泛使用的操作系统,提供了丰富的网络配置选项,允许用户手动设置网络参数,以满足各种网络需求。本文将详尽、详实、细致地介绍 Linux手动设置网络参数的方法,包括
    的头像 发表于 11-27 15:20 661次阅读