在 Linux 中,使用主设备号来表示对应某一类驱动,使用次设备号来表示这类驱动下的各个设备。假如现在驱动要支持的主设备号相同,但是次设备号不同的设备,驱动程序要怎样编写呢?上一章节学习的私有数据 private_data 在此时就派上了用场。
一、container_of 函数简介
container_of 在 Linux 内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。那么可以使用这个函数获取不同设备的地址,来对不同的设备进行操作,从而一个驱动可以兼容不同的设备。
函数原型:
c
container_of(ptr, type, member)1
函数作用:通过结构体变量中某个成员的首地址获取到整个结构体变量的首地址。
参数含义:
ptr:结构体变量中某个成员的地址type:结构体的类型member:该结构体变量的具体名字
二、实验程序编写
源代码下载
bash
git clone git@gitee.com:yangxuesong314/linux-driver.git1
- 代码位于:
linux-driver/05.字符设备驱动模型/08.一个驱动兼容多个设备实验
驱动代码(file.c)
c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
struct device_test {
dev_t dev_num;
int major;
int minor;
struct cdev cdev_test;
struct class *class;
struct device *device;
char kbuf[32];
};
struct device_test dev1;
struct device_test dev2;
static int cdev_test_open(struct inode *inode, struct file *file)
{
file->private_data = container_of(inode->i_cdev, struct device_test, cdev_test);
printk("This is cdev_test_open\r\n");
return 0;
}
static ssize_t cdev_test_write(struct file *file, const char __user *buf, size_t size, loff_t *off)
{
struct device_test *test_dev = (struct device_test *)file->private_data;
if (copy_from_user(test_dev->kbuf, buf, size) != 0) {
printk("copy_from_user error\n");
return -1;
}
printk("test_dev->kbuf is %s\n", test_dev->kbuf);
return 0;
}
static ssize_t cdev_test_read(struct file *file, char __user *buf, size_t size, loff_t *off)
{
struct device_test *test_dev = (struct device_test *)file->private_data;
if (copy_to_user(buf, test_dev->kbuf, strlen(test_dev->kbuf)) != 0) {
printk("copy_to_user error\r\n");
return -1;
}
printk("This is cdev_test_read\r\n");
return 0;
}
static int cdev_test_release(struct inode *inode, struct file *file)
{
printk("This is cdev_test_release\r\n");
return 0;
}
struct file_operations cdev_test_fops = {
.owner = THIS_MODULE,
.open = cdev_test_open,
.read = cdev_test_read,
.write = cdev_test_write,
.release = cdev_test_release,
};
static int __init chr_fops_init(void)
{
int ret;
ret = alloc_chrdev_region(&dev1.dev_num, 0, 2, "alloc_name");
if (ret < 0) {
printk("alloc_chrdev_region is error\n");
return ret;
}
printk("alloc_chrdev_region is ok\n");
dev1.major = MAJOR(dev1.dev_num);
dev1.minor = MINOR(dev1.dev_num);
printk("major is %d \r\n", dev1.major); //打印主设备号
printk("minor is %d \r\n", dev1.minor); //打印次设备号
// 设备 1
dev1.cdev_test.owner = THIS_MODULE;
cdev_init(&dev1.cdev_test, &cdev_test_fops);
cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
dev1.class = class_create(THIS_MODULE, "test1");
dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test1");
dev2.major = MAJOR(dev1.dev_num + 1);
dev2.minor = MINOR(dev1.dev_num + 1);
printk("major is %d \r\n", dev2.major); //打印主设备号
printk("minor is %d \r\n", dev2.minor); //打印次设备号
// 设备 2
dev2.cdev_test.owner = THIS_MODULE;
cdev_init(&dev2.cdev_test, &cdev_test_fops);
cdev_add(&dev2.cdev_test, dev1.dev_num + 1, 1);
dev2.class = class_create(THIS_MODULE, "test2");
dev2.device = device_create(dev2.class, NULL, dev1.dev_num + 1, NULL, "test2");
return 0;
}
static void __exit chr_fops_exit(void)
{
unregister_chrdev_region(dev1.dev_num, 2);
cdev_del(&dev1.cdev_test);
cdev_del(&dev2.cdev_test);
device_destroy(dev1.class, dev1.dev_num);
device_destroy(dev2.class, dev1.dev_num + 1);
class_destroy(dev1.class);
class_destroy(dev2.class);
}
module_init(chr_fops_init);
module_exit(chr_fops_exit);
MODULE_LICENSE("GPL v2");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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
应用代码(app.c)
c
#include <stdio.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 fd1;
int fd2;
char buf1[32] = "nihao /dev/test1";
char buf2[32] = "nihao /dev/test2";
fd1 = open("/dev/test1", O_RDWR);
if (fd1 < 0) {
perror("open error \n");
return fd1;
}
write(fd1, buf1, sizeof(buf1));
close(fd1);
fd2 = open("/dev/test2", O_RDWR);
if (fd2 < 0) {
perror("open error \n");
return fd2;
}
write(fd2, buf2, sizeof(buf2));
close(fd2);
return 0;
}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
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
编译与测试
bash
make # 编译驱动,生成 file.ko
insmod file.ko # 加载驱动1
2
2

bash
aarch64-linux-gnu-gcc app.c -o app # 编译应用
./app # 运行应用1
2
2
