翻译自https://www.linuxjournal.com/article/6717

by Greg Kroah-Hartman

on June 1, 2003

在Linux2.5内核的开发序列中,Pat Mochel创建了一个统一设备驱动模型框架。这个框架由一系列通用的函数和数据结构组成,所有的设备驱动子系统已经开始使用它了。这个框架中的一些通用的数据结构已经被内核中除驱动意外的部分使用。这篇文章讨论驱动模型的各个方面,并且提供一个例子来展示如何将一个特定的设备驱动子系统转化为驱动模型。

总线,设备,类

驱动框架将所有东西分解为总线,设备和类。通过使用这些原语控制驱动和物理及虚拟设备之间的匹配,并且告诉用户所有的东西是怎么互联的。

一个总线可以看成设备连接的对象。总线的例子有PCI,USB,I2C,PCMCIA,SCSI。一般而言只有一个总线驱动控制总线的行为,并且提供一种连接所在总线和所控制总线的桥。桥的一个例子是PCI总线上的USB控制器。它以PCI设备的角色和PCI总线通信,对于内核来说,它就是一个PCI驱动。但是它又控制对某个USB总线所有的访问,和该USB总线上不同的USB设备通信。

内核中的总线通过struct bus_type定义,可以在/linux/device.h中找到。系统中所有的总线都在/sys/bus/下展示给用户。

sidebar:sysfs

设备就是在总线上的物理或者虚拟设备。它们由struct device定义,并且由总线在看到它们出现在系统中的时候由总线创建。通常在特定的时间,只有一个驱动控制一个特定的设备。设备可以在/sys/devices/下面以一个巨大的设备树的形式看到。设备也可以在/sys/bus/BUS_TYPE/devices/目录下以特定类型的设备的形式看到。设备还有对应的驱动来控制怎么和总线上其它设备之间的通信。有些驱动知道怎么和多个总线通信,比如Tulip网络驱动,它可以和PCI和ISA Tulip设备通信。所有的驱动都由struct device_driver定义。它们可以在/sys/bus/BUS_TYPE/drivers/目录下面看到。驱动以一种总线注册,并且export一系列不同类型的设备。总线根据驱动export出的设备,匹配驱动和设备。这个export出来的设备列表同时也暴露给用户空间。所以像/sbin/hotplug工具可以用来匹配驱动没有加载的设备和对应的驱动。可以看我的文章,“Hot Plug”,Linux Journal, 2002年4月期。https://www.linuxjournal.com/article/5604

这里的类并不是面向对象的定义,而是给用户提供的一种功能。它们不是特定总线或者特定设备的东西,但是对于用户来说功能上就是相同类型的设备。类的例子有音频设备,指点设备(比如鼠标,触控板,键盘,操作杆,IDE磁盘和tty设备)。内核总是拥有这些设备,并且一直将它们放在一起(通过major/minor号区间),这样用户就可以方便的访问它们。类在内核中通过struct device_class定义。可以在/sys/class/下面看到。

如果需要对整个驱动模型的描述以及驱动模型下数据结构的介绍可以看完整的文档www.kernel.org/pub/linux/kernel/people/mochel/doc/lca/driver-model-lca2003.tar.gz。它由Pat Mochel于2003年在Linux.Conf.Au Conference发表。

Theory in action

上面的这些描述,在paper上面看上去很不错,但是驱动模型是如何影响内核代码的呢?为了展示这一点,让我们看一看I2C驱动子系统是怎样修改来支持这个驱动模型的。

I2C的代码在内核树代码外面已经存在很长一段时间,在2.0,2.2,2.4内核中,它一直以补丁的形式提供。之前一直有一个课题“使用I2C”by Simon G. Vogl,他是代码的主要作者。LJ, March 1997, www.linuxjournal.com/article/1342。在2.4的开发过程中,许多I2C核心文件和I2C总线驱动被内核接受。在2.5的开发过程中,另外一些驱动文件被添加进来。好在最后所有这些都被合并到了内核中。要一个I2C code的一个好的描述,它支持哪些设备,以及怎么使用它,请访问主要的开发网站secure.netroedge.com/~lm78/index.html。

当加载的时候,I2C总线驱动和I2C总线控制器通信,在/proc/bus下面暴露出很多文件。当一个I2C设备驱动加载,并且绑定到一个I2C设备,它会将相关文件暴露到/proc/sys/dev/sensors目录。通过将设备和总线移动到内核驱动核心,所有这些文件都可以放在/sys下面。

I2C总线

