q2113435929的个人空间 https://www.eechina.com/space-uid-126185.html [收藏] [复制] [分享] [RSS]

博客

UT4412BV03开发板学习linux设备驱动模型(一)

已有 1333 次阅读2015-10-16 14:22 | 4412, 开发板, Linux, 驱动

UT4412BV03开发板学习linux设备驱动模型(一)

设备驱动模型概述:

设备驱动模型比较复杂,linux系统将设备和驱动归结到设备驱动模型中来管理,设备驱动模型的提出解决了以前编写驱动时没有统一的方法的局面,设备驱动模型给各种驱动程序提供了很多辅助性的函数,这些函数经过严格测试,可以很大程度上提高驱动开发人员的工作效率。

设备驱动模型中用到的几个核心的数据结构,分别是kobjeck,kset,subsystem,这些结构体使设备驱动模型组成了一个层次结构,该层次结构将驱动,设备,总线等联系起来,形成了一个完整的设备驱动。

kobject结构体提供了一个最基本的设备对象管理能力,每一个在内核中注册的kobject对象都对应于sysfs文件中的一个目录。

对应头文件:#include<linux/kobject.h>

对应源文件:kobjeck.c

1.kobject结构体

struct kobject {

 const char *name;                     //kobject结构体的名字,作为一个目录显示在sysfs文件系统中

 struct list_head entry;              //链接下一个kobject结构体

 struct kobject *parent;           //指向本结构体的指针

 struct kset *kset;                    //指向kset结构体的指针

 struct kobj_type *ktype;       //指向kobj_type结构体的指针,设备属性结构体

 struct sysfs_dirent *sd;         //

 struct kref kref;                     //表示该对象的引用计数,

 内核提供增加和减少引用计数的kobject_get();kobject_put();当计数为0时,该对象的所有资源被释放

 unsigned int state_initialized:1;                //表示kobject结构体是否初始化过,1表示初始化过,0表示未初始化过。

 unsigned int state_in_sysfs:1;                  //表示kobject结构体是否已注册到sysfs文件系统中

 unsigned int state_add_uevent_sent:1; //

 unsigned int state_remove_uevent_sent:1; //

 unsigned int uevent_suppress:1

};

Kobject通过kset组织成层次化的结构,kset中包含了kobject集合,像驱动程序一样放在/sys/driver目录下,目录driver是一个kset对象,包含系统中的驱动程序对应的目录,驱动程序的目录由kobject表示。Kset结构体的定义如下:

用到的头文件#include <linux/kobject.h>

struct kset {

 struct list_head     list;   //链接所包含的的kobject对象的链表的首部

 spinlock_t          list_lock;  //维护list链表的自旋锁

 struct kobject    kobj; //内嵌的kobject结构体,说明kset本身也是一个目录

 struct kset_uevent_ops    *uevent_ops;  //热插拔事件

};

 

每一个kobject对象都有一些属性,这些属性由kobj_type结构体表示,最开始,内核开发者考虑将包含在kobject结构体中,后来考虑到同类设备会具有相同的属性,所以将属性隔离开来。由kobj_type表示。

用到的头文件#include <linux/kobject.h>

struct kobj_type {

 void (*release)(struct kobject *kobj);        //释放kobject结构体占用的资源,该函数用驱动开发者去实现

 struct sysfs_ops *sysfs_ops;

 struct attribute **default_attrs;

};

 

用到的头文件#include <linux/sysfs.h>

struct attribute {

 const char          *name;

 struct module    *owner;

 mode_t               mode;

};

struct   sysfs_ops {

 ssize_t (*show)(struct kobject *, struct attribute *,char *);   //写属性操作函数   show函数用于读取一个属性到用户空间,读取成功返回读取到的字节数

 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);   //写属性操作函数, store函数用于将属性写入内核     

};

用到的头文件#include <linux/sysfs.h>

struct sysfs_dirent {

 atomic_t s_count;

 atomic_t s_active;

 struct sysfs_dirent *s_parent;

 struct sysfs_dirent *s_sibling;

 const char *s_name;

 

 union {

  struct sysfs_elem_dir s_dir;

  struct sysfs_elem_symlink s_symlink;

  struct sysfs_elem_attr s_attr;

  struct sysfs_elem_bin_attr s_bin_attr;

 };

 

 unsigned int s_flags;

 ino_t s_ino;

 umode_t s_mode;

 struct iattr *s_iattr;

};

二. 操作kobject结构体用的函数

 

