返回顶部
首页 > 资讯 > 操作系统 >Linux驱动开发中device model的介绍以及用法
  • 654
分享到

Linux驱动开发中device model的介绍以及用法

2023-06-15 15:06:24 654人浏览 安东尼
摘要

linux驱动开发中device model的介绍以及用法 ,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、什么是 device model?Linux 的

linux驱动开发中device model的介绍以及用法 ,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

一、什么是 device model?

Linux 的 device model 是一个旨在统一管理所有设备驱动的模型。

它犹如一栋规模宏大的建筑:

以 kobject、kset、attribute 等作为基本的建筑材料,

构造出支撑驱动世界的 bus、device、driver 三大组件,

最后通过 sysfs 在各种基础的建筑材料之间建立彼此的互联层次关系,并向外界提供了与建筑内设施进行互动的文件接口。

device model 有什么作用?

可以将 device 的硬件描述 和 driver 进行分离,提升 driver 的代码复用率;

可以对 device 进行分类;

可以遍历 device 和 driver;

可以更好地呈现设备的拓扑关系;

可以通过 sysfs 访问设备;

可以让设备支持热插拔;

...

为了控制篇幅,本文将重点放在与驱动工程师关系最紧密的 bus、device、driver 3 个 组件。

二、device model 的 3 个核心概念

device model 里有 3 个核心的概念:

  • bus

  • device

  • driver

什么是 bus?

bus 代表一种总线,例如 I2C、SPI、Usb 等。

bus 是 Linux 设备驱动模型这种建筑的核心框架,系统中的设备和驱动都依附在其周围。

启动系统后,可以通过 /sys/bus 可以查看系统里当前有哪些总线。

bus 由 struct bus_type 来描述:

struct bus_type {  const char *name;  const char *dev_name;  struct device *dev_root;  const struct attribute_group **bus_groups;  const struct attribute_group **dev_groups;  const struct attribute_group **drv_groups;   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);   ...  struct subsys_private *p;  struct lock_class_key lock_key; };

不需要一下子了解各个成员的作用,用到的时候再说明。

重点关注成员:

  • int (*match)(struct device *dev, struct device_driver *drv),用于判断挂在该 bus  上的设备和驱动是否匹配的回调函数;

  • int (*probe)(struct device *dev),如果 bus 具有探测设备的能力,则会提供该回调函数;

  • struct subsys_private *p,用于管理 bus 上的设备和驱动的数据结构;

注册 bus 的 api

int bus_reGISter(struct bus_type *bus);

什么是 device ?

device 代表了某个设备。

由 struct device 来描述:

struct device {  struct device *parent;  struct device_private *p;  struct kobject kobj;  const char *init_name;  const struct device_type *type;  struct mutex mutex;  struct bus_type *bus;  struct device_driver *driver;  void *platfORM_data;  void *driver_data;     ... }

重点关注成员:

  • struct kobject kobj,内核对象;

  • struct bus_type *bus,设备所在的总线;

  • struct device_driver *driver,和设备绑定在一起的驱动,如果还没绑定,则为 NULL;

注册 device 的 api:

int device_register(struct device *dev)

什么是 driver?

driver 代表了设备驱动。

由 struct device_driver 来描述:

struct device_driver {  const char *name;  struct bus_type *bus;   struct module *owner;  const char *mod_name;    bool suppress_bind_attrs;   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;   struct driver_private *p; };

重点关注成员:

  • struct bus_type *bus;

  • int (*probe) (struct device *dev);

值得一提的是,总线控制器也是一种设备。

例如 I2C 总线控制器这个设备,对应的驱动为 I2C controller driver。

而挂在 I2C 总线上的设备,对应的驱动为 I2C device driver。

注册 driver 的 api:

int driver_register(struct device_driver *drv);

三、bus、device、driver 是如何关联的?

device model 最核心的工作就是维护这三类抽象的实例,以及建立它们之间的关联关系。