主I2C子系统需要在内核中声明,在驱动核心注册。要事先这一点,以下的代码被添加到drivers/i2c/i2c-core.c:

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{return 1;
}
struct bus_type i2c_bus_type = {.name  = "i2c";.match = i2c_device_match;
};

名字字段告诉我们总线的名字是什么,match字段告诉我们match函数是什么。目前,match函数只是当驱动核心层需要匹配驱动和设备的时候都返回1。这个逻辑会在后续进行修改。

然后,在I2C核心启动代码中,i2c_bus_type通过以下代码片进行注册

bus_register(&i2c_bus_type);

当I2C核心关闭的时候,就要释放这个注册:

unregister(&i2c_bus_type);

当上面的代码跑起来的时候,sysfs下面就创建相应的tree:

$ tree /sys/bus/i2c/
/sys/bus/i2c/
|-- devices
'-- drivers

当一个I2C核心从系统中移除的时候,上面的目录就被移除了。这就是所有创建I2C总线所需要的。

I2C适配器

一个I2C总线本身是很无聊的,现在I2C总线适配器驱动需要修改来注册。要做到这一点,一个struct device变量被添加到struct i2c_adapter结构下面:

struct i2c_adapter {......struct device dev;
};

一个to_i2c_adapter()宏被定义为:

#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)

当驱动核心层需要传递一个指针到struct device里面的时候,I2C核心层通过这个宏来获取一个指向真正i2c_adapter结构的指针。i2c_adapter中的struct device是一个完整的变量。当驱动核心层传递一个指针到struct device的时候,i2c代码可以通过使用to_i2c_adapter宏来获得一个指向真正的i2c_adapter结构。

Siderbar: container_of()

独立的struct i2c_driver变量在不同的i2c总线驱动下面进行声明,比如在i2c-piix4.c有一个struct i2c_driver类型的变量piix4_adapter。当一个piix4适配器被i2c-piix4驱动看到的时候,在i2c_add_adapter()函数中这个变量被传递到I2C核心。

在i2c-piix4.c驱动中,在i2c_add_adapter()函数被执行之前,一个指向PIIX4 adapter的父设备的指针必须要保存在i2c_driver结构里面:

piix4_adapter.dev.parent = &dev->dev;

dev是一个指向struct pci_dev的指针。dev被传递到i2c-piix4驱动的PCI probe函数;PIIX4是一个基于PCI的设备。要将i2c_driver变量和sysfs树联系起来,以下的代码被添加到i2c_add_adapter()函数里面:

/* add the adapter to the driver core.* The parent pointer should already* have been set up.*/
sprintf(adap->dev.bus_id, "i2c-%d", i);
strcpy(adap->dev.name, "i2c controller");
device_register(&adap-dev);

有了这些代码,当一个PIIX4设备被驱动检测到的时候,一个I2C总线树被创建并且连接到控制的PCI设备:

