misc 的意思是混合的杂项的,所以misc 设备驱动也叫做杂项设备驱动。它的注册跟使用比较的简单,所以比较适用于功能简单的设备。正因为简单,所以它通常嵌套在 platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果。杂项设备是字符设备的一种,杂项设备可以自动生成设备节点。
一、介绍
1.1、设备号
设备号包含主设备号和次设备号,设备号是计算机识别设备的一种方式,主设备号相同的就被视为同一类设备,主设备号在 Linux 系统里面是唯一的,次设备号不一定唯一。所有的 misc 设备驱动的主设备号都为10,不同的设备使用不同的从设备号。
1.2、miscdevice 结构体
misc 设备用 miscdevice 结构体表示,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;
const char *nodename;
umode_t mode;};
当我们创建一个 misc 设备的 miscdevice 结构体时,需要我们指定 minor、name 和fops 这三个成员变量。
minor 表示次设备号,需要用户设置,在 Linux 内核中有一些预定义的misc 设备的次设备号,定义在 include/linux/miscdevice.h 文件中,如下所示:
#define PSMOUSE_MINOR 1#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
......
#define MISC_DYNAMIC_MINOR 255
我们设置子设备号时要注意不要重复使用其他设备的子设备号。可以从这些预定义的子设备号中选择一个,也可以自定义。
name 就是这个 misc 设备的名字,当设备注册成功后,会在/dev 目录下自动生成一个名为name的设备文件。
fops 就是这个 misc 设备的操作集合。
当创建好 miscdevice 结构体后,使用 misc_register 函数向系统中注册一个misc 设备,函数原型如下:
在设备驱动的卸载函数中,使用 misc_deregister 函数来注销掉 misc 设备。函数原型如下
在 miscdevice 结构体的第四行,它指向了一个 file_operation 的结构体。file_operations 文件操作集在定义在 include/linux/fs.h 。
1.3、总结
注册杂项设备有一个通用的思路和方法,总结为三个步骤:
(1)、填充 miscdevice 这个结构体
(2)、 填充 file_operations 这个结构体
(3)、 注册杂项设备并生生成设备节点。
二、驱动代码示例
#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/miscdevice.h> /*注册杂项设备头文件*/
#include <linux/fs.h> /*注册设备节点的文件结构体*/
struct file_operations misc_fops=
{ //文件操作集
.owner = THIS_MODULE
};
struct miscdevice misc_dev =
{ //杂项设备结构体
.minor = MISC_DYNAMIC_MINOR, //动态申请的次设备号
.name = "hello_misc", //杂项设备名字是 hello_misc
.fops = &misc_fops, //文件操作集
};
static int 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 misc_exit(void)
{ //在卸载函数中注销杂项设备
misc_deregister(&misc_dev);
printk(" misc gooodbye!\n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
编写Makefile 文件
obj-m += misc.o #先写生成的中间文件的名字是什么,-m 的意思是把我们的驱动编译成模块
KDIR:=/home/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga/
PWD?=$(shell pwd) #获取当前目录的变量
all:
make -C $(KDIR) M=$(PWD) modules #make 会进入内核源码的路径,然后把当前路径下的代码编译成模块