1.6介绍一种内核与用户空间通信的方法-misc设备ioctl机制
块设备驱动开发中往往需要配合用户态的管理程序工具,管理我们的块设备,此时我们需要涉及用户空间程序与块设备通信的方法,ioctl机制就是接下来需要学习和实战的,通过ioctl机制,用户态负责发送各种命令给内核驱动;内核态接收用户态发送的命令,并根据命令执行相应的动作,如下图所示。
ioctl提供用户态程序使用内核态函数的通道,此时需要注册一个字符设备来实现,我们使用misc这个字符设备来实现。
Ioctl.h
1 #ifndef _IOCTL_H
2 #define _IOCTL_H
3
4 /* misc device name*/
5 #define MISC_NAME "ioctl_test"
6
7 /**
8 *define ioctl codes for interfacing between kernel_module and user program
9 */
10 #define IOCTL_CODE 0xcc /* major type code - adjusted for target system */
11
12 #define TEST_CMD _IOWR (IOCTL_CODE, 0x09, unsigned long)
13
14 #endif
user.c
1 #include <sys/ioctl.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <stdio.h>
5 #include "ioctl.h"
6
7 int main(void)
8 {
9 unsigned long arg = 2013;
10 int fd;
11
12 fd = open("/dev/"MISC_NAME, O_RDWR, 0);
13 if (fd < 0) {
14 printf("Failed to open/dev/%s\n", MISC_NAME);
15 goto out;
16 }
17
18 if (ioctl(fd, TEST_CMD, &arg) < 0) {
19 printf("Failed to executeioctl!\n");
20 }
21
22 close(fd);
23 out:
24 return 0;
25 }
kernel.c
1 /*
2 * IOCTL
3 *Author: talk@studio
4 */
5
6 #include <linux/fs.h>
7 #include <linux/uaccess.h>
8 #include <linux/module.h>
9 #include <linux/miscdevice.h>
10
11 #include "ioctl.h"
12
13 static int ioctl(struct inode *inode,struct file *file,
14 unsigned int cmd,unsigned long user)
15 {
16 unsigned long arg;
17 int ret = 0;
18
19 if (_IOC_TYPE(cmd) != IOCTL_CODE) {
20 printk("Unknown ioctlcommand 0x%x\n", cmd);
21 ret = -ENOTTY;
22 goto out;
23 }
24
25 switch (cmd) {
26 case TEST_CMD:
27 ret =copy_from_user(&arg, (unsigned long *)user, sizeof(unsigned lon g));
28 if (ret) {
29 printk("copy from user failed!\n");
30 } else {
31 printk("received IOctl[arg=%lu] from user!\n", arg);
32 }
33 break;
34 default:
35 printk("Unknownioctl command[0x%x].\n", cmd);
36 ret = -ENOTTY;
37 break;
38 }
39 out:
40 return ret;
41 }
42
43 static struct file_operations_misc_ctl_fops = {
44 .ioctl = ioctl,
45 .owner = THIS_MODULE,
46 };
47
48 static struct miscdevice _misc_dev = {
49 .minor = MISC_DYNAMIC_MINOR,
50 .name = MISC_NAME,
51 .fops = &_misc_ctl_fops
52 };
53
54 static int __init ioctl_init(void)
55 {
56 int ret;
57
58 ret = misc_register(&_misc_dev);
59 if (ret) {
60 printk("Register ioctldevice[%s] failed\n", _misc_dev.name);
61 }
62 return ret;
63 }
64
65 static void __exit ioctl_exit(void)
66 {
67 if (misc_deregister(&_misc_dev) < 0) {
68 printk("Deregister ioctlcontrol device[%s] failed\n", _misc_dev.name);
69 }
70 return;
71 }
72
73 module_init(ioctl_init);
74 module_exit(ioctl_exit);
75 MODULE_LICENSE("GPL");
1.7逐渐成型 - 构件完善的管理工具及内核块设备驱动模块
经过上面四节的实战和学习,我们把代码完善一下,实现一个完整的用户态管理功能和内核块设备驱动的代码设计,我们需要完成如下功能,下图展示了我们程序各个模块的关系。
我们看一下代码如何实现,首先看公用头文件定义
fbd_ioctl.h
1 /*
2 *Author: talk@studio
3 */
4
5 #ifndef _IOCTL_H
6 #define _IOCTL_H
7
8 /* misc device name*/
9 #define MISC_NAME "fbd_misc_dev"
10
11 /* param structure for device create */
12 struct create_param {
13 char fbd_dev_name[32];
14 char lower_dev_path[64];
15 };
16
17 struct delete_param {
18 char fbd_dev_name[32];
19 };
20
21 /**
22 *define ioctl codes for interfacing between kernel_module and user program
23 */
24 #define IOCTL_CODE 0xcc /* major type code - adjusted for target system */
25
26 #define CREATE_CMD _IOWR (IOCTL_CODE, 0x0A, struct create_param)
27 #define DELETE_CMD _IOWR (IOCTL_CODE, 0x0B, struct delete_param)
28
29 #endif
用户态代码
1 /*
2 *Author: talk@studio
3 */
4
5 #include <sys/ioctl.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <stdio.h>
9
10 #include"../common/include/fbd_ioctl.h"
11
12 int main(void)
13 {
14 int fd;
15 struct create_param ctr_param = {
16 .fbd_dev_name ="fbd_dev1",
17 .lower_dev_path = "/dev/sdb",
18 };
19
20 fd = open("/dev/"MISC_NAME, O_RDWR, 0);
21 if (fd < 0) {
22 printf("Failed to open/dev/%s\n", MISC_NAME);
23 goto out;
24 }
25
26 if (ioctl(fd, CREATE_CMD, &ctr_param) < 0) {
27 printf("Failed to executeioctl!\n");
28 }
29
30 close(fd);
31 out:
32 return 0;
33 }
34
1 /*
2 *Author: talk@studio
3 */
4
5 #include <sys/ioctl.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <stdio.h>
9
10 #include"../common/include/fbd_ioctl.h"
11
12 int main(void)
13 {
14 int fd;
15 struct delete_param del_param = {
16 .fbd_dev_name ="fbd_dev1",
17 };
18
19 fd = open("/dev/"MISC_NAME, O_RDWR, 0);
20 if (fd < 0) {
21 printf("Failed to open/dev/%s\n", MISC_NAME);
22 goto out;
23 }
24
25 if (ioctl(fd, DELETE_CMD, &del_param) < 0) {
26 printf("Failed to executeioctl!\n");
27 }
28
29 close(fd);
30 out:
31 return 0;
32 }
33
内核驱动代码
1 #ifndef _FBD_DRIVER_H
2 #define _FBD_DRIVER_H
3 #include <linux/init.h>
4 #include <linux/module.h>
5 #include <linux/blkdev.h>
6 #include <linux/bio.h>
7 #include <linux/genhd.h>
8 #include <linux/fs.h>
9 #include <linux/uaccess.h>
10 #include <linux/miscdevice.h>
11
12 #define SECTOR_BITS (9)
13 #define DEV_NAME_LEN 32
14
15 #define DRIVER_NAME "filter driver"
16
17 struct fbd_dev {
18 char fbd_dev_name[DEV_NAME_LEN];
19 struct request_queue *queue;
20 struct gendisk *disk;
21 sector_t size; /* devicesize in Bytes */
22
23 char lower_dev_name[DEV_NAME_LEN];
24 struct block_device *lower_bdev;
25 };
26
27 struct bio_context {
28 void *old_private;
29 void *old_callback;
30 };
31 #endif
1 /**
2 * fbd-driver - filter blockdevice driver
3 * Author: Talk@studio
4 **/
5 #include "fbd_driver.h"
6 #include "../common/include/fbd_ioctl.h"
7
8 static int fbd_driver_major = 0;
9
10 static struct fbd_dev fbd_dev =
11 {
12 .fbd_dev_name = "\0",
13 .queue = NULL,
14 .disk = NULL,
15 .lower_dev_name = "\0",
16 .lower_bdev = NULL,
17 .size = 0
18 };
19
20 static int fbddev_open(struct inode *inode,struct file *file);
21 static int fbddev_close(struct inode*inode, struct file *file);
22
23 static struct block_device_operationsdisk_fops = {
24 .open = fbddev_open,
25 .release = fbddev_close,
26 .owner = THIS_MODULE,
27 };
28
29 static int fbddev_open(struct inode *inode,struct file *file)
30 {
31 printk("device is opened by:[%s]\n", current->comm);
32 return 0;
33 }
34
35 static int fbddev_close(struct inode*inode, struct file *file)
36 {
37 printk("device is closedby:[%s]\n", current->comm);
38 return 0;
39 }
40
41 static int fbd_io_callback(struct bio *bio,unsigned int bytes_done, int error)
42 {
43 struct bio_context *ctx = bio->bi_private;
44
45 bio->bi_private = ctx->old_private;
46 bio->bi_end_io = ctx->old_callback;
47 kfree(ctx);
48
49 printk("returned [%s] io request, end on sector %llu!\n",
50 bio_data_dir(bio) == READ ?"read" : "write",
51 bio->bi_sector);
52
53 if (bio->bi_end_io) {
54 bio->bi_end_io(bio,bytes_done, error);
55 }
56
57 return 0;
58 }
59
60 static int make_request(structrequest_queue *q, struct bio *bio)
61 {
62 struct fbd_dev *dev = (struct fbd_dev *)q->queuedata;
63 struct bio_context *ctx;
64
65 printk("device [%s] recevied [%s] io request, "
66 "access on devsector [%llu], length is [%u] sectors.\n",
67 dev->disk->disk_name,
68 bio_data_dir(bio) == READ ?"read" : "write",
69 bio->bi_sector,
70 bio_sectors(bio));
71
72 ctx = kmalloc(sizeof(struct bio_context), GFP_KERNEL);
73 if (!ctx) {
74 printk("alloc memory forbio_context failed!\n");
75 bio_endio(bio,bio->bi_size, -ENOMEM);
76 goto out;
77 }
78 memset(ctx, 0, sizeof(struct bio_context));
79
80 ctx->old_private = bio->bi_private;
81 ctx->old_callback = bio->bi_end_io;
82 bio->bi_private = ctx;
83 bio->bi_end_io = fbd_io_callback;
84
85 bio->bi_bdev = dev->lower_bdev;
86 submit_bio(bio_rw(bio), bio);
87 out:
88 return 0;
89 }
90
91 static int dev_create(char *fbd_dev_name,char *lower_dev_path)
92 {
93 int ret = 0;
94 struct fbd_dev *dev = &fbd_dev;
95
96 if(fbd_dev.disk){
97 printk("device[%s] isalready exist, delete it first!\n", fbd_dev.fbd_dev_nam e);
98 ret = -EEXIST;
99 return ret;
100 }
101
102 dev->disk = alloc_disk(1);
103 if (!dev->disk) {
104 printk("alloc diskerror");
105 ret = -ENOMEM;
106 goto err_out1;
107 }
108
109 dev->queue =blk_alloc_queue(GFP_KERNEL);
110 if (!dev->queue) {
111 printk("alloc queueerror");
112 ret = -ENOMEM;
113 goto err_out2;
114 }
115
116 /* init queue */
117 blk_queue_make_request(dev->queue,make_request);
118 dev->queue->queuedata = dev;
119
120 /* init gendisk */
121 strncpy(dev->disk->disk_name,fbd_dev_name, DEV_NAME_LEN);
122 dev->disk->major =fbd_driver_major;
123 dev->disk->first_minor = 0;
124 dev->disk->fops = &disk_fops;
125
126 dev->lower_bdev =open_bdev_excl(lower_dev_path, FMODE_WRITE | FMODE_READ, dev->lower _bdev);
127 if (IS_ERR(dev->lower_bdev)) {
128 printk("Open thedevice[%s]'s lower dev [%s] failed!\n", fbd_dev_name, lower_ dev_path);
129 ret = -ENOENT;
130 goto err_out3;
131 }
132
133 dev->size =get_capacity(dev->lower_bdev->bd_disk) << SECTOR_BITS;
134
135 set_capacity(dev->disk,(dev->size >> SECTOR_BITS));
136
137 /* bind queue to disk */
138 dev->disk->queue =dev->queue;
139
140 /* add disk to kernel */
141 add_disk(dev->disk);
142
143 strncpy(dev->fbd_dev_name,fbd_dev_name, DEV_NAME_LEN);
144 strncpy(dev->lower_dev_name,lower_dev_path, DEV_NAME_LEN);
145 return 0;
146err_out3:
147 blk_cleanup_queue(dev->queue);
148err_out2:
149 put_disk(dev->disk);
150err_out1:
151 memset(&fbd_dev, 0, sizeof(structfbd_dev));
152 return ret;
153 }
154
155 staticint dev_delete(char *fbd_dev_name)
156 {
157 int ret = 0;
158 struct fbd_dev *dev = &fbd_dev;
159
160 if (strcmp(fbd_dev_name,dev->fbd_dev_name) == 0) {
161 printk("delete the device[%s]!\n", fbd_dev_name);
162 close_bdev_excl(dev->lower_bdev);
163 del_gendisk(dev->disk);
164 put_disk(dev->disk);
165 blk_cleanup_queue(dev->queue);
166 memset(&fbd_dev, 0,sizeof(struct fbd_dev));
167 } else {
168 printk("device[%s] isn'texist!\n", fbd_dev_name);
169 ret = -EEXIST;
170 }
171 return ret;
172 }
173
174 staticint ioctl(struct inode *inode, struct file *file,
175 unsigned int cmd,unsigned long user)
176 {
177 struct delete_param del_param;
178 struct create_param ctr_param;
179 int ret = 0;
180
181 if (_IOC_TYPE(cmd) != IOCTL_CODE) {
182 printk("Unknown ioctl command0x%x\n", cmd);
183 ret = -ENOTTY;
184 goto out;
185 }
186
187 switch (cmd) {
188 case CREATE_CMD:
189 ret =copy_from_user(&ctr_param, (void *)user, sizeof(struct create_p aram));
190 if (ret) {
191 printk("copy from user failed!\n");
192 } else {
193 ret = dev_create(ctr_param.fbd_dev_name,ctr_param.lower_dev_ path);
194 }
195 break;
196 case DELETE_CMD:
197 ret =copy_from_user(&del_param, (void *)user, sizeof(struct delete_p aram));
198 if (ret) {
199 printk("copy from user failed!\n");
200 } else {
201 ret =dev_delete(del_param.fbd_dev_name);
202 }
203 break;
204 default:
205 printk("Unknownioctl command[0x%x].\n", cmd);
206 ret = -ENOTTY;
207 break;
208 }
209 out:
210 return ret;
211 }
212
213 staticstruct file_operations _misc_ctl_fops = {
214 .ioctl = ioctl,
215 .owner = THIS_MODULE,
216 };
217
218 staticstruct miscdevice _misc_dev = {
219 .minor = MISC_DYNAMIC_MINOR,
220 .name = MISC_NAME,
221 .fops = &_misc_ctl_fops
222 };
223
224 staticint __init fbd_driver_init(void)
225 {
226 int ret;
227
228 /* register fbd driver, get the drivermajor number*/
229 fbd_driver_major = register_blkdev(fbd_driver_major,DRIVER_NAME);
230 if (fbd_driver_major < 0) {
231 printk("get majorfail");
232 ret = -EIO;
233 goto err_out1;
234 }
235
236 ret = misc_register(&_misc_dev);
237 if (ret) {
238 printk("Register ioctldevice[%s] failed\n", _misc_dev.name);
239 goto err_out2;
240 }
241
242 printk("block device driver initsuccessfuly!\n");
243 return ret;
244err_out2:
245 unregister_blkdev(fbd_driver_major,DRIVER_NAME);
246err_out1:
247 return ret;
248 }
249
250 staticvoid __exit fbd_driver_exit(void)
251 {
252 if(fbd_dev.disk){
253 printk("device[%s] isalready exist, delete it first!\n", fbd_dev.fbd_dev_nam e);
254 dev_delete(fbd_dev.fbd_dev_name);
255 }
256
257 if(misc_deregister(&_misc_dev)< 0) {
258 printk("Deregister ioctlcontrol device[%s] failed\n", _misc_dev.name);
259 }
260
261 /* unregister fbd driver */
262 unregister_blkdev(fbd_driver_major,DRIVER_NAME);
263 printk("block device driver exitsuccessfuly!\n");
264 }
265
266module_init(fbd_driver_init);
267module_exit(fbd_driver_exit);
268MODULE_LICENSE("GPL");