bus 如何管理 device 和 driver ?

在 struct bus_type 中有一个 struct subsys_private *p 指针,它负责管理挂在 bus  上的所有设备和驱动,其定义如下:

struct subsys_private {  struct kset subsys;  struct kset *devices_kset;  struct list_head interfaces;  struct mutex mutex;   struct kset *drivers_kset;  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; };

Linux驱动开发中device model的介绍以及用法

两个 klist 成员以链表的形式将该总线上所有的驱动与设备链接到一起。

struct kset *drivers_kset 和 struct kset *devices_kset  是在向系统注册当前新总线时动态生成的容纳该总线上所有驱动与设备的 kset。

在内核里,用 kobject 来表示一个对象,kset 则是 kobject set 的缩写,即内核对象集合

内核用 kobject 和 kset 等数据结构作为原材料,以实现面向对象的方式构建了 device model 的框架。

最后,device 和 device_driver 的 bus 成员也会指向总线:

Linux驱动开发中device model的介绍以及用法

device 和 driver 的绑定

无论是通过 device_register() 注册一个 device 到 bus 上,

还是通过 driver_register() 注册一个 device_driver 到 bus 上,

都会导致 bus 尝试执行 device 和 driver 的绑定行为。

1. device_register() 触发的绑定

注册 device 时:

int device_register(struct device *dev);  device_add(dev);   bus_probe_device(dev);    __device_attach(dev, true);

__device_attach(dev, true) 会为 device 遍历 bus 上的所有 driver:

bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);  driver_match_device(drv, dev);   drv->bus->match ? drv->bus->match(dev, drv) : 1;  driver_probe_device(drv, dev);

driver_match_device() 通过 bus 里的 match 函数来判断是否 device 和 driver 是否匹配,

是否 match 的判断标准一般是通过 of_match_table 或者是 id_table 作为衡量的标准,

以 i2c bus 的 match 函数为例:

static int i2c_device_match(struct device *dev, struct device_driver *drv) {  struct i2c_client *client = i2c_verify_client(dev);  struct i2c_driver *driver;      if (i2c_of_match_device(drv->of_match_table, client))   return 1;     if (acpi_driver_match_device(dev, drv))   return 1;   driver = to_i2c_driver(drv);     if (i2c_match_id(driver->id_table, client))   return 1;   return 0; }

一旦 match 成功,就会调用 driver_probe_device() 以触发探测设备的行为:

int driver_probe_device(struct device_driver *drv, struct device *dev);  really_probe(dev, drv);   if (dev->bus->probe) {    ret = dev->bus->probe(dev);   } else if (drv->probe) {    ret = drv->probe(dev);   }

如果 bus 具有探测设备的能力的话,例如 pci bus, 则会使用 bus->probe() 探测设备,

否则,使用 driver->probe() 探测设备,driver 的 probe 操作跟具体的硬件设备挂钩。

2. 由 driver_register() 触发的绑定

int driver_register(struct device_driver *drv);  bus_add_driver(drv);   driver_attach(drv);

driver_attach(drv) 会为 driver 遍历 bus 上的所有 device:

int driver_attach(struct device_driver *drv);  bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);   __driver_attach();    driver_match_device(drv, dev);    driver_probe_device(drv, dev);

和 device_register() 一样,最终都会调用 driver_match_device(drv, dev),

进而通过 bus 里的 match 函数来判断是否 device 和 driver 是否匹配。

同样地,一旦 match 成功,就会调用 driver_probe_device() 以触发探测设备的行为,后续的操作和注册设备时是一模一样的。

3. device 和 drvier 的绑定关系

前面说了绑定是如何被触发的,现在来明确一下绑定的具体操作。

对于能成功匹配的 device 和 driver,两者之间的关系是 N 对 1,即可以有多个 device 和 1 个 driver 绑定在一起。

Linux驱动开发中device model的介绍以及用法

对于 device:

其 driver 成员指向已绑定的 device_driver。

