Linux驅動之匯流排
阿新 • • 發佈:2018-12-20
1、匯流排存在意義
在Linux核心中,編寫驅動一般都要經歷:申請註冊裝置號、註冊操作方法集、硬體初始化、建立裝置節點,雖然裝置不同,但是每個裝置驅動的編寫都要經歷這幾步,在這些流程步驟中,只有硬體初始化隨著裝置不同,會存在很大差異,但是其他步驟都是一模一樣的,為了提高程式碼重用,降低驅動開發的複雜度,引入了匯流排概念:在編寫程式碼時,將裝置硬體資訊和操作邏輯剝離,硬體資訊獨立在device中,操作邏輯在driver中,這樣在裝置硬體資訊變動時,只需要修改device就可以了
2、匯流排結構
匯流排框架將整個驅動分成3個部分,每個部分都定義了響應的結構體:device、bus、driver:
/* bus_type型別中關於匯流排的屬性成員不止下列這些,但是這裡只以常用的作說明 */ struct bus_type { const char *name; //匯流排名稱,最後driver和device是不是屬於同一匯流排,就全看他相不相同 int (*match)(struct device *dev, struct device_driver *drv);//需要我們在自定義匯流排的時候去實現的匹配規則函式,也就是說driver和device互相匹配規則的實現 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);//生成/sys/bus下的結構,不需要我們關心,由核心提供 int (*probe)(struct device *dev); //當match匹配成功返回1的時候,才會呼叫該函式 int (*remove)(struct device *dev);//當匯流排解除安裝時呼叫 };
驅動結構體:
/*
匯流排中對驅動的常見描述
*/
struct device_driver {
const char *name; //驅動和裝置用來匹配的名稱
struct bus_type *bus; //該驅動所屬的匯流排名稱
int (*probe) (struct device *dev); //當驅動註冊的時候,和裝置匹配之後呼叫的探測函式,一般用來完成對裝置的硬體初始化、方法註冊
int (*remove) (struct device *dev);//解除安裝驅動的時候執行,和probe執行內容相反
};
裝置結構體:
/* 描述裝置物件資訊 */ struct device { const char *init_name; /* 裝置匹配名稱 */ struct bus_type *bus; /* 裝置所屬匯流排名稱 */ void (*release)(struct device *dev);/*設備註銷時呼叫的回收資料函式*/ void *platform_data; /* 描述裝置資訊的自定義結構體指標 */ };
3、建立流程
(1)單獨建立自定義匯流排的模組並註冊
注意對描述匯流排結構體的變數進行符號匯出
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <debug.h>//自己定義的dbgprintk除錯列印介面,這裡要注意,如果在Ubuntu裡面使用核心列印的時候,Ubuntu12.04預設將核心輸出對應的超級終端為1(切換時直接ctrl+alt+f1-f6,終端1就是f1)
int mybus_match(struct device *dev, struct device_driver *drv)
{
dbgprintk("in mybus_match");
return 1;
}
struct bus_type mybus = {
.name = "mybus",
.match = mybus_match,
};
static int __init mybus_init(void)
{
int ret = 0;
//1、註冊匯流排
ret = bus_register(&mybus);
if(ret){
dbgprintk("bus register err!");
}
dbgprintk("mybus init !");
return ret;
}
static void __exit mybus_exit(void)
{
dbgprintk("mybus exit !");
bus_unregister(&mybus);
}
EXPORT_SYMBOL(mybus); //一定要匯出,driver和device需要使用
module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");
(2)建立驅動模組並註冊
注意外部宣告描述匯流排結構體的變數,在編譯模組的時候和匯流排定義模組在一個Makefile中編譯(使用到匯流排模組匯出符號)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <debug.h>
extern struct bus_type mybus; //宣告,而這時候需要用到匯流排模組中的符號匯出,這需要將二者放在一個Makefile中編譯
int mydrv_probe (struct device *dev)
{
dbgprintk("driver matched device successed!");
return 0;
}
int mydrv_remove (struct device *dev)
{
dbgprintk("remove device !");
return 0;
}
static struct device_driver mydrv = {
.name = "mybus",
.bus = &mybus,
.probe = mydrv_probe,
.remove = mydrv_remove,
};
static int __init mybus_drv_init(void)
{
int ret = 0;
dbgprintk("mybus drv init !");
//1、註冊driver
ret = driver_register(&mydrv);
if(ret){
dbgprintk("driver register error !");
return ret;
}
return ret;
}
static void __exit mybus_drv_exit(void)
{
dbgprintk("mybus is exit!");
driver_unregister(&mydrv);
}
module_init(mybus_drv_init);
module_exit(mybus_drv_exit);
MODULE_LICENSE("GPL");
(3)建立裝置模組並註冊
注意外部宣告描述匯流排結構體的變數,同時自定義裝置資料,在編譯模組的時候和匯流排定義模組在一個Makefile中編譯(使用到匯流排模組匯出符號)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <debug.h>
extern struct bus_type mybus;
struct mydev_desc{
char *name;
int irqno;
unsigned long addr;
};
struct mydev_desc devinfo = {
.name = "mybus",
.irqno = 999,
.addr = 0x40008000,
};
void mydev_release(struct device *dev)
{
dbgprintk("device release !");
}
struct device mydev = {
.init_name = "my_device",
.bus = &mybus,
.release = mydev_release,
.platform_data = &devinfo,
};
static int __init mybus_init(void)
{
int ret = 0;
//1、註冊匯流排
ret = device_register(&mydev);
if(ret<0){
dbgprintk("device register err!");
}
dbgprintk("mydev init !");
return ret;
}
static void __exit mybus_exit(void)
{
dbgprintk("mydev exit !");
device_unregister(&mydev);
}
EXPORT_SYMBOL(mybus);
module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");