本文代码参考《LINUX设备驱动程序》第十六章 块设备驱动程序
本文中的“块设备”是一段大小为PAGE_SIZE的内存空间(两个扇区,每个扇区512字节)
功能:向块设备中输入内容,从块设备中读出内容
注:ldd自带块设备驱动源码在2.6.32内核的发行版上编译时会提示很多API未定义,原因是kernel 2.6中block layer API已经变更了很多,本文的程序参考了http://hi.baidu.com/casualfish/item/7931bbb58925fb951846977d,感谢!
代码:
1. sbull.c
1 #include <linux/module.h>
2 #include <linux/moduleparam.h>
3 #include <linux/init.h>
4
5 #include <linux/sched.h>
6 #include <linux/kernel.h>
7 #include <linux/slab.h>
8 #include <linux/fs.h>
9 #include <linux/errno.h>
10 #include <linux/timer.h>
11 #include <linux/types.h>
12 #include <linux/fcntl.h>
13 #include <linux/hdreg.h>
14 #include <linux/kdev_t.h>
15 #include <linux/vmalloc.h>
16 #include <linux/genhd.h>
17 #include <linux/blkdev.h>
18 #include <linux/buffer_head.h>
19 #include <linux/bio.h>
20
21 #include "sbull.h"
22
23 static int sbull_major = SBULL_MAJOR;
24 static int hardsect_size = SBULL_HARDSECT;
25 static int nsectors = 2;
26 module_param(sbull_major, int, 0);
27 module_param(hardsect_size, int, 0);
28 module_param(nsectors, int, 0);
29
30 struct sbull_dev {
31 int size;
32 u8 *data;
33 short users;
34 spinlock_t lock;
35 struct request_queue *queue;
36 struct gendisk *gd;
37 };
38
39 static struct sbull_dev *device = NULL;
40
41 static void sbull_transfer(struct sbull_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
42 {
43 unsigned long offset = sector*KERNEL_SECTOR_SIZE;
44 unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
45
46 if ((offset+nbytes) > dev->size)
47 {
48 return;
49 }
50
51 if (write)
52 {
53 memcpy(dev->data+offset, buffer, nbytes);
54 }
55 else
56 {
57 memcpy(buffer, dev->data+offset, nbytes);
58 }
59 }
60
61 static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
62 {
63 int i;
64 struct bio_vec *bvec;
65 sector_t sector = bio->bi_sector;
66
67 bio_for_each_segment(bvec, bio, i)
68 {
69 char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
70 sbull_transfer(dev, sector, bio_cur_bytes(bio)>>9, buffer, bio_data_dir(bio) == WRITE);
71 sector += bio_cur_bytes(bio)>>9;
72 __bio_kunmap_atomic(bio, KM_USER0);
73 }
74 return 0;
75 }
76
77 static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
78 {
79 struct bio *bio;
80 int nsect = 0;
81
82 __rq_for_each_bio(bio, req) {
83 sbull_xfer_bio(dev, bio);
84 nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
85 }
86 return nsect;
87 }
88
89 static void sbull_full_request(struct request_queue *q)
90 {
91 struct request *req;
92 int sectors_xferred;
93 struct sbull_dev *dev = q->queuedata;
94
95 while((req = blk_fetch_request(q)) != NULL)
96 {
97 if (req->cmd_type != REQ_TYPE_FS)
98 {
99 __blk_end_request_all(req, -EIO);
100 continue;
101 }
102
103 sectors_xferred = sbull_xfer_request(dev, req);
104
105 __blk_end_request_cur(req, 0);
106 }
107 }
108
109 static int sbull_open(struct block_device *device, fmode_t mode)
110 {
111 struct sbull_dev *dev = device->bd_disk->private_data;
112
113 spin_lock(&dev->lock);
114 dev->users++;
115 spin_unlock(&dev->lock);
116 return 0;
117 }
118
119 static int sbull_release(struct gendisk *disk, fmode_t mode)
120 {
121 struct sbull_dev *dev = disk->private_data;
122
123 spin_lock(&dev->lock);
124 dev->users--;
125 spin_unlock(&dev->lock);
126
127 return 0;
128 }
129
130 static struct block_device_operations sbull_ops = {
131 .owner = THIS_MODULE,
132 .open = sbull_open,
133 .release = sbull_release,
134 };
135
136 static void setup_device(struct sbull_dev *dev)
137 {
138 memset(dev, 0, sizeof(struct sbull_dev));
139 dev->size = nsectors*hardsect_size;
140 dev->data = vmalloc(dev->size);
141 if (dev->data == NULL)
142 {
143 return;
144 }
145
146 spin_lock_init(&dev->lock);
147
148 dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
149 if (dev->queue == NULL)
150 {
151 goto out_vfree;
152 }
153
154 blk_queue_logical_block_size(dev->queue, hardsect_size);
155 dev->queue->queuedata = dev;
156
157 dev->gd = alloc_disk(1);
158 if (!dev->gd)
159 {
160 goto out_vfree;
161 }
162
163 dev->gd->major = sbull_major;
164 dev->gd->first_minor = 0;
165 dev->gd->fops = &sbull_ops;
166 dev->gd->queue = dev->queue;
167 dev->gd->private_data = dev;
168 snprintf(dev->gd->disk_name, 6, "sbull");
169 set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
170 add_disk(dev->gd);
171
172 return;
173
174 out_vfree:
175 if (dev->data)
176 {
177 vfree(dev->data);
178 }
179 }
180
181 static int __init sbull_init(void)
182 {
183 sbull_major = register_blkdev(sbull_major, "sbull");
184 if (sbull_major <= 0)
185 {
186 return -EBUSY;
187 }
188
189 device = kmalloc(sizeof(struct sbull_dev), GFP_KERNEL);
190 if (device == NULL)
191 {
192 goto out_unregister;
193 }
194
195 setup_device(device);
196
197 return 0;
198
199 out_unregister:
200 unregister_blkdev(sbull_major, "sbull");
201 return -ENOMEM;
202 }
203
204 static void sbull_exit(void)
205 {
206 struct sbull_dev *dev = device;
207
208 if (dev->gd)
209 {
210 del_gendisk(dev->gd);
211 put_disk(dev->gd);
212 }
213
214 if (dev->queue)
215 {
216 blk_cleanup_queue(dev->queue);
217 }
218
219 if (dev->data)
220 {
221 vfree(dev->data);
222 }
223
224 unregister_blkdev(sbull_major, "sbull");
225 kfree(device);
226 }
227
228 module_init(sbull_init);
229 module_exit(sbull_exit);
230 MODULE_LICENSE("GPL");
2. sbull.h
1 #ifndef _SBULL_H
2 #define _SBULL_H
3
4 #define SBULL_MAJOR 0
5 #define SBULL_HARDSECT 512
6
7 #define KERNEL_SECTOR_SIZE 512
8
9 #endif
3. Makefile
1 obj-m += sbull.o
2
3 CURRENT_PATH:=$(shell pwd)
4 LINUX_KERNEL:=$(shell uname -r)
5 LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
6
7 all:
8 make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
9 clean:
10 make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
4. 验证效果
1)#make
2)#insmod sbull.ko
3)#ls /dev/ //会显示sbull
4)#ls -lh /tmp/test1
-rw-r--r--. 1 root root 4.3k Dec 2 20:47 test1
5)#dd if=/tmp/test1 of=/dev/sbull bs=512 count=2 //将test1中两个扇区的内容输入到块设备sbull中
6)#dd if=/dev/sbull of=/tmp/test2 bs=512 count=2 //将块设备sbull中两个扇区的内容输入到临时文件test2中
7)#ls -lh /tmp/test2
-rw-r--r--. 1 root root 1.0k Dec 2 21:19 test2
8)#vim test2 //test2中的内容跟test1前1024个字节的内容相同