Linux设备模型(二)

2540阅读 0评论2013-03-02 星闪夜空
分类:LINUX

Linux设备模型()

一、总线

Linux设备模型中,用bus_type结构表示总线,其结构如下:

struct bus_type {

    const char        * name;/*总线类型名称*/

    struct module        * owner;/*指向模块的指针(如果有), 此模块负责操作这个总线*/

    struct kset        subsys;/*与该总线相关的子系统*/

    struct kset        drivers;/*总线驱动程序的kset*/

    struct kset        devices;/* 挂在该总线的所有设备的kset*/

    struct klist        klist_devices;/*挂接在该总线的设备链表*/

    struct klist        klist_drivers;/*与该总线相关的驱动程序链表*/

    struct bus_attribute    * bus_attrs; /*总线属性*/

    struct device_attribute * dev_attrs; /*设备属性,指向为每个加入总线的设备建立的默认属性链表*/

    struct driver_attribute * drv_attrs; /*驱动程序属性*/

    struct bus_attribute drivers_autoprobe_attr;/*驱动自动探测属性*/

    struct bus_attribute drivers_probe_attr;/*驱动探测属性*/

}; 

每个总线都有自己的子系统,然而这些子系统并不在sysfs中的顶层,相反,我们会在总线子系统下面发现它们。一个总线包含两个kset,分别代表了总线的驱动程序和插入总线的所有设备。

  1、总线的注册

对于新的总线,我们必须调用bus_register进行注册,如下所示:

ret  = bus_register(&ldd_bus_type);

if(ret)

    return ret;

如果成功,新的总线子系统将被添加到系统中,可以在sysfs/sys/bus目录下看到它,然后我们就可以向这个总线添加设备。

当有必要从系统中删除一个总线的时候(比如相应的模块被删除),要使用bus_unregister函数:

void bus_unregister(struct bus_type * bus);

  2、总线方法

int (*match)(struct device * dev, struct device_driver * drv);

当一个新设备或者驱动被添加到这个总线时,该方法被调用,用于判断指定的驱动程序是否能处理指定的设备,若可以则返回非零值。

int (*uevent)(struct device *dev, char **envp,

  int num_envp, char *buffer, int buffer_size);

在为用户空间产生热插拔事件之前,这个方法允许总线添加环境变量。

  3、对设备和驱动程序的迭代

    int bus_for_each_dev(struct bus_type * bus, struct device * start,

     void * data, int (*fn)(struct device *, void *));

该函数迭代了在总线的每个设备,将相关的device结构传递给fn,同时传递data值。如果startNULL,将从总线上的第一个设备开始迭代,否则将从start后的第一个设备开始迭代。如果fn返回一个非零值,将停止迭代,而这个值也会从bus_for_each_dev返回。

相似的函数也可用于驱动程序的迭代上:

int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,

     void * data, int (*fn)(struct device_driver *, void *));

该函数的工作方式与bus_for_each_dev相同,只是它的工作对象是驱动程序而已。

注意:这两个函数在工作期间,都会拥有总线子系统的读取者/写入者信号量,因此,同时使用这两个函数会发生死锁。

  4、总线属性

    struct bus_attribute {

struct attribute attr;

ssize_t (*show)(struct bus_type *, char * buf);

ssize_t (*store)(struct bus_type *, const char * buf, size_t count);

};

有一个非常便于使用的宏,可在编译时刻创建和初始化bus_attribute结构:

BUS_ATTR(name,mode,show,store);

这个宏声明了一个结构,它将bus_attr_作为给定的name的前缀来创建总线的真正名称。

创建属于总线的任何属性,都需要显式调用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);

二、设备

    struct device {

    struct device        *parent;/* 设备的 "设备,该设备所属的设备,通常一个父设备是某种总线或者主控制器如果 parent 是 NULL, 则该设备是顶层设备,较少见 */

    struct kobject kobj;/*代表该设备并将其连接到结构体系中的 kobject; 注意:作为通用的规则, device->kobj->parent 应等于 device->parent->kobj*/

    char    bus_id[BUS_ID_SIZE];/*在总线上唯一标识该设备的字符串;例如: PCI 设备使用标准的 PCI ID 格式包含:总线设备和功能号.*/

    struct bus_type    * bus;     /*标识该设备连接在何种类型的总线上*/

    struct device_driver *driver;    /*管理该设备的驱动程序*/

    void        *driver_data;    /*该设备驱动使用的私有数据成员*/

    void        *platform_data;    /* Platform specific data, device core doesn't touch it */

   

    void    (*release)(struct device * dev);/*当这个设备的最后引用被删除时,内核调用该方法它从被嵌入的 kobject 的 release 方法中调用。所有注册到核心的设备结构必须有一个 release 方法否则内核将打印错误信息*/

};

