设备驱动模型第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);
从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);
相关文章推荐
- 设备驱动模型第1节:平台-设备-驱动2
- 设备驱动模型第1节:平台-设备-驱动3
- Linux Kernel设备驱动模型之 平台设备注册
- 平台总线设备驱动模型——基础知识
- 非常重要----1、字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
- Linux Kernel设备驱动模型之平台驱动
- 平台总线设备驱动模型——代码分析
- 平台总线设备驱动模型——基础知识
- Linux Kernel设备驱动模型之平台设备
- 平台总线设备驱动模型—代码分析
- Linux平台总线驱动设备模型
- 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
- Linux平台总线驱动设备模型
- Linux平台总线驱动设备模型
- linux内核模型---总线,设备,驱动在展讯平台上I2C设备的实例解析
- 平台设备驱动模型与uevent机制
- 嵌入式linux平台设备驱动(设备驱动模型)开发之linux内核中bus总线
- Linux Kernel设备驱动模型之平台设备初始化
- platform_driver平台设备驱动模型
- 嵌入式学习-驱动开发-lesson5-总线设备驱动模型及平台总线驱动