0%

mdev

mdevbusybox 自带的一个简化版的udev,适合于嵌入式的应用埸合。其具有使用简单的特点。它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所需的节点文件。在以busybox为基础构建嵌入式linux的根文件系统时,使用它是最优的选择。

mdev 的使用可以参看 busybox 中的 mdev.txt 文档

mdev使用

busybox 对 mdev 支持

Linux System Utilities  --->
    [*] mdev
    [*]   Support /etc/mdev.conf
    [*]     Support command execution at device addition/removal

mdev 用法

Mdev has two primary uses: initial population and dynamic updates. Both require sysfs support in the kernel and have it mounted at /sys. For dynamic updates, you also need to have hotplugging enabled in your kernel.

Here’s a typical code snippet from the init script:

[0] mount -t proc proc /proc
[1] mount -t sysfs sysfs /sys
[2] echo /sbin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s

Alternatively, without procfs the above becomes:

[1] mount -t sysfs sysfs /sys
[2] sysctl -w kernel.hotplug=/sbin/mdev
[3] mdev -s

Of course, a more “full” setup would entail executing this before the previous code snippet:

[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts

The simple explanation here is that [1] you need to have /sys mounted before executing mdev. Then you [2] instruct the kernel to execute /sbin/mdev whenever a device is added or removed so that the device node can be created or destroyed. Then you [3] seed /dev with all the device nodes that were created while the system was booting.

For the “full” setup, you want to [4] make sure /dev is a tmpfs filesystem (assuming you’re running out of flash). Then you want to [5] create the /dev/pts mount point and finally [6] mount the devpts filesystem on it.

mdev.conf

/etc/mdev.conf 配置文件,属于可选项

格式: <device regex> <uid>:<gid> <octal permissions>  [<@|$|*> <command>]
@ 创建了设备之后运行
$ 删除设备之前运行
* 在创建设备之后和删除设备之前运行

example

sd.* 0:0 0600 */etc/add_remove_log.sh
mmcblk([0-9]+) root:disk 660 >disk/mmc/%1/0

mdev -s

mdev --help

mdev -s is to be run during boot to scan /sys and populate /dev.

-s 参数调用 mdev,扫描 /sys/class/sys/block 中所有的类设备目录,如果在目录中含有名为 dev 的文件,且文件中包含设备号,则 mdev 利用这些信息为这个设备在 /dev 下创建设备节点文件,一般只在启动时执行一次 mdev -s

Hotplug

由于启动时运行了命令 echo /sbin/mdev > /proc/sys/kernel/hotplug,那么当有热插拔事件产生时,内核就会调用 mdev。这时 mdev 通过环境变量中的 ACTIONDEVPATH,(这两个变量是系统自带的)来确定此次热插拔事件的动作以及影响了/sys中的那个目录。接着会看看这个目录中是否有 dev 的属性文件,如果有就利用这些信息为这个设备在 /dev 下创建设备节点文件。

驱动中增加对类设备接口支持

在驱动程序的初始化函数中,使用以下类似的语句,就能在类设备目录下 /sys/class 添加包含设备号的名为 dev 的属性文件,并通过 mdev/dev 下生成设备节点文件。

include/linux/device.h

#define device_create_drvdata   device_create

//一个struct class结构体类型变量对应一个类
static struct class *mtd_class;

//create your own class under /sysfs
mtd_class = class_create(THIS_MODULE, "mtd");
//register your own device in sysfs, and this will cause udev to create corresponding device node
device_creat e_drvdata(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), NULL, "mtd%d", mtd->index);

//delete device node under /dev
device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
//delete class created by us
class_destroy(my_class);

这部分代码适用与字符设备。在2.6较早的内核版本中,device_create 函数名称不同,是 class_device_create

misc 设备

misc 设备是主设备号为10 的字符设备,使用的注册函数为

<linux/miscdevice.h>

static struct miscdevice flash_otp_miscdev =
{
    .minor = MISC_OTP_MINOR,
    .name  = "flash_otp",
    .fops  = &flash_otp_fops,
};

err = misc_register(&flash_otp_miscdev);
if (err) {
    printk("flash otp probe: failed to register misc device\n");
    kfree(info);
    err = -EINVAL;
}

int misc_deregister(struct miscdevice *misc);

misc 是特殊的字符设备。注册驱动程序时采用 misc_register 函数注册,此函数中会自动创建设备节点,即设备节点文件,原因是 misc_register 会调用 device_create

