設備驅動模型:bus, driver, device
struct bus_type :總線對象,描述一個總線,管理device和driver,完成匹配
struct bus_type {
const char *name;
int (*match)(struct device *dev, struct device_driver *drv);
}
註冊和註銷
int bus_register(struct bus_type *bus)
void bus_unregister(struct bus_type *bus)
device對象:設備對象,描述設備信息,包括地址,中斷號,甚至其他自定義的數據
struct device {
struct kobject kobj; //所有對象的父類
const char *init_name;
// 在總線中會有一個名字,用於做匹配,在/sys/bus/mybus/devices/名字
struct bus_type *bus; //指向該device對象依附於總線的對象
void *platform_data; // 自定義的數據,指向任何類型數據
}
註冊和註銷的方法:
int device_register(struct device *dev)
void device_unregister(struct device *dev)
driver對象:描述設備驅動的方法(代碼邏輯)
struct device_driver {
const char *name;
// 在總線中會有一個名字,用於做匹配,在/sys/bus/mybus/drivers/名字
struct bus_type *bus;//指向該driver對象依附於總線的對象
int (*probe) (struct device *dev); // 如果device和driver匹配之後,driver要做的事情
int (*remove) (struct device *dev); // 如果device和driver從總線移除之後,driver要做的事情
}
註冊和註銷:
int driver_register(struct device_driver *drv)
void driver_unregister(struct device_driver *drv)
如何實現總線匹配,匹配成功之後會自動調用driver的probe方法:
1, 實現bus對象中 match方法
2, 保證driver和device中名字要一樣
====================================================================
平臺總線模型:
爲什麼會有平臺總線:
用於平臺升級:三星: 2410, 2440, 6410, s5pc100 s5pv210 4412
硬件平臺升級的時候,部分的模塊的控制方式,基本上是類似的
但是模塊的地址是不一樣
gpio控制邏輯: 1, 配置gpio的輸入輸出功能: gpxxconf
2, 給gpio的數據寄存器設置高低電平: gpxxdata
邏輯操作基本上是一樣的
但是地址不一樣
uart控制:1,設置8n1,115200, no AFC
UCON,ULCON, UMODOEN, UDIV
邏輯基本上是一樣的
但是地址不一樣
問題:
當soc升級的時候, 對於相似的設備驅動,需要編寫很多次(如果不用平臺總線)
但是會有大部分重複代碼
解決:引入平臺總線
device(中斷/地址)和driver(操作邏輯) 分離
在升級的時候,只需要修改device中信息即可(中斷/地址)
實現一個driver代碼能夠驅動多個平臺相似的模塊,並且修改的代碼量很少
平臺總線中的三元素:
1, bus
platform_bus:不需要自己創建,開機的時候自動創建
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
匹配方法:
1,優先匹配pdriver中的id_table,裏面包含了支持不同的平臺的名字
2,直接匹配driver中名字和device中名字
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
if (pdrv->id_table)// 如果pdrv中有idtable,平臺列表名字和pdev中的名字
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
2,device對象:
struct platform_device {
const char *name; //用於做匹配
int id; // 一般都是直接給-1
struct device dev; // 繼承了device父類
u32 num_resources; // 資源的個數
struct resource *resource; // 資源:包括了一個設備的地址和中斷
}
註冊和註銷
int platform_device_register(struct platform_device * pdev);
void platform_device_unregister(struct platform_device * pdev)
3,driver對象
struct platform_driver {
int (*probe)(struct platform_device *); //匹配成功之後被調用的函數
int (*remove)(struct platform_device *);//device移除的時候調用的函數
struct device_driver driver; //繼承了driver父類
|
const char *name;
const struct platform_device_id *id_table; //如果driver支持多個平臺,在列表中寫出來
}
註冊和註銷
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv)
==========================================
編寫代碼: 編寫一個能在多個平臺下使用的led驅動
1,註冊一個platform_device,定義資源:地址和中斷
struct resource {
resource_size_t start; // 開始
resource_size_t end; //結束
const char *name; //描述,自定義
unsigned long flags; //區分當前資源描述的是中斷(IORESOURCE_IRQ)還是內存(IORESOURCE_MEM)
struct resource *parent, *sibling, *child;
};
2,註冊一個platform_driver,實現操作設備的代碼
註冊完畢,同時如果和pdev匹配成功,自動調用probe方法:
probe方法: 對硬件進行操作
a,註冊設備號,並且註冊fops--爲用戶提供一個設備標示,同時提供文件操作io接口
b, 創建設備節點
c, 初始化硬件
ioremap(地址); //地址從pdev需要獲取
readl/writle();
d,實現各種io接口: xxx_open, xxx_read, ..
獲取資源的方式:
//獲取資源
// 參數1: 從哪個pdev中獲取資源
// 參數2: 資源類型
// 參數3: 表示獲取同種資源的第幾個
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)