1.kobject结构体初始化函数

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

 char *err_str;

 

 if (!kobj) {

  err_str = "invalid kobject pointer!";

  goto error;

 }

 if (!ktype) {

  err_str = "must have a ktype to be initialized properly!\n";

  goto error;

 }

 if (kobj->state_initialized) {

  /* do not error out as sometimes we can recover */

  printk(KERN_ERR "kobject (%p): tried to init an initialized "

         "object, something is seriously wrong.\n", kobj);

  dump_stack();

 }

 

 kobject_init_internal(kobj);

 kobj->ktype = ktype;

 return;

 

error:

 printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);

 dump_stack();

}

2.初始化kobject结构体内部成员的函数

static void kobject_init_internal(struct kobject *kobj)

{

 if (!kobj)

  return;                                                        //kobj为空指针时退出程序

 kref_init(&kobj->kref);                            //增加kobject引用计数

 INIT_LIST_HEAD(&kobj->entry);            // 初始化kobject链表

 kobj->state_in_sysfs = 0;                      // 表示kobject结构体还没注册到sysfs文件系统中

 kobj->state_add_uevent_sent = 0;    // 始终初始化为0

 kobj->state_remove_uevent_sent = 0;         // 始终初始化为0

 kobj->state_initialized = 1;                         // 表示kobject结构体以始化过

}

3.此函数用于初始化和加载kobject到内核中

  此函数完成两个功能:

  1.调用kobject_init(kobj, ktype);对kobject函数进行初始化

  2.调用kobject_add_varg(kobj, parent, fmt, args);将kobject加入设备驱动模型中

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

    struct kobject *parent, const char *fmt, ...)

{

 va_list args;

 int retval;

 

 kobject_init(kobj, ktype);

 

 va_start(args, fmt);

 retval = kobject_add_varg(kobj, parent, fmt, args);

 va_end(args);

 

 return retval;

}

4.表示增加kobject结构体的引用计数

truct kobject *kobject_get(struct kobject *kobj)

{

 if (kobj)

  kref_get(&kobj->kref);

 return kobj;

}

5.表示减少kobject结构体的引用计数

void kobject_put(struct kobject *kobj)

{

 if (kobj) {

  if (!kobj->state_initialized)

   WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "

          "initialized, yet kobject_put() is being "

          "called.\n", kobject_name(kobj), kobj);

  kref_put(&kobj->kref, kobject_release);

 }

}

6.表示用于设置kobject结构体名字的函数

int kobject_set_name(struct kobject *kobj, const char *fmt, ...)

{

 va_list vargs;

 int retval;

 

 va_start(vargs, fmt);

 retval = kobject_set_name_vargs(kobj, fmt, vargs);

 va_end(vargs);

 

 return retval;

}

int kobject_rename(struct kobject *kobj, const char *new_name);

 

7.将kobject加入设备驱动模型中的函数

static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,

       const char *fmt, va_list vargs)

{

 int retval;

 

 retval = kobject_set_name_vargs(kobj, fmt, vargs);

 if (retval) {

  printk(KERN_ERR "kobject: can not set name properly!\n");

  return retval;

 }

 kobj->parent = parent;

 return kobject_add_internal(kobj);

}

8.用于向设备驱动模型中添加kobjct结构体的函数

static int kobject_add_internal(struct kobject *kobj)

{

 int error = 0;

 struct kobject *parent;

 

 if (!kobj)

  return -ENOENT;

 

 if (!kobj->name || !kobj->name[0]) {

  WARN(1, "kobject: (%p): attempted to be registered with empty "

    "name!\n", kobj);

  return -EINVAL;

 }

 

 parent = kobject_get(kobj->parent);

 

 /* join kset if set, use it as parent if we do not already have one */

 if (kobj->kset) {

  if (!parent)

   parent = kobject_get(&kobj->kset->kobj);

  kobj_kset_join(kobj);

  kobj->parent = parent;

 }

 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",

   kobject_name(kobj), kobj, __func__,

   parent ? kobject_name(parent) : "<NULL>",

   kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

 

 error = create_dir(kobj);

 if (error) {

  kobj_kset_leave(kobj);

  kobject_put(parent);

  kobj->parent = NULL;

 

  /* be noisy on error issues */

  if (error == -EEXIST)

   printk(KERN_ERR "%s failed for %s with "

          "-EEXIST, don't try to register things with "

          "the same name in the same directory.\n",

          __func__, kobject_name(kobj));

  else

   printk(KERN_ERR "%s failed for %s (%d)\n",

          __func__, kobject_name(kobj), error);

  dump_stack();

 } else

  kobj->state_in_sysfs = 1;

 

 return error;

}

9.从设备驱动模型中删除一个kobject对象

void kobject_del(struct kobject *kobj)

