您的位置:首页 > 运维架构 > Linux

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方法,则调用它,否则看驱动的驱动程序中有没有实现该方法,如果实现了则内核调用该函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: