linux設備模型bus,device,driver
作者 codercjg 在 10 十一月 2015, 2:43 下午
linux2.6提供了新的設備模型:總線、驅動、設備?;娟P系簡要的概括如下:
驅動核心可以注冊多種類型的總線。
每種總線下面可以掛載許多設備。(通過kset devices)
每種總線下可以用很多設備驅動。(通過包含一個kset drivers)}
每個驅動可以處理一組設備。按照我的理解就是所有的設備都掛載到總線上,當加載驅動時,驅動就支總線上找到自己對應的設備?;蛘呦劝羊寗蛹虞d上,來了一個設備就去總線找驅動。
一:總線
總線是處理器與設備之間通道,在設備模型中,所有的設備都通過總線相連。
關于總線的一些結構體:bus_type,
(1)bus_type:
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_attr_name),然后利用bus_create_file來創建總線屬性
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
參數中的attr,即為bus_attr_name。
另外, 就是參數中的 show 方法,設置方法如下
static ssize_t show_bus_version(struct bus_type *bus, char *buf) {
return snprintf(buf, PAGE_SIZE, “%s\n”, Version);
}
總線屬性的刪除, 使用:
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
(4)總線實例:
1:首先是要準備一個總線bus_type.也就是定義一個bus_type,然后給它填上一些成員。定義如下:
struct bus_type my_bus_type = {
.name = “my_bus”,
.match = my_match,
};
這里就對其兩個成員賦值了。一個是名稱。另一個則是匹配函數:
2,總線本身也是要對應一個設備的。還要為總線創建設備。
struct device my_bus = {
.bus_id = “my_bus0″,
.release = my_bus_release
};
源代碼:
include <linux/module.h>
include <linux/kernel.h>
include <linux/init.h>
include <linux/device.h>
include <linux/string.h>
static char *version = “version 1.0″;
//用于判斷指定的驅動程序是否能處理指定的設備。
static int my_match(struct device *dev, struct device_driver *driver) {
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
static int my_bus_release(struct device *dev) {
return 0;
}
static ssize_t show_bus_version(struct bus_type *bus, char buf) {
return sprintf(buf, PAGE_SIZE, “%s\n”, version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
struct device my_bus = {//定義總線設備
.bus_id = “my_bus0″,
.release = my_bus_release,
};
EXPORT_SYMBOL(my_bus);
struct bus_type my_bus_type = { //定義總線類型
.name = “my_bus”,
.match = my_match,
};
EXPORT_SYMBOL(my_bus_type);
static int __init my_bus_init(void){
int ret;
ret = bus_register(&my_bus_type);//注冊總線
if(ret)
printk(“bus_register failed!\n”);
if(bus_create_file(&my_bus_type, &bus_attr_version))//創建總線屬性
printk(“Creat bus failed!\n”);
ret = device_register(&my_bus);//注冊總線設備
if (ret)
printk(“device_register failed!\n”);
return ret;
}
static void __exit my_bus_exit(void) {
bus_unregister(&my_bus_type);//刪除總線屬性
device_unregister(&my_bus);//刪除總線設備
}
module_init(my_bus_init);
module_exit(my_bus_exit);
MODULE_AUTHOR(“Fany”);
MODULE_LICENSE(“GPL”);
(5)測試
將bus.c以動態加載的方式加載到內核,insmod bus.ko,在/sys/bus/目錄下會有一個my_bus目錄,這就是我們添加的總線。該目錄下有devices,drivers目錄,因為該總線上沒有掛載任何設備和驅動,所以這兩個目錄都為空;同時,在/sy/devices目錄下,還可看到my_bus0設備(總線本身也是一種設備)。
二:設備:
關于設備的一些常用結構體:device,
1:
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);
3:創建設備實例:
include <linux/module.h>
include <linux/kernel.h>
include <linux/init.h>
include <linux/device.h>
include <linux/string.h>
extern struct device my_bus; //這里用到了總線設備中定義的結構體
extern struct bus_type my_bus_type;
static int my_device_release() {
return 0;
}
struct device my_dev={ //創建設備屬性
.bus = &my_bus_type,//定義總線類型
.parent = &my_bus,//定義my_dev的父設備。
.release = my_device_release,
};
static ssize_t mydev_show(struct device dev, 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;
strncpy(my_dev.bus_id, “my_dev”, BUS_ID_SIZE); //初始化設備
ret = device_register(&my_dev); //注冊設備
if (ret)
printk(“device register!\n”);
device_create_file(&my_dev, &dev_attr_dev); //創建設備文件
return ret;
}
static void __exit my_device_exit(void) {
device_unregister(&my_dev);//卸載設備
}
module_init(my_device_init);
module_exit(my_device_exit);
MODULE_AUTHOR(“Fany”);
MODULE_LICENSE(“GPL”);
4:測試
將設備device.c,編譯成模塊,以動態加載的方式加載到內核。會發現在sys/bus/my_bus/devices/目錄下有一個my_dev設備,查看屬性,它是掛在/sys/devices/my_bus0/my_dev目錄下,至此添加設備成功。
三:設備驅動:
1:關于驅動的常用結構體:device_driver
struct device_driver {
const char name; /驅動程序的名字( 在 sysfs 中出現 )/
struct bus_type bus; /驅動程序所操作的總線類型/
struct module *owner;
const char mod_name; / used for built-in modules /
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);
struct attribute_group *groups;
struct pm_ops pm;
struct driver_private p;
};
2:驅動程序的注冊和注銷
/注冊device_driver 結構的函數是:/
int driver_register(struct device_driver drv);
void driver_unregister(struct device_driver drv);
3:驅動程序的屬性
/driver的屬性結構在:/
struct driver_attribute {
struct attribute attr;
ssize_t (show)(struct device_driver *drv, char buf);
ssize_t (store)(struct device_driver drv, const char buf, size_t count);
};
DRIVER_ATTR(_name,_mode,_show,_store) /屬性文件創建的方法:/
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);//創建設備驅動的屬性
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);
4:驅動實例
include <linux/module.h>
include <linux/kernel.h>
include <linux/init.h>
include <linux/device.h>
include <linux/string.h>
extern struct bus_type my_bus_type;
static int my_probe(struct device *dev) {
printk(“Driver found device!\n”);
return 0;
};
static int my_remove(struct device *dev) {
printk(“Driver unpluged!\n”);
return 0;
};
struct device_driver my_driver = {
.name = “my_dev”,
.bus = &my_bus_type,
.probe = my_probe,
.remove = my_remove,
};
//定義設備驅動屬性
static ssize_t my_driver_show(struct device_driver *driver, char *buf) {
return sprintf(buf, “%s\n”, “This is my driver!”);
};
static DRIVER_ATTR(drv, S_IRUGO, my_driver_show, NULL);
static int __init my_driver_init(void){
int ret;
//注冊設備驅動
ret = driver_register(&my_driver);
if(ret)
printk(“driver_register failed!\n”);
//創建設備驅動屬性
ret = driver_create_file(&my_driver, &driver_attr_drv);
if(ret)
printk(“create_driver_file failed!\n”);
return ret;
}
static void __exit my_driver_exit(void){
driver_unregister(&my_driver);//注銷設備驅動
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_AUTHOR(“Fany”);
MODULE_LICENSE(“GPL”);
5:測試
當加載驅動程序時,終端界面上打印
Driver found device!
說明驅動找到了匹配的設備,看到打印這個時,想到在windows下插U盤,立馬彈出“發現可移動設備”,有點相像!
再看看相應的目錄:/sys/bus/my_bus/drivers/,多了一個my_dev。
說明添加驅動成功
原文地址:http://blog.chinaunix.net/uid-23254875-id-341060.html