注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

学习笔记

正确的方法如同学习书法,开始的时候要临摹,临摹好了然后创造自己的风格。

 
 
 

日志

 
 

[Linux笔记]总线、设备和驱动程序  

2011-07-03 17:41:02|  分类: Linux |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

总线

       总线是处理器与一个或者多个设备之间的通道。在设备模型中,所有的设备都通过总线相连。在Linux设备模型中,用bus_type结构表示总线,它的定义包含在<linux/device.h>。

 

struct bus_type {

        const char              * name;

        struct module           * owner;

 

        struct kset             subsys;

        struct kset             drivers;

        struct kset             devices;

        struct klist            klist_devices;

        struct klist            klist_drivers;

 

        struct blocking_notifier_head bus_notifier;

 

        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 (*suspend_late)(struct device * dev, pm_message_t state);

        int (*resume_early)(struct device * dev);

        int (*resume)(struct device * dev);

 

        unsigned int drivers_autoprobe:1;

};

       一个总线包括两个kset,分别代表了总线的驱动程序和插入总线的所有设备。

 

总线的注册

       例如platform总线,位于drivers/base/platform.c,头文位于include/linux/platform_device.h:

struct bus_type platform_bus_type = {

       .name           = "platform",

       .dev_attrs      = platform_dev_attrs,

       .match          = platform_match,

       .uevent         = platform_uevent,

       .suspend        = platform_suspend,

       .suspend_late   = platform_suspend_late,

       .resume_early   = platform_resume_early,

       .resume         = platform_resume,

};

       只有非常少的bus_type成员需要初始化;它们中的大多数都由设备模型核心所控制。但是,我们必须为总线指定名字以及其他一些必要的方法。

       对于新的总线,我们必须调用bus_register进行注册。

error =  bus_register(&platform_bus_type);

if (error)

       return error;

       当然,这个调用可能会失败,因此必须检查它的返回值。如果成功,新的总线子系统将被添加到系统中,可以在sysfs的/sys/bus目录下看到它。然后,我们可以向这个总线添加设备。

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

device_unregister(&platform_bus);

 

总线方法

       在bus_type结构中,定义了一些方法。这些方法允许总线核心作为中间介质,在设备核心与单独的驱动程序之间提供服务。这些总线方法有:

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

       当一个总线上的新设备或者新驱动程序被添加时,会一次或多次调用这个函数。如果指定的驱动程序能够处理指定的设备,该函数返回非零值。必须在总线层上使用该函数,因为那里存在着正确的逻辑。核心内核不知道如何为每个总线类型匹配设备和驱动程序。

int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

       在为用户空间产生热插拔事件前,这个方法允许总线添加环境变量。其参数与kset的uenent方法相同。

       例如,platform总线驱动里有match和uevent的方法:

static int platform_match(struct device * dev, struct device_driver * drv)

{

        struct platform_device *pdev = container_of(dev, struct platform_device, dev);

        return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);

}

 

static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)

{

        struct platform_device  *pdev = to_platform_device(dev);

        add_uevent_var(env, "MODALIAS=platform:%s", pdev->name);

        return 0;

}

 

遍历设备和驱动程序

       为了操作注册到总线的每个设备,可使用:

/*******************************include/linux/device.h*******************************/

int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int (*fn)(struct device *, void *))

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

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

/*******************************include/linux/device.h*******************************/

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相同,只是它的工作对象是驱动程序而已。

       值得注意的是,这两个函数都会在工作期间,持有总线子系统的读或写信号量。因此,当同时使用这两个函数时会发生死锁——它们都会试图去获取同一个信号量。修改总线的操作(比如注销设备)也有同样的问题。因此使用bus_for_each函数时要多加小心。

 

总线属性

       总线属性bus_attribute类型在<linux/device.h>中定义,其代码如下:

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

};

       已经在“默认属性”一节中讨论过attribute了。

       有一个非常便于使用的宏,可在编译的时候创建和初始化bus_attribute结构:
/*******************************include/linux/device.h*******************************/
BUS_ATTR(name,mode,show,store)
       这个宏声明了一个结构,它将bus_attr_作为给定的name的前缀来创建总线的真正名称。
       创建属于总线的任何属性,都需要显式地调用bus_create_file,删除属性用bus_remove_file:
/*******************************include/linux/device.h*******************************/
int bus_create_file(struct bus_type *, struct bus_attribute *)
void bus_remove_file(struct bus_type *, struct bus_attribute *);


设备

       在最底层,Linux系统中的每一个设备都用device结构来表示:

/*******************************include/linux/device.h*******************************/

struct device {

       struct klist  klist_children;

       struct klist_node knode_parent;  /* node in sibling list */

       struct klist_node knode_driver;

       struct klist_node knode_bus;

       struct device  *parent;

 

       struct kobject kobj;

       char bus_id[BUS_ID_SIZE]; /* position on parent bus */

       struct device_type *type;

       unsigned  is_registered:1;

       unsigned  uevent_suppress:1;

 

       struct semaphore sem; /* semaphore to synchronize calls to its driver. */

 

       struct bus_type * bus;  /* type of bus device is on */

       struct device_driver *driver; /* which driver has allocated this device */

       void  *driver_data; /* data private to the driver */

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

       struct dev_pm_info power;

 

#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;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */

 

       struct list_head dma_pools; /* dma pools (if dma'ble) */

 

       struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */

       /* arch specific additions */

       struct dev_archdata archdata;

 

       spinlock_t  devres_lock;

       struct list_head devres_head;

 

       /* class_device migration path */

       struct list_head node;

       struct class  *class;