注意:在注册device结构前,至少要设置parentbus_idbusrelease成员。

  1、设备注册

常用的注册和注销函数是:

int device_register(struct device *dev);

void device_unregister(struct device * dev);

注意:一个实际的总线是一个设备,因此必须被单独注册。

  2、设备属性

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

};

我们可以在编译时刻用下面的宏构造这些attribute结构:

DEVICE_ATTR(name,mode,show,store);

该结构将dev_attr_作为指定名字的前缀来构造结构的名称。

用下面的两个函数实现对属性文件的实际处理:

int device_create_file(struct device * dev, struct device_attribute * attr);

void device_remove_file(struct device * dev, struct device_attribute * attr);

  3、设备结构的嵌入

单纯用device结构表示的设备是很少见的,而是通过把device内嵌在设备的高层表示之中。如下所示:

struct ldd_device {

 char *name;

 struct ldd_driver *driver;

 struct device dev; 

}; 

#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);

三、设备驱动程序

    struct device_driver {

    const char        * name;  /*驱动程序的名字(sysfs中出现)*/

    struct bus_type        * bus;/*驱动程序所操作的总线类型*/

    struct kobject        kobj;/*内嵌的kobject对象*/

    struct klist        klist_devices;/*当前驱动程序能操作的设备链表*/

    struct klist_node    knode_bus;

    int    (*probe)    (struct device * dev);/*查询一个特定设备是否存在及驱动是否可以使用它的函数*/

    int    (*remove)    (struct device * dev);/*将设备从系统中删除*/

    void    (*shutdown)    (struct device * dev);/*关闭设备*/

};

  1、驱动程序注册

int driver_register(struct device_driver * drv);

void driver_unregister(struct device_driver * drv);

  2、驱动程序的属性结构

struct driver_attribute {

struct attribute attr;

ssize_t (*show)(struct device_driver *, char * buf);

ssize_t (*store)(struct device_driver *, const char * buf, size_t count);

};

我们可以在编译时刻用下面的宏构造这些attribute结构:

DRIVER_ATTR(name,mode,show,store);

该结构将drv_attr_作为指定名字的前缀来构造结构的名称。

使用下面的函数创建属性文件:

int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);

void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);

  3、驱动程序结构的嵌入

对于大多数驱动程序核心结构来说,device_driver结构通常被包含在高层和总线相关的结构中。如下所示:

struct ldd_driver {

 char *version;

 struct module *module;

 struct device_driver driver;

 struct driver_attribute version_attr; 

}; 

#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);

四、

为了管理类,驱动程序核心导出了一些接口,其目的之一是提供包含设备号的属性以便自动创建设备节点,所以udev的使用离不开类。 类函数和结构与设备模型的其他部分遵循相同的模式。

  1、管理类

class结构的一个实例来定义类:

struct class {

    const char        * name;/*每个类需要一个唯一的名字它将显示在/sys/class*/

    struct module        * owner;

    struct class_attribute        * class_attrs;/* 指向类属性的指针(以NULL结尾) */

    struct class_device_attribute    * class_dev_attrs;/* 指向类中每个设备的一组默认属性的指针 */

    struct device_attribute        * dev_attrs;

    int    (*uevent)(struct class_device *dev, char **envp,

             int num_envp, char *buffer, int buffer_size);/* 类热插拔产生时添加环境变量的函数 */

    int    (*dev_uevent)(struct device *dev, char **envp, int num_envp,

                char *buffer, int buffer_size);/* 类中的设备热插拔时添加环境变量的函数 */

    void    (*release)(struct class_device *dev);/* 把设备从类中删除的函数 */

    void    (*class_release)(struct class *class);/* 删除类本身的函数 */

};

注册函数是:

int class_register(struct class * cls);

void class_unregister(struct class * cls);

处理属性的接口如下:

struct class_attribute {

 struct attribute attr;

 ssize_t (*show)(struct class *cls, char *buf);

 ssize_t (*store)(struct class *cls, const char *buf, size_t count); 

}; 

CLASS_ATTR(_name,_mode,_show,_store); 

int class_create_file(struct class *cls, const struct class_attribute *attr);

void class_remove_file(struct class *cls, const struct class_attribute *attr);

  2、类设备

    struct class_device {

    struct class        * class; /* 指向该设备所属的类,必须*/

    struct device        * dev;  /* 指向此设备相关的 device结构体,可选。若不为NULL,应是一个从类入口到/sys/devices 下相应入口的符号连接,以便用户空间查找设备入口*/

    void            * class_data;/* 私有数据指针 */

    char    class_id[BUS_ID_SIZE];/*包含了要在sysfs中显示的设备名*/

};

常用的注册函数如下:

int class_device_register(struct class_device *cd);

void class_device_unregister(struct class_device *cd);

