Linux 驅動開發 | 驅動世界里的宏偉建筑
本文轉載自微信公眾號「嵌入式Hacker」,作者吳偉東Jack。轉載本文請聯系嵌入式Hacker眾號。
哈嘍,我是老吳。
是否每一個上進的人都會覺得自己還可以再努力一點?
事情到了最后,只要沒達成目的,總能把失敗的原因歸為 "沒有再努力一點"。
但是,對努力的最大錯誤認知就是:時間越長,過程越痛苦,代表我越努力。
想一想,是否有更合理的努力方式?
以下是正文:
- 一、什么是 device model?
- 二、device model 的 3 個核心概念
- 三、bus、device、driver 是如何關聯的?
- 四、bus、device、driver 最簡單示例
- 五、小結
- 六、相關參考
一、什么是 device model?
Linux 的 device model 是一個旨在統一管理所有設備驅動的模型。
它猶如一棟規模宏大的建筑:
以 kobject、kset、attribute 等作為基本的建筑材料,
構造出支撐驅動世界的 bus、device、driver 三大組件,
最后通過 sysfs 在各種基礎的建筑材料之間建立彼此的互聯層次關系,并向外界提供了與建筑內設施進行互動的文件接口。
device model 有什么作用?
可以將 device 的硬件描述 和 driver 進行分離,提升 driver 的代碼復用率;
可以對 device 進行分類;
可以遍歷 device 和 driver;
可以更好地呈現設備的拓撲關系;
可以通過 sysfs 訪問設備;
可以讓設備支持熱插拔;
...
為了控制篇幅,本文將重點放在與驅動工程師關系最緊密的 bus、device、driver 3 個 組件。
二、device model 的 3 個核心概念
device model 里有 3 個核心的概念:
- bus
- device
- driver
什么是 bus?
bus 代表一種總線,例如 I2C、SPI、Usb 等。
bus 是 Linux 設備驅動模型這種建筑的核心框架,系統中的設備和驅動都依附在其周圍。
啟動系統后,可以通過 /sys/bus 可以查看系統里當前有哪些總線。
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;
- };
不需要一下子了解各個成員的作用,用到的時候再說明。
重點關注成員:
- int (*match)(struct device *dev, struct device_driver *drv),用于判斷掛在該 bus 上的設備和驅動是否匹配的回調函數;
- int (*probe)(struct device *dev),如果 bus 具有探測設備的能力,則會提供該回調函數;
- struct subsys_private *p,用于管理 bus 上的設備和驅動的數據結構;
注冊 bus 的 api:
- int bus_register(struct bus_type *bus);
什么是 device ?
device 代表了某個設備。
由 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;
- ...
- }
重點關注成員:
- struct kobject kobj,內核對象;
- struct bus_type *bus,設備所在的總線;
- struct device_driver *driver,和設備綁定在一起的驅動,如果還沒綁定,則為 NULL;
注冊 device 的 api:
- int device_register(struct device *dev)
什么是 driver?
driver 代表了設備驅動。
由 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;
- };
重點關注成員:
- struct bus_type *bus;
- int (*probe) (struct device *dev);
值得一提的是,總線控制器也是一種設備。
例如 I2C 總線控制器這個設備,對應的驅動為 I2C controller driver。
而掛在 I2C 總線上的設備,對應的驅動為 I2C device driver。
注冊 driver 的 api:
- int driver_register(struct device_driver *drv);
三、bus、device、driver 是如何關聯的?
device model 最核心的工作就是維護這三類抽象的實例,以及建立它們之間的關聯關系。
bus 如何管理 device 和 driver ?
在 struct bus_type 中有一個 struct subsys_private *p 指針,它負責管理掛在 bus 上的所有設備和驅動,其定義如下:
- 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;
- };
兩個 klist 成員以鏈表的形式將該總線上所有的驅動與設備鏈接到一起。
struct kset *drivers_kset 和 struct kset *devices_kset 是在向系統注冊當前新總線時動態生成的容納該總線上所有驅動與設備的 kset。
在內核里,用 kobject 來表示一個對象,kset 則是 kobject set 的縮寫,即內核對象集合。
內核用 kobject 和 kset 等數據結構作為原材料,以實現面向對象的方式構建了 device model 的框架。
最后,device 和 device_driver 的 bus 成員也會指向總線:
device 和 driver 的綁定
無論是通過 device_register() 注冊一個 device 到 bus 上,
還是通過 driver_register() 注冊一個 device_driver 到 bus 上,
都會導致 bus 嘗試執行 device 和 driver 的綁定行為。
1. device_register() 觸發的綁定
注冊 device 時:
- int device_register(struct device *dev);
- device_add(dev);
- bus_probe_device(dev);
- __device_attach(dev, true);
__device_attach(dev, true) 會為 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 函數來判斷是否 device 和 driver 是否匹配,
是否 match 的判斷標準一般是通過 of_match_table 或者是 id_table 作為衡量的標準,
以 i2c bus 的 match 函數為例:
- 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 成功,就會調用 driver_probe_device() 以觸發探測設備的行為:
- 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 具有探測設備的能力的話,例如 pci bus, 則會使用 bus->probe() 探測設備,
否則,使用 driver->probe() 探測設備,driver 的 probe 操作跟具體的硬件設備掛鉤。
2. 由 driver_register() 觸發的綁定
- int driver_register(struct device_driver *drv);
- bus_add_driver(drv);
- driver_attach(drv);
driver_attach(drv) 會為 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() 一樣,最終都會調用 driver_match_device(drv, dev),
進而通過 bus 里的 match 函數來判斷是否 device 和 driver 是否匹配。
同樣地,一旦 match 成功,就會調用 driver_probe_device() 以觸發探測設備的行為,后續的操作和注冊設備時是一模一樣的。
3. device 和 drvier 的綁定關系
前面說了綁定是如何被觸發的,現在來明確一下綁定的具體操作。
對于能成功匹配的 device 和 driver,兩者之間的關系是 N 對 1,即可以有多個 device 和 1 個 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 最簡單示例
下面的例子,
構造了一個名為 "simple_bus" 的 bus 實例。
- 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個名為 "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個名為 "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);
運行效果:
- 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 經由 bus 判斷是否 match 之后,執行了 driver 的 probe() 函數,符合我們前面的分析。
五、小結
Linux 的 device model 是個非常復雜的系統。
從一個比較高的層次來看,主要由總線、設備和驅動構成。
內核為了實現這些組件間的相關關系,定義了 kobject 和 kset 這樣的基礎底層數據結構,然后通過 sysfs 文件系統向用戶空間展示發生在內核空間中的各組件間的互聯層次關系,并以文件系統接口的方式為用戶空間程序提供了訪問內核對象屬性信息的簡易方法。
為了控制篇幅,本文并沒有涉及到 kojbect 和 sysfs。
如果你感興趣的話,去挖掘一下以下內容:
- device model 的底層數據結構 kojbect、kset 是如何工作的?
- 內核是如何使用 device model 去構建 i2c、spi、usb 等驅動框架?
- device model 和 sysfs 是如何協同工作的?
- sysfs 里如何創建屬性文件以訪問設備驅動?
- sysfs 里的 class 有什么作用?
六、相關參考
《Linux 設備驅動》
- 第 14 章 Linux 設備模型
《深入 Linux 設備驅動程序內核機制》
- 第 9 章 Linux 設備驅動模型
《Linux設備驅動開發詳解》
- 第 5 章 Linux文件系統與設備文件
- 第 12 章 Linux設備驅動的軟件架構思想
Linux/Documentation/driver-model
- bus.txt
- class.txt
- device.txt
- driver.txt
- overview.txt