分类: LINUX
2013-09-07 19:45:35
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PART_NUM 16 /* 次设备号==分区数+1------这个磁盘有15个分区,其中0表示整个块设备 */
#define BLOCK_MAJOR 32 /* 主设备号 */
#define BLOCK_DEVICE_NAME "block_device_driver_demo"
#define DEV_SIZE 4*1024*1024 /* 块设备的大小 为8M*/
#define use_request_queue /* 定义一个宏表示使用请求队列 */
struct blk_dev_drv_demo{ /* 块设备描述结构体 */
struct gendisk *blk_dev_drv_demo_gendisk;
struct request_queue *blk_dev_drv_demo_rq;
struct block_device_operations *blk_dev_drv_demo_ops;
unsigned char *name;
unsigned int major;
unsigned int partition_num;
spinlock_t lock;
unsigned int user; /* 用户打开设备的计数 */
unsigned short hardsect_size;/* 扇区中以字节为单位的大小 */
unsigned int dev_size; /* 设备容量,以字节为单位 */
char *block_device; /* 定义一个基于内存的虚拟块设备 */
};
static struct blk_dev_drv_demo *blk_dev_drv_demo;
static int blk_dev_drv_demo_open(struct block_device *bdev, fmode_t mode)
{
struct blk_dev_drv_demo *blk_dev = bdev->bd_disk->private_data;
blk_dev->user++; /* 增加设备打开的计数 */
/*
............
具体的打开设备
*/
return 0;
}
static int blk_dev_drv_demo_close (struct gendisk *disk, fmode_t mode)
{
struct blk_dev_drv_demo *blk_dev = disk->private_data;
if(blk_dev->user)
blk_dev->user--;
/*
............
具体的关闭设备
*/
return 0;
}
static int blk_dev_drv_demo_ioctl(struct block_device *bdev,fmode_t mode, unsigned cmd, unsigned long data)
{
return 0;
}
static int blk_dev_drv_demo_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/* 容量=heads*cylinders*sectors*512 */
geo->heads = 2;
geo->cylinders = 32;
geo->sectors = DEV_SIZE/2/32/512;
return 0;
}
struct block_device_operations blk_ops=
{
.owner = THIS_MODULE,
.open = blk_dev_drv_demo_open,
.release = blk_dev_drv_demo_close,
.ioctl = blk_dev_drv_demo_ioctl,
.getgeo = blk_dev_drv_demo_getgeo,
};
static void read_dev(struct blk_dev_drv_demo *blk_dev,unsigned long offset,char *buffer, unsigned long size)
{
char *block_device=blk_dev->block_device;
memcpy(buffer,block_device+offset,size);
}
static void write_dev(struct blk_dev_drv_demo *blk_dev,unsigned long offset,char *buffer, unsigned long size)
{
char *block_device=blk_dev->block_device;
memcpy(block_device+offset,buffer,size);
}
/*
*功能:完成具体的块设备I/O操作
*参数: blk_dev--->指向具体操作的设备;sector--->数据在设备上存放的起始扇区号; current_nr_sectors--->当前bio的当前段中要传输的扇区数
* buffer--->指向当前数据传输的内存缓冲区的指针,数据应该被传送或者来自这个缓冲区;mode--->从设备读数据还是写
* 数据到设备------0表示读数据,1表示写数据
*
**/
static void blk_dev_drv_demo_transfer(struct blk_dev_drv_demo *blk_dev ,sector_t sector,unsigned int data_size,char *buffer,int mode)
{
unsigned long offset=sector<<9; /* 将扇区号转换为要操作的数据在设备上的偏移量 */
unsigned long size=data_size<<9; /* 将 要操作的数据大小(单位为扇区)转换为单位为字节*/
switch(mode)
{
case READ:
read_dev(blk_dev,offset,buffer, size); /*从设备读取数据 */
break;
case WRITE:
write_dev(blk_dev,offset,buffer, size); /* 向设备写入数据 */
break;
default:
break;
}
}
/* 在使用请求队列的情况下的请求函数 -----IO调度器在合适的时候会调用该函数来处理请求*/
#if defined( use_request_queue)
static void blk_dev_drv_demo_request (struct request_queue *rq)
{
struct request *req; /* 指向要处理请求的指针 */
while((req=elv_next_request(rq))!=NULL) /* 获得队列中第一个未完成的请求(该函数返回下一个需要处理的请求指针(由I/O调度器决定)) */
{
struct blk_dev_drv_demo *blk_dev =rq->queuedata; /* 获取设备的指针 */
if(!blk_fs_request(req)) /* 如果不是文件系统的请求则打印错误并结束请求 */
{
printk(KERN_NOTICE"Skip non-fs request\n");
end_request(req,0); /* 结束请求(参数2为0表示请求处理失败) 将一个已经处理的请求从请求队列中删除并唤醒等待这些数据传输完成的进程*/
continue; /* 继续下一个请求 */
}
if(((req->sector+req->current_nr_sectors)<<9)>blk_dev->dev_size) /* 如果数据存放在设备上的起始扇区号加上 当前bio的当前段中要传输的扇区数大于设备容量则作出错处理*/
{
printk(KERN_ERR"bad request\n");
end_request(req,0); /* 结束请求(参数2为0表示请求处理失败) */
continue; /* 继续下一个请求 */
}
blk_dev_drv_demo_transfer(blk_dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req)); /* 传输数据 */
end_request(req,1);/* 结束请求(参数2为1表示请求成功) -----将一个已经处理的请求从请求队列中删除并唤醒等待这些数据传输完成的进程*/
}
}
#else
/* 单独传输一个bio */
static void blk_dev_drv_demo_make_transfer_bio(struct blk_dev_drv_demo *blk_dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector=bio->bi_sector; /* 获取 该bio结构所要传输的第一个(512字节)扇区*/
bio_for_each_segment(bvec, bio, i)/* 变量bio中的每个段-----*bi_io_vec指向的数组 */
{
char *buffer=__bio_kmap_atomic(bio, i, KM_USER0); /* 给定bio的第i个缓冲区的虚拟地址*/
blk_dev_drv_demo_transfer(blk_dev,sector,bio_cur_sectors(bio),buffer,bio_data_dir(bio)); /* 传输数据 */
sector+=bio_cur_sectors(bio); /* 更新传输到设备的偏移地址 */
__bio_kunmap_atomic(buffer, KM_USER0); /* 取消映射 */
}
}
/*制造请求函数 */
static int blk_dev_drv_demo_make_request (struct request_queue *rq, struct bio *bio)
{
struct blk_dev_drv_demo *blk_dev=rq->queuedata;
if((bio->bi_sector<<9+bio->bi_size)>blk_dev->dev_size) /* 第一个扇区的数据大小加上 所需要传输的数据大小大于设置的容量则做出错处理*/
{
printk(KERN_ERR"bad bio\n");
bio_endio(bio,-EIO); /* 结束一个失败的bio */
return 0;
}
blk_dev_drv_demo_make_transfer_bio(blk_dev, bio);/* 传输一个bio */
bio_endio(bio,0);/* 结束一个bio */
}
#endif
static int __init blk_dev_drv_demo_init(void)
{
int ret=0;
blk_dev_drv_demo=kzalloc(sizeof(struct blk_dev_drv_demo) , GFP_KERNEL ); /* 分配一个 struct blk_dev_drv_demo结构体*/
if(!blk_dev_drv_demo)
return -ENOMEM;
blk_dev_drv_demo->blk_dev_drv_demo_gendisk=alloc_disk(PART_NUM); /* 分配一个struct gendisk结构体 */
if(!blk_dev_drv_demo->blk_dev_drv_demo_gendisk)
goto out;
/* 初始化设备描述结构体的成员 */
blk_dev_drv_demo->name=BLOCK_DEVICE_NAME; /* 名字 */
blk_dev_drv_demo->partition_num=PART_NUM; /* 分区数 */
blk_dev_drv_demo->major=BLOCK_MAJOR; /* 主设备号 */
blk_dev_drv_demo->hardsect_size=512; /* 扇区中以字节为单位的大小为512B*/
blk_dev_drv_demo->user=0;
blk_dev_drv_demo->blk_dev_drv_demo_ops=&blk_ops; /* 设置块设备操作函数 */
blk_dev_drv_demo->dev_size=DEV_SIZE; /* 设置设备容量 */
blk_dev_drv_demo->block_device=kzalloc(blk_dev_drv_demo->dev_size, GFP_KERNEL); /* 分配一块内存用作块设备 */
if(!blk_dev_drv_demo->major) /* 如果主设备号=0则采用动态分配方式分配一个主设备号 */
blk_dev_drv_demo->major=register_blkdev( blk_dev_drv_demo->major , blk_dev_drv_demo->name); /* 注册块设备------其实就是用这个函数分配主设备号 */
else
ret=register_blkdev(blk_dev_drv_demo->major , blk_dev_drv_demo->name);
if(ret<0)
goto out;
spin_lock_init(&blk_dev_drv_demo->lock); /* 初始化自旋锁 */
#if defined(use_request_queue) /* 如果定义了该宏则表示使用请求队列 */
blk_dev_drv_demo->blk_dev_drv_demo_rq=blk_init_queue(blk_dev_drv_demo_request, &blk_dev_drv_demo->lock); /* 创建和初始化请求队列,并将请求函数与队列绑定 */
if(!blk_dev_drv_demo->blk_dev_drv_demo_rq)
goto out;
#else /* 否则不使用请求队列 -----使用自定义的制造请求函数*/
blk_dev_drv_demo->blk_dev_drv_demo_rq=blk_alloc_queue(GFP_KERNEL); /* 分配一个请求队列 */
if(!blk_dev_drv_demo->blk_dev_drv_demo_rq)
goto out;
blk_queue_make_request(blk_dev_drv_demo->blk_dev_drv_demo_rq, blk_dev_drv_demo_make_request); /* 绑定制造请求函数 */
#endif
blk_queue_hardsect_size(blk_dev_drv_demo->blk_dev_drv_demo_rq, blk_dev_drv_demo->hardsect_size); /* 设置硬件扇区尺寸 为512B*/
/* 初始化struct gendisk结构体的相关成员 */
blk_dev_drv_demo->blk_dev_drv_demo_gendisk->major=blk_dev_drv_demo->major; /* 主设备号 */
blk_dev_drv_demo->blk_dev_drv_demo_gendisk->first_minor=0; /* 次设备号 */
blk_dev_drv_demo->blk_dev_drv_demo_gendisk->fops=blk_dev_drv_demo->blk_dev_drv_demo_ops; /* 操作函数 */
blk_dev_drv_demo->blk_dev_drv_demo_gendisk->queue=blk_dev_drv_demo->blk_dev_drv_demo_rq; /* 设置请求队列 */
/* 将块设备结构体保存起来方便以后使用 */
blk_dev_drv_demo->blk_dev_drv_demo_gendisk->private_data=blk_dev_drv_demo;
blk_dev_drv_demo->blk_dev_drv_demo_rq->queuedata=blk_dev_drv_demo;
sprintf(blk_dev_drv_demo->blk_dev_drv_demo_gendisk->disk_name,blk_dev_drv_demo->name); /* 名字 */
set_capacity(blk_dev_drv_demo->blk_dev_drv_demo_gendisk,blk_dev_drv_demo->dev_size>>9); /* 设置设备容量(以内核扇区(512B)为单位) ,如果设备不是
以512B为扇区大小,则需要转换*/
add_disk(blk_dev_drv_demo->blk_dev_drv_demo_gendisk); /* 激活磁盘,会增加磁盘的引用计数 */
out:
put_disk(blk_dev_drv_demo->blk_dev_drv_demo_gendisk); /* 删除gendisk磁盘引用 */
blk_cleanup_queue(blk_dev_drv_demo->blk_dev_drv_demo_rq); /* 释放请求队列 */
return ret;
}
static void __exit blk_dev_drv_demo_exit(void)
{
del_gendisk(blk_dev_drv_demo->blk_dev_drv_demo_gendisk); /* 删除由add_disk函数添加的磁盘----注销 */
put_disk(blk_dev_drv_demo->blk_dev_drv_demo_gendisk); /* 删除gendisk磁盘引用 */
blk_cleanup_queue(blk_dev_drv_demo->blk_dev_drv_demo_rq);/* 释放请求队列 */
unregister_blkdev(blk_dev_drv_demo->major , blk_dev_drv_demo->name); /* 注销块设备 */
}
module_init(blk_dev_drv_demo_init);
module_exit(blk_dev_drv_demo_exit);
MODULE_AUTHOR("shenchaoping");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("v1.0");
MODULE_DESCRIPTION("A Block Device Driver Demo ");
MODULE_ALIAS("Block Device Driver Demo");