UT4412BV03开发板学习Linux设备驱动模型(二)
UT4412BV03开发板学习Linux设备驱动模型(二)
设备驱动模型有三个重要部分,分别是总线(bus_type),设备(device),驱动(driver)
下面对三个组件分别进行介绍。
一.总线
从硬件上来讲,物理总线有数据总线和地址总线,在设备驱动模型中所有设备都是通过总线相连接的,驱动程序依附在总线上,下面将表示总线,设备,驱动三者之间的关系。
了解了总线的结构之后,下面具体说明总线中用到的一些结构体及相关的函数。
#include <asm/device.h>
1.总线的数据结构bus_type
struct bus_type {
const char *name; //总线的名字
struct bus_attribute *bus_attrs; //总线属性和导出到sysfs中的方法
struct device_attribute *dev_attrs; //设备属性和导出到sysfs中的方法
struct driver_attribute *drv_attrs; //驱动程序属性和导出到sysfs中的方法
int (*match)(struct device *dev, struct device_driver *drv);//匹配函数,检验参数二的驱动是否支持参数一的设备
//当一条总线上新设备或新驱动被添加时,会一次或多次调用该函数,
// 如果指定的驱动能适用于指定的设备,那么该函数返回非0,否则返回0
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 (*suspend_late)(struct device *dev, pm_message_t state); //挂起函数
int (*resume_early)(struct device *dev); //唤醒函数
int (*resume)(struct device *dev); //恢复供电状态,是设备正常工作的方法
struct dev_pm_ops *pm; //关于电源管理的操作符
struct bus_type_private *p; //总线私有数据
};
2.总线属性数据结构
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); //属性写函数
};
struct attribute {
const char *name; //属性名字
struct module *owner;
mode_t mode; //属性读写权限
};
3.初始化bus_attribute结构体用的宏
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
例: static BUS_ATTR(config, 0644, ap_config_time_show,ap_config_time_store);
对此宏进行扩展为
struct bus_attribute bus_attr_config_time={
.attr={.name=config_time,.mode=0644},
.show=ap_config_time_show,
.store=ap_config_time_store,
}
4.创建总线属性的函数
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;
}
5.移除总线属性用到的函数
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
{
if (bus_get(bus)) {
sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
}
}
6.总线上用到的方法
int (*match)(struct device *dev, struct device_driver *drv);//匹配函数,检验参数二的驱动是否支持参数一的设备
//当一条总线上新设备或新驱动被添加时,会一次或多次调用该函数,
// 如果指定的驱动能适用于指定的设备,那么该函数返回非0,否则返回0
当用户空间产生热插拔事件前,可能需要内核传递一些参数给用户空间,这里只能使用环境变量来传递,
传递环境变量用到的函数 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
该函数只能在内核支持热插拔事件时才有用,否则该函数被定义为NULL。
举例:
static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct amba_device *pcdev = to_amba_device(dev);
int retval = 0;
//向env中添加环境变量“AMBA_ID”
retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
return retval;
}
#else
#define amba_uevent NULL //表示不支持热插拔事件
#endif
7.总线注册函数
int bus_register(struct bus_type *bus)
为驱动程序定义一条新总线,调用此函数,这函数可能会调用失败,因此我们一般
要检查其返回值,如果调用成功将在/sys/bus目录下生成总线目录
8.总线注销函数
void bus_unregister(struct bus_type *bus)
{
pr_debug("bus: '%s': unregistering\n", bus->name);
bus_remove_attrs(bus);
remove_probe_files(bus);
kset_unregister(bus->p->drivers_kset);
kset_unregister(bus->p->devices_kset);
bus_remove_file(bus, &bus_attr_uevent);
kset_unregister(&bus->p->subsys);
kfree(bus->p);
bus->p = NULL;
}
二.设备
在linux驱动中,每一个设备都由一个device结构体来描述,对于驱动开发者来说,当遇到新设备时,
需要定义一个新的设备结构体,并将device这个结构体包含在新的设备结构体中
1.device结构体
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
struct device_type *type;
struct semaphore sem; /* semaphore 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 *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data, device core doesn't touch it */
struct dev_pm_info power;
#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; /* Like dma_mask, but for alloc_coherent mappings as not all hardware supports
64 bit addresses for consistent allocations such descriptors. */
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 */
struct dev_archdata archdata; /* arch specific additions */
dev_t devt; /* dev_t, creates the sysfs "dev" */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
2.设备注册用到的函数
设备必须要向linux内核注册后才能使用,下面是设备的注册函数
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
3.设备卸载用到的函数
void device_unregister(struct device *dev)
{
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
device_del(dev);
put_device(dev);
}
4.设备属性结构体
struct device_attribute {
struct attribute attr; //设备属性
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf); //显示属性的方法
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count); //设置属性的方法
};
写程序时,可以使用宏DEVICE_ATTR初始化attribute结构体
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
5.用device_create_file函数在device目录下创建一个属性文件
int device_create_file(struct device *dev, struct device_attribute *attr)
{
int error = 0;
if (dev)
error = sysfs_create_file(&dev->kobj, &attr->attr);
return error;
}
6.用device_remove_file函数在device目录下删除一个属性文件
void device_remove_file(struct device *dev, struct device_attribute *attr)
{
if (dev)
sysfs_remove_file(&dev->kobj, &attr->attr);
}
三.驱动
在设备驱动模型中,记录了注册到系统中的所有设备,并不是所有设备都能使用,本节重点介绍驱动和设备需要绑定在一起才能使用。一个设备对应一个驱动,一个驱动可能对应多个设备驱动,设备驱动模型中的探测函数自动探测新设备,并为其分配一个合适的驱动,这样新设备就能够使用了,对于驱动都应该有下面的驱动结构体。
1.设备驱动device_driver结构体
struct device_driver {
const char *name; /*设备驱动程序的名字*/
struct bus_type *bus; /*指向驱动属于的总线,总线上有很多设备*/
struct module *owner; /*设备驱动自身的模块*/
const char *mod_name; /* used for built-in modules */ /*驱动模块的名字*/
/*探测设备的方法,并检测设备驱动可以控制哪些装备*/
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); /*设备恢复正常状态时所调用的方法*/
struct attribute_group **groups; /*属性组*/
struct dev_pm_ops *pm; /*用于电源管理*/
struct driver_private *p; /*设备驱动的私有数据*/
};
struct driver_private {
struct kobject kobj; /*内嵌的kobject结构,用来构建设备驱动模型的结构*/
struct klist klist_devices; /*该设备支持的所有设备链表*/
struct klist_node knode_bus; /*该驱动所属总线*/
struct module_kobject *mkobj; /*驱动的模块*/
struct device_driver *driver; /*指向驱动本身*/
};
3.驱动的属性结构体
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *driver, const char *buf,
size_t count);
};
4.在驱动所属目录中创建一个属性
int driver_create_file(struct device_driver *drv,
struct driver_attribute *attr)
{
int error;
if (drv)
error = sysfs_create_file(&drv->p->kobj, &attr->attr);
else
error = -EINVAL;
return error;
}
5.在驱动所属目录中删除一个属性
void driver_remove_file(struct device_driver *drv,
struct driver_attribute *attr)
{
if (drv)
sysfs_remove_file(&drv->p->kobj, &attr->attr);
}
4.驱动程序的注册
此函数是向设备驱动模型中插入一个新的device_driver对象
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) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EEXIST;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
4.设备驱动程序的注销
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); //从总线中移除驱动
}
以上内容简单的梳理了一下linux系统中设备驱动模型的相关知识,分析了linux设备驱动模型中设备,总线,驱动三者之间的关系,并将设备驱动模型中的总线,设备,驱动的重要结构体,及函数进行了相关分析,这样将有助于在以后用到设备驱动模型编程时,能够快速的理解和编写设备驱动程序。