/*重命名一个已经注册的类设备入口:*/

int class_device_rename(struct class_device *cd, char *new_name); 

/*类设备属性:*/

struct class_device_attribute {

 struct attribute attr;

 ssize_t (*show)(struct class_device *cls, char *buf);

 ssize_t (*store)(struct class_device *cls, const char *buf,

 size_t count);

};

CLASS_DEVICE_ATTR(_name, _mode, _show, _store); 

/*创建和删除除struct class中设备默认属性外的属性*/

int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);

void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);

  3、类接口

    struct class_interface {

    struct list_head    node;

    struct class        *class;/* 指向该接口所属的类*/

    int (*add) (struct class_device *, struct class_interface *);

 

 /*当一个类设备被加入到在 class_interface 结构中指定的类时将调用接口的 add 函数,进行一些设备需要的额外设置,通常是添加更多属性或其他的一些工作*/

    void (*remove)    (struct class_device *, struct class_interface *);/*一个接口的功能是简单明了的当设备从类中删除将调用remove 方法来进行必要的清理*/

    int (*add_dev)     (struct device *, struct class_interface *);

    void (*remove_dev) (struct device *, struct class_interface *);

};

/*注册或注销接口的函数:*/

int class_interface_register(struct class_interface *class_intf);

void class_interface_unregister(struct class_interface *class_intf);

五、Linux设备模型的驱动程序

  1、总线驱动程序(mybus.c)

#include 

#include 

#include   /*bus_typedevicedevice_driver*/

#include   /*printk*/

#include   /*strncmp*/

static char *Version="$ Version:1.0 $";

//用于判断指定的驱动程序是否能处理指定的设备,若可以返回非零值

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 bus_type my_bus_type={

    .name  = "my_bus", //初始化总线的名字

    .match = my_match,

};

EXPORT_SYMBOL(my_bus_type);

/*定义总线设备(一条总线也是个设备,也必须按设备注册)*/

struct device my_bus={

    .bus_id  = "my_bus0",     //初始化总线设备的名字

    .release = my_bus_release,

};

EXPORT_SYMBOL(my_bus);

//显示总线的属性

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

{

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

}

/*在编译时刻创建和初始化bus_attribute结构*/

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); //驱动程序将在/sys/bus/pci中创建一个sysfs目录,其中包含了两个目录:devicesdrivers

   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 "Unable to register my_bus0\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);

MODULE_AUTHOR("chenqi");

MODULE_LICENSE("GPL");

  2、设备驱动程序

#include 

#include 

#include   /*bus_typedevicedevice_driver*/

#include   /*printk*/

#include   /*strncmp*/

/*申明外部变量*/

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 snprintf(buf,PAGE_SIZE,"%s\n","This is mydevice!");

}

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

   /*注册设备*/

   ret=device_register(&my_dev);

   if(ret)

      printk(KERN_NOTICE "Unable to register my_dev\n");

   /*创建设备属性文件*/ 

   ret=device_create_file(&my_dev,&dev_attr_dev);

   if(ret)

      printk(KERN_NOTICE "Unable to create file\n");

   return ret;

}

static void my_device_exit(void)

{

   //卸载设备

   device_unregister(&my_dev);

}

module_init(my_device_init);

module_exit(my_device_exit);

MODULE_AUTHOR("chenqi");

MODULE_LICENSE("GPL");

  3、设备驱动程序

#include 

#include 

#include   /*bus_typedevicedevice_driver*/

#include   /*printk*/

#include   /*strncmp*/

/*申明外部变量*/

extern struct bus_type my_bus_type;

/*当驱动程序与设备匹配成功后,调用该驱动程序的probe函数*/

static int my_probe(struct device *dev)

{

   printk(KERN_NOTICE"Driver found device which my driver can handle!\n");

   return 0;

}

/*将设备从系统中删除后调用*/

static int my_remove(struct device *dev)

{

   printk(KERN_NOTICE"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 mydriver!");

}

/*在编译时刻创建和初始化driver_attribute结构*/

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

static int __init my_driver_init(void)

{

    int ret=0;

   

    /*注册设备驱动*/

    ret=driver_register(&my_driver);

    if(ret)

       printk(KERN_NOTICE"driver register failed:\n");

    

    /*创建驱动程序属性文件*/

    ret=driver_create_file(&my_driver,&driver_attr_drv);

    if(ret)

       printk(KERN_NOTICE"create file failed:\n");

    

    return ret;

}

static void my_driver_exit(void)

{

    /*卸载设备驱动*/

    driver_unregister(&my_driver);

}

module_init(my_driver_init);

module_exit(my_driver_exit);

MODULE_AUTHOR("chenqi");

MODULE_LICENSE("GPL");

上一篇:Linux设备模型(一)
下一篇:Linux的内存映射