正点原子 I.MX6ULL MINI 开发板学习笔记:misc 设备——字符设备驱动的快捷方式

上两篇分别用手动 mknod 和 class_create+device_create 注册字符设备,调用链一长串。这篇用 misc(混杂)设备 一步搞定。代码来自实际工程 code/2026/20260803/05_misc_led,已验证可用。
1. 完成什么实验?
用 misc_register 重写 LED 驱动:一步完成设备号分配、字符设备注册、/dev/myled0 节点创建,同时保留上一篇的 sysfs 属性 led_status。
2. 什么是 misc 设备?
misc(混杂)设备是 Linux 给“不好归类的小字符设备”准备的快捷框架:所有 misc 设备共用主设备号 10,次设备号可以指定或用 MISC_DYNAMIC_MINOR 动态分配。注册后自动出现在 /sys/class/misc/ 下,/dev 节点由 mdev 自动创建。它底层仍然封装了 cdev 那一套,只是内核帮你把活干完了。
3. 关键代码
misc 设备对象把名字、次设备号、file_operations 打包在一起:
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
};
/* misc 设备对象:MISC_DYNAMIC_MINOR 表示次设备号动态分配(主设备号固定 10),
* name 决定 /dev 节点名,fops 挂接文件操作集 */
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NODE, /* "myled0" */
.fops = &fops,
};init 里一条语句替代了之前的一整串:
/* 注册 misc 设备:一步完成设备号分配(主 10)+ /dev/myled0 节点 +
* /sys/class/misc/myled0/ 目录,成功后次设备号写回 misc_dev.minor */
ret = misc_register(&misc_dev);
if (ret)
{
pr_err("misc_register error : %d\n", ret);
return ret;
}
/* ... ioremap 四个寄存器(每个都检查 NULL,失败 goto 倒序释放)、配置 GPIO1_IO03 输出、默认关灯,与前两篇相同 ... */
pr_info("主设备号:%d, 次设备号:%d\r\n", 10, misc_dev.minor);
/* sysfs 属性挂在 misc 设备自己的 device 上:
* /sys/class/misc/myled0/led_status;失败跳倒序释放链 */
ret = device_create_file(misc_dev.this_device, &attr);
if (ret)
goto fail_unmap_pdata;
return 0;
/* 倒序释放链:ioremap 失败也会跳到这里 */
fail_unmap_pdata:
iounmap(pdata);
fail_unmap_pdir:
iounmap(pdir);
fail_unmap_ppad:
iounmap(ppad);
fail_unmap_pmux:
iounmap(pmux);
fail_misc:
misc_deregister(&misc_dev);
return ret;/* exit:反序释放 */
device_remove_file(misc_dev.this_device, &attr);
iounmap(pmux); iounmap(ppad); iounmap(pdir); iounmap(pdata);
misc_deregister(&misc_dev); /* 注销后 /dev/myled0 一并删除 */4. 三种注册方式对比
| 项目 | 手动 mknod | class+device | misc 设备 |
|---|---|---|---|
| 设备号 | 动态申请,需先查询 | 动态申请 | 主设备号固定 10,次设备号动态 |
| /dev 节点 | 手动 mknod | mdev 自动 | mdev 自动 |
| sysfs 位置 | 无 | /sys/class/led/ | /sys/class/misc/ |
| 注册调用 | 2 步 + 命令行 | 4 步 | 1 步 |
| 适用场景 | 学习原理 | 需要自定义类名 | 简单小设备(推荐) |
5. 关键点说明
- minor 写回:misc_register 成功后会把实际分配到的次设备号写进 misc_dev.minor,所以打印设备号要在注册之后。
- this_device:misc 注册后 misc_dev.this_device 指向内核创建的 device,sysfs 属性就挂在它上面。
- 本质没变:misc 底层仍是字符设备 + cdev,应用层 open/read/write 的路径和前两篇完全一样。
- goto 错误处理:更新后的工程代码里,ioremap 和 device_create_file 失败都会跳到倒序释放链,最末的 fail_misc 负责 misc_deregister,不会残留设备节点。
- 别滥用:主设备号 10 是共享资源,正经的大类设备(input、framebuffer 等)都有自己的框架,misc 适合 LED、蜂鸣器这类小设备。
6. 实验现象
# insmod drv_led.ko 之后:
dmesg | tail -3 # 主设备号:10, 次设备号:55(动态分配,数值可能不同)
ls /dev/myled0 # 节点已自动存在
ls /sys/class/misc/myled0/ # 能看到 led_status
cat /sys/class/misc/myled0/led_status # LED_OFF
echo 0 > /sys/class/misc/myled0/led_status # 灯亮
rmmod drv_led # /dev/myled0 和 /sys/class/misc/myled0/ 同时消失总结
| 问题 | 答案 |
|---|---|
| misc 设备主设备号? | 固定 10,次设备号动态分配 |
| 注册/注销函数? | misc_register / misc_deregister |
| /dev 节点还要 mknod 吗? | 不要,mdev 自动创建 |
| sysfs 属性挂哪? | misc_dev.this_device 上,/sys/class/misc/ 下 |
| misc 底层是什么? | 还是字符设备 cdev 框架的封装 |
三篇下来,LED 驱动从手动 mknod 一路简化到 misc_register。下一篇是总结篇《Linux 驱动架构详解》:把驱动怎么编译(树内 zImage vs 树外 .ko)、每个操作在系统运行后到底产生了什么效果,从头到尾串一遍。