转自 http://blog.csdn.net/gqb_driver/article/details/8449588

转自:无为和尚的Linux内核大讲堂系列,并对个别地方进行了补充更正(见标红处)。http://blog.csdn.net/z2007b/article/details/6388753

可能把驱动模型放在第一章讲会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备驱动的基石。大部分人把驱动模型叫做设备模型,但是我查了linux的帮助文档,就是在下载源码路径下的Documentation目录中找到driver-model这个目录,里面包含的文件就是我在本章中所要讲述的东西,也就是我所说的驱动模型。因此本文都会用驱动模型这个术语(如果各位觉得这种叫法是错误的,请在评论中指出,并给出理由,本人非常诚恳的接受各位善意的批评与指正)。驱动模型的核心结构就是我们通常所说的bus、device、device_driver。即总线、设备、设备驱动。首先分析linux内核要有层次的概念,linux从设计上来说是一层套一层的,那么在这一层之下,还有一层由kobject、kobj_type、kset这三者组成,也可以认为其属于驱动模型的范围内,我们可以看到内核对它的描述是:generic kernel object infrastructure。就是通用内核对象基础的意思。我们暂且叫它内核对象层吧。在驱动模型的上层我们可以封装各种子模块子系统,这个以后再做讲解。
 我们首先来看看内核对象层是什么东西,都有些什么功能。在这个分析的过程中请多一点耐心,在这中间需要的仅仅是耐心而已。
 首先给出内核对象层各成员的原型:
 struct kobject {
 const char  *name;
 struct list_head entry;
 struct kobject  *parent;
 struct kset  *kset;
 struct kobj_type *ktype;
 struct sysfs_dirent *sd;
 struct kref  kref;
 unsigned int state_initialized:1;
 unsigned int state_in_sysfs:1;
 unsigned int state_add_uevent_sent:1;
 unsigned int state_remove_uevent_sent:1;
 unsigned int uevent_suppress:1;
};

struct kset {
 struct list_head list;
 spinlock_t list_lock;
 struct kobject kobj;
 struct kset_uevent_ops *uevent_ops;
};

struct kobj_type {
 void (*release)(struct kobject *kobj);
 struct sysfs_ops *sysfs_ops;
 struct attribute **default_attrs;
};
首先从各结构体的成员我们发现这没有三角恋的关系,kobject中包含有kobj_type和kset及自身,kset中包含有kobject,而kobj_type则不包含上面两者,只要是在道上混的兄弟,一眼就可以看出kobject在这场恋爱关系中是占据绝对地位的。
针对这三者,linux内核提供了一些操作这些成员的方法。(位于kobject.c)。
我们挑几个名角讲一讲:
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();
}

