您的位置:首页 > 其它

设备驱动模型第1节:平台-设备-驱动1

2014-02-17 14:28 190 查看
第一节 三国演义 总线-设备-驱动
从linux2.6开始内核的驱动提供了新的设备模型:总线、驱动、设备(包括平台设备).也就是说一个符合linux设备驱动模型的设备与其驱动必须挂靠在同一根总线上,无论这根总线是实际存在的总线还是系统虚拟出来的总线。我坚信读者您对“总线”的理解已有一个程度。基本关系简要的概括如下:驱动核心可以注册多种类型的总线。每种总线下面可以挂载许多的设备;每种总线下可以有很多的设备驱动;每个驱动可以处理一组功能相似的设备,所有的设备都挂载到总线上,当加载驱动时,驱动就到总线上遍历自己对应的设备。或者先把驱动加载上,来了一个设备就去总线遍历相应的驱动。(实际工作中更多的是和设备与驱动打交道,总线一般不大涉及到,很多的总线的体系架构内核都已经设定好了)。linux内核驱动机制一般都是有几个大型的数据结构
基石。

总线

  总线是处理器与设备之间通道,在设备模型中,所有的设备都通过总线相连

(1)bus_type <include/linux/device.h>

struct bus_type {

const char * name; //设备名称(总线也是一种设备)


struct bus_attribute *bus_attrs; //总线属性

structdevice_attribute *dev_attrs; //设备属性

structdriver_attribute *drv_attrs; //驱动属性


int (*match)(struct device * dev, struct device_driver * drv);//设备驱动匹配函数,这个匹配函数是很关键的东西,这是建立总线上设备与驱动的桥梁,当一个新的设备或驱动被添加到一个总线上时该回调被执行;

int (*uevent)(struct device *dev, char**envp, int num_envp, char *buffer, intbuffer_size);//热拔插事件

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 *p; //总线上一组跟电源管理相关的操作集,用来对总线上的设备进行电源管理


struct subsys_private *p;

};

struct subsys_private {

struct kset subsys; //该总线所在的子系统

struct kset *devices_kset; //总线所有设备的一个集合

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; //指向所在的总线

struct list_head class_interfaces;

struct kset glue_dirs;

struct mutex class_mutex;

struct class *class;

};

总线、设备、驱动三者之间的层次关系如下图(该图引自《深入linux设备驱动机制》一书)所示:






(2)总线的操作:</driver/base/bus.c>

初始化:int __init buses_init (struct bus_type * bus)

PS: buses_init将在sysfs文件系统的根目录下建立一个“bus”目录,(即/sys/bus/),这个创建的“bus”将是系统中所有后续注册总线的祖先。

注册:int bus_register(struct bus_type * bus)

注销:void bus_unregister(struct bus_type *bus);

注册一个总线可以用下面的一个拓扑图来描述:



(3)总线属性:

用户空间的程序可以通过该文件接口(sys)的方式来显示和更改总线的属性,

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);

};

BUS_ATTR(name, mode, show, store);

这个宏声明一个结构, 产生它的名子通过前缀字符串bus_attr_ 到给定的名子.

任何属于一个总线的属性应当明确使用bus_create_file 来创建:

int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);

属性也可被去除, 使用:

void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);

lddbus 驱动创建一个简单属性文件, 再次, 包含源码版本号.show 方法和 bus_attribute 结构设置如下:

static ssize_t show_bus_version(struct bus_type *bus, char *buf)

{

return snprintf(buf, PAGE_SIZE,"%s\n", Version);

}

这个总线属性现到目前为止我还没有发现它的作用。估计用来查看一些版本信息的。


(4)总线实例:

其实在这个程序中操作很简单:

1:首先是要准备一个总线bus_type.也就是定义一个bus_type,然后给它填上一些成员。

定义如下:

struct bus_type my_bus_type = {

.name = "my_bus",

.match = my_match,

};


这里就对其两个成员赋值了。一个是名称。另一个则是匹配函数:

static int my_match(struct device *dev, structdevice_driver *driver)

{

return !strncmp(dev->init_name,driver->name, strlen(driver->name));

}


这里匹配的逻辑则是设备的名字与驱动的名字一致。

准备好了总线后就在模块初始化函数中注册:

ret = bus_register(&my_bus_type);

if (ret)

return ret;


然后在模块退出函数中注销总线:

bus_unregister(&my_bus_type);

总线操作完后还要为总线创建属性文件:

static BUS_ATTR(version, S_IRUGO,show_bus_version, NULL);这句话就定义了一个总线属性文件。BUS_ATTR宏的定义如下:

#define BUS_ATTR(_name, _mode, _show, _store) \

struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)


show_bus_version定义如下:

static ssize_t show_bus_version(struct bus_type*bus, char *buf)

{

return snprintf(buf, PAGE_SIZE,"%s\n", Version);

}


定义好之后就是调用 函数创建文件

/*创建属性文件*/

if (bus_create_file(&my_bus_type,&bus_attr_version))

printk(KERN_NOTICE "Fail to createversion attribute!\n");


总线本身也是要对应一个设备的。还要为总线创建设备。

static void my_bus_release(struct device *dev)

{

printk(KERN_DEBUG "my busrelease\n");

}

struct device my_bus = {

.init_name = "my_bus0",

.release = my_bus_release

};


/*注册总线设备*/

ret = device_register(&my_bus);

if (ret)

printk(KERN_NOTICE "Fail toregister device:my_bus!\n");


可是这是有疑问,我还没有找到这个总线设备和刚才的总线的联系(注意这个总线也可以是虚拟的,也就是不存在的,但是也可以是一个实实在在的总线比如I2C 总线,常规的总线内核一般对它有2种架构,一个是core,另外一个是控制器本身驱动这个一般和那个CPU有关了)。

源示例代码:

#include<linux/device.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/string.h>


static char *Version ="Revision: 1.0 ";

static int my_match(structdevice *dev, struct device_driver *driver)

{

return!strncmp(dev->init-name, driver->name, strlen(driver->name));

}


static voidmy_bus_release(struct device *dev)

{

printk(KERN_DEBUG"my bus release\n");

}

struct device my_bus = {

.init_name = "my_bus0",

.release = my_bus_release

};




struct bus_type my_bus_type = {

.name = "my_bus",

.match = my_match,

};


EXPORT_SYMBOL(my_bus);

EXPORT_SYMBOL(my_bus_type);




/*

* Export a simple attribute.

*/

static ssize_t show_bus_version(struct bus_type *bus, char *buf)

{

return snprintf(buf, PAGE_SIZE, "%s\n", Version);

}


static BUS_ATTR(version,S_IRUGO, show_bus_version, NULL);



static int __init my_bus_init(void)

{

int ret;

/*注册总线*/

ret = bus_register(&my_bus_type);

if (ret)

return ret;

/*创建属性文件*/

if (bus_create_file(&my_bus_type,&bus_attr_version))

printk(KERN_NOTICE "Fail to createversion attribute!\n");

/*注册总线设备*/

ret = device_register(&my_bus);

if (ret)

printk(KERN_NOTICE "Fail toregister device:my_bus!\n");

return ret;

}


static void my_bus_exit(void)

{

device_unregister(&my_bus);

bus_unregister(&my_bus_type);

}


module_init(my_bus_init);

module_exit(my_bus_exit);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: