#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
/*
* insmod tiny4412_blkdev.ko
* # or insmod tiny4412_blkdev.ko size=numK/M/G/T
* fdisk /dev/tiny4412_blkdev # create 2 patitions
* mkfs.ext2 /dev/tiny4412_blkdev1
* mkfs.ext2 /dev/tiny4412_blkdev2
* mount /dev/tiny4412_blkdev1 /mnt/temp1/
* mount /dev/tiny4412_blkdev2 /mnt/temp2/
* # play in /mnt/temp1/ and /mnt/temp2/
* umount /mnt/temp1/
* umount /mnt/temp2/
* rmmod tiny4412_blkdev.ko
*
*/
static int Tiny4412_block_major=0;
static struct request_queue *tiny4412_blkdev_queue;
static struct gendisk *tiny4412_blkdev_disk;
#define TINY4412_BLK_DEV_BYTES (1024*1024*50) /*设置块设备的大小*/
static unsigned char *sizeof_p;
/*
* Handle an I/O request.
* 实现扇区的读写
unsigned long sector: 当前扇区位置
unsigned long nsect : 扇区读写数量
char *buffer : 读写的缓冲区指针
int write : 是读还是写
*/
static void Tiny4412_block_dev_sector_read_write(unsigned long sector,unsigned long nsect, char *buffer, int write)
{
/*块设备最小单位是一个扇区,一个扇区的字节数是512字节*/
unsigned long offset = sector; /*写入数据的位置*/
unsigned long nbytes = nsect; /*写入的长度*/
if((offset + nbytes)>TINY4412_BLK_DEV_BYTES)
{
printk("写超出范围,强制结束(%ld %ld)\n", offset, nbytes);
return;
}
if(write) /*为真,表示是写*/
memcpy(sizeof_p + offset, buffer, nbytes);
else /*读操作*/
memcpy(buffer,sizeof_p + offset, nbytes);
}
/*
处理请求
*/
static int tiny4412_blkdev_make_request(struct request_queue *q, struct bio *bio)
{
int dir;
unsigned long long dsk_offset;
struct bio_vec *bvec;
int i;
void *iovec_mem;
/*判断读写方向*/
if(bio_data_dir(bio) == WRITE) dir = 1;
else dir = 0;
dsk_offset = bio->bi_sector << 9;
bio_for_each_segment(bvec, bio, i)
{
iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
//起始位置,长度,源数据,方向
Tiny4412_block_dev_sector_read_write(dsk_offset,bvec->bv_len,iovec_mem,dir);
kunmap(bvec->bv_page);
dsk_offset += bvec->bv_len;
}
bio_endio(bio, 0);
return 0;
}
struct block_device_operations tiny4412_blkdev_fops =
{
.owner= THIS_MODULE,
};
static int __init tiny4412_blkdev_init(void)
{
sizeof_p=vmalloc(TINY4412_BLK_DEV_BYTES);
if(sizeof_p==NULL)
{
printk("空间申请失败!\n");
return 0;
}
/*动态分配请求队列*/
tiny4412_blkdev_queue = blk_alloc_queue(GFP_KERNEL);
/*绑定请求队列*/
blk_queue_make_request(tiny4412_blkdev_queue,tiny4412_blkdev_make_request);
/*动态分配次设备号结构*/
/*每一个磁盘(分区)都是使用一个gendisk结构保存*/
tiny4412_blkdev_disk = alloc_disk(64);
/*磁盘名称赋值*/
strcpy(tiny4412_blkdev_disk->disk_name, "tiny4412_blkdev");
/*注册一个块设备,自动分配主设备号*/
Tiny4412_block_major = register_blkdev(0,"Tiny4412_block");
printk("Tiny4412_block_major=%d\n",Tiny4412_block_major);
tiny4412_blkdev_disk->major=Tiny4412_block_major; /*主设备号*/
tiny4412_blkdev_disk->first_minor = 0; /*次设备号*/
tiny4412_blkdev_disk->fops = &tiny4412_blkdev_fops; /*文件操作结合*/
tiny4412_blkdev_disk->queue = tiny4412_blkdev_queue; /*处理数据请求的队列*/
/*设置磁盘结构 capacity 的容量*/
/*注意: 块设备的大小使用扇区作为单位设置,而扇区的大小默认是512字节。
cat /sys/block/xxxx/size 可以查看到设置的大小
把字节为单位的大小转换为以扇区为单位时,我们需要除以512,或者右移9位
*/
set_capacity(tiny4412_blkdev_disk,TINY4412_BLK_DEV_BYTES>>9);
//添加磁盘信息到内核
add_disk(tiny4412_blkdev_disk);
return 0;
}
static void __exit tiny4412_blkdev_exit(void)
{
//删除磁盘
del_gendisk(tiny4412_blkdev_disk);
put_disk(tiny4412_blkdev_disk);
//清除队列
blk_cleanup_queue(tiny4412_blkdev_queue);
/*注销块设备*/
unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");
vfree(sizeof_p);
}
module_init(tiny4412_blkdev_init);
module_exit(tiny4412_blkdev_exit);
MODULE_LICENSE("GPL");
评论