我们避重就轻的看一下(前面初始化和合法条件判断在实际的运行当中是很重要的,但是对我们分析来说只要能抓主线,分析我们感兴趣的内容就可以了),可以把这个函数简化:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
 char *err_str;
 kobject_init_internal(kobj);
 kobj->ktype = ktype;
 return;
}
OK,我们看见传入了两个参数,一个是kobject的指针kobj,一个是kobj_type的指针ktype,在调用完kobject_init_internal(kobj)之后,就将传入的ktype赋值给了kobject的ktype成员。我们先来看看ktype到底是何方神圣。在分析ktype之前,我们先要往上跑一层,这一层我们选择int device_register(struct device *dev)这个函数,先给出函数原型:
int device_register(struct device *dev)
{
 device_initialize(dev);
 return device_add(dev);
}
void device_initialize(struct device *dev)
{
 dev->kobj.kset = devices_kset;
 kobject_init(&dev->kobj, &device_ktype);
 INIT_LIST_HEAD(&dev->dma_pools);
 init_MUTEX(&dev->sem);
 spin_lock_init(&dev->devres_lock);
 INIT_LIST_HEAD(&dev->devres_head);
 device_init_wakeup(dev, 0);
 device_pm_init(dev);
 set_dev_node(dev, -1);
}
我们找到我们感兴趣的kobject_init(&dev->kobj, &device_ktype);
我们查看device_ktype的定义:
static struct kobj_type device_ktype = {
 .release = device_release,
 .sysfs_ops = &dev_sysfs_ops,
};
很明显release的作用就是release,至于怎么release我们先不看,下面一个就是sysfs_ops。这个sysfs与用户空间通信的一个接口,我们点击进去查看一下:
static struct sysfs_ops dev_sysfs_ops = {
 .show = dev_attr_show,
 .store = dev_attr_store,
};分别对应了我们读和写sysfs下面节点的两个动作。至于里面干嘛的我们先不管。从上面我们知道,ktype包含了一个sysfs的读写接口,另外包含了一个具有release功能的函数。
回到我们之前的内容:简化版的kobject_init函数:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
 char *err_str;
 kobject_init_internal(kobj);
 kobj->ktype = ktype;
 return;
}
剩下的就是kobject_init_internal(kobj)了。
static void kobject_init_internal(struct kobject *kobj)
{
 if (!kobj)
  return;
 kref_init(&kobj->kref);
 INIT_LIST_HEAD(&kobj->entry);
 kobj->state_in_sysfs = 0;
 kobj->state_add_uevent_sent = 0;
 kobj->state_remove_uevent_sent = 0;
 kobj->state_initialized = 1;
}
这个函数的功能纯粹就是初始化。
从这个初始经我们了解一些更新的东西:
kref_init(&kobj->kref);
这个叫引用计数,kref_init的作用就是将kobjct->kref设为1。
接下来就是初始化kobject->entry这条链表(linux内核的链表是非常重要且比较精妙的,网上相关的好文章也很多,请同志们自行查阅学习)。 
接下来就是一堆的位域。
kobj->state_in_sysfs这个成员正如其名:指明是否使用了sysfs。初始化为0,显然是说:哥现在还没用。
kobj->state_add_uevent_sent、kobj->state_remove_uevent_sent 这两个成员的名命也是非常直观的:指明是否有加载或删除事件。这个是和热插拔相关的,当我们增加一个设备或者删除一个设备的时候,会在合适的时候将此位域置为1。
kobj->state_initialized指明kobject是否有被初始化,这们是唯一个置1的。显然自身被初始化了。
在分析之前有必要说明一下,为了让我们的分析更加简练,我们只会在合适的时候分析结构体的相关成员,不会在没有用到的情况下将成员的作用全都描述出来。
int device_add(struct device *dev)
{
 struct device *parent = NULL;
 struct class_interface *class_intf;
 int error = -EINVAL;

dev = get_device(dev);
 if (!dev)
  goto done;

if (!dev->p) {
  error = device_private_init(dev);
  if (error)
   goto done;
 }

/*
  * for statically allocated devices, which should all be converted
  * some day, we need to initialize the name. We prevent reading back
  * the name, and force the use of dev_name()
  */
 if (dev->init_name) {
  dev_set_name(dev, "%s", dev->init_name);
  dev->init_name = NULL;
 }

if (!dev_name(dev))
  goto name_error;

pr_debug("device: '%s': %s/n", dev_name(dev), __func__);

parent = get_device(dev->parent);
 setup_parent(dev, parent);

/* use parent numa_node */
 if (parent)
  set_dev_node(dev, dev_to_node(parent));

/* first, register with generic layer. */
 /* we require the name to be set before, and pass NULL */
 error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
 if (error)
  goto Error;

/* notify platform of device entry */
 if (platform_notify)
  platform_notify(dev);

error = device_create_file(dev, &uevent_attr);
 if (error)
  goto attrError;

if (MAJOR(dev->devt)) {
  error = device_create_file(dev, &devt_attr);
  if (error)
   goto ueventattrError;

error = device_create_sys_dev_entry(dev);
  if (error)
   goto devtattrError;

devtmpfs_create_node(dev);
 }

error = device_add_class_symlinks(dev);
 if (error)
  goto SymlinkError;
 error = device_add_attrs(dev);
 if (error)
  goto AttrsError;
 error = bus_add_device(dev);
 if (error)
  goto BusError;
 error = dpm_sysfs_add(dev);
 if (error)
  goto DPMError;
 device_pm_add(dev);

/* Notify clients of device addition.  This call must come
  * after dpm_sysf_add() and before kobject_uevent().
  */
 if (dev->bus)
  blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
          BUS_NOTIFY_ADD_DEVICE, dev);

