Liunx学习笔记 实现一个misc设备驱动

系统环境如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
                         ./+o+-       root@linux
yyyyy- -yyyyyy+ OS: Ubuntu 18.04 bionic
://+//////-yyyyyyo Kernel: x86_64 Linux 5.0.6-laohu-v1.0
.++ .:/++++++/-.+sss/` Uptime: 1h 16m
.:++o: /++++++++/:--:/- Packages: 2048
o:+o+:++.`..```.-/oo+++++/ Shell: bash
.:+o:+o/. `+sssoo+/ Resolution: 2560x1080
.++/+:+oo+o:` /sssooo. WM: GNOME Shell
/+++//+:`oo+o /::--:. WM Theme: Adwaita
\+/+o+++`o++o ++////. CPU: Intel Core i5-4308U @ 4x 3.3GHz [27.8°C]
.++.o+++oo+:` /dddhhh. GPU: intel
.+.o+oo:. `oddhhhh+ RAM: 1798MiB / 3862MiB
\+.++o+o``-````.:ohdhhhhh+
`:o+++ `ohhhhhhhhyo++os:
.o:`.syhhhhhhh/.oo++o`
/osyyyyyyo++ooo+++/
````` +oo+++o\:
`oo++.

为什么要学习misc设备的编程?

因为,如果我们 每个驱动设备都要像最初那样子去写一个字符设备驱动一样,要分配主设备号,次设备号,实现对应的文件操作函数等等的步骤,未免就有点多了,而且也不好记住它,为此,Linux内核提供了一系列偷懒的技巧,那就是实现了misc设备,其实misc设备,也算是字符设备,只不过对字符设备进行了封装,看看下面的介绍就知道了。

打开内核/include/linux/miscdevice.h 找到misc设备的结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct miscdevice  {
//次设备号一般赋值为MISC_DYNAMIC_MINOR---->由内核自动去分配次设备号
int minor;
//misc设备的名称
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(文件操作函数).
misc设备是对字符设备做了一个再次的封装,而且,在misc设备中,主设备号都是一样的,都是10,只有次设备号不同,当我们不知道内核中应该去分配那个此设备号时,可以直接给minor赋值为MISC_DYNAMIC_MINOR这个宏,意思就是由内核来帮我们分配次设备号。
name就不用说了,如果设备注册成功,在根文件系统/dev/下就会有注册设备后的name。
fops就是一系列的文件操作函数啦,什么open , read ,write , ioctl等等,很多,跟写字符设备是一样的

驱动实现源码代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/miscdevice.h>
#include<linux/fs.h>


#define DEVICE_NAME "my_misc_dev"

//实现open函数
int my_misc_dev_open(struct inode *inode, struct file *filp)
{
printk("minipc misc dev open!\n");
return 0 ;
}

//实现close函数
int my_misc_dev_close(struct inode *inode, struct file *filp)
{
printk("minipc misc dev close!\n");
return 0 ;
}

//初始化文件操作结构体
struct file_operations file_ops = {
.owner = THIS_MODULE,
.open = my_misc_dev_open,
.release = my_misc_dev_close,
};

//初始化misc设备结构体
struct miscdevice my_misc_dev = {
//由内核自动分配次设备号
.minor = MISC_DYNAMIC_MINOR ,
//初始化设备名称
.name = DEVICE_NAME ,
//初始化文件操作结构体
.fops = &file_ops,
};


static int __init my_misc_dev_init(void)
{
int ret_error ;
//注册misc设备
int ret = misc_register(&my_misc_dev);
if(ret != 0){
ret_error = ret ;
printk("misc register fair!\n");
goto fair ;
}
printk("misc init success!\n");
return ret ;
fair:
return ret_error ;
}

static void __exit my_misc_dev_exit(void)
{
//注销misc设备
misc_deregister(&my_misc_dev);
}

module_init(my_misc_dev_init);
module_exit(my_misc_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LAOHU add misc driver");

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
TARGET		?= $(shell uname -r)
KERNEL_MODULES ?= /lib/modules/$(TARGET)
KERNEL_BUILD ?= $(KERNEL_MODULES)/build
SYSTEM_MAP ?= $(KERNEL_BUILD)/System.map

DRIVER := my_misc

obj-m := $(patsubst %,%.o,$(DRIVER))
obj-ko := $(patsubst %,%.ko,$(DRIVER))

MAKEFLAGS += --no-print-directory

.PHONY: all modules clean

all: modules

# Targets for running make directly in the external module directory:

modules clean:
@$(MAKE) -C $(KERNEL_BUILD) M=$(CURDIR) $@

编译程序,加载驱动,可以看到设备注册成功,主设备号:10,次设备号55

![misc](Linux驱动学习笔记-实现一个misc设备驱动/misc 01.png)

------ 本文结束------