日期:2014-05-16 浏览次数:20667 次
这是块设备驱动的第一期,我们就从ldd3的sbull开始吧,但是ldd3用的linux版本太老了,你直接用它的例子在linux-3.2.x上是很麻烦的。
我主要做的就是在高版本上的移植。
里面有个NOQUEUE宏,可以选择不用一个请求队列 。
自己对着ldd3中的讲解看看吧
/*********************************** Copyright(C), 2013 LDP FileName: bdev.c Author: Date: Description: History: Author Date Desc ************************************/ #include <linux/module.h>//MODULE_* #include <linux/fs.h>//fops #include <linux/init.h>//printk #include <linux/slab.h>//kzalloc() kfree() #include <linux/blkdev.h>//register_blkdev #include <linux/bio.h>//strucut bio #include <linux/spinlock.h>//spinlock #include <linux/hdreg.h> //HDIO_GETGEO #include <asm/uaccess.h>//copy_to_user #define DEV_NAME "vDisk" #define NOQUEUE//不用一个请求队列 #define vDisk_MINORS 1 //磁盘分区数 #define HARDSECT_SIZE 512 //硬件的扇区大小 #define KERNEL_SECTOR_SIZE 512 //内核与快设备驱动交互的扇区单位 #define SECTOR_CNT 1024 //扇区数 /***************************************************** module description *****************************************************/ MODULE_LICENSE("GPL");//GPL, GPL v2, GPL and additional rights, Dual BSD/GPL, Dual MPL/GPL, Proprietary. MODULE_AUTHOR("..."); MODULE_DESCRIPTION("..."); MODULE_VERSION("..."); MODULE_ALIAS("..."); /****************************************************/ static int vDisk_major = 0; module_param(vDisk_major, int, 0); /***************************************************/ struct LDP_vDisk { int size; u8 *data; struct gendisk *gd; struct request_queue *queue; bool media_change; spinlock_t lock; }; /**************************************************** request operation ****************************************************/ static void vDisk_transfer(struct LDP_vDisk *dev, unsigned long sector, unsigned long nsect, char *buffer, int write) { unsigned long offset = sector * KERNEL_SECTOR_SIZE; unsigned long nbytes = nsect * KERNEL_SECTOR_SIZE; if ((offset + nbytes) > dev->size) { printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes); return; } if (write) { memcpy(dev->data + offset, buffer, nbytes); } else { memcpy(buffer, dev->data + offset, nbytes); } } static int vDisk_xfer_bio(struct LDP_vDisk *dev, struct bio *bio) { int i; struct bio_vec *bvec; sector_t sector = bio->bi_sector; char *buffer; /* Do each segment independently. */ bio_for_each_segment(bvec, bio, i) { buffer = __bio_kmap_atomic(bio, i, KM_USER0); vDisk_transfer(dev, sector, bio_cur_bytes(bio) >> 9, buffer, bio_data_dir(bio) == WRITE); sector += bio_cur_bytes(bio) >> 9; __bio_kunmap_atomic(bio, KM_USER0); } return 0; /* Always "succeed" */ } #ifdef NOQUEUE static void vDisk_make_request(struct request_queue *q, struct bio *bio) { struct LDP_vDisk *dev = q->queuedata; int status; status = vDisk_xfer_bio(dev, bio); bio_endio(bio, status); } #else static void vDisk_request(struct request_queue *q) { struct request *req; //int sectors_xferred = 0; struct bio *bio; struct LDP_vDisk *dev = q->queuedata; req = blk_fetch_request(q); while (req != NULL) { if (req->cmd_type != REQ_TYPE_FS)//文件系统请求 { __blk_end_request_all(req, 1); continue; } __rq_for_each_bio(bio, req) { vDisk_xfer_bio(dev, bio); } if (!__blk_end_request_cur(req, 0)) //通知设备层, { //add_disk_randomness(req->rq_disk);//for random req = blk_fetch_request(q); } } } #endif