{

 if (!kobj)

  return;

 

 sysfs_remove_dir(kobj);                    //从文件系统中删除kobj对象

  obj->state_in_sysfs = 0;                 //表示该kobj没有在sysfs中

 kobj_kset_leave(kobj); //

 kobject_put(kobj->parent);        //减少父目录的引用计数

 kobj->parent = NULL;               //将父目录设为空

}

三. 操作kobj_type结构体用的函数和结构体

struct kobj_type {

 void (*release)(struct kobject *kobj);                 //释放kobject结构体占用的资源,该函数用驱动开发者去实现

 struct sysfs_ops *sysfs_ops;                                //操作下一个属性数组的方法

 struct attribute     **default_attrs;                    // 属性数组

};

以下的函数和结构体在#include <sysfs.h>中

struct attribute {

 const char *name;                              // 属性的名字,对应某个目录下的属性文件

 struct module *owner;                    // 指向拥有该属性的模块

 mode_t mode;                               // 属性读写权限 S_IRUGO:表示可读,S_IWUGO:表示可写,S_IRWXUGO:表示可读可写

};

 

struct   sysfs_ops {

 ssize_t (*show)(struct kobject *, struct attribute *,char *);   //写属性操作函数   show函数用于读取一个属性到用户空间,读取成功返回读取到的字节数

 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);   //写属性操作函数, store函数用于将属性写入内核     

};

10.非默认属性的建立函数

static inline int sysfs_create_file(struct kobject *kobj,

        const struct attribute *attr)

{

 return 0;

}

11.非默认属性的移除函数

static inline void sysfs_remove_file(struct kobject *kobj,

         const struct attribute *attr)

{

}

 

 

四.设备驱动模型(注册kobject到sysfs)

1.设备驱动模型用到的结构体

struct kset {

 struct list_head list;                                                  //链接所包含的kobject对象的链表首部

 spinlock_t list_lock;                                                //维护list链表的自旋锁

 struct kobject kobj;                                              //指向kobject结构体的变量,说明kset也是一个目录

 struct kset_uevent_ops *uevent_ops;                //热插拔事件用到的函数

};

2.热插拔事件用到的函数

一个热插拔事件是从内核空间发送到用户空间的通知,表明系统某些部分的配置已经发生变化,例如,当U盘插入到USB系统时,会产生一个热插拔事件,内核会捕捉到这个事件并调用用户空间的/sbin/hostplug程序,改程序通过加载驱动程序来响应U盘的插入动作。

当内核调用kobject_add()和kobject_del()函数时,会产生热插拔事件。并执行kset_uevent_ops结构体中定义的函数。

struct kset_uevent_ops { 

 int (*filter)(struct kset *kset, struct kobject *kobj); // 通过此函数可以决定是否向用户空间发送事件产生信号,如果filter返回0,表示不产生事件,如果filter返回1,表示产生事件,

 const char *(*name)(struct kset *kset, struct kobject *kobj);  //若用户空间的热插拔程序需要知道只系统的名字时调用此函数,该函数返回给用户空间程序一个字符窜数据

 int (*uevent)(struct kset *kset, struct kobject *kobj,

        struct kobj_uevent_env *env);

};

 

操作kset用到的函数

Kset_init用来初始化kset对象的成员,其中最重要的是初始化kset.kobj成员,使用上面介绍的kobject_init_internal()函数

void kset_init(struct kset *k)

{ 

 kobject_init_internal(&k->kobj);         //初始化kset.kobj成员

 INIT_LIST_HEAD(&k->list);                  //初始化链接kobject的链表

 spin_lock_init(&k->list_lock);            //初始化自旋锁,该锁用于对kobject的添加和删除等操作

}

4.该函数用于完成系统对kset的注册

int kset_register(struct kset *k)

5.该函数用于完成系统对kset的注销

void kset_unregister(struct kset *k)

{

 if (!k)

  return;

 kobject_put(&k->kobj);

}

6.kest的引用计数用到的函数,kset的引用计数由kobj成员来实现。

增加引用计数用到的函数

static inline struct kset *kset_get(struct kset *k)

{

 return k ? to_kset(kobject_get(&k->kobj)) : NULL;

}

7.减少引用计数用到的函数

static inline void kset_put(struct kset *k)

{

 kobject_put(&k->kobj);

}

通过上面的学习,主要是让大家去了解linux设备驱动模型这么一个概念,了解linux设备驱动模型中常用的一些函数,及设备驱动模型的整体构架,以及设备驱动模型在sysfs文件系统中的显示,熟悉设备驱动模型的编程方法,设备驱动模型是linux中比较难得部分,没有几年的编程经验,一般建议初学者简单的了解设备驱动模型,工作几年后再去详细的分析这部分的代码。


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 立即注册

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
返回顶部