mdev
是 busybox
自带的一个简化版的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
通过环境变量中的 ACTION
和DEVPATH
,(这两个变量是系统自带的)来确定此次热插拔事件的动作以及影响了/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);