眾所周知,Linux靠設(shè)備與驅(qū)動(dòng)之間的match,來(lái)完成設(shè)備與驅(qū)動(dòng)的bind,從而觸發(fā)驅(qū)動(dòng)的probe()成員函數(shù)被執(zhí)行。每個(gè)bus都有相應(yīng)的match方法,完成match的總的入口函數(shù)是:
static inline int driver_match_device(struct device_driver *drv, struct device *dev){ return drv->bus->match ? drv->bus->match(dev, drv) : 1;}
而這個(gè)總的入口函數(shù)又會(huì)調(diào)用到各自不同總線的match函數(shù),對(duì)于platform bus而言,它的match函數(shù)就是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有很多方法成立,比如設(shè)備的name和驅(qū)動(dòng)的name相同:
strcmp(pdev->name, drv->name) == 0
比如,設(shè)備的名字出現(xiàn)在驅(qū)動(dòng)的ID表中:
if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL;
比如device tree里面的compatible字段與驅(qū)動(dòng)的dt兼容性字段匹配:
if (of_driver_match_device(dev, drv)) return 1;
只要符合其中任意一種,driver和device都可以匹配上。
這種自動(dòng)匹配非常簡(jiǎn)單,實(shí)施起來(lái)也非常容易。
但是有時(shí)候,這種自動(dòng)匹配并不一定是我們想要的。比如我們有時(shí)候就是希望XXX設(shè)備用YYY驅(qū)動(dòng),而不是用XXX驅(qū)動(dòng)。工程中有手動(dòng)匹配的需求,最典型的場(chǎng)景是VFIO的場(chǎng)景,想讓設(shè)備與內(nèi)核空間原本綁定的驅(qū)動(dòng)解綁,轉(zhuǎn)而采用內(nèi)核空間的通用VFIO驅(qū)動(dòng),而VFIO驅(qū)動(dòng)又提供了userspace駕馭設(shè)備的能力。
下面我們來(lái)從原理和實(shí)踐上演示這種手動(dòng)的unbind和bind是怎么進(jìn)行的。在《Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》一書(shū)中,我們給出了一個(gè)簡(jiǎn)單的globalfifo設(shè)備和globalfifo驅(qū)動(dòng):
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”,符合此行的匹配規(guī)則:
strcmp(pdev->name, drv->name) == 0
設(shè)備和驅(qū)動(dòng)匹配成功,從sysfs也可以看出:
globalfifo的device和driver各自找到了對(duì)方。
現(xiàn)在我們來(lái)寫(xiě)一個(gè)第三者driver,名字叫做globalxxx,然后我們想把globalfifo device的driver指向globalxxx。因此我們要完成2步:
unbind:解除globalfifo driver與globalfifo device的綁定
bind: 進(jìn)行g(shù)lobalxxxdriver與globalfifo device的綁定
第三者globalxxx驅(qū)動(dòng)代碼類(lèi)似:
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);
下面我們來(lái)完成第一步的unbind,這一步很簡(jiǎn)單,跑到/sys/bus/platform/drivers/globalfifo目錄,把設(shè)備globalfifo的名字寫(xiě)進(jìn)去unbind文件:
當(dāng)然我們也可以來(lái)回折騰著unbind,bind著玩:
這樣我們看到一堆的probe(每次設(shè)備和驅(qū)動(dòng)bind成功,驅(qū)動(dòng)probe都會(huì)執(zhí)行),remove(每次設(shè)備和驅(qū)動(dòng)unbind成功,驅(qū)動(dòng)remove都會(huì)執(zhí)行),最后處于unbind狀態(tài)。
現(xiàn)在我們來(lái)把globalfifo設(shè)備bind到globalxxx驅(qū)動(dòng):
綁定的時(shí)候提示錯(cuò)誤!
綁定的時(shí)候提示錯(cuò)誤!!
綁定的時(shí)候提示錯(cuò)誤!?。?/p>
前面我們用globalfifo的driver去bind globalfifo的device的時(shí)候,是想怎么綁就怎么綁的,想綁多少次就綁多少次的!為什么換了globalxxx來(lái)綁就不行了呢?
愛(ài)情不是你想賣(mài)想買(mǎi)就能賣(mài)
讓我掙開(kāi) 讓我明白
放手你的愛(ài)
我們來(lái)看看這個(gè)bind sysfs入口工作的函數(shù)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; } } ...}
看起來(lái),如果要強(qiáng)行bind,仍然需要device_driver_attach()成立,否則內(nèi)核會(huì)返回-ENODEV錯(cuò)誤:
} else if (err == 0) { /* driver didn't accept device */ err = -ENODEV; }
根據(jù)前文對(duì)platform_match()的代碼分析,globalxxx driver和globalfifo device確實(shí)八竿子都打不著?。](méi)有任何匹配因子。
下面我們來(lái)把globalxxx的代碼稍微改一下,通過(guò)ID表來(lái)增加一個(gè)匹配因子:
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:
現(xiàn)在globalfifo device可以在globalxxx和globalfifo這2個(gè)driver里面進(jìn)行自由地bind和unbind!
看到這里,客官們一定覺(jué)得這太特么狗血了!不是說(shuō)可以自由地綁定第三者嗎?為嘛還要求這個(gè)第三者驅(qū)動(dòng)與這個(gè)原先的設(shè)備匹配呢?這有嘛意思呢?
別忘了,在platform_match中還有這么一行:
if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name);
設(shè)備完全可以自由地宣布她喜歡的第三者driver,哪怕這個(gè)第三者driver和她本身完全沒(méi)有任何的匹配因子,操作的入口就是driver_override sysfs文件。
我們完全可以保留globalxxx驅(qū)動(dòng)的原樣
static struct platform_driver globalxxx_driver = { .driver = { .name = "globalxxx", .owner = THIS_MODULE, }, .probe = globalxxx_probe, .remove = globalxxx_remove,};
不去增加任何的id_table,而換做到globalfifo device里面去寫(xiě)driver_override文件,宣布globalxxx driver可以匹配globalfifo device。
這樣之后,哪怕globalxxx driver和globalfifo device八竿子打不著,也是可以驅(qū)動(dòng)globalfifo device的。工程里面如果我們想用VFIO的方式來(lái)驅(qū)動(dòng)一個(gè)設(shè)備,就可以這樣做:
echo vfio-platform > driver_override
-
驅(qū)動(dòng)
+關(guān)注
關(guān)注
12文章
1812瀏覽量
85045 -
Linux
+關(guān)注
關(guān)注
87文章
11161瀏覽量
208460 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4256瀏覽量
62223
原文標(biāo)題:宋寶華:Linux設(shè)備與驅(qū)動(dòng)的手動(dòng)解綁與手動(dòng)綁定
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論