日期:2014-05-16 浏览次数:20788 次
这是块设备驱动的第一期,我们就从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