加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_扬州站长网 (https://www.0514zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux的设备驱动模型

发布时间:2022-09-26 15:15:05 所属栏目:Linux 来源:
导读:  只要牵扯到“模型”两个字,都属于高级操作,本文希望一步步的帮你理解驱动模式。1. 设备驱动模型

  在前面写的驱动中,我们发现编写驱动有个固定的模式只有往里面套代码就可以了,它们之间的大
  只要牵扯到“模型”两个字,都属于高级操作,本文希望一步步的帮你理解驱动模式。1. 设备驱动模型
 
  在前面写的驱动中,我们发现编写驱动有个固定的模式只有往里面套代码就可以了,它们之间的大致流程可以总结如下:
 
  因此,在Linux开发驱动,只要能够掌握了这些“套路”,开发一个驱动便不是难事。但是linux 驱动,如果我们将硬件的信息都写进了驱动里了, 根据某个硬件编写的驱动只要修改了一下引脚接口,这个驱动代码就得重新修改才能使用,这显然是不合理的。
 
  那有没有合适的解决方案呢?答案是肯定的:
 
  Linux引入了设备驱动模型分层的概念, 将我们编写的驱动代码分成:设备与驱动。
 
  这样子就构成以下图形中的关系:

  在实际操作上:
 
  当然实际中,同一总线下的设备有很多,驱动也有很多,在总线上管理着两个链表,分别管理着设备和驱动,当我们向系统注册一个驱动时,便会向驱动的管理链表插入我们的新驱动, 同样当我们向系统注册一个设备时,便会向设备的管理链表插入我们的新设备。
 
  在插入的同时总线会执行一个匹配方法对新插入的设备/驱动进行匹配,在匹配成功的时候会调用驱动中的初始化方法,在移除设备或驱动时会调用注销方法。
 
  以上只是设备驱动模型的 机制 。
 
  看到这里是对设备驱动模型的一个粗略的介绍,接下来我们结合代码看一下实现的细节。
 
  2. 总线
 
  总线要干的核心工作就是:关联和匹配(match)
 
  总线数据结构:
 
  总线的类型也有很多种,在内核中使用结构体bus_type来表示总线,如下所示:
 
  /* (内核源码/include/linux/device.h)*/
  struct bus_type {
       const char              *name;
       const struct attribute_group **bus_groups; // 为bus目录创建属性
       const struct attribute_group **dev_groups; // 为device目录创建属性
       const struct attribute_group **drv_groups; // 为driver目录创建属性
   ?
       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);
   ?
       int (*suspend)(struct device *dev, pm_message_t state);
       int (*resume)(struct device *dev);
   ?
       const struct dev_pm_ops *pm;
   ?
       struct subsys_private *p; // 私有数据
  };
  P 指向了一个私有数据结构,我们看一下这个数据结构的定义:
 
  /* (内核源码/include/linux/base.h)*/
  struct subsys_private {
       struct kset subsys;         // bus 目录
       struct kset *devices_kset;  // device 目录
       struct list_head interfaces;
       struct mutex mutex;
   ?
       struct kset *drivers_kset;  // driver 目录
       struct klist klist_devices; // 设备链表头
       struct klist klist_drivers; // 驱动链表头
       struct blocking_notifier_head bus_notifier;
       unsigned int drivers_autoprobe:1;
       struct bus_type *bus;
   ?
       struct kset glue_dirs;
       struct class *class;
  };
  两个关键的结构体,组成bus的基本数据结构:
 
  嵌入式linux驱动程序设计从入门到精通 pdf_linux 驱动_linux cdc wwan驱动架构
 
  总线操作:
 
  总线不是凭空出来的,可以通过以下的函数注册新的总线和注销总线,原型如下:
 
  // 注册/注销总线API(内核源码/drivers/base/bus.c)
  int bus_register(struct bus_type *bus);
  void bus_unregister(struct bus_type *bus);
  3. 设备
 
  设备数据结构:
 
  在内核使用device结构体来描述我们的物理设备,如下所示,
 
  /* device结构体(内核源码/include/linux/device.h)*/
  struct device {
       const char *init_name;
       struct device           *parent;
       struct kobject kobj;
       struct bus_type *bus;
       struct device_driver *driver;
       void            *platform_data;
       void            *driver_data;
       struct device_node      *of_node;
       dev_t                   devt;
       struct class            *class;
       void (*release)(struct device *dev);
       const struct attribute_group **groups;  /* optional groups */
       struct device_private   *p;
  };
  /* driver_private 结构体(内核源码/include/linux/base.h) */
  struct device_private {
       struct klist klist_children;
       struct klist_node knode_parent;
       struct klist_node knode_driver;
       struct klist_node knode_bus; // 链表头,关联总线的driver链表头
       struct list_head deferred_probe;
       struct device *device;
  };
  device的数据结构和bus的数据结构组合如下:
 
  linux 驱动_linux cdc wwan驱动架构_嵌入式linux驱动程序设计从入门到精通 pdf
 
  设备操作:
 
  内核也提供相关的API来注册和注销设备,如下所示:
 
  /* 内核注册/注销设备(内核源码/driver/base/core.c) */
  int device_register(struct device *dev);
  void device_unregister(struct device *dev);
  在讲解总线的时候,我们说过,当成功注册总线时,会在/sys/bus目录下创建对应总线的目录,该目录下有两个子目录,分别是drivers和devices, 我们使用device_register注册的设备从属于某个总线时,该总线的devices目录下便会存在该设备文件。
 
  4. 驱动
 
  前面两小节,已经大致介绍完总线以及设备。设备能否正常工作,取决于驱动。驱动需要告诉内核, 自己可以驱动哪些设备,如何初始化设备。
 
  驱动数据结构:
 
  在内核中,使用device_driver结构体来描述我们的驱动,如下所示:
 
  /* device_driver 结构体(内核源码/include/linux/device.h) */
  struct device_driver {
       const char      *name;
       struct bus_type     *bus;
   ?
       struct module       *owner;
       const char      *mod_name;  /* used for built-in modules */
   ?
       bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
       enum probe_type probe_type;
   ?
       const struct of_device_id   *of_match_table;
       const struct acpi_device_id *acpi_match_table;
   ?
       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);
       const struct attribute_group **groups;
   ?
       const struct dev_pm_ops *pm;
       void (*coredump) (struct device *dev);
   ?
       struct driver_private *p;
  };
  driver_private 结构如下:
 
  完整的数据结构连起来如下:
 
  嵌入式linux驱动程序设计从入门到精通 pdf_linux 驱动_linux cdc wwan驱动架构
 
  驱动操作:
 
  内核提供了driver_register函数以及driver_unregister函数来注册/注销驱动,成功注册的驱动会记录在/sys/bus//drivers目录, 函数原型如下所示:
 
  /* device_driver结构体(内核源码/include/linux/device.h) */
  int driver_register(struct device_driver *drv);
  void driver_unregister(struct device_driver *drv);
  5. 注册总线、设备、驱动
 
  总线、设备、驱动基本的数据结构明白了之后,接下来我们要通过系统提供的接口,完成总线、设备、驱动的构建和关联,大致分步骤如下:
 
  嵌入式linux驱动程序设计从入门到精通 pdf_linux cdc wwan驱动架构_linux 驱动
 
  系统启动之后会调用buses_init函数创建/sys/bus文件目录,这部分系统在开机时已经帮我们准备好了, 接下去就是通过总线注册函数bus_register进行总线注册,注册完总线后在总线的目录下生成devices文件夹和drivers文件夹, 最后分别通过device_register以及driver_register函数注册相对应的设备和驱动。
 
  ①:总线初始化
 
  系统启动之后会调用 buses_init() 函数创建 /sys/bus 这个文件目录,这部分操作在系统开机时已经帮我们准备好了。
 
  ②:总线注册
 
  系统中不一定有你需要的总线,linux提供了一些函数来添加或注销总线;大部分情况下编写linux驱动模块时,内核已经为我们写好了大部分总线驱动,正常情况下我们一般不会去注册一个新的总线。
 
  总线注册和注销的函数原型如下:
 
  int bus_register(struct bus_type *bus);
  以注册xbus总线为例:
 
  ③:设备注册
 
  添加设备,关联硬件相关代码
 
  int device_register(struct device *dev)
  ④:驱动注册
 
  添加驱动,关联软件相关代码
 
  int driver_register(struct device_driver *drv)
 

(编辑:应用网_扬州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!