转自:http://www.cnblogs.com/hnrainll/archive/2011/06/10/2077469.html

转自:http://blog.chinaunix.net/space.php?uid=20543672&do=blog&cuid=460882

热插拔
有 2 个不同角度来看待热插拔:
   从内核角度看,热插拔是在硬件、内核和内核驱动之间的交互。
   从用户角度看,热插拔是内核和用户空间之间,通过调用用户空间程序(如hotplug、udev 和 mdev)的交互。 当需要通知用户内核发生了某种热插拔事件时,内核才调用这个用户空间程序。
现在的计算机系统,要求 Linux 内核能够在硬件从系统中增删时,可靠稳定地运行。这就对设备驱动作者增加了压力,因为在他们必须处理一个毫无征兆地突然出现或消失的设备。


热插拔工具
当用户向系统添加或删除设备时,内核会产生一个热插拔事件,并在 /proc/sys/kernel/hotplug 文件里查找处理设备连接的用户空间程序。这个用户空间程序主要有

hotplug:这个程序是一个典型的 bash 脚本, 只传递执行权给一系列位于 /etc/hot-plug.d/ 目录树的程序。hotplug 脚本搜索所有的有 .hotplug 后缀的可能对这个事件进行处理的程序并调用它们, 并传递给它们许多不同的已经被内核设置的环境变量。(基本已被淘汰,具体内容请参阅《LDD3》)

udev :用于linux2.6.13或更高版本的内核上,为用户空间提供使用固定设备名的动态/dev目录的解决方案。它通过在 sysfs 的 /class/ 和/block/ 目录树中查找一个称为 dev 的文件,以确定所创建的设备节点文件的主次设备号。所以要使用udev,驱动必须为设备在sysfs中创建类接口及其dev属性文件,方法和sculld模块中创建dev属性相同。 udev的资料网上十分丰富,我就不在这废话了,给出以下链接有兴趣的自己研究:
 
《udev规则编写》(luofuchong翻译),地址:http://www.cnitblog.com/luofuchong/archive/2007/12/18/37831.html
 
 
《udev-FAQ 中文翻译》地址:http://gnawux.bokee.com/3225765.html
 
 
 
 
在《LFS》中也有介绍udev的使用,很值得参考!下载地址:http://lfs.osuosl.org/lfs/downloads/stable/
 
 
mdev:一个简化版的udev,是busybox所带的程序,十分适合嵌入式系统。
 

因为hotplug现在也在被慢慢地淘汰,udev不再依赖hotplug了,所以这里不再介绍;

udev较mdev复杂,不太适合嵌入式使用。(本人也有做udev的实验,交叉编译是通过了,但是使用上有问题,没有实现其功能。也许是我的文件系统没做好,以后有时间再研究和写记录。有成功高人的通知一声,交流一下经验。^_^谢谢!);

mdev简单易用,比较适合嵌入式系统,实验成功。以下详细介绍mdev的使用。


mdev
 
在一开始建立根文件系统时,我根据 WeiBing 的博客上《UDEV on embeded Linux-2.6.19.2》(地址:http://weibing.blogbus.com/logs/4485453.html) 这篇文章的提示,开始使用mdev,但是当时只是启动时mdev -s 一下,并没有深究。现在在学习了Linux设备模型之后,对于Linux中/dev目录的动态管理有了更深的认识,并认真的看了一下busybox中的 mdev.txt文档并翻译了一下,做成了PDF(下载地址:http://blogimg.chinaunix.net/blog/upfile2/080111091002.pdf),在看下面的内容时请先看看这篇文档。
 
先声明一个要点:要实现设备节点文件的自动、动态的增删,必须在你自己的驱动源码中实现 类 接口,并在类设备的目录中添加包含设备号的名为“dev”的属性文件。
 
mdev原理及bug
 
要使用mdev,适当知道一下原理是必不可少的(能完整地研究mdev源码是最好的)。说实话起初我并没有想看mdev的源码,是在使用时发现了问题后才去研究了一下mdev的源码。现在简单介绍一下mdev的原理:
 
执行mdev -s :以‘-s’为参数调用位于 /sbin目录写的mdev(其实是个链接,作用是传递参数给/bin目录下的busybox程序并调用它),mdev扫描 /sys/class 和 /sys/block 中所有的类设备目录,如果在目录中含有名为“dev”的文件,且文件中包含的是设备号,则mdev就利用这些信息为这个设备在/dev 下创建设备节点文件。一般只在启动时才执行一次 “mdev -s”。
 
 热插拔事件:由于启动时运行了命 令:echo /sbin/mdev > /proc/sys/kernel/hotplug ,那么当有热插拔事件产生时,内核就会调用位于 /sbin目录的mdev。这时mdev通过环境变量中的 ACTION 和 DEVPATH,来确定此次热插拔事件的动作以及影响了/sys中的那个目录。接着会看看这个目录中是否有“dev”的属性文件,如果有就利用这些信息为 这个设备在/dev 下创建设备节点文件。
 

源码的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行:

if (!strcmp(action, "remove"))
       make_device(temp, 1);

else if (!strcmp(action, "add")) {
         if (env_path[2]=='l') make_device(temp,0);  //tekkamanninja
            if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE)
                load_firmware(getenv("FIRMWARE"), temp);
        }

也就是在增加设备节点文件之前检查/sys/目录下的路径是否为/class和/block(通过检查路径字符串的第3个字符是否为‘l’)。

本人推荐第二种做法!

 
mdev使用
mdev的使用在busybox中的mdev.txt文档已经将得很详细了。但作为例子,我简单讲讲我的使用过程:
 
(1)在编译时加上对mdev的支持(我是使用的是busybox1.9.0):
    Linux System Utilities  --->    
           [*] mdev       
           [*]   Support /etc/mdev.conf 
           [*]     Support command execution at device addition/removal
 
(2)在启动时加上使用mdev的命令:
我在自己创建的根文件系统(nfs)中的/linuxrc文件中添加了如下指令:

#挂载/sys为sysfs文件系统
    echo "----------mount /sys as sysfs"
    /bin/mount -t tmpfs mdev /dev 
    /bin/mount -t sysfs sysfs /sys
    echo "----------Starting mdev......"
    /bin/echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev -s

注意:是/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug,并非/bin/echo /bin/mdev > /proc/sys/kernel/hotplug。busybox的文档有错!!

(3)在你的驱动中加上对类设备接口的支持,并在类设备目录下添加包含设备号的名为“dev”的属性文件。
 
(4)至于/etc/mdev.conf文件,可有可无,不影响使用,只是添加了些功能。
     为了实验我在/etc创建了mdev.conf文件并输入了:

    sculld[0-1] 0:0 666 * echo tekkaman > /tmp/mdev

     这样,在挂载和卸载sculld.ko时,在/tmp/下会出现mdev文件,里面字符为tekkaman

具体的实验源码和现象在文章后面有。
 

firmware

硬件市场的激烈竞争, 使得制造商连一点用于设备控制固件的 EEPROM 的成本都不愿意花费。因此固件一般发布在和硬件配套的驱动包中,由操作系统(其实是驱动程序)负责传送固件到设备。

内核固件接口

获取固件的正确方法是当需要时从用户空间获取它。一定不要试图从内核空间直接打开包含固件的文件,那是一个易出错的操作, 因为它把策略(以文件名的形式)包含进了内核。正确的方法是使用固件接口:

#include <linux/firmware.h>
int request_firmware(const struct firmware **fw,

                     const char *name, /* name 为固件文件名*/

                     struct device *device);
/*要求用户空间定位并提供一个固件映象给内核;若成功加载, 返回值是 0(否则返回错误码)*/

/*因为 request_firmware 需要用户空间的操作, 所以返回前将保持休眠。若驱动必须使用固件而不能进入休眠时,可使用以下异步函数:*/
int request_firmware_nowait(
    struct module *module, /* = THIS_MODULE*/
    int uevent,
    const char *name, 
    struct device *device, 
    void *context,/*不由固件子系统使用的私有数据指针*/
    void (*cont)(const struct firmware *fw, void *context));
/*如果一切正常,request_firmware_nowait 开始固件加载过程并返回 0. 过了一段时间后(默认10秒),将用加载的结果(若加载失败, fw 为 NULL)作为参数调用 cont。*/

/* fw 参数指向以下结构体:*/
struct firmware {
    size_t size;
    u8 *data;
};
/*那个结构包含实际的固件, 它现在可被下载到设备中.但是请注意:在发送它到硬件之前,必须检查这个文件以确保它是正确的固件映象(设备固件常常包含标识字符串、 校验和等等)*/

/*当固件已经发送到设备后,应当释放 firmware 结构体, 使用:*/
void release_firmware(struct firmware *fw);

注意:要使用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 接口允许使用驱动发布设备固件。当正确地集成进热插拔机制后, 固件加载子系统允许设备不受干扰地工作。显然这是处理问题的最好方法,但固件受版权保护,小心违反版权法。


ARM9开发板实验
 
 

实验现象:

