在上一篇文章中详细讲解了块设备驱动的相关知识,并有一些参考代码,但是由于linux系统版本的原因,在2.6.35.6版本中,编译有错误,故在这篇文章中,我们贴出了2.6.35.6版本下的块设备驱动的一个简单例子,代码如下所示。
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/spinlock.h> #include <linux/sched.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/hdreg.h> #include <linux/genhd.h> #include <linux/blkdev.h> #include <linux/slab.h> #define BLK_MAJOR 250 #define BLK_NAME "myblkdev" #define DISK_SECTOR_SIZE 512 #define DISK_BYTES (1024*1024*64) int blk_major=0; struct myblk_dev{ int size; /*以字节为单位,设备大小*/ unsigned char *data; /*数据数组*/ short users; /*用户数目*/ struct request_queue *queue; /*设备请求队列*/ struct gendisk *gd; /*gendisk结构*/ spinlock_t lock; /*用于互斥*/ }; struct myblk_dev *myblkdev; /*块设备请求处理函数*/ static int myblk_make_request(struct request_queue *q,struct bio *bio) { int i; char *mem_pbuf; char *disk_pbuf; struct myblk_dev *pmyblkdev; struct bio_vec *pbvec; if((bio->bi_sector*DISK_SECTOR_SIZE + bio->bi_size) > DISK_BYTES){ bio_io_error(bio); return 0; } pmyblkdev = (struct myblk_dev*)bio->bi_bdev->bd_disk->private_data; /*得到设备结构体*/ //printk("myblk_make_request\n"); disk_pbuf = pmyblkdev->data+bio->bi_sector*DISK_SECTOR_SIZE; /*得到要读写的起始位置*/ /*开始遍历这个bio中的每个bio_vec*/ bio_for_each_segment(pbvec,bio,i){ /*循环分散的内存segment*/ mem_pbuf = kmap(pbvec->bv_page)+pbvec->bv_offset;/*获得实际内存地址*/ switch(bio_data_dir(bio)){ /*读写*/ case READA: case READ: memcpy(mem_pbuf, disk_pbuf, pbvec->bv_len); break; case WRITE: memcpy(disk_pbuf, mem_pbuf, pbvec->bv_len); break; default: kunmap(pbvec->bv_page); bio_io_error(bio); return 0; } kunmap(pbvec->bv_page);/*清除映射*/ disk_pbuf += pbvec->bv_len; } bio_endio(bio,0); return 0; } static int blk_ioctl(struct block_device *dev, fmode_t no, unsigned cmd, unsigned long arg) { printk("#############\n"); return -ENOTTY; } static int blk_open(struct block_device *dev, fmode_t no) { struct myblk_dev *device = dev->bd_inode->i_bdev->bd_disk->private_data; spin_lock(&device->lock); device->users ++; spin_unlock(&device->lock); printk("blk mount succeed\n"); return 0; } static int blk_release(struct gendisk *gd, fmode_t no) { struct myblk_dev *device = gd->private_data; spin_lock(&device->lock); device->users --; spin_unlock(&device->lock); printk("blk umount succeed\n"); return 0; } struct block_device_operations blk_ops={ .owner = THIS_MODULE, .open = blk_open, .release = blk_release, .ioctl = blk_ioctl, }; static int __init myblkdev_init(void) { blk_major = register_blkdev(blk_major,BLK_NAME); /*注册驱动*/ /* register_blkdev注册函数注册失败时,返回一个负值,如果主设备号参数为0,表示动态分配,分配成功返回主设备号, 如果主设备号参数非0,表示静态分配,分配成功返回0*/ if(blk_major < 0){ printk("unable to get major number\n"); return -EBUSY; }else if(blk_major == 0){ blk_major = BLK_MAJOR; printk("regiser blk dev succeed\n"); } myblkdev = kmalloc (sizeof(struct myblk_dev), GFP_KERNEL); if(myblkdev == NULL){ unregister_blkdev(blk_major,BLK_NAME); return -ENOMEM; } memset(myblkdev, 0, sizeof(struct myblk_dev)); myblkdev->size = DISK_BYTES; /*设备的大小*/ myblkdev->data = vmalloc(DISK_BYTES); /*设备的数据空间*/ if(myblkdev->data == NULL){ printk(KERN_NOTICE "vmalloc failure.\n"); return -EBUSY; } spin_lock_init(&myblkdev->lock); myblkdev->queue = blk_alloc_queue(GFP_KERNEL);/*生成队列*/ if(myblkdev->queue == NULL){ printk("blk_alloc_queue failure\n"); goto out_vfree; } blk_queue_make_request(myblkdev->queue,myblk_make_request);/*注册make_request,绑定请求制造函数*/ blk_queue_logical_block_size(myblkdev->queue, DISK_SECTOR_SIZE);/*告知内核块设备硬件扇区的大小*/ myblkdev->queue->queuedata = myblkdev;/*queuedata是void *指针,用来指向所需要的数据*/ /*分配gendisk,参数是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改,数量1表示不包含分区*/ myblkdev->gd = alloc_disk(1); if(! myblkdev->gd){ printk("alloc_disk failure\n"); goto out_vfree; } myblkdev->gd->major = blk_major;/*主设备号*/ myblkdev->gd->first_minor = 0;/*第一个设备号*/ myblkdev->gd->fops = & blk_ops;/*块文件结构变量*/ myblkdev->gd->queue = myblkdev->queue;/*请求队列*/ myblkdev->gd->private_data = myblkdev;/*私有数据指针*/ snprintf(myblkdev->gd->disk_name,32,"myblkdev%c",'a');/*名字*/ set_capacity(myblkdev->gd,DISK_BYTES>>9);/*设置gendisk容量*/ /*增加gendisk,gendisk结构体被分配之后,系统还不能使用这个磁盘,需要调用如下函数来注册这个磁盘设备*/ add_disk(myblkdev->gd); printk("gendisk init success\n"); return 0; out_vfree: if(myblkdev->data){ vfree(myblkdev->data); } return 0; } static void __exit myblkdev_exit(void) { /*清除请求队列*/ blk_cleanup_queue(myblkdev->gd->queue); /*释放gendisk,当不再需要一个磁盘时,应当使用如下函数释放gendisk*/ del_gendisk(myblkdev->gd); put_disk(myblkdev->gd); if(myblkdev->data){ vfree(myblkdev->data); } unregister_blkdev(blk_major,BLK_NAME); kfree(myblkdev); printk("blk dev exit success!\n"); } module_init(myblkdev_init); module_exit(myblkdev_exit); MODULE_AUTHOR("Fang Xieyun"); MODULE_LICENSE("GPL");
struct gendisk *alloc_disk(int minors)其功能是分配gendisk,参数minors是这个磁盘使用的次设备号的数量,一般也就是磁盘分区的数量,此后minors不能被修改,当这个参数为1时,表示不包含分区,也就是不能对这个磁盘进行分区。如下图所示,参数为1时,进行分区就会报错。
而当我们修改参数为2时,就表示有两个分区,可以对这个磁盘进行分区,分区结果如下所示。
其中我们的Makefile文件内容如下所示:
obj-m := myblkdev.o KERNELDIR := /lib/modules/$(shell uname -r)/build/ PWD:=$(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) clean install: insmod myblkdev.ko uninstall: rmmod myblkdev