int misc_register(struct miscdevice * misc)
{
    struct miscdevice *c;
    dev_t dev;
    int err = 0;

    INIT_LIST_HEAD(&misc->list);  //链表项使用时必须初始化

    mutex_lock(&misc_mtx);
    list_for_each_entry(c, &misc_list, list) {
        if (c->minor == misc->minor) {
            mutex_unlock(&misc_mtx);
            return -EBUSY;
        }
    } //遍历链表如果发现次设备号一样的,返回错误

    if (misc->minor == MISC_DYNAMIC_MINOR) {  //动态次设备号
        int i = DYNAMIC_MINORS;
        while (--i >= 0)
            if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
                break;
        if (i<0) {
            mutex_unlock(&misc_mtx);
            return -EBUSY;
        }
        misc->minor = i;
    }

    if (misc->minor < DYNAMIC_MINORS)
        misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
    dev = MKDEV(MISC_MAJOR, misc->minor);

    //misc_class 在 misc_init 函数中创建
    misc->this_device = device_create(misc_class, misc->parent, dev,
                        misc, "%s", misc->name);
    //udev创建设备节点使用,linux设备模型相关
    if (IS_ERR(misc->this_device)) {
        err = PTR_ERR(misc->this_device);
        goto out;
    }

    /*
    * Add it to the front, so that later devices can "override"
    * earlier defaults
    */
    list_add(&misc->list, &misc_list); //添加到misc_list之中
    out:
    mutex_unlock(&misc_mtx);
    return err;
}

杂项设备作为字符设备的封装,为字符设备提供的简单的编程接口,如果编写新的字符驱动,可以考虑使用杂项设备接口,方便简单,只需要初始化一个miscdevice的结构,调用misc_register就可以了。

block 设备节点文件

mtdblock 文件流程

static struct mtd_blktrans_ops mtdblock_tr = {
    .name       = "mtdblock",
    .major      = 31,
    .part_bits  = 0,
    .blksize    = 512,
    .open       = mtdblock_open,
    .flush      = mtdblock_flush,
    .release    = mtdblock_release,
    .readsect   = mtdblock_readsect,
    .writesect  = mtdblock_writesect,
    .add_mtd    = mtdblock_add_mtd,
    .remove_dev = mtdblock_remove_dev,
    .owner      = THIS_MODULE,
};

static int __init init_mtdblock(void)
{
    return register_mtd_blktrans(&mtdblock_tr);
}

static void __exit cleanup_mtdblock(void)
{
    deregister_mtd_blktrans(&mtdblock_tr);
}

module_init(init_mtdblock);
module_exit(cleanup_mtdblock);

mtdblock_add_mtd -> add_mtd_blktrans_dev -> add_disk -> register_disk
-> device_add -> device_create_file -> sysfs_create_file

loop 流程

static int __init loop_init(void)
{
    int i, nr;
    unsigned long range;
    struct loop_device *lo, *next;

    /*
     * loop module now has a feature to instantiate underlying device
     * structure on-demand, provided that there is an access dev node.
     * However, this will not work well with user space tool that doesn't
     * know about such "feature".  In order to not break any existing
     * tool, we do the following:
     *
     * (1) if max_loop is specified, create that many upfront, and this
     *     also becomes a hard limit.
     * (2) if max_loop is not specified, create 8 loop device on module
     *     load, user can further extend loop device by create dev node
     *     themselves and have kernel automatically instantiate actual
     *     device on-demand.
     */

    part_shift = 0;
    if (max_part > 0)
        part_shift = fls(max_part);

    if (max_loop > 1UL << (MINORBITS - part_shift))
        return -EINVAL;

   if (max_loop) {
        nr = max_loop;
        range = max_loop;
    } else {
        nr = 8;
        range = 1UL << (MINORBITS - part_shift);
    }

    if (register_blkdev(LOOP_MAJOR, "loop"))
        return -EIO;

    for (i = 0; i < nr; i++) {
        lo = loop_alloc(i);
        if (!lo)
            goto Enomem;
        list_add_tail(&lo->lo_list, &loop_devices);
    }

    /* point of no return */

    list_for_each_entry(lo, &loop_devices, lo_list)
        add_disk(lo->lo_disk);

    blk_register_region(MKDEV(LOOP_MAJOR, 0), range,
                  THIS_MODULE, loop_probe, NULL, NULL);

    printk(KERN_INFO "loop: module loaded\n");
    return 0;

Enomem:
    printk(KERN_INFO "loop: out of memory\n");

    list_for_each_entry_safe(lo, next, &loop_devices, lo_list)
        loop_free(lo);

    unregister_blkdev(LOOP_MAJOR, "loop");
    return -ENOMEM;
}

module_init(loop_init);

Ref

  1. mdev
  2. mdev的使用方法和原理
  3. mdev使用
  4. mdev的使用以及mdev.conf的规则配置–busybox