Chinaunix首页 | 论坛 | 博客
  • 博客访问: 330758
  • 博文数量: 85
  • 博客积分: 3433
  • 博客等级: 中校
  • 技术积分: 844
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-29 01:11
文章分类

全部博文(85)

文章存档

2013年(1)

2012年(12)

2011年(13)

2010年(59)

我的朋友

分类: LINUX

2010-10-31 16:44:33

linux设备模型bus,device,driver

 

  虽然看了上面一篇转载的《使用/sys/访问系统》对总线,驱动,设备都讲得比较细但还是没有太多的感觉。在此就先把自己今天所学回忆一下。

  为了满足新的要求,linux2.6提供了新的设备模型:总线、驱动、设备。基本关系简要的概括如下:
驱动核心可以注册多种类型的总线。
每种总线下面可以挂载许多设备。(通过kset devices)
每种总线下可以用很多设备驱动。(通过包含一个kset drivers)}
每个驱动可以处理一组设备。按照我的理解就是所有的设备都挂载到总线上,当加载驱动时,驱动就支总线上找到自己对应的设备。或者先把驱动加载上,来了一个设备就去总线找驱动。

一:总线

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

(1)bus_type.
struct bus_type {
 const char  * name;//设备名称

 struct subsystem subsys;//代表自身
 struct kset  drivers;   //当前总线的设备驱动集合
 struct kset  devices; //所有设备集合
 struct klist  klist_devices;
 struct klist  klist_drivers;

 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, char **envp,  
      int num_envp, char *buffer, int buffer_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);
};

在后面的实例当中用到了里面的两个成员

1:const char *name;

2: int  (*match)(struct device * dev, struct device_driver * drv);//设备驱动匹配函数

这个匹配函数是很关键的东西,这是建立总线上设备与驱动的桥梁,当一个新的设备或驱动被添加到一个总线上时被调用。

(2)总线的操作:

注册:int bus_register(struct bus_type * bus)

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

(3)总线属性 bus_attribute

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, struct device_driver *driver)
{
 return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}

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

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

       
 ret = bus_register(&my_bus_type);
 if (ret)
  return ret;

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

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

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

bus_unregister(&my_bus_type);

#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 create version attribute!\n");

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

static void my_bus_release(struct device *dev)
{
 printk(KERN_DEBUG "my bus release\n");
}
 
struct device my_bus = {
 .bus_id   = "my_bus0",
 .release  = my_bus_release
};

 
 ret = device_register(&my_bus);
 if (ret)
  printk(KERN_NOTICE "Fail to register device:my_bus!\n");

可是这是有疑问,我还没有找到这个总线设备,和刚才的总线的联系。

源代码:

#include
#include
#include
#include
#include

MODULE_AUTHOR("Larry");
MODULE_LICENSE("Dual BSD/GPL");

static char *Version = "$Revision: 1.9 $";

static int my_match(struct device *dev, struct device_driver *driver)
{
 return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}

static void my_bus_release(struct device *dev)
{
 printk(KERN_DEBUG "my bus release\n");
}
 
struct device my_bus = {
 .bus_id   = "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);



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 create version attribute!\n");
 
 
 ret = device_register(&my_bus);
 if (ret)
  printk(KERN_NOTICE "Fail to register 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:

struct device {

struct device  * parent; //父设备,一般一个bus也对应一个设备。
struct kobject kobj;//代表自身
char bus_id[BUS_ID_SIZE]; 
struct bus_type * bus;  
struct device_driver *driver; 

void  *driver_data; 
 void  *platform_data; 

///更多字段忽略了

};

注册设备:int device_register(sruct device *dev)

注销设备:void device_unregister(struct device *dev);

2:设备属性:

sysfs 中的设备入口可有属性. 相关的结构是:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, char *buf);
ssize_t (*store)(struct device *dev, const char *buf,
size_t count);
};
这些属性结构可在编译时建立, 使用这些宏:
DEVICE_ATTR(name, mode, show, store);
结果结构通过前缀 dev_attr_ 到给定名子上来命名. 属性文件的实际管理使用通常的函数对来处理:
int device_create_file(struct device *device, struct device_attribute *entry);
void device_remove_file(struct device *dev, struct device_attribute *attr);
struct bus_type 的 dev_attrs 成员指向一个缺省的属性列表, 这些属性给添加到总线的每个设备创建.

3:创建设备实例:

创建设备和创建总线基本一样这里只贴出程序:

#include
#include
#include
#include
#include

MODULE_AUTHOR("Larry");
MODULE_LICENSE("Dual BSD/GPL");

extern struct device my_bus;
extern struct bus_type my_bus_type;
static void my_dev_release(struct device *dev)
 
}

struct device my_dev = {
 .bus = &my_bus_type,//与总线接上关系
 .parent = &my_bus,//与总线设备接上关系
 .release = my_dev_release,
};


static ssize_t mydev_show(struct device *dev,struct device_attribute *attr, char *buf)
{
 return sprintf(buf, "%s\n", "This is my device!");
}

static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL);

static int __init my_device_init(void)
{
 int ret = 0;
       
       
 strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);
       
       
 device_register(&my_dev);
  
 
 device_create_file(&my_dev, &dev_attr_dev);
 
 return ret; 

}

static void my_device_exit(void)
{
 device_unregister(&my_dev);
}

module_init(my_device_init);
module_exit(my_device_exit);

三:设备驱动:

在总线上挂载了设备后就要为其准备驱动程序。

驱动程序的实现与设备的实现类似,代码如下:

#include
#include
#include
#include
#include

MODULE_AUTHOR("Larry");
MODULE_LICENSE("Dual BSD/GPL");

extern struct bus_type my_bus_type;

static int my_probe(struct device *dev)
{
    printk("Driver found device which my driver can handle!\n");
    return 0;
}

static int my_remove(struct device *dev)
{
    printk("Driver found device unpluged!\n");
    return 0;
}

struct device_driver my_driver = {
 .name = "my_dev",//对应的设备名称
 .bus = &my_bus_type,//挂载的总线
 .probe = my_probe,//这个函数就是在找到与自己对应的设备时被调用。
        .remove = my_remove,
};


static ssize_t mydriver_show(struct device_driver *driver, char *buf)
{
 return sprintf(buf, "%s\n", "This is my driver!");
}

static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);

static int __init my_driver_init(void)
{
 int ret = 0;
       
       
 driver_register(&my_driver);
  
 
 driver_create_file(&my_driver, &driver_attr_drv);
 
 return ret; 

}

static void my_driver_exit(void)
{
 driver_unregister(&my_driver);
}

module_init(my_driver_init);
module_exit(my_driver_exit);

阅读(1235) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-10-31 19:08:30

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com