int driver_probe_device(struct device_driver *drv, struct device *dev)  really_probe(dev, drv);   dev->driver = drv;

对于 driver:

在 device_driver 里链表 klist_devices 保存了该 driver 上已绑定的所有 device。

int driver_probe_device(struct device_driver *drv, struct device *dev)  really_probe(dev, drv);   driver_bound(dev);    klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

在 /driver/base/driver.c 中,提供了一些 api,用于遍历处理 driver 上绑定的所有 device:

  • int driver_for_each_device()

  • struct device *driver_find_device()

四、bus、device、driver 最简单示例

下面的例子,

构造了一个名为 "simple_bus" 的 bus 实例。

static int sb_match(struct device *dev, struct device_driver *driver) {  return !strncmp(dev_name(dev), driver->name, strlen(driver->name)); }  struct bus_type sb_bus_type = {  .name = "sb",  .match = sb_match, };  static ssize_t version_show(struct bus_type *bus, char *buf) {  return snprintf(buf, PAGE_SIZE, "%s\n", Version); }  static BUS_ATTR_RO(version);  static void sb_dev_release(struct device *dev) { }  int register_sb_device(struct sb_device *sbdev) {     sbdev->dev.bus = &sb_bus_type;  sbdev->dev.release = sb_dev_release;     dev_set_name(&sbdev->dev, sbdev->name);     return device_register(&sbdev->dev); } EXPORT_SYMBOL(register_sb_device);  void unregister_sb_device(struct sb_device *sbdev) {  device_unregister(&sbdev->dev); } EXPORT_SYMBOL(unregister_sb_device);  static int sb_drv_probe(struct device *dev) {  printk(KERN_INFO"sb_drv probe %s\n", dev_name(dev));  return 0; }  int register_sb_driver(struct sb_driver *sdrv) {  sdrv->driver.bus = &sb_bus_type;  sdrv->driver.probe = &sb_drv_probe;  return driver_register(&sdrv->driver); } EXPORT_SYMBOL(register_sb_driver);  void unregister_sb_driver(struct sb_driver *driver) {  driver_unregister(&driver->driver); } EXPORT_SYMBOL(unregister_sb_driver);  static int __init sb_bus_init(void) {  int ret;   ret = bus_register(&sb_bus_type);  if (ret) {   printk(KERN_ERR "Unable to register sb bus, failure was %d\n",ret);   return ret;  }  if (bus_create_file(&sb_bus_type, &bus_attr_version))   printk(KERN_ERR "Unable to create version attribute\n");  return 0; }  static void sb_bus_exit(void) {  bus_unregister(&sb_bus_type); }  module_init(sb_bus_init); module_exit(sb_bus_exit);

xxx_chip.c:注册4个名为 "chipX" 的 device

struct xxx_chip {  char devname[20];  struct sb_device sdev; };  int chipdev_num = 4; struct xxx_chip *chipdev;  static void chip_register_dev(struct xxx_chip *dev, int index) {  snprintf(dev->devname, sizeof(dev->devname), "chip%d", index);  dev->sdev.name = dev->devname;  dev_set_drvdata(&dev->sdev.dev, dev);  register_sb_device(&dev->sdev); }  int chip_init(void) {     int i;      chipdev = kmalloc(chipdev_num*sizeof (struct xxx_chip), GFP_KERNEL);      memset(chipdev, 0, chipdev_num*sizeof (struct xxx_chip));     for (i = 0; i < chipdev_num; i++) {   chip_register_dev(chipdev + i, i);  }      return 0; }  void chip_cleanup(void) {     int i;     for (i = 0; i < chipdev_num; i++) {   unregister_sb_device(&chipdev[i].sdev);  }     kfree(chipdev); }  module_init(chip_init); module_exit(chip_cleanup);

xxx_chip_drv.c:注册1个名为 "chip" 的 driver

