Linux内核-最小驱动示例

本文最后更新于:1 年前

驱动代码示例

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
67
68
69
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>

#define BUFFER_MAX (10)
#define OK (0)
#define ERROR (-1)

struct cdev *gDev;
struct file_operations *gFile;
dev_t devNum;
unsigned int subDevNum = 1;
int reg_major = 232;
int reg_minor = 0;
char *buffer;
int flag = 0;
int hello_open(struct inode *p, struct file *f)
{
printk(KERN_EMERG"hello_open\r\n");
return 0;
}

ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l)
{
printk(KERN_EMERG"hello_write\r\n");
return 0;
}
ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
{
printk(KERN_EMERG"hello_read\r\n");
return 0;
}
int hello_init(void)
{

devNum = MKDEV(reg_major, reg_minor); //获取设备号,实际是主设备号左移后|上次设备号
if(OK == register_chrdev_region(devNum, subDevNum, "helloworld")){ //也可以让内核自己注册
printk(KERN_EMERG"register_chrdev_region ok \n");
}else {
printk(KERN_EMERG"register_chrdev_region error n");
return ERROR;
}
printk(KERN_EMERG" hello driver init \n");
gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
gFile->open = hello_open; //实现file_operations中的函数指针
gFile->read = hello_read;
gFile->write = hello_write;
gFile->owner = THIS_MODULE;
cdev_init(gDev, gFile);
cdev_add(gDev, devNum, 3); //这两句之后,内核可以通过设备号找到对应操作
return 0;
}

void __exit hello_exit(void)
{
cdev_del(gDev);
unregister_chrdev_region(devNum, subDevNum);
return;
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

其中 file_operations结构体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
        int (*fasync) (int, struct file *, int);
...
};

Makefile

1
2
3
4
5
6
7
8
9
10
ifneq ($(KERNELRELEASE),)
obj-m := helloworld.o
else
PWD := $(shell pwd)
KDIR := /lib/modules/$(shell uname -r)/build
all:
        make -C $(KDIR) M=$(PWD)
clean:        
        rm -rf *.o *.ko *.mod.c *.symvers *.c~ *~
endif

进入内核目录执行makefile再返回当前目录时会定义KERNELRELEASE

如果没有bulid目录说明缺少内核开发包,需要apt下载

测试时需要手动创建设备文件

mknod /dev/hello c 232 0