简单linux块设备驱动程序

本文代码参考《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个字节的内容相同

原文地址:https://www.cnblogs.com/tanghuimin0713/p/3454698.html