LINUX设备模型BUS,DEVICE,DRIVER
雖然看了上面一篇轉載的《使用/sys/訪問系統》對總線,驅動,設備都講得比較細但還是沒有太多的感覺。在此就先把自己今天所學回憶一下。
為了滿足新的要求,linux2.6提供了新的設備模型:總線、驅動、設備。基本關系簡要的概括如下:
驅動核心可以注冊多種類型的總線。
每種總線下面可以掛載許多設備。(通過kset devices)
每種總線下可以用很多設備驅動。(通過包含一個kset drivers)}
每個驅動可以處理一組設備。按照我的理解就是所有的設備都掛載到總線上,當加載驅動時,驅動就支總線上找到自己對應的設備。或者先把驅動加載上,來了一個設備就去總線找驅動。
一:總線
總線是處理器與設備之間通道,在設備模型中,所有的設備都通過總線相連
(1)bus_type.
struct bus_type {
?const char??* name;//設備名稱
struct bus_type {?const char??* name;//設備名稱
?struct subsystem?subsys;//代表自身
?struct kset??drivers;?? //當前總線的設備驅動集合
?struct kset??devices;?//所有設備集合
?struct klist??klist_devices;
?struct klist??klist_drivers;
?struct bus_attribute?* bus_attrs;//總線屬性
?struct device_attribute?* dev_attrs;//設備屬性
?struct driver_attribute?* drv_attrs;
?int??(*match)(struct device * dev, struct device_driver * drv);//設備驅動匹配函數
?int??(*uevent)(struct device *dev, char **envp,???
????? int num_envp, char *buffer, int buffer_size);//熱拔插事件
?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);
};
在后面的實例當中用到了里面的兩個成員
1:const char *name;
2:?int??(*match)(struct device * dev, struct device_driver * drv);//設備驅動匹配函數
這個匹配函數是很關鍵的東西,這是建立總線上設備與驅動的橋梁,當一個新的設備或驅動被添加到一個總線上時被調用。
(2)總線的操作:
注冊:int bus_register(struct bus_type * bus)
注銷:void bus_unregister(struct bus_type *bus);
(3)總線屬性 bus_attribute
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf,
size_t count);
};
BUS_ATTR(name, mode, show, store);
這個宏聲明一個結構, 產生它的名子通過前綴字符串 bus_attr_ 到給定的名子.
任何屬于一個總線的屬性應當明確使用 bus_create_file 來創建:
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);?
屬性也可被去除, 使用:
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);?
lddbus 驅動創建一個簡單屬性文件, 再次, 包含源碼版本號. show 方法和 bus_attribute 結構設置如下:
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
這個總線屬性現目前為止我還沒有發現它的作用。
(4)總線實例:
其實在這個程序中操作很簡單:
1:首先是要準備一個總線bus_type.也就是定義一個bus_type,然后給它填上一些成員。
定義如下:
struct bus_type my_bus_type = {
?.name = "my_bus",
?.match = my_match,
};
這里就對其兩個成員賦值了。一個是名稱。另一個則是匹配函數:
static int my_match(struct device *dev, struct device_driver *driver)
{
?return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
這是匹配的邏輯則是設備的名字與驅動的名字一樣。
準備好了總線后就在模塊初始化函數中注冊:
??????? /*注冊總線*/
?ret = bus_register(&my_bus_type);
?if (ret)
??return ret;
然后在模塊退出函數中注解總線:
總線操作完后還要為總線創建屬性文件:
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);這句話就定義了一個總線屬性文件。BUS_ATTR宏的定義如下:
bus_unregister(&my_bus_type);
#define BUS_ATTR(_name, _mode, _show, _store)?\
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
show_bus_version定義如下:
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
?return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}
定義好之后就是調用 函數創建文件
?/*創建屬性文件*/?
?if (bus_create_file(&my_bus_type, &bus_attr_version))
??printk(KERN_NOTICE "Fail to create version attribute!\n");
總線本身也是要對應一個設備的。還要為總線創建設備。
static void my_bus_release(struct device *dev)
{
?printk(KERN_DEBUG "my bus release\n");
}
?
struct device my_bus = {
?.bus_id?? = "my_bus0",
?.release? = my_bus_release
};
?/*注冊總線設備*/
?ret = device_register(&my_bus);
?if (ret)
??printk(KERN_NOTICE "Fail to register device:my_bus!\n");
可是這是有疑問,我還沒有找到這個總線設備,和剛才的總線的聯系。
源代碼:
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
MODULE_AUTHOR("David Xie");
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->bus_id, driver->name, strlen(driver->name));
}
static void my_bus_release(struct device *dev)
{
?printk(KERN_DEBUG "my bus release\n");
}
?
struct device my_bus = {
?.bus_id?? = "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);
二:設備:
1:device結構體
struct device {
struct device ?* parent; //父設備,一般一個bus也對應一個設備。
struct kobject kobj;//代表自身
char?bus_id[BUS_ID_SIZE];?
struct bus_type?* bus;??/*?所屬的總線 */
struct device_driver *driver;?/* 匹配的驅動*/
void??*driver_data;?/* data private to the driver?指向驅動?*/
?void??*platform_data;?/* Platform specific data,由驅動定義并使用*/
///更多字段忽略了
};
注冊設備:int device_register(sruct device *dev)
注銷設備:void device_unregister(struct device *dev);
2:設備屬性:
sysfs 中的設備入口可有屬性. 相關的結構是:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, char *buf);
ssize_t (*store)(struct device *dev, const char *buf,
size_t count);
};
這些屬性結構可在編譯時建立, 使用這些宏:
DEVICE_ATTR(name, mode, show, store);
結果結構通過前綴 dev_attr_ 到給定名子上來命名. 屬性文件的實際管理使用通常的函數對來處理:
int device_create_file(struct device *device, struct device_attribute *entry);
void device_remove_file(struct device *dev, struct device_attribute *attr);
struct bus_type 的 dev_attrs 成員指向一個缺省的屬性列表, 這些屬性給添加到總線的每個設備創建.
3:創建設備實例:
創建設備和創建總線基本一樣這里只貼出程序:
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
MODULE_AUTHOR("David Xie");
MODULE_LICENSE("Dual BSD/GPL");
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;
????????
??????? /* 初始化設備 */
?strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);
????????
??????? /*注冊設備*/
?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);
三:設備驅動:
在總線上掛載了設備后就要為其準備驅動程序。
驅動程序的實現與設備的實現類似,代碼如下:
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
MODULE_AUTHOR("David Xie");
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;
????????
??????? /*注冊驅動*/
?driver_register(&my_driver);
??
?/*創建屬性文件*/
?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);
總結
以上是生活随笔為你收集整理的LINUX设备模型BUS,DEVICE,DRIVER的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PLL详细设计方案
- 下一篇: linux 其他常用命令