kobject_uevent(&dev->kobj, KOBJ_ADD);
 bus_probe_device(dev);
 if (parent)
  klist_add_tail(&dev->p->knode_parent,
          &parent->p->klist_children);

if (dev->class) {
  mutex_lock(&dev->class->p->class_mutex);
  /* tie the class to the device */
  klist_add_tail(&dev->knode_class,
          &dev->class->p->class_devices);

/* notify any interfaces that the device is here */
  list_for_each_entry(class_intf,
        &dev->class->p->class_interfaces, node)
   if (class_intf->add_dev)
    class_intf->add_dev(dev, class_intf);
  mutex_unlock(&dev->class->p->class_mutex);
 }
done:
 put_device(dev);
 return error;
 DPMError:
 bus_remove_device(dev);
 BusError:
 device_remove_attrs(dev);
 AttrsError:
 device_remove_class_symlinks(dev);
 SymlinkError:
 if (MAJOR(dev->devt))
  device_remove_sys_dev_entry(dev);
 devtattrError:
 if (MAJOR(dev->devt))
  device_remove_file(dev, &devt_attr);
 ueventattrError:
 device_remove_file(dev, &uevent_attr);
 attrError:
 kobject_uevent(&dev->kobj, KOBJ_REMOVE);
 kobject_del(&dev->kobj);
 Error:
 cleanup_device_parent(dev);
 if (parent)
  put_device(parent);
name_error:
 kfree(dev->p);
 dev->p = NULL;
 goto done;
}
当你看到这一大段的时候,是不是感觉很郁闷,我也很郁闷,但是哥很高兴的说:依我们目前的功能,我们只分析kobject_add(&dev->kobj, dev->kobj.parent, NULL)就够了。从人生的低谷瞬间又找回自信其实很简单,就在现在。先给出函数定义:
int kobject_add(struct kobject *kobj, struct kobject *parent,
  const char *fmt, ...)
{
 va_list args;
 int retval;

if (!kobj)
  return -EINVAL;

if (!kobj->state_initialized) {
  printk(KERN_ERR "kobject '%s' (%p): tried to add an "
         "uninitialized object, something is seriously wrong./n",
         kobject_name(kobj), kobj);
  dump_stack();
  return -EINVAL;
 }
 va_start(args, fmt);
 retval = kobject_add_varg(kobj, parent, fmt, args);
 va_end(args);

return retval;
}
这下代码少多了。
我们可以看到核心函数是kobject_add_varg(kobj, parent, fmt, args),其定义如下:
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);
}
其中的kobject_set_name_vargs就是用于设置kobject的名字。
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
      va_list vargs)
{
 const char *old_name = kobj->name;
 char *s;

if (kobj->name && !fmt)
  return 0;

kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
 if (!kobj->name)
  return -ENOMEM;

/* ewww... some of these buggers have '/' in the name ... */
 while ((s = strchr(kobj->name, '/')))
  s[0] = '!';

kfree(old_name);
 return 0;
}
下面就是kobject_add_internal这个函数了,其定义如下:
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;
}
凭着一个程序员的直觉,我们可以看到最重要的是create_dir(kobj);没错,哥猜的很对,就是它了,它和sysfs相关,创建了一个目录,具体这个函数因为牵涉的非常广,我们暂且不做分析。君子报仇,十年不晚,我们看谁笑到最后。在create_dir(kobj)之后我们将kobj->state_in_sysfs =置为1,很亲切吧。撞到老相识的感觉很爽吧,我们在后续分析内核的过程中会撞到越来越多的老相识,并且结识更多的新朋友。连著名歌唱家殷秀梅都知道学习内核的方法:结识新朋友,不忘老朋友……(80后朋友应该都认识,90后的有可能就不认识了)。
接下来我们来分析一下和kset有关的一个函数,那就是先给出函数原型:
struct kset *kset_create_and_add(const char *name,
     struct kset_uevent_ops *uevent_ops,
     struct kobject *parent_kobj)
{
 struct kset *kset;
 int error;

kset = kset_create(name, uevent_ops, parent_kobj);
 if (!kset)
  return NULL;
 error = kset_register(kset);
 if (error) {
  kfree(kset);
  return NULL;
 }
 return kset;
}
和上一节分析kobject一样,为了更好的讲解这个函数我们先要跳到上一层,我们先有必要看一下都有哪些朋友调用了它:int bus_register(struct bus_type *bus)。大名鼎鼎的总线注册。
我们看到bus_register函数中有这样几行代码:

