busybox mdev

来源:http://www.cnblogs.com/vs2008_taotao/archive/2010/08/17/1801681.html
移植SD卡驱动 for mini2440 & Linux2.6.30.4


配置USB for mini2440 & Linux2.6.30.4

1)确保内核支持热插拔
# cd linux-2.6.30.4# vi .config
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_HOTPLUG=y  //确保这项为y
CONFIG_PRINTK=y
CONFIG_NET=y     //确保这项也为y
确保以上两项都选上,不然/proc/sys/kernel/目录不会出现hotplug文件!
2)确保busybox支持mdev
mdev是busybox中udev设备文件系统的精简版
# make menuconfig
确保以下几项被选中:
Linux System Utilities  --->

  • mdev                                           
  •    Support /etc/mdev.conf                                
  •      Support subdirs/symlinks                            
  •        Support regular expressions substitutions when renaming dev
  •      Support command execution at device addition/removal       
  •    Support loading of firmwares
    3)根文件系统
    etc/fatab文件的内容为:
    proc  /proc           proc    defaults        0       0
    sysfs            /sys            sysfs    defaults        0        0
    tmpfs            /var            tmpfs    defaults        0        0
    tmpfs            /tmp            tmpfs    defaults        0        0
    tmpfs            /dev            tmpfs    defaults        0        0
    etc/init.d/rcS添加如下的内容:mount -a
    mkdir /dev/pts
    mount -t devpts devpts /dev/pts
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev -s
    4)解析
    执行 mdev -s:
    以 "-s" 为参数调用位于 /sbin 目录下的 mdev(其实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 目录下创建或删除设备节点文件并执行相应的动作 ACTION 。
    5)添加对热插拔事件的响应,实现U盘和SD卡的自动挂载与缷载
    # vi /etc/mdev.conf        
    内容为:                                
    sd[a-z][0-9]      0:0 666        @ /etc/hotplug/usb/udisk_insert                              
    sd[a-z]           0:0 666        $ /etc/hotplug/usb/udisk_remove
    mmcblk[0-9]p[0-9] 0:0 666        @ /etc/hotplug/sd/sd_insert
    mmcblk[0-9]       0:0 666        $ /etc/hotplug/sd/sd_remove
    # vi /etc/hotplug/usb/udisk_insert                              
    内容为:
    #!/bin/sh
    mkdir -p /media/udisk
    mount /dev/sda /media/udisk
    # vi /etc/hotplug/usb/udisk_remove
    内容为:
    #!/bin/sh
    umount /media/udisk
    为以上两个脚本加上可执行权限:
    # chmod +x /etc/hotplug/usb/udisk_insert
    # chmod +x /etc/hotplug/usb/udisk_remove  

    mdev的基本工作原理

    分析过mdev(udev的BusyBox简化版)源码的都知道mdev的基本原理:

      a、执行mdev -s命令时,mdev扫描/sys/block(块设备保存在/sys/block目录下,内核2.6.25版本以后,块设备也保存在/sys /class/block目录下。mdev扫描/sys/block是为了实现向后兼容)和/sys/class两个目录下的dev属性文件,从该dev属性文件中获取到设备编号(dev属性文件以"major:minor\n"形式保存设备编号),并以包含该dev属性文件的目录名称作为设备名device_name(即包含dev属性文件的目录称为device_name,而/sys/class和device_name之间的那部分目录称为subsystem。也就是每个dev属性文件所在的路径都可表示为/sys/class/subsystem/device_name/dev),在/dev目录下创建相应的设备文件。例如,cat  /sys/class/tty/tty0/dev会得到4:0,subsystem为tty,device_name为tty0。

      b、当mdev因uevnet事件(以前叫hotplug事件)被调用时,mdev通过由uevent事件传递给它的环境变量获取到:引起该uevent事件的设备action及该设备所在的路径device  path。然后判断引起该uevent事件的action是什么。若该action是add,即有新设备加入到系统中,不管该设备是虚拟设备还是实际物理 设备,mdev都会通过device path路径下的dev属性文件获取到设备编号,然后以device  path路径最后一个目录(即包含该dev属性文件的目录)作为设备名,在/dev目录下创建相应的设备文件。若该action是remote,即设备已 从系统中移除,则删除/dev目录下以device  path路径最后一个目录名称作为文件名的设备文件。如果该action既不是add也不是remove,mdev则什么都不做。

       由上面可知,如果我们想在设备加入到系统中或从系统中移除时,由mdev自动地创建和删除设备文件,那么就必须做到以下三点:1、在/sys/class的某一subsystem目录下,2、创建一个以设备名device_name作为名称的目录,3、并且在该device_name目录下还必须包含一个dev属性文件,该dev属性文件以"major:minor\n"形式输出设备编号。

      思路有了,剩下的就是如何实现了。下面通过介绍linux设备驱动模型里两个重要的数据结构:class和class_device,顺带讲述如何架构mdev驱动程序,并给出2个例子:HelloWorld和udev_demo。

      注意:下面讲到的结构体、函数以及例子都是针对linux-2.6.24.7版本内核,并不适合于所有版本的linux内核。各版本内核的结构体和函数原型可能有变。比如我在Ubuntu9.04下make一下第二个驱动例子,出现了好几个错误,后来发现Ubuntu9.04的内核linux-2.6.28里面关于linux设备驱动模型的数据结构有了很大改变,没有了class_device。所以当你跑下面两个例子时,如不是linux-2.6.24.7版本内核,那就要注意一下例子里使用数据结构与你内核里的数据结构是否一致。

      1、class

      一个类是一个设备的高层视图,它抽象掉了底层的实现细节。例如,在驱动层面时,你可能会见到SCSI磁盘或者ATA磁盘;但在类层面时,它们都是磁盘。类允许用户空间基于它们做什么来使用设备,而不是它们如何被连接或者它们如何工作。

      class表示一类设备,所有class都属于class_subsys(class子系统),即出现在/sys/class目录下,除了块设备(可能出现在/sys/block/或/sys/class/block,上面讲过了)。

      其实,class在/sys/class下生成的目录也就是上面提到subsystem。这样第1点就有了。

      /* class结构体 */ structclass{

      constchar            * name;        /* class的名称 */

      struct module        * owner;    /* 拥有该class的模块 */

      struct kset            subsys;        /* 该class对应的子系统 */

      struct list_head    children;    /* 该class的class_device列表 */

      struct list_head    devices;    

      struct list_head    interfaces;    

      struct kset            class_dirs;        

      struct semaphore    sem;    /* locks both the children and interfaces lists */

      struct class_attribute            * class_attrs;/* 该class的默认属性,以NULL结尾 */

      struct class_device_attribute    * class_dev_attrs;/* 添加到class的class_device所拥有的默认属性 */

      struct device_attribute            * dev_attrs;

      /* 该函数提供在产生热插拔class_device事件时,添加环境变量的能力 */

      int    (*uevent)(struct class_device *dev,struct kobj_uevent_env *env);

      /* 该函数提供在产生热插拔device(物理设备)事件时,添加环境变量的能力 */

      int    (*dev_uevent)(struct device *dev,struct kobj_uevent_env *env);

      /* 添加到class的class_device移除时,调用该函数进行必要的清理工作 */

      void(*release)(struct class_device *dev);    

      /* class被移除时,调用该函数进行必要的清理工作 */

      void(*class_release)(structclass*class);

      void(*dev_release)(struct device *dev);

      int    (*suspend)(struct device *, pm_message_t state);

      int    (*resume)(struct device *);

      };

      /* class注册函数 */

      int __must_check class_register(structclass*);

      void class_unregister(structclass*);         建立一个class有两种方法

      a、根据需要,填充一个struct class,然后再调用class_register注册该class就ok了(此法比较灵活,可以自己定制很多东西)

      b、就是通过下面的class_create来创建一个class,该函数会返回一个指向刚建立的class的指针(创建class的最简单方法)

      /* class_create用于创建一个名为name的class,其owner参数一般为THIS_MODULE。class_create内部调用了class_register */

      structclass*class_create(struct module *owner,constchar*name);

      /* class_destroy用于删除一个class,实际上其内部只是简单调用了class_unregister(cls)来注销cls */

       void class_destroy(structclass*cls);         一个class属性对应于/sys/class/class.name(class.name就是该class的名称)目录里的一个文件。通过这些文件, 可以向用户空间输出一些关于该class的信息,也可从用户空间获取到一些信息。

      /* class属性结构体 */

      struct class_attribute {

      struct attribute    attr;

      /* 当用户空间读取该属性时,调用show函数输出一个"属性值"给用户空间 */

      ssize_t (*show)(structclass*,char* buf);

      /* 当用户空间写该属性时,调用store函数保存用户写入的"属性值" */

      ssize_t (*store)(structclass*,constchar* buf,size_tcount);

      };

      struct attribute {

      constchar        * name;

      struct module    * owner;

      mode_t            mode;

      };

      /* CLASS_ATTR可以在编译时创建一个class属性,该属性的名称为class_attr_name */

      #define CLASS_ATTR(_name,_mode,_show,_store)            \

      struct class_attribute class_attr_##_name = __ATTR(_name,_mode,_show,_store)

      /* class_create_file与class_remove_file用于创建与删除class默认属性外的属性 */

      int __must_check class_create_file(structclass*,

      conststruct class_attribute *);

      void class_remove_file(structclass*,conststruct class_attribute *);

      2、class_device

      一个class可以看成是一个容器(一个子系统subsystem),包含了很多的class_device,这些class_device是由class这个大的容器来管理的,而每个class_device都对应着一个具体的设备。

       每个class对象包括一个class_device链表,每个class_device对象表示一个逻辑设备并通过struct  class_device中的dev成员(一个指向struct  device的指针)关联一个物理设备。一个逻辑设备总是对应一个物理设备,而一个物理设备却可以对应多个逻辑设备。

      实际上,class_device在/sys/class/subsystem生成的目录就是上面提到的class_device。这样第2点也有了。

      /* class_device结构体 */ struct class_device {

      struct list_head    node;            /* 仅供驱动核心内部使用 */

      struct kobject        kobj;            /* 该class_device相应的kobject,仅供驱动核心内部使用 */

      structclass        *class;        /* 该class_device所属的class,必须有 */

      dev_t                devt;            /* 该class_device的设备编号,用于创建其dev属性文件,仅供驱动核心内部使用 */

       struct device        * dev;            /* 指向该class_device相关的device结构体(物理设备),可选.若不为NULL,用于创建一个从class入口到/sys/devices下相应入口的符号连接,以便用户空间查找设备入口 */

      void                * class_data;    /* 该class_device的私有数据指针 */

      struct class_device    *parent;        /* parent of this child device, if there is one */

      struct attribute_group ** groups;    /* optional groups */

      void    (*release)(struct class_device *dev);

      int    (*uevent)(struct class_device *dev,struct kobj_uevent_env *env);

      char    class_id[BUS_ID_SIZE];        /* 该class_device的名称,在其所属class中应是唯一的,不可重名 */

      };

      /* class_devic注册函数 */

      int __must_check class_device_register(struct class_device *);

      void class_device_unregister(struct class_device *);         与class一样,建立一个class_device也有两种方法

      a、根据需要,填充一个struct class_device,然后再调用class_device_register注册该class_device就ok了(此法比较灵活,可以自己定制很多东西)

      b、就是通过下面的class_device_create来创建一个class_device,该函数会返回一个指向刚建立的class_device的指针(创建class_device的最简单方法)

      /* class_device_create用于创建一个class_device,其名称最后两个参数决定(类似于printf的格式化字符串)

      * cls指明了其所属的class,可以是自己填充的class或由class_create返回的class

      * parent指明了该class_device的父class_device,若没有,则为NULL

      * 该class_device的设备编号,用于创建其dev属性文件,必须指明

      * device指明了该class_device(逻辑设备)对应的device(物理设备),可有可无,无则为NULL

      * 实际上,class_device_create也就是填充一个class_device,然后调用了class_device_register注册该class_device

      */

      struct class_device *class_device_create(structclass*cls,

      struct class_device *parent,

      dev_t devt,

      struct device *device,

      constchar*fmt,...)

      __attribute__((format(printf,5,6)));

      /* class_destroy用于删除一个class,内部调用了class_unregister(cls)来注销cls */

       void class_device_destroy(structclass*cls, dev_t devt);             class_device属性对应于/sys/class/class.name/class_device.class_id目录下一个文件。通过这些 文件,可以向用户空间输出一些关于该class_device的信息,也可从用户空间获取到一些信息。

      /* class_device属性,其show和store函数类似于class属性的show和store函数 */

      struct class_device_attribute {

      struct attribute    attr;

      ssize_t (*show)(struct class_device *,char* buf);

      ssize_t (*store)(struct class_device *,constchar* buf,size_tcount);

      };

      /* CLASS_DEVICE_ATTR可以在编译时创建一个class_device属性,该属性的名称为class_device_attr_name */

      #define CLASS_DEVICE_ATTR(_name,_mode,_show,_store)        \

      struct class_device_attribute class_device_attr_##_name =     \

      __ATTR(_name,_mode,_show,_store)

      /* class_device_create_file与class_device_remove_file用于创建与删除class_device默认属性外的属性 */

      int __must_check class_device_create_file(struct class_device *,

      conststruct class_device_attribute *);

      void class_device_remove_file(struct class_device * class_dev,

      conststruct class_device_attribute * attr);            其实,在调用class_device_register注册一个class_device时,该函数内部调用了

      int class_device_add(struct class_device *class_dev)

       在class_device_add内,通过class_device_create_file创建dev、uevent和该class_device所拥有的默认属性(由class_device.class->class_dev_attrs指定)等属性文件。这样第3点也有了。

      dev属性文件用于向用户空间输出该class_device的设备编号。

      uevent属性文件使用户可以手动触发uevent事件(通过向该文件写,如add、remove等字符串)。

      接下来是两个基于mdev的驱动例子。

      struct class_interface {

      struct list_head node;

      struct class  *class;   /* 该class_interface所属的class */

      int (*add) (struct class_device *, struct class_interface *);  /* class属性 */

      void (*remove) (struct class_device *, struct class_interface *); /* class属性 */

      int (*add_dev)  (struct device *, struct class_interface *);

      void (*remove_dev) (struct device *, struct class_interface *);

      };

      /* class_interface注册函数 */

      int __must_check class_interface_register(struct class_interface *);

      void class_interface_unregister(struct class_interface *);

     

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值