前言
前面字符设备(一)-----驱动结构 字符设备(二)-----驱动模型 已经对字符设备有了一定的了解,下面看下杂项设备。杂项设备是一种特殊的字符设备,是对字符设备的一种封装。相比字符设备杂项设备有如下优势:
a、节省主设备号:杂项设备的主设备号固定为 10,而字符设备不管是动态分配还是静态分配设备号,都会消耗一个主设备号,进而造成了主设备号浪费。当系统中注册了多个 misc 设备驱动时,只需使用子设备号进行区分即可。
b、更加简单:杂项设备不需要进行复杂字符设备注册和class_create、和device_create等操作来创建设备节点,而只需要将基本信息通过结构体传递给相应处理函数即可。
在字符设备中使用cdev 结构体来描述一个设备,而在杂项设备中也有同样描述杂项设备的结构体 struct miscdevice,定义一个misc设备,一般只需要填充minor、name、fops这三个成员变量。
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;
};
minor次设备号可以手动在/include/linux/miscdevice.h 中预定的次设备号挑选,也可以自行设置没有被使用的设备号。通常情况下将该参数设置为MISC_DYNAMIC_MINOR,表示自动分配子设备号。
name 表示 misc 设备的名字。misc 设备驱动注册成功之后,会在 dev 目录下生成名为name 的设备节点。
一、杂项设备注册
int misc_register(struct miscdevice *misc)
二、 杂项设备卸载
杂项设备的卸载可以通过misc_deregister函数完成,非常的简单方便。
int misc_deregister(struct miscdevice *misc)
三、实验代码
#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/miscdevice.h> //注册杂项设备头文件
#include <linux/fs.h> //注册设备节点的文件结构体
static int misc_open(struct inode *inode, struct file *file)
{
printk("This is misc_open -\n");
return 0;
}
static ssize_t misc_read(struct file *file,char __user *buf, size_t size, loff_t *off)
{
printk("This is misc_read -\n");
return 0;
}
static ssize_t misc_write(struct file *file,const char __user *buf,size_t size,loff_t *off)
{
printk("This is misc_write- \n");
return 0;
}
static int misc_release(struct inode *inode, struct file *file)
{
printk("This is misc_release- \n");
return 0;
}
struct file_operations misc_fops = { //文件操作集
.owner = THIS_MODULE, 将 owner 字段指向本模块,可以避免在模块的操作正在被使用时卸载该模
.open = misc_open, //将 open 字段指向 misc_open(...)函数
.read = misc_read, //将 open 字段指向 misc_read(...)函数
.write = misc_write,//将 open 字段指向 misc_write(...)函数
.release = misc_release,//将 open 字段指向 misc_release(...)函数
};
struct miscdevice misc_dev = { //杂项设备结构体
.minor = MISC_DYNAMIC_MINOR, //动态申请的次设备号
.name = "test", //杂项设备名字是 hello_misc
.fops = &misc_fops, //文件操作集
};
static int __init misc_init(void)
{
int ret;
ret = misc_register(&misc_dev); //在初始化函数中注册杂项设备
if (ret < 0)
{
printk("misc registe 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");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ll");
app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd;//定义 int 类型的文件描述符
char buf[32];//定义读取缓冲区 buf
fd=open(argv[1],O_RDWR,0666);//调用 open 函数,打开输入的第一个参数文件,权限为可读可写
if(fd<0){
printf("open is error\n");
return -1;
}
printf("open is ok\n");
if(!strcmp(argv[2], "read")){
read(fd,buf,32);
}
else if(!strcmp(argv[2], "write")){
write(fd,"hello\n",6);
}
close(fd);//调用 close 函数,对取消文件描述符到文件的映射
return 0;
}
Makefile
export ARCH=arm64
export CC=/home/ljw/work/project/rk3568/rk356x_linux/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc
DEV:=misc
obj-m += $(DEV).o #xxx.c 对应.o 文件的名称。名称要保持一致。
APP:=app
KDIR :=/home/ljw/work/project/rk3568/rk356x_linux/kernel #内核源码所在虚拟机 ubuntu 的实际路径
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules #make 操作
$(CC) $(APP).c -o $(APP)
cp *.ko $(APP) /mnt/hgfs/share/$(DEV)
dir:
mkdir /mnt/hgfs/share/$(DEV)
app:
$(CC) $(APP).c -o $(APP)
cp:
cp *.ko $(APP) /mnt/hgfs/share/
clean:
make -C $(KDIR) M=$(PWD) clean
rm $(APP)
四、运行测试
insmod misc.ko
./app /dev/test read