linux ------ 设备驱动模型之二(bus, device, driver)
2014-03-04 15:36
573 查看
bus, device, driver属于设备驱动模型的高层部分,驱动程序员直接跟它们打交道,它们构成LINUX设备驱动模型这个建筑的外在表现。
一. 总线及其注册
总线是设备驱动模型的核心,它把设备与驱动紧密的联系起来,完成各自的使命。 总线有具体的物理总线抽象,比如PCI, I2C总线,也有虚拟的总线,比如platform总线。符合LINUX设备驱动模型的设备与驱动必须挂靠在一个总线上,无论是物理总线还是虚拟总线。平台相关的BSP设备驱动(RTC, 看门狗,LCD控制器等)一般是挂靠在platform这个虚拟总线上。
1. 总线数据结构
struct bus_type{
const char *name; //总线名称
const char *dev_name;
struct device *dev_root;
struct bus_attribute *bus_attrs; //总线属性,包括操作属性的函数
struct device_attribute *dev_attrs;
//挂在总线上的设备的属性
struct driver_attribute *drv_attrs; //挂在总线上的驱动的属性
int (*match)(struct device *dev, struct device_driver *drv); //尝试对挂载在总线上的设备与驱动进行匹配
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
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 dev_pm_ops *pm; //对总线上设备进行电源管理的一组函数
struct iommu_ops *iommu_ops; //对总线的IO内存进行管理
struct subsys_private *p; //管理总线上设备与驱动的数据结构
};
subsys_private数据结构
struct subsys_private{
struct kset subsys; //总线所在的子系统
struct kset *devices_kset; //该总线上所有设备的集合
struct list_head interfaces; //总线子系统接口链表
struct mutex mutex; //对设备进行操作的互斥锁
struct kset *drivers_kset; //该总线上所有驱动的集合
struct klist klist_devices;
//该总线上所有设备组成的链表
struct klist klist_drivers; //该总线上所有驱动组成的链表
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1; //当向总线中注册某一设备或驱动时是否进行设备与驱动的绑定
struct bus_type *bus; //指向与该bus_type_private对象关联的总线
struct kset glue_dirs;
struct class *class;
};
2. 相关操作函数
(1). int __init buses_init(void); //在系统初始化阶段调用这个函数,它将创建一个名称为"bus"的 kset并将其加入到sysfs 文件树中,即创建目录/sys/bus
(2). int bus_register(struct bus_type *bus); //向系统中注册一个总线,创建一个具体的总线,即在目录/sys/bus下创建一个总线名称的目录,比如/sys/bus/platform,同时在该目录下创建devices与drivers目录,比如/sys/bus/platform/devices与/sys/bus/platform/drivers.
二. 总线的属性
总线属性代表总线特有的信息与配置,通过sysfs 为总线生成属性文件,用户空间程序可以通过文件接口的方式显示或修改总线的属性。根据需要,可以为总线不止创建一个属性文件,每个文件代表总线的一个或一组属性信息。
1. 总线属性的数据结构
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); //更改总线的属性
};
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
BUS_ATTR定义一个"bus_attr_"开头的总线属性对象。
2. 相关函数,这个函数生成总线属性文件。
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
int error;
if (bus_get(bus)) {
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
三. 设备与驱动的绑定
绑定其实就是把设备与它的驱动程序紧密结合到一起,总线在设备与驱动的绑定中发挥着核心的作用。总线为驱动程序员提供一组外在接口,简化驱动程序的开发。在总线上发生的两类事件将导致设备与驱动的绑定行为发生:一个是通过函数device_register向某一个总线上注册一个设备,另一个通过driver_register将某一驱动注册到其所处的总线上。
1. device_register 函数
内核将设备加入到总线的设备链表尾端,同时试图将此设备与总线上的所有驱动对象进行绑定。它会调函数device_bind_driver进行绑定。
2. driver_register
将驱动注册到其所属的总线上,同时将驱动对象加入到总线的所有驱动对象构成的链表的尾端,试图将驱动与总线上的所有设备进行绑定。
四. 设备
1. 数据结构
struct device {
struct device *parent; //当前设备的父设备
struct device_private *p;
//该设备驱动相关的数据
struct kobject kobj;
//代表struct device的内核对象
const char *init_name; /* initial name of the device */ //设备对象的名称,在sysfs中表现为一个目录
const struct device_type *type;
struct mutex mutex;
struct bus_type *bus; /* type of bus device is on */ //设备所在的总线对象指针
struct device_driver *driver;
//当前设备是否已经与它的驱动进行绑定,如果该值为NULL,说明没有找到它的驱动
struct list_head deferred_probe;
void *platform_data;
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
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
#ifdef CONFIG_CMA
struct cma *cma_area;
#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;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
2. 主要操作函数
(1). 设备初始化函数,初始化dev的一些成员,dev->kobj.kset=devices_kset表明dev所属的kset对象为devices_kset.
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->deferred_probe);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}
(2). 设备注册函数
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
先调用device_initialize来初始化dev对象,然后通过device_add将设备对象dev加入到系统。
(3). 设备注销函数
void device_unregister(struct device *dev)
{
device_del(dev);
put_device(dev);
}
将设备从系统中注销,重点在函数device_del中。
五. 驱动
1. 数据结构
struct device_driver{
const char *name; //驱动名称
struct bus_type *bus; //驱动所属总线
struct module *owner; //驱动所在的内核模块
const char *mod_name;
bool suppress_bind_attrs;
const struct of_device_id *of_match_table;
int (*probe) (struct device *dev); //驱动程序的探测函数,在总线对设备与驱动进行绑定时,如果总线没有定义probe函数,内核会调用驱动的probe函数
int (*remove) (struct device *dev);
//卸载函数,当driver_unregister卸载驱动时,如果总线没有定义remove函数,在内核调用驱动的remove函数
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;
};
2. 操作函数
(1). driver_find, 在总线的driver_kset 集合中查找指定的驱动,name为查找的驱动名称,参数bus 指明在哪个总线上查找,如果成功,返回驱动对象指针,否则返回0
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
struct driver_private *priv;
if (k) {
/* Drop reference added by kset_find_obj() */
kobject_put(k);
priv = to_driver(k);
return priv->driver;
}
return NULL;
}
(2). 驱动注册函数,先调用driver_find在总线上查找当前要注册的drv,防止向系统重复注册同一驱动,如果没有注册过,调用bus_add_driver(drv)进行注册。
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
(3). 驱动注销函数
void driver_unregister(struct device_driver *drv)
{
if (!drv || !drv->p) {
WARN(1, "Unexpected driver unregister!\n");
return;
}
driver_remove_groups(drv, drv->groups);
bus_remove_driver(drv);
}
参数drv指定要注销的某一驱动对象,函数做的是driver_register的反向工作,主要在bus_remove_driver(drv)函数中完成。在注销一个驱动对象的过程中,如果其所在的总线定义了remove方法,则调用它,否则看驱动的驱动程序中有没有实现该方法,如果实现了则内核调用该函数。
一. 总线及其注册
总线是设备驱动模型的核心,它把设备与驱动紧密的联系起来,完成各自的使命。 总线有具体的物理总线抽象,比如PCI, I2C总线,也有虚拟的总线,比如platform总线。符合LINUX设备驱动模型的设备与驱动必须挂靠在一个总线上,无论是物理总线还是虚拟总线。平台相关的BSP设备驱动(RTC, 看门狗,LCD控制器等)一般是挂靠在platform这个虚拟总线上。
1. 总线数据结构
struct bus_type{
const char *name; //总线名称
const char *dev_name;
struct device *dev_root;
struct bus_attribute *bus_attrs; //总线属性,包括操作属性的函数
struct device_attribute *dev_attrs;
//挂在总线上的设备的属性
struct driver_attribute *drv_attrs; //挂在总线上的驱动的属性
int (*match)(struct device *dev, struct device_driver *drv); //尝试对挂载在总线上的设备与驱动进行匹配
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
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 dev_pm_ops *pm; //对总线上设备进行电源管理的一组函数
struct iommu_ops *iommu_ops; //对总线的IO内存进行管理
struct subsys_private *p; //管理总线上设备与驱动的数据结构
};
subsys_private数据结构
struct subsys_private{
struct kset subsys; //总线所在的子系统
struct kset *devices_kset; //该总线上所有设备的集合
struct list_head interfaces; //总线子系统接口链表
struct mutex mutex; //对设备进行操作的互斥锁
struct kset *drivers_kset; //该总线上所有驱动的集合
struct klist klist_devices;
//该总线上所有设备组成的链表
struct klist klist_drivers; //该总线上所有驱动组成的链表
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1; //当向总线中注册某一设备或驱动时是否进行设备与驱动的绑定
struct bus_type *bus; //指向与该bus_type_private对象关联的总线
struct kset glue_dirs;
struct class *class;
};
2. 相关操作函数
(1). int __init buses_init(void); //在系统初始化阶段调用这个函数,它将创建一个名称为"bus"的 kset并将其加入到sysfs 文件树中,即创建目录/sys/bus
(2). int bus_register(struct bus_type *bus); //向系统中注册一个总线,创建一个具体的总线,即在目录/sys/bus下创建一个总线名称的目录,比如/sys/bus/platform,同时在该目录下创建devices与drivers目录,比如/sys/bus/platform/devices与/sys/bus/platform/drivers.
二. 总线的属性
总线属性代表总线特有的信息与配置,通过sysfs 为总线生成属性文件,用户空间程序可以通过文件接口的方式显示或修改总线的属性。根据需要,可以为总线不止创建一个属性文件,每个文件代表总线的一个或一组属性信息。
1. 总线属性的数据结构
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); //更改总线的属性
};
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
BUS_ATTR定义一个"bus_attr_"开头的总线属性对象。
2. 相关函数,这个函数生成总线属性文件。
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
int error;
if (bus_get(bus)) {
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
三. 设备与驱动的绑定
绑定其实就是把设备与它的驱动程序紧密结合到一起,总线在设备与驱动的绑定中发挥着核心的作用。总线为驱动程序员提供一组外在接口,简化驱动程序的开发。在总线上发生的两类事件将导致设备与驱动的绑定行为发生:一个是通过函数device_register向某一个总线上注册一个设备,另一个通过driver_register将某一驱动注册到其所处的总线上。
1. device_register 函数
内核将设备加入到总线的设备链表尾端,同时试图将此设备与总线上的所有驱动对象进行绑定。它会调函数device_bind_driver进行绑定。
2. driver_register
将驱动注册到其所属的总线上,同时将驱动对象加入到总线的所有驱动对象构成的链表的尾端,试图将驱动与总线上的所有设备进行绑定。
四. 设备
1. 数据结构
struct device {
struct device *parent; //当前设备的父设备
struct device_private *p;
//该设备驱动相关的数据
struct kobject kobj;
//代表struct device的内核对象
const char *init_name; /* initial name of the device */ //设备对象的名称,在sysfs中表现为一个目录
const struct device_type *type;
struct mutex mutex;
struct bus_type *bus; /* type of bus device is on */ //设备所在的总线对象指针
struct device_driver *driver;
//当前设备是否已经与它的驱动进行绑定,如果该值为NULL,说明没有找到它的驱动
struct list_head deferred_probe;
void *platform_data;
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
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
#ifdef CONFIG_CMA
struct cma *cma_area;
#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;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
2. 主要操作函数
(1). 设备初始化函数,初始化dev的一些成员,dev->kobj.kset=devices_kset表明dev所属的kset对象为devices_kset.
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->deferred_probe);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}
(2). 设备注册函数
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
先调用device_initialize来初始化dev对象,然后通过device_add将设备对象dev加入到系统。
(3). 设备注销函数
void device_unregister(struct device *dev)
{
device_del(dev);
put_device(dev);
}
将设备从系统中注销,重点在函数device_del中。
五. 驱动
1. 数据结构
struct device_driver{
const char *name; //驱动名称
struct bus_type *bus; //驱动所属总线
struct module *owner; //驱动所在的内核模块
const char *mod_name;
bool suppress_bind_attrs;
const struct of_device_id *of_match_table;
int (*probe) (struct device *dev); //驱动程序的探测函数,在总线对设备与驱动进行绑定时,如果总线没有定义probe函数,内核会调用驱动的probe函数
int (*remove) (struct device *dev);
//卸载函数,当driver_unregister卸载驱动时,如果总线没有定义remove函数,在内核调用驱动的remove函数
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;
};
2. 操作函数
(1). driver_find, 在总线的driver_kset 集合中查找指定的驱动,name为查找的驱动名称,参数bus 指明在哪个总线上查找,如果成功,返回驱动对象指针,否则返回0
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
struct driver_private *priv;
if (k) {
/* Drop reference added by kset_find_obj() */
kobject_put(k);
priv = to_driver(k);
return priv->driver;
}
return NULL;
}
(2). 驱动注册函数,先调用driver_find在总线上查找当前要注册的drv,防止向系统重复注册同一驱动,如果没有注册过,调用bus_add_driver(drv)进行注册。
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
(3). 驱动注销函数
void driver_unregister(struct device_driver *drv)
{
if (!drv || !drv->p) {
WARN(1, "Unexpected driver unregister!\n");
return;
}
driver_remove_groups(drv, drv->groups);
bus_remove_driver(drv);
}
参数drv指定要注销的某一驱动对象,函数做的是driver_register的反向工作,主要在bus_remove_driver(drv)函数中完成。在注销一个驱动对象的过程中,如果其所在的总线定义了remove方法,则调用它,否则看驱动的驱动程序中有没有实现该方法,如果实现了则内核调用该函数。
相关文章推荐
- LINUX设备驱动之设备模型三--device&driver&bus
- LINUX设备驱动之设备模型四--device&driver&bus(二)
- linux驱动编写之十三(设备模型之sysfs,bus,device,driver源代码分析)
- LINUX设备驱动之设备模型五--device&driver&bus(三)
- LINUX设备驱动之设备模型五--device&driver&bus(三)
- LINUX设备驱动之设备模型三--device&driver&bus(一)
- LINUX设备驱动之设备模型五--device&driver&bus
- LINUX设备驱动之设备模型三--device&driver&bus(一)
- LINUX设备驱动之设备模型四--device&driver&bus(二)
- LINUX设备驱动之设备模型四--device&driver&bus(二)
- LINUX设备驱动之设备模型五--device&driver&bus(三)
- Linux设备模型中三个很重要的概念: 总线,设备,驱动.即bus,device,driver
- linux设备模型bus,device,driver
- Linux内核部件分析--设备驱动模型之device-driver
- 设备驱动模型:总线bus_type 设备device 驱动driver
- linux设备模型之bus,device,driver分析一
- linux 设备模型中 bus 、device 、driver 三者关系
- linux设备模型之bus,device,driver分析一
- linux内核部件分析(九)——设备驱动模型之device-driver
- 总线(bus)、设备(device)、驱动(driver)三者构成了设备驱动的模型