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