static struct sb_driver sculld_driver = {  .driver = {   .name = "chip",  }, };  int xxx_chipdrv_init(void) {     return register_sb_driver(&sculld_driver); }  void xxx_chipdrv_cleanup(void) {     unregister_sb_driver(&sculld_driver); }  module_init(xxx_chipdrv_init); module_exit(xxx_chipdrv_cleanup);

运行效果:

root@buildroot:~# insmod simple_bus.ko  root@buildroot:~# tree /sys/bus/sb /sys/bus/sb ├── devices ├── drivers ├── drivers_autoprobe ├── drivers_probe ├── uevent └── version  root@buildroot:~# insmod xxx_chip.ko  root@buildroot:~# tree /sys/bus/sb /sys/bus/sb ├── devices │   ├── chip0 -> ../../../devices/chip0 │   ├── chip1 -> ../../../devices/chip1 │   ├── chip2 -> ../../../devices/chip2 │   └── chip3 -> ../../../devices/chip3 ├── drivers ├── drivers_autoprobe ├── drivers_probe ├── uevent └── version  root@buildroot:~# insmod xxx_chip_drv.ko sb_drv probe chip0 sb_drv probe chip1 sb_drv probe chip2 sb_drv probe chip3  root@buildroot:~# tree /sys/bus/sb /sys/bus/sb ├── devices │   ├── chip0 -> ../../../devices/chip0 │   ├── chip1 -> ../../../devices/chip1 │   ├── chip2 -> ../../../devices/chip2 │   └── chip3 -> ../../../devices/chip3 ├── drivers │   └── chip │       ├── bind │       ├── chip0 -> ../../../../devices/chip0 │       ├── chip1 -> ../../../../devices/chip1 │       ├── chip2 -> ../../../../devices/chip2 │       ├── chip3 -> ../../../../devices/chip3 │       ├── uevent │       └── unbind ├── drivers_autoprobe ├── drivers_probe ├── uevent └── version

通过打印信息可知,device 和 driver 经由 bus 判断是否 match 之后,执行了 driver 的 probe()  函数,符合我们前面的分析。

五、小结

Linux 的 device model 是个非常复杂的系统。

从一个比较高的层次来看,主要由总线、设备和驱动构成。

内核为了实现这些组件间的相关关系,定义了 kobject 和 kset 这样的基础底层数据结构,然后通过 sysfs  文件系统向用户空间展示发生在内核空间中的各组件间的互联层次关系,并以文件系统接口的方式为用户空间程序提供了访问内核对象属性信息的简易方法。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注编程网操作系统频道,感谢您对编程网的支持。

--结束END--

本文标题: Linux驱动开发中device model的介绍以及用法

