众所周知,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
干电池1.5V和两节干电池3V升压到3.3V的测试
“两轮车市场思考和产品创新” 主题演讲
初代未拆封iPhone拍出113万 iPhone15系列新机或提前发布
谁说国产手机没有好货?华为荣耀8竟然颜值高到这种程度
什么是LCM,LCM正的意思是什么?LCD亮度和对比度如何调节
Linux设备与驱动的手动解绑与手动绑定
有源滤波器的设计与计算
充电桩芯片第一股!东微半导体科创板上市,年营收超3亿,拟募资近10亿
华为禁令加速存储器国产化进程 长江存储加足马力
ADC信噪比的分析及高速高分辨率ADC电路的实现
一个安全性的漏洞会导致什么样的混乱?
应急救援的“新武器”:消防灭火侦察机器人
魅族科技宣布Flyme 8系统将和魅族16s Pro一起亮相新品发布会
采用LATTICE的XP系列芯片实现FPGA的自动白平衡系统的设计
44780 16x2 字符液晶屏驱动演示程序总线方式
卧薪16年,大唐联仪代表的中国通信测试仪表行业领跑5G
胶带母卷三季度行情的简述及其市场发展趋势的分析
海信胶片DP2988H三无灯亮
高抗干扰3路3键触摸IC-VK3603
ADSP-21565 高达 1GHz SHARC+ DSP 带 640KB L1