Linux驅(qū)動(dòng)開發(fā)中device model的介紹以及用法 ,很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
10年建站經(jīng)驗(yàn), 成都網(wǎng)站制作、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)客戶的見證與正確選擇。成都創(chuàng)新互聯(lián)提供完善的營銷型網(wǎng)頁建站明細(xì)報(bào)價(jià)表。后期開發(fā)更加便捷高效,我們致力于追求更美、更快、更規(guī)范。
一、什么是 device model?
Linux 的 device model 是一個(gè)旨在統(tǒng)一管理所有設(shè)備驅(qū)動(dòng)的模型。
它猶如一棟規(guī)模宏大的建筑:
以 kobject、kset、attribute 等作為基本的建筑材料,
構(gòu)造出支撐驅(qū)動(dòng)世界的 bus、device、driver 三大組件,
最后通過 sysfs 在各種基礎(chǔ)的建筑材料之間建立彼此的互聯(lián)層次關(guān)系,并向外界提供了與建筑內(nèi)設(shè)施進(jìn)行互動(dòng)的文件接口。
device model 有什么作用?
可以將 device 的硬件描述 和 driver 進(jìn)行分離,提升 driver 的代碼復(fù)用率;
可以對 device 進(jìn)行分類;
可以遍歷 device 和 driver;
可以更好地呈現(xiàn)設(shè)備的拓?fù)潢P(guān)系;
可以通過 sysfs 訪問設(shè)備;
可以讓設(shè)備支持熱插拔;
...
為了控制篇幅,本文將重點(diǎn)放在與驅(qū)動(dòng)工程師關(guān)系最緊密的 bus、device、driver 3 個(gè) 組件。
二、device model 的 3 個(gè)核心概念
device model 里有 3 個(gè)核心的概念:
bus
device
driver
什么是 bus?
bus 代表一種總線,例如 I2C、SPI、Usb 等。
bus 是 Linux 設(shè)備驅(qū)動(dòng)模型這種建筑的核心框架,系統(tǒng)中的設(shè)備和驅(qū)動(dòng)都依附在其周圍。
啟動(dòng)系統(tǒng)后,可以通過 /sys/bus 可以查看系統(tǒng)里當(dāng)前有哪些總線。
bus 由 struct bus_type 來描述:
struct bus_type { const char *name; const char *dev_name; struct device *dev_root; const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); ... struct subsys_private *p; struct lock_class_key lock_key; };
不需要一下子了解各個(gè)成員的作用,用到的時(shí)候再說明。
重點(diǎn)關(guān)注成員:
int (*match)(struct device *dev, struct device_driver *drv),用于判斷掛在該 bus 上的設(shè)備和驅(qū)動(dòng)是否匹配的回調(diào)函數(shù);
int (*probe)(struct device *dev),如果 bus 具有探測設(shè)備的能力,則會(huì)提供該回調(diào)函數(shù);
struct subsys_private *p,用于管理 bus 上的設(shè)備和驅(qū)動(dòng)的數(shù)據(jù)結(jié)構(gòu);
注冊 bus 的 api:
int bus_register(struct bus_type *bus);
什么是 device ?
device 代表了某個(gè)設(shè)備。
由 struct device 來描述:
struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; const struct device_type *type; struct mutex mutex; struct bus_type *bus; struct device_driver *driver; void *platform_data; void *driver_data; ... }
重點(diǎn)關(guān)注成員:
struct kobject kobj,內(nèi)核對象;
struct bus_type *bus,設(shè)備所在的總線;
struct device_driver *driver,和設(shè)備綁定在一起的驅(qū)動(dòng),如果還沒綁定,則為 NULL;
注冊 device 的 api:
int device_register(struct device *dev)
什么是 driver?
driver 代表了設(shè)備驅(qū)動(dòng)。
由 struct device_driver 來描述:
struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ enum probe_type probe_type; const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p; };
重點(diǎn)關(guān)注成員:
struct bus_type *bus;
int (*probe) (struct device *dev);
值得一提的是,總線控制器也是一種設(shè)備。
例如 I2C 總線控制器這個(gè)設(shè)備,對應(yīng)的驅(qū)動(dòng)為 I2C controller driver。
而掛在 I2C 總線上的設(shè)備,對應(yīng)的驅(qū)動(dòng)為 I2C device driver。
注冊 driver 的 api:
int driver_register(struct device_driver *drv);
三、bus、device、driver 是如何關(guān)聯(lián)的?
device model 最核心的工作就是維護(hù)這三類抽象的實(shí)例,以及建立它們之間的關(guān)聯(lián)關(guān)系。
bus 如何管理 device 和 driver ?
在 struct bus_type 中有一個(gè) struct subsys_private *p 指針,它負(fù)責(zé)管理掛在 bus 上的所有設(shè)備和驅(qū)動(dòng),其定義如下:
struct subsys_private { struct kset subsys; struct kset *devices_kset; struct list_head interfaces; struct mutex mutex; struct kset *drivers_kset; struct klist klist_devices; struct klist klist_drivers; struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; struct bus_type *bus; struct kset glue_dirs; struct class *class; };
兩個(gè) klist 成員以鏈表的形式將該總線上所有的驅(qū)動(dòng)與設(shè)備鏈接到一起。
struct kset *drivers_kset 和 struct kset *devices_kset 是在向系統(tǒng)注冊當(dāng)前新總線時(shí)動(dòng)態(tài)生成的容納該總線上所有驅(qū)動(dòng)與設(shè)備的 kset。
在內(nèi)核里,用 kobject 來表示一個(gè)對象,kset 則是 kobject set 的縮寫,即內(nèi)核對象集合。
內(nèi)核用 kobject 和 kset 等數(shù)據(jù)結(jié)構(gòu)作為原材料,以實(shí)現(xiàn)面向?qū)ο蟮姆绞綐?gòu)建了 device model 的框架。
最后,device 和 device_driver 的 bus 成員也會(huì)指向總線:
device 和 driver 的綁定
無論是通過 device_register() 注冊一個(gè) device 到 bus 上,
還是通過 driver_register() 注冊一個(gè) device_driver 到 bus 上,
都會(huì)導(dǎo)致 bus 嘗試執(zhí)行 device 和 driver 的綁定行為。
1. device_register() 觸發(fā)的綁定
注冊 device 時(shí):
int device_register(struct device *dev); device_add(dev); bus_probe_device(dev); __device_attach(dev, true);
__device_attach(dev, true) 會(huì)為 device 遍歷 bus 上的所有 driver:
bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); driver_match_device(drv, dev); drv->bus->match ? drv->bus->match(dev, drv) : 1; driver_probe_device(drv, dev);
driver_match_device() 通過 bus 里的 match 函數(shù)來判斷是否 device 和 driver 是否匹配,
是否 match 的判斷標(biāo)準(zhǔn)一般是通過 of_match_table 或者是 id_table 作為衡量的標(biāo)準(zhǔn),
以 i2c bus 的 match 函數(shù)為例:
static int i2c_device_match(struct device *dev, struct device_driver *drv) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; /* Attempt an OF style match */ if (i2c_of_match_device(drv->of_match_table, client)) return 1; /* Then ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; driver = to_i2c_driver(drv); /* Finally an I2C match */ if (i2c_match_id(driver->id_table, client)) return 1; return 0; }
一旦 match 成功,就會(huì)調(diào)用 driver_probe_device() 以觸發(fā)探測設(shè)備的行為:
int driver_probe_device(struct device_driver *drv, struct device *dev); really_probe(dev, drv); if (dev->bus->probe) { ret = dev->bus->probe(dev); } else if (drv->probe) { ret = drv->probe(dev); }
如果 bus 具有探測設(shè)備的能力的話,例如 pci bus, 則會(huì)使用 bus->probe() 探測設(shè)備,
否則,使用 driver->probe() 探測設(shè)備,driver 的 probe 操作跟具體的硬件設(shè)備掛鉤。
2. 由 driver_register() 觸發(fā)的綁定
int driver_register(struct device_driver *drv); bus_add_driver(drv); driver_attach(drv);
driver_attach(drv) 會(huì)為 driver 遍歷 bus 上的所有 device:
int driver_attach(struct device_driver *drv); bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); __driver_attach(); driver_match_device(drv, dev); driver_probe_device(drv, dev);
和 device_register() 一樣,最終都會(huì)調(diào)用 driver_match_device(drv, dev),
進(jìn)而通過 bus 里的 match 函數(shù)來判斷是否 device 和 driver 是否匹配。
同樣地,一旦 match 成功,就會(huì)調(diào)用 driver_probe_device() 以觸發(fā)探測設(shè)備的行為,后續(xù)的操作和注冊設(shè)備時(shí)是一模一樣的。
3. device 和 drvier 的綁定關(guān)系
前面說了綁定是如何被觸發(fā)的,現(xiàn)在來明確一下綁定的具體操作。
對于能成功匹配的 device 和 driver,兩者之間的關(guān)系是 N 對 1,即可以有多個(gè) device 和 1 個(gè) driver 綁定在一起。
對于 device:
其 driver 成員指向已綁定的 device_driver。
int driver_probe_device(struct device_driver *drv, struct device *dev) really_probe(dev, drv); dev->driver = drv;
對于 driver:
在 device_driver 里鏈表 klist_devices 保存了該 driver 上已綁定的所有 device。
int driver_probe_device(struct device_driver *drv, struct device *dev) really_probe(dev, drv); driver_bound(dev); klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
在 /driver/base/driver.c 中,提供了一些 api,用于遍歷處理 driver 上綁定的所有 device:
int driver_for_each_device()
struct device *driver_find_device()
四、bus、device、driver 最簡單示例
下面的例子,
構(gòu)造了一個(gè)名為 "simple_bus" 的 bus 實(shí)例。
static int sb_match(struct device *dev, struct device_driver *driver) { return !strncmp(dev_name(dev), driver->name, strlen(driver->name)); } struct bus_type sb_bus_type = { .name = "sb", .match = sb_match, }; static ssize_t version_show(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", Version); } static BUS_ATTR_RO(version); static void sb_dev_release(struct device *dev) { } int register_sb_device(struct sb_device *sbdev) { sbdev->dev.bus = &sb_bus_type; sbdev->dev.release = sb_dev_release; dev_set_name(&sbdev->dev, sbdev->name); return device_register(&sbdev->dev); } EXPORT_SYMBOL(register_sb_device); void unregister_sb_device(struct sb_device *sbdev) { device_unregister(&sbdev->dev); } EXPORT_SYMBOL(unregister_sb_device); static int sb_drv_probe(struct device *dev) { printk(KERN_INFO"sb_drv probe %s\n", dev_name(dev)); return 0; } int register_sb_driver(struct sb_driver *sdrv) { sdrv->driver.bus = &sb_bus_type; sdrv->driver.probe = &sb_drv_probe; return driver_register(&sdrv->driver); } EXPORT_SYMBOL(register_sb_driver); void unregister_sb_driver(struct sb_driver *driver) { driver_unregister(&driver->driver); } EXPORT_SYMBOL(unregister_sb_driver); static int __init sb_bus_init(void) { int ret; ret = bus_register(&sb_bus_type); if (ret) { printk(KERN_ERR "Unable to register sb bus, failure was %d\n",ret); return ret; } if (bus_create_file(&sb_bus_type, &bus_attr_version)) printk(KERN_ERR "Unable to create version attribute\n"); return 0; } static void sb_bus_exit(void) { bus_unregister(&sb_bus_type); } module_init(sb_bus_init); module_exit(sb_bus_exit);
xxx_chip.c:注冊4個(gè)名為 "chipX" 的 device
struct xxx_chip { char devname[20]; struct sb_device sdev; }; int chipdev_num = 4; struct xxx_chip *chipdev; static void chip_register_dev(struct xxx_chip *dev, int index) { snprintf(dev->devname, sizeof(dev->devname), "chip%d", index); dev->sdev.name = dev->devname; dev_set_drvdata(&dev->sdev.dev, dev); register_sb_device(&dev->sdev); } int chip_init(void) { int i; chipdev = kmalloc(chipdev_num*sizeof (struct xxx_chip), GFP_KERNEL); memset(chipdev, 0, chipdev_num*sizeof (struct xxx_chip)); for (i = 0; i < chipdev_num; i++) { chip_register_dev(chipdev + i, i); } return 0; } void chip_cleanup(void) { int i; for (i = 0; i < chipdev_num; i++) { unregister_sb_device(&chipdev[i].sdev); } kfree(chipdev); } module_init(chip_init); module_exit(chip_cleanup);
xxx_chip_drv.c:注冊1個(gè)名為 "chip" 的 driver
static struct sb_driver sculld_driver = { .driver = { .name = "chip", }, }; int xxx_chipdrv_init(void) { return register_sb_driver(&sculld_driver); } void xxx_chipdrv_cleanup(void) { unregister_sb_driver(&sculld_driver); } module_init(xxx_chipdrv_init); module_exit(xxx_chipdrv_cleanup);
運(yùn)行效果:
root@buildroot:~# insmod simple_bus.ko root@buildroot:~# tree /sys/bus/sb /sys/bus/sb ├── devices ├── drivers ├── drivers_autoprobe ├── drivers_probe ├── uevent └── version root@buildroot:~# insmod xxx_chip.ko root@buildroot:~# tree /sys/bus/sb /sys/bus/sb ├── devices │ ├── chip0 -> ../../../devices/chip0 │ ├── chip1 -> ../../../devices/chip1 │ ├── chip2 -> ../../../devices/chip2 │ └── chip3 -> ../../../devices/chip3 ├── drivers ├── drivers_autoprobe ├── drivers_probe ├── uevent └── version root@buildroot:~# insmod xxx_chip_drv.ko sb_drv probe chip0 sb_drv probe chip1 sb_drv probe chip2 sb_drv probe chip3 root@buildroot:~# tree /sys/bus/sb /sys/bus/sb ├── devices │ ├── chip0 -> ../../../devices/chip0 │ ├── chip1 -> ../../../devices/chip1 │ ├── chip2 -> ../../../devices/chip2 │ └── chip3 -> ../../../devices/chip3 ├── drivers │ └── chip │ ├── bind │ ├── chip0 -> ../../../../devices/chip0 │ ├── chip1 -> ../../../../devices/chip1 │ ├── chip2 -> ../../../../devices/chip2 │ ├── chip3 -> ../../../../devices/chip3 │ ├── uevent │ └── unbind ├── drivers_autoprobe ├── drivers_probe ├── uevent └── version
通過打印信息可知,device 和 driver 經(jīng)由 bus 判斷是否 match 之后,執(zhí)行了 driver 的 probe() 函數(shù),符合我們前面的分析。
五、小結(jié)
Linux 的 device model 是個(gè)非常復(fù)雜的系統(tǒng)。
從一個(gè)比較高的層次來看,主要由總線、設(shè)備和驅(qū)動(dòng)構(gòu)成。
內(nèi)核為了實(shí)現(xiàn)這些組件間的相關(guān)關(guān)系,定義了 kobject 和 kset 這樣的基礎(chǔ)底層數(shù)據(jù)結(jié)構(gòu),然后通過 sysfs 文件系統(tǒng)向用戶空間展示發(fā)生在內(nèi)核空間中的各組件間的互聯(lián)層次關(guān)系,并以文件系統(tǒng)接口的方式為用戶空間程序提供了訪問內(nèi)核對象屬性信息的簡易方法。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。
本文題目:Linux驅(qū)動(dòng)開發(fā)中devicemodel的介紹以及用法
文章出自:http://www.rwnh.cn/article2/gopgoc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、建站公司、外貿(mào)建站、網(wǎng)站改版、ChatGPT、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)