本文链接: https://lsjlt.com/news/280585.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Linux驱动开发中device model的介绍以及用法
    Linux驱动开发中device model的介绍以及用法 ,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、什么是 device modelLinux 的 d...
    99+
    2023-06-15
  • Vue.js中v-model指令的用法介绍
    一、v-model指令 v-model 用来获取表单元素的值。对应input输入框获取的是输入的值,单选按钮、复选框、下拉框获取的是选择的状态。 使用v-model可以在表单控件或者...
    99+
    2024-04-02
  • 由Google开发的开源JavaScript引擎V8的介绍以及使用方法
    由Google开发的开源JavaScript引擎V8的介绍以及使用方法,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。V8 是由 Google ...
    99+
    2024-04-02
  • Linux中Nmap的用法及参数介绍
    这篇文章主要讲解了“Linux中Nmap的用法及参数介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Linux中Nmap的用法及参数介绍”吧!扫描器是一种能够自动检测主机安全性弱点的程序。...
    99+
    2023-06-13
  • SpringBoot整合JWT的介绍以及用法
    本篇内容主要讲解“SpringBoot整合JWT的介绍以及用法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringBoot整合JWT的介绍以及用法”吧!目录JWTJWT登录执行流程图为什么...
    99+
    2023-06-20
  • JavaScript中null的介绍以及用法是怎样的
    本篇文章给大家分享的是有关JavaScript中null的介绍以及用法是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。JavaScrip...
    99+
    2024-04-02
  • mysql中的MMM的介绍以及部署方法
    这期内容当中小编将会给大家带来有关mysql中的MMM的介绍以及部署方法,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、 MMM 简介:MMM 即 Multi-Mast...
    99+
    2024-04-02
  • MySQL高可用集群的介绍以及用法
    MySQL高可用集群的介绍以及用法,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1 MMM概述1.1 关于mysql-mmmM...
    99+
    2024-04-02
  • mongodb的介绍以及安装用法是怎样的
    本篇文章为大家展示了mongodb的介绍以及安装方法,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、mongodb的介绍1.什么是MongoDBMongoDB是由...
    99+
    2024-04-02
  • python中f字符串以及其常见用法介绍
    本篇文章和大家了解一下python中f字符串以及其常见用法介绍。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。前言有个小伙伴看到之前写的Faker库相关文章中出现了f'{}' 这个东东,他问我是啥意思?实际...
    99+
    2023-07-06
  • Mysql中的触发器定义及语法介绍
    目录1.定义:2.语法:3.删除触发器4.查询触发器5.触发器类型OLD和NEW的使用1.定义:   触发器和存储过程相似,都是嵌入到 mysql 中的一段程序。触发器是由事件来触发某个操作。当数据库执行这些事件时,就会...
    99+
    2022-06-23
    mysql触发器
  • Android 游戏开发之Canvas画布的介绍及方法
    Canvas,在英语中,这个单词的意思是帆布。在Android中,则把Canvas当做画布,只要我们借助设置好的画笔(Paint类)就可以在画布上绘制我们想要的任何东西;另外它...
    99+
    2022-06-06
    方法 游戏开发 canvas Android
  • python中string模块各属性以及函数的用法介绍
    任何语言都离不开字符,那就会涉及对字符的操作,尤其是脚本语言更是频繁,不管是生产环境还是面试考验都要面对字符串的操作。 python的字符串操作通过2部分的方法函数基本上就可以解决所有的字符串操作需求: &...
    99+
    2022-06-04
    函数 模块 属性
  • Python中关于函数的具体用法范例以及介绍
    目录1.函数的介绍2.函数的定义和调用3.函数的参数4.参数的分类4.1.位置参数4.2.关键字参数4.3.缺省参数4.4.不定长参数1.不定长参数*args2.不定长参数* * k...
    99+
    2024-04-02
  • linux中sed命令的用法介绍
    本篇内容介绍了“linux中sed命令的用法介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!sed命令行格式为:  &...
    99+
    2023-06-13
  • Python中,hasattr()函数的详细介绍以及使用
    引言 在Python中,hasattr()函数是一种重要的工具,用于判断对象是否具有指定的属性或方法。通过使用hasattr()函数,我们可以在运行时动态地检查对象的能力,提高代码的灵活性和可维护性。...
    99+
    2023-09-02
    python 开发语言
  • K8S中五种控制器的介绍以及使用
    目录k8s的控制器类型pod与控制器之间的关系Deployment(无状态化应用)状态与无状态化对特点Deployment的更新Deployment的回滚CronJob控制器总结k8...
    99+
    2024-04-02
  • Java中super关键字介绍以及super()的使用
    目录1、super的使用:2、先复习一下this关键字的使用。3、super关键字:总结:1、super的使用: (1)super是一个关键字。 (2)super和this很类似,我...
    99+
    2024-04-02
  • CSS中margin属性的介绍及用法
    这篇文章主要介绍“CSS中margin属性的介绍及用法”,在日常操作中,相信很多人在CSS中margin属性的介绍及用法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”CSS中...
    99+
    2024-04-02
  • Linux的内存中Swap和Cache以及Buffer的介绍是怎样的
    Linux的内存中Swap和Cache以及Buffer的介绍是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。 total:总内存大小。 used:已经...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作