priv->devices_kset = kset_create_and_add("devices", NULL,
       &priv->subsys.kobj);
 if (!priv->devices_kset) {
  retval = -ENOMEM;
  goto bus_devices_fail;
 }

priv->drivers_kset = kset_create_and_add("drivers", NULL,
       &priv->subsys.kobj);
可见总线包括两组kset,devices和drivers。OK。我们以第一段为基础讲解,分别传入了一个常字符串”devices”,一个空指针,一个kobject指针。
函数内部首先调用
static struct kset *kset_create(const char *name,
    struct kset_uevent_ops *uevent_ops,
    struct kobject *parent_kobj)
{
 struct kset *kset;
 int retval;

kset = kzalloc(sizeof(*kset), GFP_KERNEL);             //分配一个kset结构体并初始化
 if (!kset)
  return NULL;
retval = kobject_set_name(&kset->kobj, name); //将传入的常字符串("devices")赋值给kset->kobj->name
 if (retval) {
  kfree(kset);
  return NULL;
 }
 kset->uevent_ops = uevent_ops;       //将uevent_ops
 kset->kobj.parent = parent_kobj;       //将父类kobject指针赋值给kset->kobj.parent

/*
  * The kobject of this kset will have a type of kset_ktype and belong to
  * no kset itself.  That way we can properly free it when it is
  * finished being used.
  */
 kset->kobj.ktype = &kset_ktype;      //将kset_ktyp赋值给kset->kobj.ktype
 kset->kobj.kset = NULL;             //将NULL赋值给kset->kobj.kset

return kset;
}
从上面的注释我们发现kset内嵌的kobject的重要性了。这是kset和kobject的重要关系。有一句话来形容叫我中有你,你中有我。接下来我们将创建好的kset以指针的形式传给kset_register.
int kset_register(struct kset *k)
{
 int err;

if (!k)
  return -EINVAL;

kset_init(k);
 err = kobject_add_internal(&k->kobj);
 if (err)
  return err;
 kobject_uevent(&k->kobj, KOBJ_ADD);
 return 0;
}

void kset_init(struct kset *k)
{
 kobject_init_internal(&k->kobj);
 INIT_LIST_HEAD(&k->list);
 spin_lock_init(&k->list_lock);
}
接下来就是kobject_add_internal(&k->kobj),又撞到老相识了,让我们再次高歌:结识新朋友,不忘老朋友…
好了,kobject,kobj_type,kset的关系我们大概清楚了,下面是我画的一个图用于表示这三者的关系:


好了,先抽根烟吧,下节我们继续分析。

