[转载]Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)
2011年09月13日
  可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个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) {
  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_type的指针,在调用完 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;
  }
  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);
  if (parent)
  set_dev_node(dev, dev_to_node(parent));
  error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
  if (error)
  goto Error;
  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);
  if (dev->bus)
  blocking_notifier_call_chain(&dev->bus->p->bus_notifi er,
  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);
  klist_add_tail(&dev->knode_class,
  &dev->class->p->class_devices);
  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;
  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);
  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) : "",
  kobj->kset ? kobject_name(&kobj->kset->kobj) : "");
  error = create_dir(kobj);
  if (error) {
  kobj_kset_leave(kobj);
  kobject_put(parent);
  kobj->parent = NULL;
  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和总线是关系的。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); //将传入的常字符串赋值给//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
  kset->kobj.ktype = &kset_ktype; //将kset_ktyp赋值给kset->kobj.parent
  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)

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

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

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

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

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

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

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

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

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

  6. [转载]Linux内核中的platfor…

    原文地址:[转载]Linux内核中的platform机制作者:joee33     从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_drive ...

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

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

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

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

  9. linux下jtag命令,[转载]LINUX内核调试过程(使用OpenJtag + OpenOCD)

    [转载]LINUX内核调试过程(使用OpenJtag + OpenOCD) (2012-04-12 02:02:27) 标签: 杂谈 [转载]LINUX内核调试过程(使用OpenJtag + Open ...

最新文章

  1. jsp 环境配置记录
  2. 网站前端和后台性能优化1
  3. Mjpeg‐stream移植
  4. 日志规范之slf4j整合JDK14以及Simple的使用
  5. java socket 通信协议_java网络通信(基于TCP协议可靠通信的socket编程)
  6. 《分布式系统:概念与设计》一2.3.2 体系结构模式
  7. 前端学习(623):交换两个变量的值
  8. 的setinterval函数_ES6 极简教程 lt;6gt; 函数扩展
  9. 服务:OracleDBConsoleorcl [Agent process exited abnormally during initialization]
  10. 解决swagger-ui加了Oauth2后无法访问的问题
  11. 计算机网络的拓扑结构三种基本型,2018年自考《计算机网络基本原理》试题库四...
  12. Nginx的主要函数调用关系分析
  13. Java http处理get请求,参数中带特殊字符处理方式
  14. 横河涡街流量计安装参数说明及要求
  15. xps测试数据处理软件,XPS数据处理时 XPSpeaks 分峰拟合
  16. GenericObjectPoolConfig属性
  17. 云授权系统服务器系统连接失败,金蝶云授权码连接不到服务器
  18. 异步电机三相电流滞环矢量控制
  19. 步步为营二 C语言基本概念 和 数据类型
  20. tomcat介绍和TCP传输文件的实现

热门文章

  1. RNN 入门教程 Part 3 – 介绍 BPTT 算法和梯度消失问题
  2. MySQL常用语句(CURD)
  3. 2018年全国职业院校技能大赛中职组“网络空间安全”正式赛卷及评分标准
  4. 串口接收-控制LED闪烁(VerilogVivado)
  5. python三方库ping_Python pingping包_程序模块 - PyPI - Python中文网
  6. 个人电脑也做做宏基因组玩玩
  7. 华为nova7 pro怎么升级鸿蒙,nova7、nova7Pro怎么升级鸿蒙系统?nova7Pro什么时候更新鸿蒙?...
  8. Python图像处理库PIL——resize()函数
  9. 关于头颈胸矫形器的介绍-矫形支具
  10. Web 3D机房,智能数字机房HTML5 WebGL(ThreeJS)匠心打造