MISC 设备驱动笔记(MD格式)
57.1 MISC 设备驱动简介
图解
原来我们cdev的写法,把前端创建用户空间的设备文件节点也放到设备结构体里面

现在用杂项,把这些完全剥离开来,设备结构体内存仅描述设备的特征属性,相当于开辟第三块内存,单独做用户空间的前端链路

核心概念与优势
MISC 设备驱动是 Linux 字符设备驱动的简化方案,核心解决主设备号资源紧张的问题,所有 MISC 设备驱动的主设备号固定为 10,无需手动创建 cdev,大幅简化字符设备驱动的编写流程。
就是原来我们编写一个led的驱动需要,在probe中需要:
- 初始化led设备结构体(后端链路:驱动-dts)
开辟一块内存A,设置设备结构体
- 获取设备节点
- 申请gpio
- xxx
- 注册字符设备(前端链路:用户空间-设备文件节点-驱动)
以前cdev这块内存(B∈A)是嵌在设备结构体的内存A里面的,cdev里面再指定操作方法集合(这个里面又操作设备结构体)
- 申请设备号
- 注册cdev
- 创建class,device
现在前端链路不需要做这么一大节了,只需要方便的注册misc设备(打包后的cdev设备)
- (前端链路)注册杂项设备
也开辟一块内存C(C不属于A),作为杂项设备,设置操作方法(内部来操作设备结构体的内存A)
- misc_register(&beep_miscdev)
注意:
beep_miscdev是杂项设备,是struct miscdevice类型,他表示一个抽象的杂项设备。里面指定了他的操作方法,而这个操作方法里面,则是绑定我们真实的设备结构体(比如open方法里面,把我们的设备结构体加入到私有数据中)
miscdevice 结构体解析
MISC 设备通过 miscdevice 结构体描述,定义于 include/linux/miscdevice.h,核心成员及含义如下:
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
umode_t mode;
const char *nodename;
};
|
关键成员设置
定义 MISC 设备时,必须设置 minor、name、fops 三个核心成员:
minor:子设备号,主设备号固定为 10,需用户手动指定(需保证未被其他设备占用);
name:设备名称,注册成功后会在 /dev 目录生成同名设备文件;
fops:字符设备操作集合(如 open/read/write 等函数实现),是 MISC 设备驱动的核心逻辑载体。
预定义子设备号
Linux 内核在 include/linux/miscdevice.h 中预定义了部分 MISC 子设备号(示例):
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2
#define ATARIMOUSE_MINOR 5
#define SUN_MOUSE_MINOR 6
...
#define MISC_DYNAMIC_MINOR 255
|
使用规则
可直接选用内核预定义的未占用子设备号;
也可自定义未被占用的子设备号,需保证唯一性。
MISC 设备注册与注销函数
注册函数:misc_register
函数原型:int misc_register(struct miscdevice *misc)
功能:一次性完成字符设备驱动的申请设备号、初始化 cdev、添加 cdev、创建类、创建设备等所有步骤,替代传统繁琐的操作流程。
参数:misc - 待注册的 MISC 设备结构体指针;
返回值:0 成功,负数 失败。
注销函数:misc_deregister
函数原型:int misc_deregister(struct miscdevice *misc)
功能:一次性完成 MISC 设备的删除 cdev、注销设备号、删除设备、删除类等所有清理工作,简化卸载流程。
参数:misc - 待注销的 MISC 设备结构体指针;
返回值:0 成功,负数 失败。
传统驱动流程 vs MISC 驱动流程
传统字符设备驱动流程(繁琐)
alloc_chrdev_region();
cdev_init();
cdev_add();
class_create();
device_create();
|
传统注销流程
cdev_del();
unregister_chrdev_region();
device_destroy();
class_destroy();
|
MISC 驱动流程(极简)
后续应用
基于 MISC 驱动框架,可结合 platform 驱动模型编写具体设备驱动(如 beep 蜂鸣器驱动),简化开发步骤。
#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h>
#include <linux/init.h> #include <linux/module.h> #include <linux/errno.h>
#include <linux/gpio.h> #include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#define BEEPOFF 0 #define BEEPON 1
struct miscbeep_dev{ struct device_node *nd; int beep_gpio; } miscbeep;
static int miscbeep_open(struct inode *inode, struct file *filp) { filp->private_data = &miscbeep; return 0; }
static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue; unsigned char databuf[1]; unsigned char beepstat; struct miscbeep_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt); if(retvalue < 0) { printk("kernel write failed!\r\n"); return -EFAULT; }
beepstat = databuf[0]; if(beepstat == BEEPON) { gpio_set_value(dev->beep_gpio, 0); } else if(beepstat == BEEPOFF) { gpio_set_value(dev->beep_gpio, 1); } return 0; }
static struct file_operations miscbeep_fops = { .owner = THIS_MODULE, .open = miscbeep_open, .write = miscbeep_write, };
#define MISCBEEP_NAME "miscbeep" #define MISCBEEP_MINOR 144
static struct miscdevice beep_miscdev = { .minor = MISCBEEP_MINOR, .name = MISCBEEP_NAME, .fops = &miscbeep_fops };
static int miscbeep_probe(struct platform_device *dev) { int ret = 0;
#if 1
printk("beep driver and device was matched!\r\n"); miscbeep.nd = of_find_node_by_path("/mygpiobeep@0"); if(miscbeep.nd == NULL) { printk("beep node not find!\r\n"); return -EINVAL; }
miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0); if(miscbeep.beep_gpio < 0) { printk("can't get beep-gpio"); return -EINVAL; }
ret = gpio_direction_output(miscbeep.beep_gpio, 1); if(ret < 0) { printk("can't set gpio!\r\n"); }
#endif
#if 1
ret = misc_register(&beep_miscdev); if(ret < 0){ printk("misc device register failed!\r\n"); return -EFAULT; }
#endif
return 0; }
static int miscbeep_remove(struct platform_device *dev) { gpio_set_value(miscbeep.beep_gpio, 1);
misc_deregister(&beep_miscdev); return 0; }
static const struct of_device_id beep_of_match[] = { { .compatible = "my-misc-beep" }, { } };
static struct platform_driver beep_driver = { .driver = { .name = "my-misc-beep", .of_match_table = beep_of_match, }, .probe = miscbeep_probe, .remove = miscbeep_remove, };
static int __init miscbeep_init(void) { return platform_driver_register(&beep_driver); }
static void __exit miscbeep_exit(void) { platform_driver_unregister(&beep_driver); }
module_init(miscbeep_init); module_exit(miscbeep_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");
|