我们已经对字符设备驱动框架有了一定的理解, 而本章要讲解的杂
项设备属于特殊的一种字符型设备, 是对字符设备的一种封装, 为最简单的字符设备。
文章目录
基本概念
杂项设备驱动具有以下特点:
- 主设备号固定为10
- 次设备号动态分配
- 简化了字符设备驱动的注册流程
- 适用于功能简单的设备
参考资料
字符设备相关资料,建议对比看看。尤其是这里的杂项设备和标准字符设备对比看,看流程、方法 对比来理解。
字符设备号申请
字符设备注册
创建字符设备节点
主要数据结构 miscdevice
在 驱 动 中 使 用 miscdevice 结 构 体 描 述 misc 设 备 , 该 结 构 体 定 义 在 “ 内 核 源 码
/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;
const char *nodename;
umode_t mode;
};
定义一个 misc 设备, 一般只需要填充 minor、 name、 fops 这三个成员变量。
minor 指次设备号, 可以从“内核源码/include/linux/miscdevice.h”文件中预定义的次设备号
挑选, 也可以自行定义子设备号
- minor:通常情况下将该参数设置为MISC_DYNAMIC_MINOR, 表示自动分配子设备号
- name 表示 misc 设备的名字。 misc 设备驱动注册成功之后, 会在 dev 目录下生成名为 name
的设备节点。 - fops 指向了 file_operations 的结构体, 表示字符设备的操作集合。
杂项设备的注册和卸载
不同于字符设备的注册和卸载的繁琐, 杂项设备的注册可以直接使用函数 misc_register 函
数来完成, 杂项设备的卸载可以直接使用 misc_deregister 函数来完成。
注册 misc_register(struct miscdevice *misc)
基于 misc_class 构造一个设备, 将 miscdevice 结构挂载到 misc_list 列表上, 并初始化与 linux
设备模型相关的结构。 进而起到杂项设备注册的作用
卸载 misc_deregister
从 mist_list 中删除 miscdevice, 进而起到杂项设备卸载的作用
实验
测试-源码demo
#include <linux/init.h> //初始化的头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块
#include <linux/fs.h> //注册设备节点的文件结构体
#include <linux/miscdevice.h> //注册杂项设备头文件
static int chrdev_open(struct inode *inode, struct file *file)
{
printk("This is chrdev_open \n");
return 0;
}
static ssize_t chrdev_read(struct file *file,char __user *buf, size_t size, loff_t *off)
{
printk("This is chrdev_read \n");
return 0;
}
static ssize_t chrdev_write(struct file *file,const char __user *buf,size_t size,loff_t *off)
{
printk("This is chrdev_write \n");
return 0;
}
static int chrdev_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations misc_fops = {
.owner=THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
.open = chrdev_open,//将open字段指向chrdev_open(...)函数
.read = chrdev_read,//将open字段指向chrdev_read(...)函数
.write = chrdev_write,//将open字段指向chrdev_write(...)函数
.release = chrdev_release,//将open字段指向chrdev_release(...)函数
};//定义file_operations结构体类型的变量misc_fops
struct miscdevice misc_dev = { //杂项设备结构体
.minor = MISC_DYNAMIC_MINOR, //动态申请的次设备号
.name = "hello_misc", //杂项设备名字是hello_misc
.fops = &misc_fops, //文件操作集
};
static int __init misc_init(void)//驱动入口函数
{
int ret;//定义int类型的变量ret,用来判断函数返回值
ret = misc_register(&misc_dev); //在初始化函数中注册杂项设备
if(ret < 0 ){
printk("cdev_add is error\n");
}
printk("misc registe is succeed \n");//打印注册杂项设备成功
return 0;
}
static void __exit misc_exit(void)//驱动出口函数
{
misc_deregister(&misc_dev); //在卸载函数中注销杂项设备
printk(" misc goodbye! \n");
printk("module exit \n");
}
module_init(misc_init);//注册入口函数
module_exit(misc_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("wang fang chen "); //作者信息
源码分析:misc_dev 、file_operations、misc_register
- misc_dev:杂项设备结构体。 标准字符设备的字符设备结构体为 cdev; 对于设备号申请,申请设备号的结构体是 dev_t
- file_operations: 文件操作结构体,这里和标准字符设备结构体一样的。
- misc_register:杂项设备的注册就一个函数方法;标准字符设备的注册就需要:初始化结构体、添加字符设备到内核;同时还需要创建类、创建类下面的设备
编译文件 Makefile
#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
obj-m += miscdevice.o
KDIR :=/home/wfc123/Linux/rk356x_linux/kernel
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
测试
insmod miscdevice.ko
misc registe is succeed” ,说明 misc 驱动注册成功。 输入以下命令查看加载的驱动模块,
找class 下的对应的杂项 device ls /sys/class/misc
到/sys/class/misc 目录下, 可以看到名为“test” 的文件夹已经被创建了, 在/sys/clas
s/misc 目录下有 misc 类的所有设备, 每个注册的杂项设备对应一个文件夹目录, 如下图
查看dev 下生成的杂项设备 ls /dev/hello_misc
[root@topeet:/]# ls /dev/hello_misc
/dev/hello_misc
如果需要查看所有信息,后面加上参数 -al ,结果如下:
[root@topeet:/]# ls /dev/hello_misc -al
crw------- 1 root root 10, 53 Jan 1 14:51 /dev/hello_misc
[root@topeet:/]#
可以看到这个杂项设备 主设备号10 次设备号53
总结
看杂项设备还是要和标准字符设备对比来看,
- 主设备号固定为10,次设备号动态分配,简化了字符设备驱动的注册流程,适用于功能简单的设备
- 杂项设备也有自己的结构体miscdevice 、file_operations ,却别与标准字符设备的 设备号结构体dev_t ,注册的字符设备结构体cdev ,文件操作集合结构体file_operations
- 操作流程上:杂项设备开发流程是 将杂项设备结构体miscdevice通过file_operations 结构体关联起来,然后通过方法 misc_register(&misc_dev) 注册一次 就完成了整个开发流程。 但是标准字符设备开发流程是 申请设备号->初始化字符设备->添加字符设备到内核-> /sys/class 下面的字符类创建->创建类下面的设备创建