Linux总线驱动模型
基本概念
1、什么是总线设备驱动模型?
设备模型是将硬件设备归纳、分类,并抽象出一套标准数据结构和接口方法;
随着技术的进步,系统的拓扑结构变得越来越复杂,Linux 内核使用了全新的设备模型—总线设备驱动模型,对设备与驱动分离管理,提高程序的可移植性;Linux分别使用了3个数据结构来描述总线设备模型,分别对应于总线、设备和驱动及它们之间的关系:
(1)所有的设备和驱动都必须挂载在某一总线上;
(2)通过总线可以绑定设备和驱动
2、总线(Bus)
总线是处理器和设备之间的通道,有多种类型(可为虚拟总线),可以挂载多个设备,在linux设备模型中,所有的设备都挂在某一总线上,使用总线来管理设备和驱动,总线使用bus_type结构体来描述:
注意:内核版本的不同,struct bus_type结构体不同!
#include<linux/device.h>
struct bus_type {
const char name; //总线名
//该名称与struct device结构中的init_name有关,某些设备(如批量化USB设备),
//允许设计者允许将设备名字留空,当设备注册到内核时会以
//“bus->dev_name+device ID”的形式生成设备名称;
const char *dev_name; //总线的设备名
//dev_root设备为bus的默认父设备,
//在内核实际实现中只与称为sub system的功能有关
struct device *dev_root; //根设备
//在bus总线添加到内核时自动添加相应的属性
struct bus_attribute *bus_attrs; //bus总线的默认属性
// 在device设备添加到内核时自动添加相应的属性
struct device_attribute *dev_attrs; // bus总线上设备的默认属性
//在device_driver驱动添加到内核时自动添加相应的属性
struct driver_attribute *drv_attrs; // bus总线上驱动的默认属性
//当新设备或驱动添加到总线上时match函数被调用,设备和驱动匹配成功返回0
int (*match)(struct device *dev, struct device_driver *drv);
//设备添加、移除或添加环境变量时,调用uevent函数;
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
//新设备或驱动添加时,调用probe函数,并回调驱动的probe函数初始化关联
int (*probe)(struct device *dev);
//设备移除时调用remove函数
int (*remove)(struct device *dev);
//设备关闭时时调用shutdown函数
void (*shutdown)(struct device *dev);
//设备挂起时调用suspend函数
int (*suspend)(struct device *dev, pm_message_t state);
//设备唤醒时调用resume
int (*resume)(struct device *dev);
//总线电源管理选项,将回调设备驱动的电源管理模块
const struct dev_pm_ops *pm;
struct iommu_ops *iommu_ops;
// bus总线的私有数据
struct subsys_private *p;
};
Struct subsys_private {
struct kset subsys; //总线在/sys/bus/目录下的目录项
struct kset *devices_kset; //总线目录下的devices目录
struct kset *drivers_kset; //总线目录下的drivers目录
struct klist klist_devices; //挂在总线上的设备
struct klist klist_drivers; //挂在总线上的设备驱动
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus; //指向总线
struct list_head class_interfaces;
struct kset glue_dirs;
struct mutex class_mutex;
struct class *class;
}
3、设备(Device)
在linux总线设备模型中用struct device结构体表示设备
注意:内核版本的不同,struct device结构体不同!
<linux/device.h>
struct device {
//设备的父设备,常为是设备所从属的bus、controller等设备
struct device *parent;
//设备的私有数据结构指针,用于保存子设备链表
struct device_private *p;
//设备的kobject
struct kobject kobj;
//设备的名称
const char init_name; / initial name of the device */
//设备类型
const struct device_type type;
//互斥信号量
struct mutex mutex; / mutex to synchronize calls to
* its driver.
*/
//设备所挂在的总线
struct bus_type bus; / type of bus device is on */
//设备对应的驱动
struct device_driver driver; / which driver has allocated this device */
//设备平台相关的数据
void platform_data; / Platform specific data, device
core doesn’t touch it */
//设备电源相关的信息
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
//设备DMA相关的数据
u64 *dma_mask;
u64 coherent_dma_mask;
struct device_dma_parameters dma_parms;
struct list_head dma_pools; / dma pools (if dma’ble) */
struct dma_coherent_mem dma_mem; / internal for coherent mem override */
#ifdef CONFIG_CMA
struct cma cma_area; / contiguous memory area for dma allocations /
#endif
//设备架构相关数据
/ arch specific additions */
struct dev_archdata archdata;
//与设备树相关的节点
struct device_node of_node; / associated device tree node /
//设备号
dev_t devt; / dev_t, creates the sysfs “dev” /
//设备标识符
u32 id; / device instance */
//设备自旋锁
spinlock_t devres_lock;
//设备资源双向链表头
struct list_head devres_head;
//接入class链表时所需要的klist节点
struct klist_node knode_class;
//设备所属class的指针
struct class *class;
//设备的属性集合
const struct attribute_group *groups; / optional groups */
//设备释放时调用函数指针
void (*release)(struct device *dev);
};
4、设备驱动(Driver)
在linux总线设备模型中用struct device_driver结构体表示设备驱动
注意:内核版本的不同,struct device_driver结构体不同!
<linux/device.h>
struct device_driver {
//设备驱动的名称
const char *name;
//设备驱动所在的总线
struct bus_type *bus;
//设备驱动所属的内核模块
struct module *owner;
//内嵌内核模块名称
const char mod_name; / used for built-in modules /
//sys文件系统中的bind/unbind逻辑
bool suppress_bind_attrs; / disables bind/unbind via sysfs */
//openfirmware框架中的匹配表
const struct of_device_id *of_match_table;
//设备驱动的探测函数(新的设备或设备驱动添加到总线时调用)
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);
//设备驱动组属性
const struct attribute_group **groups;
//设备驱动电源管理信息
const struct dev_pm_ops *pm;
//设备驱动私有数据
struct driver_private *p;
};
总线设备驱动模块步骤
1、总线模块
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Alan”);
MODULE_DESCRIPTION (“LEDS BUS");
//导出符号leds_bus_type,其它内核模块(device、device_driver)要使用到
EXPORT_SYMBOL(leds_bus_type);
//模块初始化
module_init(leds_bus_init);
//模块退出
module_exit(leds_bus_exit);
(a)定义总线
//LED的总线匹配函数
int leds_match(struct device *dev, struct device_driver *drv)
{
return !strncmp(dev->kobj.name,drv->name,strlen(drv->name));
}
//定义LEDS总线
struct bus_type leds_bus_type = {
.name = “leds_bus”, //总线名为leds_bus
.match = leds_match, //总线匹配函数
};
(b)注册总线
int leds_bus_init()
{
int ret;
//注册总线leds_bus_type
ret = bus_register(&leds_bus_type);
return ret;
}
(c)注销总线
void leds_bus_exit()
{
//注册总线leds_bus_type
bus_unregister(&leds_bus_type);
}
2、设备模块
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Alan”);
MODULE_DESCRIPTION (“LEDS DEVICE");
//模块初始化
module_init(leds_device_init);
//模块退出
module_exit(leds_device_exit);
(a)定义设备
//声明外部结构体变量,LED的总线匹配函数
extern struct bus_type leds_bus_type;
//定义LEDS总线
struct device leds_dev = {
//设备名称
.init_name = “leds_dev",
//设备所属的总线
.bus = &leds_bus_type,
// 设备的释放函数
.release = leds_dev_release,
.platform_data =& Tiny4412_LEDS,
};
(b)注册设备
int leds_device_init()
{
int ret;
//注册leds_dev设备
ret = device_register(&leds_dev);
return ret;
}
(c)注销设备
void leds_device_exit()
{
//注销设备
device_unregister(&my_dev);
}
3、设备驱动模块
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Alan”);
MODULE_DESCRIPTION (“LEDS DEVICE DRIVER");
//模块初始化
module_init(leds_driver_init);
//模块退出
module_exit(leds_driver_exit);
(a)定义设备驱动
//声明外部结构体变量,LED的总线匹配函数
extern struct bus_type leds_bus_type;
//设备驱动探测函数
int leds_probe(struct device *dev)
{
printk(“driver found the device it can handle!\n”);
//驱动相关的代码
struct Tiny4412_leds *pLEDS = dev->platform_data;
gpiocon = ioremap(pLEDS->CON_GPIOPA, 4);
gpiodat = ioremap(pLEDS->DAT_GPIOPA, 4);
return 0;
}
struct device_driver leds_driver = {
.name = “leds_dev",
//设备驱动所属总线
.bus = &leds_bus_type,
//在leds_bus_type总线找匹配设备后leds_probe;
//删除设备时调用leds_remove
.probe = leds_probe,
};
注意:设备驱动程序何时在总线上查找匹配的设备?如果匹配?
答:设备驱动注册时,设备驱动与设备都属于总线,调用总线中的match函数
(b)注册设备驱动
int leds_driver_init()
{
int ret;
//注册设备驱动
ret = driver_register(&leds_driver);
return ret;
}
(c)注销设备驱动
void leds_driver_exit()
{
//注销设备驱动
driver_unregister(&leds_driver);
}