1. 程式人生 > >Linux驅動之匯流排

Linux驅動之匯流排

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");