|
在将lddbus子系统装载到内核和从内核卸载的源码如下:
|
lddbus模块的主要部分就是这些,很简单。因为这只不过是一个虚拟的总线,没有实际的驱动。模块还导出了加载总线设备和总线驱动时需要用到的注册和注 销函数。对于实际的总线,应该还要导出总线的读写例程。
将总线设备和驱动注册函数放在lddbus模块,并导出给其他的总线驱动程序使用,是因为注册总线设备和驱动需要总线结构体的信息,而且这些注册函数对于 所有总线设备和驱动都一样。只要这个总线驱动一加载,其他的总线驱动程序就可以通过调用这些函数注册总线设备和驱动,方便了总线设备驱动的作者,减少了代 码的冗余。
这些注册函数内部调用driver_register、 device_register 和 driver_unregister、device_unregister 这些函数。
|
修改后好模块就可以实现向sysfs文件系统导出信息。
三、分析设备 和驱动注册和注销核心函数,了解一般 的注册、注销过程。
以下也参考了《LDD3》中的PCI驱动分析
(1)设备的注册
在驱动程序中对设备进行注册的核心函数是:
|
在 device_register 函数中, 驱动核心初始化 device 结构体中的许多成员, 向 kobject 核心注册设备的 kobject ( 导致热插拔事件产生), 接着添加设备到其 parent 节点所拥有的设备链表中。此后所有的设备都可通过正确的顺序被访问, 并知道其位于设备层次中的哪一点。
设备接着被添加到总线相关的设备链表(包含了所有向总线注册的设备)中。接着驱动核心遍历这个链表, 为每个驱动程序调用该总线的match函数。
match函数 主要是将驱动核心传递给它的 struct device 和 struct device_driver转换为特定的设备、驱动结构体 ,检查设备的特定信息, 以确定驱动程序是否支持该设备:
若不支持, 函数返回 0 给驱动核心,这样驱动核心移向链表中的下一个驱动;
若支持, 函数返回 1 给驱动核心,使驱动核心设置struct device 中的 driver 指针指向这个驱动, 并调用在 struct device_driver 中指定的 probe 函数.
probe 函数 (又一次) 将驱动核心传递给它的 struct device 和 struct device_driver转换为特定的设备、驱动结构体 ,并再次验证这个驱动是否支持这个设备, 递增设备的引用计数, 接着调用总线驱动的 probe 函数:
若总线 probe 函数认为它不能处理这个设备,则返回一个负的错误值给驱动核心,这样驱动核心移向链表中的下一个设备;
若这个 probe 函数能够处理这个设备, 则初始化这个设备, 并返回 0 给驱动核心。这会使驱动核心添加设备到与这个特定驱动所绑定的设备链表中, 并在 /sys/bus的总线目录中的 drivers 目录中创建一个到这个设备符号链接(指向/sys/devices中的设备),使用户准确知道哪个驱动被绑定到了哪个设备。
|
(3)驱动程序的注册
在驱动程序中对驱动程序进行注册的核心函数是:
|
driver_register 函数初始化 struct device_driver 结构体(包括 一个设备链表及其增删对象函数 和 一个自旋锁), 然后调用 bus_add_driver 函数。
bus_add_driver进行如下操作:
(1)查找驱动关联的总线:若未找到, 立刻返回负的错误值;
(2)根据驱动的名字和关联的总线,创建驱动的 sysfs 目录;
(3) 获取总线的内部锁, 遍历所有的已经注册到总线的设备,为这些设备调用match函数, 若成功,进行剩下的绑定过程。(类似注册设备,不再赘述)
(4)驱动程序的注销
删除驱动程序是一个简单的过程,在驱动程序中对驱动程序进行注销的核心函数是:
|
在所有的设备与驱动程序脱离后,通常在驱动程序中会使用下面两个函数: down(&drv->unload_sem); 它们在函数返回给调用者之前完成。这样做是因为在安全返回前,代码需要等待所有的对这个驱动的引用计数为 0。 |
四、ARM9 开发板实验
|
热插拔
有 2 个不同角度来看待热插拔:
从内核角度看 , 热插拔是在硬件、内核和内核驱动之间的交互。
从用户角度看 , 热插拔是内核和用户空间之间,通过调用用户空间程序(如hotplug、udev 和 mdev)的交互。 当需要通知用户内核发生了某种热插拔事件时,内核才调用这个用户空间程序。
现在的计算机系统,要求 Linux 内核能够在硬件从系统中增删时,可靠稳定地运行。这就对设备驱动作者增加了压力,因为在他们必须处理一个毫无征兆地突然出现或消失的设备。
热插拔工具
当 用户向系统添加或删除设备时,内核会产生一个热插拔事件,并在 /proc/sys/kernel/hotplug 文件里查找处理设备连接的用户空间程序。这个用户空间程序主要有
hotplug: 这 个程序是一个典型的 bash 脚本 ,只传递执行权给一 系列位于 /etc/hot-plug.d/ 目录树的程序。hotplug 脚本搜索所有的有 .hotplug 后缀的可能对这个事件进行处理的程序并调用它们, 并传递给它们许多不同的已经被内核设置的环境变量。(基本已被淘汰,具体内容请参阅《LDD3》)
《 UDEV Primer 》(英文),地址:
http://webpages.charter.net/decibelshelp/LinuxHelp_UDEVPrimer.html
《udev规则编写》(luofuchong翻译),地址:
http://www.cnitblog.com/luofuchong/archive/2007/12/18/37831.html
《什么是udev》地址:
http://blog.csdn.net/steganography/archive/2006/04/10/657620.aspx
《udev-FAQ 中文翻译》地址:
http://gnawux.bokee.com/3225765.html
《udev轻松上路》地址:
http://www.blog.edu.cn/user1/3313/archives/2007/1635169.shtml
《Udev (简体中文)》地址:
http://wiki.archlinux.org/index.php/Udev_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87 )
Udev官方主页:
http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
下载地址:
http://www.kernel.org/pub/linux/utils/kernel/hotplug/
在《LFS》中也有介绍udev的使用,很值得参考!下载地址:
http://lfs.osuosl.org/lfs/downloads/stable/
|
因为hotplug 现在也在被慢慢地淘汰,udev不再依 赖hotplug了,所以这里不再介绍;
udev 较mdev复杂,不太适合嵌入式使用。(本人也有做udev的实验,交叉编译是通过了,但是 使用上有问题,没有实现其功能。也许是我的文件系统没做好,以后有时间再研究和写记录。有成功高人的通知一声,交流一下经验。^_^谢谢!);
mdev 简单易用,比较适合嵌入式系统,实验成功。以下详细介绍mdev的使用。
源码的bug (个人意见): 由于 mdev是通过判断“dev”属性文件的路径字符串中的第6个字符是否为‘c’,来决定设备是字符设备还是块设备【type = (path[5] == 'c' ? S_IFCHR : S_IFBLK ); 例如path = "/sys/class/ldd/sculld*/"为字符设备,而/ sys/ devices/ ldd0/sculld*/ 就会被误判为块设备】 ,那么如果你 在非 /sys/class 和 /sys/block 目录下建立了“dev”属性文件且内容是设备号(像sculld中就这样做了),那么mdev也会在/dev 下创建设备节点文件。这样可能所创建的设备节点文件是错的。 以我实验为例,我以上一篇的文章中的sculld为基础,加上了类接 口(这样在/ sys/ devices/ ldd0/sculld*/和 /sys/class/ldd/sculld* 中都有内容为设备号的“dev”属性文件 )。在运行时发现一 直会将有的sculld*创建为块设备节点文件。郁闷死了,难道我的驱动有错???最后研究了mdev源码之后发现,只要在 /sys中建立了“dev”属性文件且内容是设备号,mdev就会以所在的目录为名在/dev 下创建设备节点文件。像sculld模块,mdev会为一个设备创建两次设备文件,由于文件名一样,第二次的文件会覆盖第一次的。如果第二次是因为/ sys/ devices/ ldd0/sculld*/dev 产生的设备节点文件,那么设备节点文件就会被错误地创建为块设备。 我认为这个bug的解决办法有如下两种: (1)在你写驱动的时候,只在/sys/class 和 /sys/block 中的类 设备目录中存在包含设备号的“dev”属性文件。(你无法保证被人的驱动会这么做) (2)修正mdev源码: 修改/busybox-1.9.0/util-linux/mdev.c文件的第328行:
也就是在增加设备节点文件之前检查 /sys/目录下的 路径是 否为/cl ass和/bl ock(通过检查路径字符串的第3个字符是否为‘l’)。 本人推荐第二种做法! |
|
/ bin/ echo / sbin / mdev > / proc/ sys/ kernel/ hotplug,并非
/ bin/ echo / bin / mdev > / proc/ sys/ kernel/ hotplug。busybox的文档有错!!
|
firmware
硬件市场的激烈竞争, 使得制造商连一点用于设备控制固件的 EEPROM 的成本都不愿意花费。因此固件一般发布在和硬件配套的驱动包中,由操作系统(其实是驱动程序)负责传送固件到设备。
内核固件接口
获取固件的正确方法是当需要时从用户空间获取它。一定不要试图从内核空间直接打开包含固件的文件,那是一个易出错的操作, 因为它把策略(以文件名的形式)包含进了内核。正确的方法是使用固件接口:
|
注意:要使用firmware,必须 要在配置内核时选上:
Device Drivers --->
Generic Driver Options --->
<*> Userspace firmware loading support
否则会出现: Unknown symbol release_firmware 和: Unknown symbol request_firmware 的错误。
固件接口工作 原理
固件子系统使用 sysfs 和热插拔机制工作 。当调 用 request_firmware时, 函数将在 /sys/class/firmware 下创建一个以设备名为目录名的新目录,其中包含 3 个属性:
loading :这个属性应当被加载固件的用户空间进程设置为 1。当加载完毕, 它将被设为 0。被设为 -1 时,将中止固件加载。
data :一个用来接收固件数据的二进制属性。在设置 loading 为1后, 用户空间进程将固件写入这个属性。
device :一个链接到 /sys/devices 下相关入口项的符号链接。
一旦创建了 sysfs 入口项, 内核将为设备产生一个热插拔事件,并传递包括变量 FIRMWARE 的环境变量给处理热插拔的用户空间程序。FIRMWARE 被设置为提供给 request_firmware 的固件文件名。
用户空间程序定位固件文件, 并将其拷贝到内核提供的二进制属性;若无法定位文件, 用户空间程序设置 loading 属性为 -1。
若固件请求在 10 秒内没有被服务, 内核就放弃并返回一个失败状态给驱动。超时周期可通过 sysfs 属性 /sys/class/firmware/timeout 属性改变。
request_firmware 接口允许使用驱动发布设备固件。当正确地集成进热插拔机制后, 固件加载子系统允许设备不受干扰地工作。显然这是处理问题的最好方法,但固件受版权保护,小心违反版权法。
[ Tekkaman2440@SBC2440V4] # ls - l / dev/ sculld*
ls: / dev/ sculld* : No such file or directory
[ Tekkaman2440@SBC2440V4] # cat / tmp/ mdev
cat: can't open ' / tmp/ mdev': No such file or directory
[Tekkaman2440@SBC2440V4]#insmod /lib/modules/lddbus.ko
Mount lddbus ok !
Bus device is ldd0 !
You can see me in sys/module/ , sys/devices/ , sys/class/ and sys/bus/ !
[Tekkaman2440@SBC2440V4]#insmod /lib/modules/sculld.ko
[Tekkaman2440@SBC2440V4]#ls -l /dev/sculld*
crw-rw-rw- 1 root root 252, 0 Jan 1 00:00 /dev/sculld0
crw-rw-rw- 1 root root 252, 1 Jan 1 00:00 /dev/sculld1
crw-rw---- 1 root root 252, 2 Jan 1 00:00 /dev/sculld2
crw-rw---- 1 root root 252, 3 Jan 1 00:00 /dev/sculld3
[Tekkaman2440@SBC2440V4]#rmmod sculld
The LDD class
ldd_classdev_release : sculld0 release!
The LDD class
ldd_classdev_release : sculld1 release!
The LDD class
ldd_classdev_release : sculld2 release!
The LDD class
ldd_classdev_release : sculld3 release!
[Tekkaman2440@SBC2440V4]#ls -l /dev/sculld*
ls: /dev/sculld*: No such file or directory
[Tekkaman2440@SBC2440V4]#cat /tmp/mdev
tekkaman