linux裝置模型十(bus_device_driver總結)
前面九章分別對linux驅動模型中的細節部分進行了分析,本節作為小節,使用一個簡單的例子,分別使用前面分析的內容,實現一個簡單的匯流排,裝置,驅動之間的關係。
實現一條匯流排
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> MODULE_LICENSE("Dual BSD/GPL"); static char *Version = "$Revision: 1.9 $"; /* 驅動和裝置匹配 */ static int my_match(struct device *dev, struct device_driver *driver) { return !strncmp(dev_name(dev), driver->name, strlen(driver->name)); } static void my_bus_release(struct device *dev) { printk(KERN_DEBUG "my bus release\n"); } /* 匯流排也是一個裝置 */ struct device my_bus = { .init_name = "my_bus0", .release = my_bus_release }; /* 定義一個匯流排 */ struct bus_type my_bus_type = { .name = "my_bus", .match = my_match, }; /* 匯出匯流排和匯流排裝置 */ EXPORT_SYMBOL(my_bus); EXPORT_SYMBOL(my_bus_type); /* * Export a simple attribute. */ static ssize_t show_bus_version(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", Version); } static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL); static int __init my_bus_init(void) { int ret; /*註冊匯流排*/ ret = bus_register(&my_bus_type); if (ret) return ret; /*建立屬性檔案*/ if (bus_create_file(&my_bus_type, &bus_attr_version)) printk(KERN_NOTICE "Fail to create version attribute!\n"); /*註冊匯流排裝置*/ ret = device_register(&my_bus); if (ret) printk(KERN_NOTICE "Fail to register device:my_bus!\n"); return ret; } static void my_bus_exit(void) { device_unregister(&my_bus); bus_unregister(&my_bus_type); } module_init(my_bus_init); module_exit(my_bus_exit);
這裡是註冊一個匯流排,和前面匯流排章節那個例子相比,匯流排作為一個設備註冊了下來(我們可以sys/devices/目錄下面看到)。
當然也同樣實現了一個屬性檔案,可以使用它的讀功能,檢視版本。
這裡看一下注冊匯流排前後的對比:
可以看到匯流排名和裝置名都能對應上。
同時因為這對匯流排的設備註冊時沒父節點以及掛接到別的bus上的,所以這個裝置直接是在/sys/devices/這個目錄下。
同時我們這裡對匯流排和匯流排所代表的裝置的符號進行了匯出,可以讓其他核心模組使用。
實現一個掛接在上面匯流排的裝置
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> MODULE_LICENSE("Dual BSD/GPL"); /* 使用bus模組中匯出的符號 */ extern struct device my_bus; extern struct bus_type my_bus_type; /* Why need this ?*/ static void my_dev_release(struct device *dev) { } struct device my_dev = { .bus = &my_bus_type, .parent = &my_bus, .release = my_dev_release, }; /* * Export a simple attribute. */ static ssize_t mydev_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%s\n", "This is my device!"); } static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL); static int __init my_device_init(void) { int ret = 0; /* 初始化裝置 */ dev_set_name(&my_dev, "my_dev"); /*註冊裝置*/ ret = device_register(&my_dev); /*建立屬性檔案*/ device_create_file(&my_dev, &dev_attr_dev); return ret; } static void my_device_exit(void) { device_unregister(&my_dev); } module_init(my_device_init); module_exit(my_device_exit);
這個裝置的實現就簡單很多了,初始化時,確定了裝置是掛載那個匯流排下面,其父裝置是那個後,註冊這個裝置即可。
這裡也實現了一個讀的屬性檔案。
裝置名叫“my_dev”
實現一個掛接在上面匯流排的驅動程式
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> MODULE_LICENSE("Dual BSD/GPL"); /* 使用匯出的匯流排 */ extern struct bus_type my_bus_type; static int my_probe(struct device *dev) { printk("Driver found device which my driver can handle!\n"); return 0; } static int my_remove(struct device *dev) { printk("Driver found device unpluged!\n"); return 0; } struct device_driver my_driver = { .name = "my_dev", /* 注意這裡和裝置名字一樣 */ .bus = &my_bus_type, /* 掛接我們實現的匯流排 */ .probe = my_probe, .remove = my_remove, }; /* * Export a simple attribute. */ static ssize_t mydriver_show(struct device_driver *driver, char *buf) { return sprintf(buf, "%s\n", "This is my driver!"); } static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL); static int __init my_driver_init(void) { int ret = 0; /*註冊驅動*/ ret = driver_register(&my_driver); /*建立屬性檔案*/ ret = driver_create_file(&my_driver, &driver_attr_drv); return ret; } static void my_driver_exit(void) { driver_unregister(&my_driver); } module_init(my_driver_init); module_exit(my_driver_exit);
驅動這裡要注意兩點:
1.同樣綁定了我們實現的匯流排
2.驅動的name和裝置那邊的name一樣
同時我們也知道,probe函式是裝置和驅動匹配上後,被呼叫的。
實驗測試:
顯示看一下匯流排以及匯流排裝置安裝前後的對比。
當然這時候匯流排下面的裝置和驅動都是沒東西的。
在匯流排安裝的前提下,安裝裝置
這裡要知道的一點是,bus下的裝置都是,devices下的符號連結,而具體裝置裡的subsystme則是bus下的具體匯流排的符號連結。
有了上面兩個,就可以在devices目錄下直接進入bus目錄,bus也可以直接進入devices目錄。
在匯流排安裝的前提下,安裝驅動
因為上面我們先安裝了裝置,這裡再安裝驅動。所以驅動安裝完就直接匹配上了裝置。
下面我們先解除安裝裝置。
可以看到,在解除安裝了裝置後,因為沒了和驅動匹配的裝置,所以驅動下面的裝置的符號連結也被移除了。
如果我們分析一下linux中的那條虛擬匯流排platform匯流排,會發現,其實和我們上面的實現是一樣的。
只不過platform總選,完善了match的id_table的情況。
platform匯流排,實現了一個uevnt函式,來通知上層。
總之,基本的驅動模型以及實現。