[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

作者:Leo Chin
本博客文章,大多系网络中收集,转载请注明出处

Linux设备模型(热插拔、mdev 与 firmware)【转】的更多相关文章

  1. Linux设备模型——设备驱动模型和sysfs文件系统解读

    本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的.在分析过程中,本文将以platform总线和spi主控制器的 ...

  2. linux设备模型:扩展篇

    Linux设备模型组件:总线  一.定义:总线是不同IC器件之间相互通讯的通道;在计算机中,一个总线就是处理器与一个或多个不同外设之间的通讯通道;为了设备模型的目的,所有的设备都通过总线相互连接,甚至 ...

  3. linux设备模型_转

    建议原博文查看,效果更佳. 转自:http://www.cnblogs.com/wwang/category/269350.html Linux设备模型 (1) 随着计算机的周边外设越来越丰富,设备管 ...

  4. Linux设备模型(总线、设备、驱动程序和类)

    Linux设备驱动程序学习(13) -Linux设备模型(总线.设备.驱动程序和类)[转] 文章的例子和实验使用<LDD3>所配的lddbus模块(稍作修改). 提示:在学习这部分内容是一 ...

  5. Linux 设备模型浅析之 uevent 篇(2)

    Linux 设备模型浅析之 uevent 篇 本文属本人原创,欢迎转载,转载请注明出处.由于个人的见识和能力有限,不可能面 面俱到,也可能存在谬误,敬请网友指出,本人的邮箱是 yzq.seen@gma ...

  6. Linux设备模型:基础篇

    linux提供了新的设备模型:总线(bus).设备(device).驱动(driver).其中总线是处理器与设备之间通道,在设备模型中,所有的设备都通过总线相连:设备是对于一个设备的详细信息描述,驱动 ...

  7. Linux设备模型 (1)

    随着计算机的周边外设越来越丰富,设备管理已经成为现代操作系统的一项重要任务,这对于Linux来说也是同样的情况.每次Linux内核新版本的发布,都会伴随着一批设备驱动进入内核.在Linux内核里,驱动 ...

  8. Linux 设备模型

    在 2.5 开发循环中一个声明的目标是为内核创建一个统一的设备模型. 之前的内核没有单一的数据结 构, 使它们可以来获取关于系统如何整合的信息. 尽管缺乏信息, 有时事情也进行的不错. 新系统, 带 ...

  9. Linux设备模型(9)_device resource management ---devm申请空间【转】

    转自:http://www.wowotech.net/linux_kenrel/device_resource_management.html . 前言 蜗蜗建议,每一个Linux驱动工程师,都能瞄一 ...

随机推荐

  1. 10分钟了解 react 引入的 Hooks

    "大家好,我是谷阿莫,今天要将的是一个...",哈哈哈,看到这个题我就想到这个开头.最近react 官方在 2018 ReactConf 大会上宣布 React v16.7.0-a ...

  2. Linux 系统中 sudo 命令的 10 个技巧

    概览 sudo 表示 "superuser do". 它允许已验证的用户以其他用户的身份来运行命令.其他用户可以是普通用户或者超级用户.然而,大部分时候我们用它来以提升的权限来运行 ...

  3. Java堆内存又溢出了!教你一招必杀技

    JAVA堆内存管理是影响性能主要因素之一.堆内存溢出是JAVA项目非常常见的故障,在解决该问题之前,必须先了解下JAVA堆内存是怎么工作的. 先看下JAVA堆内存是如何划分的,如图: JVM内存划分为 ...

  4. 从源码带你看懂functools的partial方法

    1.what? partial是什么, partial也叫偏函数.源码的描述是: 部分应用给定参数和关键字的新函数. New function with partial application of ...

  5. stark组件前戏(1)之项目启动前加载指定文件

    django项目启动时,可以自定义执行某个py文件,这需要在任意app的apps.py中的Config类定义ready方法,并调用.   from django.apps import AppConf ...

  6. A1055 The World's Richest(25 分)

    A1055 The World's Richest(25 分) Forbes magazine publishes every year its list of billionaires based ...

  7. poj 3259 时光穿梭问题 bellman_ford算法

    题意:有n个空地,有m条双向大路,w条时光隧道单向路.问能否回到过去? 思路:判断是否有负环存在,如果有负环存在那么就可以一直小就可以回到过去了 创建源顶点 V到其他顶点的距离d 初始为INF d[1 ...

  8. BZOJ 3420: Poi2013 Triumphal arch

    二分答案 第二个人不会走回头路 那么F[i]表示在i的子树内(不包括i)所需要的额外步数 F[1]==0表示mid可行 k可能为0 #include<cstdio> #include< ...

  9. Redis实现之复制(一)

    复制 在Redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制(replicate)另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器 ...

  10. pycharm中某些方法被标黄的原因及解决办法

    在编辑python文件时,会遇到上图所示,函数方法被标黄的问题,但是不影响使用. 引起原因:,如果不报错说明,这是因为你配置的python解释器中有该方法,但是pycharm没有找到这个方法,即加载失 ...