$ tree /sys/devices/pci0/00:07.3/i2c-0
/sys/devices/pci0/00:07.3/i2c-0
|-- name
`-- power

当i2c-piix4驱动卸载的时候,i2c_del_adapter()函数被调用。下面的代码用来清除i2c总线设备:

/* Clean up the sysfs representation */
device_unregister(&adap->dev);

I2C驱动

I2C总线有许多不同的驱动来控制大量的在这个总线上的I2C设备。这些驱动用一个struct i2c_driver结构来声明。在这个结构里面,一个struct device_driver变量用来注册到驱动核心。

struct i2c_driver {......struct device_driver driver;
};

然后一个to_i2c_driver()宏定义如下:

#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)

一个i2c驱动通过调用i2c_add_driver()在i2c核心层注册。为了给i2c驱动添加驱动核心的支持需要添加以下code:

/* Add the driver to the list of * i2c drivers in the driver core */
driver->driver.name = driver->name;
driver->driver.bus  = &i2c_bus_type;
driver->driver.probe = i2c_device_probe;
driver->driver.remove = i2c_device_remove;
retval = driver_register(&driver->driver);
if (reval)return reval;

这一步设置了驱动核心结构。

probe和remove函数被设置为局部的i2c函数,目前它们被声明为:

int i2c_device_probe(struct device *dev)
{return -ENODEV;
}
int i2c_device_remove(struct device *dev)
{return 0;
}

因为还没有I2C设备添加进去。这些函数会在I2C设备添加或者删除的时候调用。这些会在下一篇文章中讨论。

当i2c_add_driver()被调用的时候,驱动以i2c_bus_type的形式注册,并且在sysfs下面展示出来:

$ tree /sys/bus/i2c/
/sys/bus/i2c/
|-- devices
`-- drivers|-- EEPROM READER`-- W83781D sensors

要移除一个i2c驱动,只要调用i2c_del_driver()函数。为了从驱动核心中移除I2C驱动(由driver_register注册):

driver_unregister(&driver->driver);

总结:

我们介绍了新的驱动核心的基础,以及为了帮助更好的了解新的驱动核心对不同子系统的影响,我们介绍了I2C代码的变化。

Linux驱动模型核心,第一部分相关推荐

  1. Linux驱动模型之注册驱动

    前言 驱动的话我们关心几个点: 驱动是怎么添加到总线管理的设备链表上的? 注册驱动后,它是怎么和设备匹配,并最终调用驱动中的probe()函数的? 数据结构 首先看下数据结构: struct devi ...

  2. LINUX驱动模型中bus与platform_bus区别和异同

     LINUX驱动模型中bus与platform_bus区别和异同 首先要明确的是platform_bus是BUS的一个字集,也就是说platform_bus是BUS定义的一个总线类型.可以看到pl ...

  3. 从串口驱动到Linux驱动模型,想转Linux的必会!

    关注.星标公众号,直达精彩内容 ID:技术让梦想更伟大 整理:李肖遥 本文通过对Linux下串口驱动的分析.由最上层的C库.到操作系统系统调用层的封装.再到tty子系统的核心.再到一系列线路规程.再到 ...

  4. 嵌入式驱动解析:从串口驱动到Linux驱动模型

    本文通过对Linux下串口驱动的分析.由最上层的C库.到操作系统系统调用层的封装.再到tty子系统的核心.再到一系列线路规程.再到最底层的硬件操作. 对Linux中的tty子系统进行简要的说明.从理论 ...

  5. 从串口驱动到Linux驱动模型

    大学的时候,帮朋友写的操作系统调研的作业,最近整理过去的文档时候偶然发现,遂作为博客发出来. 从串口驱动到Linux的tty子系统驱动模型简要分析 基于ARM920T核心 Samsung的S3C244 ...

  6. linux 设备驱动 probe,Linux驱动模型Probe解惑

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 问题 首先来回顾下,Linux设备驱动模型中bus.device和driver三者的关系:bus是物理总线的抽象. de ...

  7. linux驱动模型开发——linux platform总线机制讲解与实例开发

    1.概述: 通常在Linux中,把SoC系统中集成的独立外设单元(如:I2C.IIS.RTC.看门狗等)都被当作平台设备来处理. 从Linux2.6起,引入了一套新的驱动管理和注册机制:Platfor ...

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

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

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

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

最新文章

  1. 前 Google 工程师总结的算法面试指南
  2. 【pmcaff】苏杰:产品经理对技术做这些,就完蛋了
  3. OpenCV中Mat属性step,size,step1,elemSize,elemSize1
  4. 《oracle每日一练》oralce数据库的导入导出
  5. cocos creator基础-(十三)cc.Loader使用
  6. 事业编前提下,在一个大单位工作好还是在一个小单位工作好?
  7. 防脱洗发水是个伪命题?8979 条数据告诉你答案!
  8. 趣味Python — 不到20行代码制作一个 “手绘风” 视频
  9. Poker Ⅱ 机械键盘使用说明书 自备
  10. 显色指数(CRI)计算软件-升级版可视化界面这个人大家自己斟酌人品
  11. median filter
  12. 春夏喝绿茶花茶、秋冬喝乌龙普洱红茶
  13. idea使用中项目出现library root
  14. 小米、华为、智汀家庭云:让你实现不同设备之间的互联互通?
  15. Java开发项目常见BUG
  16. 自动化学报latex模板使用说明
  17. 高等数学18讲(19版)7.31(区间再现公式)
  18. 用java实现邮件发送
  19. 替代台湾安格AG6200 AG6201 HDMI转VGA带音频方案+设计电路|CS5213可替代AG6200 AG6201
  20. Flutter实现App功能引导页

热门文章

  1. python-JASN 基本用法
  2. MQTT学习笔记(1)--网络调试助手连接阿里云物联网
  3. 色盲java_[蓝桥杯][算法提高VIP]色盲的民主-题解(Java代码)
  4. 支付宝变身,从首页到定位都改了
  5. 倒计时js代码精确到时分秒
  6. maven与layUI的权限设计
  7. linux上配置达梦ODBC
  8. 分享收集的WebGL 3D学习资源
  9. 静态时序分析—时钟偏斜(Clock Skew:Global Skew与Local Skew)
  10. Blockly 的配置