Misc(杂项设备)
1. 什么是 Misc
Misc(Miscellaneous Device,杂项设备)是 Linux 提供的一种简化字符设备驱动框架,本质上仍然属于字符设备。
对于 LED、蜂鸣器、ADC 等功能较为简单的设备,如果仍然按照普通字符设备的方式编写驱动,需要完成:
- 申请设备号
- 初始化
cdev - 添加
cdev - 创建
class - 创建设备节点
这些步骤在很多驱动中都是重复的,因此 Linux 将这些公共代码进行了封装,形成了Misc 驱动框架。
开发者只需要:
- 定义
miscdevice - 编写
file_operations - 调用
misc_register()
即可完成字符设备注册,大大减少了代码量。
2. Misc 与普通字符设备的区别
普通字符设备注册流程:
申请设备号 │ ▼ alloc_chrdev_region() │ ▼ cdev_init() │ ▼ cdev_add() │ ▼ class_create() │ ▼ device_create() │ ▼ 生成 /dev/xxxMisc 注册流程:
填写 miscdevice │ ▼ misc_register() │ ▼ 自动完成以上所有步骤因此:
misc_register() ≈ alloc_chrdev_region() + cdev_init() + cdev_add() + class_create() + device_create()3. 设备树修改
3.1 添加 Beep 节点
beep { compatible = "alientek,beep"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_beep>; beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>; status = "okay"; };各属性说明:
| 属性 | 说明 |
|---|---|
| compatible | 与 Platform Driver 进行匹配 |
| pinctrl-names | 默认使用的引脚配置 |
| pinctrl-0 | 指向具体的引脚配置节点 |
| beep-gpio | 指定蜂鸣器所使用的 GPIO |
| status | "okay"表示启用设备 |
3.2 添加 pinctrl 节点
在iomuxc_snvs节点下添加:
pinctrl_beep: beepgrp { fsl,pins = < MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 >; };说明:
| 配置 | 作用 |
|---|---|
| pinctrl_beep | 标签,供其它节点引用 |
| beepgrp | 节点名称 |
| MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 | 将引脚复用为 GPIO5_IO01 |
| 0x10B0 | PAD 电气属性配置 |
4. 驱动主体框架
整个 Misc + Platform 驱动主要由六部分组成:
Platform Driver │ ├── file_operations 字符设备操作集 ├── miscdevice Misc设备描述 ├── probe() 设备初始化 ├── remove() 设备移除 ├── of_match_table 设备树匹配 └── platform_driver Platform驱动5. 驱动模板
#include<linux/module.h>#include<linux/kernel.h>#include<linux/init.h>#include<linux/fs.h>#include<linux/uaccess.h>#include<linux/gpio.h>#include<linux/of.h>#include<linux/of_gpio.h>#include<linux/platform_device.h>#include<linux/miscdevice.h>#defineXXX_NAME"xxx"#defineXXX_MINOR144/*--------------------设备结构体--------------------*/structxxx_dev{structdevice_node*nd;intxxx_gpio;};staticstructxxx_devxxx;/*--------------------open--------------------*/staticintxxx_open(structinode*inode,structfile*filp){filp->private_data=&xxx;return0;}/*--------------------release--------------------*/staticintxxx_release(structinode*inode,structfile*filp){return0;}/*--------------------write--------------------*/staticssize_txxx_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*ppos){intret;unsignedchardatabuf[1];structxxx_dev*dev=filp->private_data;ret=copy_from_user(databuf,buf,count);if(ret)return-EFAULT;if(databuf[0])gpio_set_value(dev->xxx_gpio,1);elsegpio_set_value(dev->xxx_gpio,0);returncount;}/*--------------------文件操作集--------------------*/staticconststructfile_operationsxxx_fops={.owner=THIS_MODULE,.open=xxx_open,.write=xxx_write,.release=xxx_release,};/*--------------------Misc设备--------------------*/staticstructmiscdevicexxx_miscdev={.minor=XXX_MINOR,.name=XXX_NAME,.fops=&xxx_fops,};/*--------------------probe--------------------*/staticintxxx_probe(structplatform_device*pdev){intret;/* 1. 获取设备树节点 */xxx.nd=pdev->dev.of_node;/* 2. 获取GPIO */xxx.xxx_gpio=of_get_named_gpio(xxx.nd,"xxx-gpio",0);if(xxx.xxx_gpio<0)return-EINVAL;/* 3. 申请GPIO */ret=gpio_request(xxx.xxx_gpio,"xxx-gpio");if(ret)returnret;/* 4. 设置GPIO方向 */ret=gpio_direction_output(xxx.xxx_gpio,1);if(ret)gotofail_gpio;/* 5. 注册Misc设备 */ret=misc_register(&xxx_miscdev);if(ret)gotofail_gpio;printk("probe success!\n");return0;fail_gpio:gpio_free(xxx.xxx_gpio);returnret;}/*--------------------remove--------------------*/staticintxxx_remove(structplatform_device*pdev){gpio_set_value(xxx.xxx_gpio,1);gpio_free(xxx.xxx_gpio);misc_deregister(&xxx_miscdev);printk("remove success!\n");return0;}/*--------------------设备树匹配表--------------------*/staticconststructof_device_idxxx_of_match[]={{.compatible="manufacturer,xxx"},{},};/*--------------------Platform Driver--------------------*/staticstructplatform_driverxxx_driver={.driver={.name="xxx",.of_match_table=xxx_of_match,},.probe=xxx_probe,.remove=xxx_remove,};/*--------------------驱动入口--------------------*/staticint__initxxx_init(void){returnplatform_driver_register(&xxx_driver);}/*--------------------驱动出口--------------------*/staticvoid__exitxxx_exit(void){platform_driver_unregister(&xxx_driver);}module_init(xxx_init);module_exit(xxx_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("huangjiajing");6. probe() 初始化流程
Platform 总线匹配成功后,会自动调用probe()。
整个初始化流程如下:
Platform匹配成功 │ ▼ probe() │ ├──① 获取设备树节点 ├──② 获取GPIO编号 ├──③ 申请GPIO资源 ├──④ 设置GPIO方向 ├──⑤ 注册Misc设备 │ ▼ 生成 /dev/xxx其中:
misc_register(&xxx_miscdev);放在最后执行。
原因是:
只有 GPIO 等硬件资源全部初始化完成后,才允许用户访问/dev/xxx,避免设备尚未初始化完成就被应用程序打开。
因此 Linux 驱动遵循:
先初始化硬件,再注册设备。
7. remove() 流程
驱动卸载时,Linux 会自动调用remove()。
主要完成以下工作:
remove() │ ├──关闭设备 ├──释放GPIO ├──注销Misc设备 └──释放资源对应代码:
gpio_set_value(xxx.xxx_gpio,1);gpio_free(xxx.xxx_gpio);misc_deregister(&xxx_miscdev);8. 开发流程总结
修改设备树 │ ▼ 添加 compatible │ ▼ 编写 file_operations │ ▼ 定义 miscdevice │ ▼ 编写 probe() │ ├──获取设备树 ├──获取GPIO ├──申请GPIO ├──配置GPIO └──misc_register() │ ▼ 编写 remove() │ ├──misc_deregister() └──gpio_free() │ ▼ 注册 Platform Driver │ ▼ Platform 自动匹配 │ ▼ 调用 probe() │ ▼ 生成 /dev/xxx9. 使用模板时需要修改的内容
| 修改位置 | 蜂鸣器 | LED |
|---|---|---|
XXX_NAME | "miscbeep" | "miscled" |
XXX_MINOR | 144 | 145 |
struct xxx_dev | beep_gpio | led_gpio |
| GPIO 属性 | "beep-gpio" | "led-gpio" |
compatible | "alientek,beep" | "atkalpha,gpioled" |
| GPIO 控制逻辑 | 控制蜂鸣器高低电平 | 控制 LED 高低电平 |
.name | "miscbeep" | "gpioled" |
10. 普通字符设备与 Misc 对比
| 普通字符设备 | Misc 杂项设备 |
|---|---|
alloc_chrdev_region() | misc_register() |
cdev_init() | 不需要 |
cdev_add() | 不需要 |
class_create() | 不需要 |
device_create() | 不需要 |
device_destroy() | misc_deregister() |
class_destroy() | 不需要 |
unregister_chrdev_region() | 不需要 |
| 代码量较多 | 代码量少,开发效率高 |
总结:Misc 本质上仍然是字符设备,只是 Linux 对字符设备注册流程进行了封装。对于 LED、蜂鸣器、按键、ADC 等简单外设,优先使用 Misc 驱动可以减少大量重复代码,提高开发效率。