Linux内核大讲堂之设备驱动的基石驱动模型(1)相关推荐

  1. [转载]Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    [转载]Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1) 2011年09月13日 可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备 ...

  2. Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备驱动的基石.大部分人把驱动模型叫做设备模型,但是我查了linux的帮助文档,就是在下载源码路 ...

  3. 【转】Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    原文传送门http://blog.csdn.net/z2007b/archive/2011/05/03/6388753.aspx 可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会 ...

  4. linux 内核 网卡驱动 移植,Linux内核移植步骤_添加DM9000网卡驱动(设备树).docx

    Linux内核移植步骤_添加DM9000网卡驱动(设备树) Linux内核移植步骤2015年05月13日星期三上午 11:05往设备树中添加网卡驱动:1.选平台,指定交叉编译工具链:(1).在Make ...

  5. Linux内核部件分析 设备驱动模型之driver ---mark 详细

    Linux内核部件分析 设备驱动模型之driver 转载:https://www.linuxidc.com/Linux/2011-10/44627p7.htm 上节我们分析设备驱动模型中的device ...

  6. USB总线-Linux内核USB3.0设备控制器之dwc3 gadget驱动初始化过程分析(五)

    1.概述 USB设备控制器(UDC)驱动的框图如下图所示,由三部分组成.第一部分是UDC驱动核心层,在drivers/usb/gadget/udc/core.c文件中实现,该层是一个兼容层,将USB ...

  7. Linux利用platform_driver和设备树实现PWM驱动

    Linux利用platform_driver和设备树实现PWM驱动 字符设备PWM驱动 一.PWM驱动的硬件资源 1.PWM工作原理 2.PWM电路原理 3.PWM内部结构 二.具体代码 1.设备树 ...

  8. linux内核定义注册设备,linux字符型设备驱动 一.注册设备并创建设备文件

    1.字符设备 字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系 Linux内核中: a -- 使用cdev结构体来描述字符设备; b -- 通过其成员dev_t来定义设备号(分为主.次设 ...

  9. Linux内核USB总线--设备控制器驱动框架分析

    正文 1.概述 如下图所示,USB控制器可以呈现出两种不同的状态.USB控制器作为Host时,称为USB主机控制器,使用USB主机控制器驱动.USB控制器作为Device时,称为USB设备控制器,使用 ...

最新文章

  1. android适配右到左布局注意事项
  2. Redis安装异常解决办法
  3. spring+quartz定时任务配置---MethodInvokingJobDetailFactoryBean
  4. 运维少年系列 python and cisco (1)
  5. 解决spark-shell输出日志信息过多
  6. matlab gui 中指定axes窗口画进度条
  7. LeetCode之Max Consecutive Ones
  8. AI 从业者都会用到的 10 个深度学习方法
  9. 《软件项目管理(第二版)》第 6 章——项目质量管理 重点部分总结
  10. Linux GDB Debugging
  11. 艾默生变频器报警PHP,艾默生变频器故障代码
  12. Mac PyCharm下numpy安装
  13. 新的分享之路开启,感谢您的陪伴
  14. 用Python制作一个文件加密器(支持中文)
  15. nginx服务器404错误页面设置完整版
  16. 大于2TB的卷的知识.主要关于windows, EFI,GPT
  17. 码Ubuntu常用命令持续更新
  18. Python实现自定义竖线的线型
  19. 使用理想低通滤波器对图像进行处理显示
  20. 步进电机调试心得体会

热门文章

  1. app架构升级,该如何高效实用Kotlin?学习路线+知识点梳理
  2. 我写了一套SpringBoot+SpringSecurity+Vue权限系统 实战课程,免费分享给CSDN的朋友们
  3. 禅道(一)——简单安装
  4. 小米、百度等巨头加速布局,物联网成争夺新风口
  5. 关于OnOK()、OnCancel()、OnClose()、OnDestroy()之间的区别
  6. 产品经理的思维是什么?
  7. elgamal签名算法c语言,ElGamal算法
  8. 博弈论sg函数——《移旗子游戏》《剪纸游戏》
  9. react input 自动focus 失效问题
  10. C# 模拟温室大棚控制系统