       dev_t   devt;  /* dev_t, creates the sysfs "dev" */

       struct attribute_group **groups; /* optional groups */

 

       void (*release)(struct device * dev);

};

       重要的成员如下 :

struct device *parent

       设备的父设备——指的是该设备所属的设备。如果parent为NUll,表示该设备是顶层设备,但这种情况很少出现。

struct kobject kobj;

       kobject用来表示该设备并把它连接到结构体系中去。注意,作为一个通用的法则,device->kobj->parent与&device->parent->kobj是相同的。

char    bus_id[BUS_ID_SIZE]

       在总线上唯一标识该设备的字符串。

struct bus_type *bus

       标识了该设备连接在何种类型的总线上。

struct device_driver *driver

       管理该设备的驱动程序

struct *driver_data 

       由设备驱动程序使用的私有数据成员

void (*release)(struct device *dev)

       当指向设备的最后一个引用被删除时,内核调用该函数;它从内嵌的kobject的release函数中被调用。所有向核心注册的device结构都必须有一个release方法,否则,内核将会打印错误信息。

/*******************************include/linux/platform_device.h*******************************/
struct platform_device {
       const char      * name;
       int             id;
       struct device   dev;
       u32             num_resources;
       struct resource * resource;
};
#define to_platform_device(x) container_of((x), struct platform_device, dev)

/*******************************include/linux/ioport.h*******************************/
struct resource {
       resource_size_t start;
       resource_size_t end;
       const char *name;
       unsigned long flags;
       struct resource *parent, *sibling, *child;
};
/*******************************include/linux/types.h*******************************/
#ifdef CONFIG_RESOURCES_64BIT
       typedef u64 resource_size_t;
#else
       typedef u32 resource_size_t;

设备注册
       常用的注册和注销函数是:
int device_register(struct device * dev);
void device_unregister(struct device * dev);

struct device platform_bus = {
       .bus_id         = "platform",
};

int error;
error = device_register(&platform_bus);
       if (error)
return error;
       完成这个调用后,我们就可以在sysfs中的/sys/devices目录中看到它。任何添加到该总线的设备都会在/sys/devices/platform/中显示。

设备属性
       sysfs中的设备入口可以有属性,相关的结构是:
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了。

       有一个非常便于使用的宏,可在编译的时候创建和初始化bus_attribute结构:
/*******************************include/linux/device.h*******************************/
DEVICE_ATTR(name,mode,show,store)
       这个宏声明了一个结构,它将device_attr_作为给定的name的前缀来创建总线的真正名称。
       创建属于总线的任何属性,都需要显式地调用bus_create_file,删除属性用bus_remove_file:
/*******************************include/linux/device.h*******************************/
int device_create_file(struct device * device, struct device_attribute * entry)
void device_remove_file(struct device * device, struct device_attribute * attr)
       bus_type结构中的dev_attrs域,指向一个为每个加入总线的设备建立的默认属性链表。

设备驱动程序
       设备驱动追踪系统所知道的设备。进行追踪的主要原因是能让驱动核心协调驱动和新设备。一旦驱动程序是系统中的已知对象,就可能完成大量的工作。例如,设备驱动程序可以导出那些独立于特定设备的信息和配置变量等。
/*******************************include/linux/device.h*******************************/
struct device_driver {
       const char              * name;
       struct bus_type         * bus;

       struct kobject          kobj;
       struct klist            klist_devices;
       struct klist_node       knode_bus;

       struct module           * owner;
       const char              * mod_name;     /* used for built-in modules */
       struct module_kobject   * mkobj;

       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);
};
       其中,name是驱动程序的名字(它将在sysfs中显示);bus是该驱动程序所操作的总线类型;kobj是必需的kobject;devices是当前驱动程序能操作的设备链表。probe是用来查询特定设备是否存在的函数(以及这个驱动程序能否操作它);当设备从系统中删除时,要调用remove函数;在关机的时候调用shutdown函数关闭设备。
       操作device_driver结构的函数是:
/*******************************include/linux/device.h*******************************/
int driver_register(struct device_driver * drv);
void driver_unregister(struct device_driver * drv);

       例如platform_driver的声明与注册:
struct platform_driver {
       int (*probe)(struct platform_device *);
       int (*remove)(struct platform_device *);
       void (*shutdown)(struct platform_device *);
       int (*suspend)(struct platform_device *, pm_message_t state);
       int (*suspend_late)(struct platform_device *, pm_message_t state);
       int (*resume_early)(struct platform_device *);
       int (*resume)(struct platform_device *);
       struct device_driver driver;
};

int platform_driver_register(struct platform_driver *drv)
{
       drv->driver.bus = &platform_bus_type;
       if (drv->probe)
              drv->driver.probe = platform_drv_probe;
       if (drv->remove)
              drv->driver.remove = platform_drv_remove;
       if (drv->shutdown)
              drv->driver.shutdown = platform_drv_shutdown;
       f (drv->suspend)
              drv->driver.suspend = platform_drv_suspend;
       if (drv->resume)
              drv->driver.resume = platform_drv_resume;
       return driver_register(&drv->driver);
}

void platform_driver_unregister(struct platform_driver *drv)
{
       driver_unregister(&drv->driver);
}

       常用的属性结构:
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);
};
DRIVER_ATTR(_name,_mode,_show,_store)
       使用下列函数来创建文件:
int driver_create_file(struct device_driver *, struct driver_attribute *);
void driver_remove_file(struct device_driver *, struct driver_attribute *);
       bus_type结构中的drv_attrs域,指向一个为属于该总线的所有驱动建立的默认属性集合。

  评论这张
 
阅读(1414)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018