linux系统块设备驱动实战

/***************************************************************
  *Project Name:			ramdisk driver  
  *File Name:				ramdisk_driver.c  
  *Description:				do not use the request queue of system
  **************************************************************/

#include <linux/module.h>    /*module_init()*/
#include <linux/kernel.h>	/* printk() */
#include <linux/init.h>		/* __init __exit */
#include <linux/fs.h>		/* file_operation */
#include <asm/uaccess.h>	/* copy_to_user, copy_from_user */
#include <linux/device.h>  /*class ,class_create ,device_create 等*/
#include <linux/errno.h>	/* Error number */
#include <linux/delay.h>	/* mdelay ,ndelay*/
#include <asm/delay.h>     /* udelay */

#include <linux/blkdev.h>
#include <linux/hdreg.h>	/* geo */
#include <linux/blkpg.h>
#include <linux/vmalloc.h>

#define DEBUG			//open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...)		printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...)		printk(KERN_DEBUG fmt, ##arg)
#endif


#define		VRD_COUNT				2
#define 	VRD_PARTITION			4
#define 	VRD_SIZE				10*1024*1024		//size is 10M
#define 	VRD_SECTOR_SIZE			512
#define		VRD_SECTOR_COUNT		(VRD_SIZE/VRD_SECTOR_SIZE)
#define 	KERNEL_SECTOR_SIZE		512			//内核认为的扇区大小固定为512字节

#define 	DRIVER_NAME		"sun_ramdisk"
static int MAJOR_NR = 0;

typedef struct ramdisk_dev{
	int major;
	char* vmem;
	unsigned long mem_size;
	unsigned long sector_size;
	unsigned long sector_count;
	struct gendisk* gdisk;
}ramdisk_dev_t;

static ramdisk_dev_t rdisk[VRD_COUNT];


//自定义数据处理接口,不使用请求队列
static int vrd_make_request(struct request_queue *q, struct bio *bio)
{
	unsigned long flag;
    struct bio_vec *bvec;
    unsigned long  i;
    
    char	*pBuffer; 
	ramdisk_dev_t *prdisk = bio->bi_bdev->bd_disk->private_data;	//得到randisk的首地址
	sector_t sector = bio->bi_sector;		//要传输的第一个扇区
	unsigned long offset;	//要传输扇区的偏移量
	unsigned long nbytes;
    
	//循环取得数据操作缓冲区信息(操作地址及访问数据大小)
    bio_for_each_segment(bvec, bio, i){
		offset = sector*KERNEL_SECTOR_SIZE;		//获取存储位置偏移量
		nbytes = bio_cur_bytes(bio);		//获取数据传输的字节数
		
		//首先判断操作的数据空间是否超出块设备容量
		if((offset + nbytes) > prdisk->mem_size){
			PRINTK("bio error!\n");
			return 0;  
		}
//		pBuffer = __bio_kmap_atomic(bio,i,KM_USER0);	//kmap映射出缓冲区实际地址
		pBuffer = bvec_kmap_irq(bvec,&flag);			//同上方法二
//		pBuffer = kmap(bvec->bv_page) + bvec->bv_offset;	//同上方法三
		switch(bio_data_dir(bio)) {
		case READ:
			memcpy(pBuffer, prdisk->vmem+offset, nbytes); 
			break; 
		case WRITE:
			memcpy(prdisk->vmem+offset, pBuffer, nbytes); 
			break;
		}
		sector += nbytes>>9;
//		__bio_kunmap_atomic(bio, KM_USER0);		//释放映射的内存缓冲区
		bvec_kunmap_irq(bvec, &flag);			//同上方法二
//		kunmap(bvec->bv_page); 					//同上方法三
    }
	//通知处理结束
    bio_endio(bio, 0);
    return 0;			//必须返回零否则该bio会重新被提交
} 



static int vrd_open(struct block_device *bdev, fmode_t mod)
{
	ramdisk_dev_t *prd;
	prd = bdev->bd_disk->private_data;
	PRINTK("in open, the device major is %d\n",prd->major);
	
    return 0;
}

static int vrd_release (struct gendisk *gdisk, fmode_t mod)
{
	ramdisk_dev_t *prd;
	prd = gdisk->private_data;
	PRINTK("in realse, the device major is %d\n",prd->major);
    return 0;
}

static int vrd_ioctl(struct block_device *bdev, fmode_t mod, unsigned int cmd,unsigned long arg)
{
	ramdisk_dev_t *prd;
	prd = bdev->bd_disk->private_data;
	PRINTK("in ioctl, the device major is %d\n", prd->major);
	
    return -ENOTTY;
}

/*检测设备的变化,例如用户打开光驱,更换了一张光盘*/
static int vrd_media_changed(struct gendisk *gdisk)
{
	ramdisk_dev_t *prd;
	prd = gdisk->private_data;
	PRINTK("in media changed, the device major is %d\n",prd->major);
	return 0;
}
/*检测到设备变化后的响应处理函数*/
static int vrd_revalidate_disk(struct gendisk *gdisk)
{
	ramdisk_dev_t *prd;
	prd = gdisk->private_data;
	PRINTK("in revalidate disk, the device major is %d\n",prd->major);
	
	return 0;
}
/*获取磁盘分区信息,通过配置hd_geo参数,传递给内核*/
static int vrd_getgeo(struct block_device* bdev, struct hd_geometry* hd_geo)
{
	ramdisk_dev_t *prd;
	prd = bdev->bd_disk->private_data;
	PRINTK("in getgeo, the device major is %d\n",prd->major);
	
	hd_geo->cylinders = (prd->mem_size*prd->sector_size)/KERNEL_SECTOR_SIZE >> 6;
	hd_geo->heads = 4;
	hd_geo->sectors = 16;
	hd_geo->start = 0;
	return 0;
}

static struct block_device_operations vrd_fops =
{
    .owner   = THIS_MODULE,    
    .open    = vrd_open,
    .release = vrd_release,  
    .ioctl   = vrd_ioctl,
	.media_changed = vrd_media_changed,
	.revalidate_disk = vrd_revalidate_disk,
	.getgeo = vrd_getgeo,
};

static int __init vrd_init(void)
{
	int lp = 0;
	struct gendisk *pgdisk = NULL;
	MAJOR_NR = register_blkdev(MAJOR_NR, DRIVER_NAME);
	if(MAJOR_NR < 0){
		PRINTK("register block device fail!\n");
		return MAJOR_NR;
	}
	PRINTK("\nMAJOR_NR = %d\n",MAJOR_NR);
	
    for(lp = 0; lp < VRD_COUNT; lp++){
		rdisk[lp].major = MAJOR_NR;
		rdisk[lp].sector_size = VRD_SECTOR_SIZE;
		rdisk[lp].mem_size = VRD_SIZE;
		rdisk[lp].vmem = vmalloc(VRD_SIZE);
		rdisk[lp].sector_count = VRD_SECTOR_COUNT;
	
        rdisk[lp].gdisk = alloc_disk(VRD_PARTITION);
		pgdisk = rdisk[lp].gdisk;
		
		//设置主设备号
		pgdisk->major = rdisk[lp].major;
		pgdisk->first_minor = lp*VRD_PARTITION;
		
		//为块设备命名
		sprintf(pgdisk->disk_name,"ramdisk%c",'a'+lp); 
		
		//不使用请求队列,请求函数
        pgdisk->queue = blk_alloc_queue(GFP_KERNEL);  //生成请求处理队列
        blk_queue_make_request(pgdisk->queue, &vrd_make_request);
		set_capacity(pgdisk, rdisk[lp].sector_count*rdisk[lp].sector_size/KERNEL_SECTOR_SIZE); //设置块设备的总扇区数
		
        //设置块设备的处理函数
        pgdisk->fops = &vrd_fops;
        
        pgdisk->private_data  = rdisk+lp; //保存自定义结构体本身
        add_disk(pgdisk);//注册块设备结构     
    }
    return 0;
    
}

static void __exit vrd_exit(void)
{
    int lp;

    for(lp = 0; lp < VRD_COUNT; lp++){
        del_gendisk(rdisk[lp].gdisk); //注销块设备结构,与add_disk()配对使用
        put_disk(rdisk[lp].gdisk); //清除gendisk结构体分配的内存,与alloc_disk()配对使用
		vfree(rdisk[lp].vmem);
    }
	unregister_blkdev(MAJOR_NR, DRIVER_NAME);
}

module_init(vrd_init);
module_exit(vrd_exit);

MODULE_LICENSE("GPL");

# If KERNELRELEASE is defined, we've been invoked from the 
# kernel build system and can use its language. 

ifneq ($(KERNELRELEASE),) 
obj-m := ramdisk_driver.o 

else 
KERNELDIR = /home/workspace/kernel/unsp210_linux-2.6.35.7
PWD := $(shell pwd)

.PHONY: modules modules_install clean

modules: 
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 
	
clean:
	rm -rf *.o *.mod.c *.order *.symvers
	
endif

/***************************************************************
  *Project Name:			ramdisk driver2
  *File Name:				ramdisk_driver2.c  
  *Description:				use the request queue of system
  **************************************************************/

#include <linux/module.h>    /*module_init()*/
#include <linux/kernel.h>	/* printk() */
#include <linux/init.h>		/* __init __exit */
#include <linux/fs.h>		/* file_operation */
#include <asm/uaccess.h>	/* copy_to_user, copy_from_user */
#include <linux/device.h>  /*class ,class_create ,device_create 等*/
#include <linux/errno.h>	/* Error number */
#include <linux/delay.h>	/* mdelay ,ndelay*/
#include <asm/delay.h>     /* udelay */
#include <linux/spinlock.h>

#include <linux/blkdev.h>
#include <linux/hdreg.h>	/* geo */
#include <linux/blkpg.h>
#include <linux/vmalloc.h>

#define DEBUG			//open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...)		printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...)		printk(KERN_DEBUG fmt, ##arg)
#endif


#define		VRD_COUNT				2
#define 	VRD_PARTITION			1
#define 	VRD_SIZE				10*1024*1024		//size is 10M
#define 	VRD_SECTOR_SIZE			512
#define		VRD_SECTOR_COUNT		(VRD_SIZE/VRD_SECTOR_SIZE)
#define 	KERNEL_SECTOR_SIZE		512			//内核认为的扇区大小固定为512字节

#define 	DRIVER_NAME		"sun_ramdisk"
static int MAJOR_NR = 0;

typedef struct ramdisk_dev{
	int major;
	char* vmem;
	unsigned long mem_size;
	unsigned long sector_size;
	unsigned long sector_count;
	spinlock_t splock;
	struct gendisk* gdisk;
}ramdisk_dev_t;

static ramdisk_dev_t rdisk[VRD_COUNT];

/*请求处理函数,用来处理I/O调度层处理后的request*/
void vrd_request(struct request_queue *q)
{
	struct request *req;
	ramdisk_dev_t *prdisk;
	sector_t start;
	unsigned long offset;
	unsigned long size;
	
	req = blk_fetch_request(q);			//提取一个request请求
	while(req){
		start = blk_rq_pos(req);		//获取读写操作的起始扇区
		prdisk = req->rq_disk->private_data;	//获取用户私有数据
		
		offset = start*KERNEL_SECTOR_SIZE;		//通过起始扇区获取ramdisk的偏移地址
		size = blk_rq_cur_bytes(req);		//获取数据读写的大小
		
		switch(rq_data_dir(req)){		//计算数据请求的方向
			case READ:
				memcpy(req->buffer, prdisk->vmem+offset, size);
			break;
			case WRITE:
				memcpy(prdisk->vmem+offset, req->buffer, size);
			break;
		}
		
		if(!__blk_end_request_cur(req,0))		//通知内核请求处理结束
			req = blk_fetch_request(q);			//获取下一个request请求
	}
}

static int vrd_open(struct block_device *bdev, fmode_t mod)
{
	ramdisk_dev_t *prd;
	prd = bdev->bd_disk->private_data;
	PRINTK("in open, the device major is %d\n",prd->major);
	
    return 0;
}

static int vrd_release (struct gendisk *gdisk, fmode_t mod)
{
	ramdisk_dev_t *prd;
	prd = gdisk->private_data;
	PRINTK("in realse, the device major is %d\n",prd->major);
    return 0;
}

static int vrd_ioctl(struct block_device *bdev, fmode_t mod, unsigned int cmd,unsigned long arg)
{
	ramdisk_dev_t *prd;
	prd = bdev->bd_disk->private_data;
	PRINTK("in ioctl, the device major is %d\n", prd->major);
	
    return -ENOTTY;
}

/*检测设备的变化,例如用户打开光驱,更换了一张光盘*/
static int vrd_media_changed(struct gendisk *gdisk)
{
	ramdisk_dev_t *prd;
	prd = gdisk->private_data;
	PRINTK("in media changed, the device major is %d\n",prd->major);
	//added here
	
	return 0;
}
/*检测到设备变化后的响应处理函数*/
static int vrd_revalidate_disk(struct gendisk *gdisk)
{
	ramdisk_dev_t *prd;
	prd = gdisk->private_data;
	PRINTK("in revalidate disk, the device major is %d\n",prd->major);
	//added here
	
	return 0;
}

/*获取磁盘分区信息,通过配置hd_geo参数,传递给内核*/
static int vrd_getgeo(struct block_device* bdev, struct hd_geometry* hd_geo)
{
	ramdisk_dev_t *prd;
	prd = bdev->bd_disk->private_data;
	PRINTK("in getgeo, the device major is %d\n",prd->major);
	
	hd_geo->cylinders = (prd->mem_size*prd->sector_size)/KERNEL_SECTOR_SIZE >> 6;
	hd_geo->heads = 4;
	hd_geo->sectors = 16;
	hd_geo->start = 0;
	return 0;
}

static struct block_device_operations vrd_fops =
{
    .owner   = THIS_MODULE,    
    .open    = vrd_open,
    .release = vrd_release,  
    .ioctl   = vrd_ioctl,
	.media_changed = vrd_media_changed,
	.revalidate_disk = vrd_revalidate_disk,
	.getgeo = vrd_getgeo,
};

static int __init vrd_init(void)
{
	int lp = 0;
	struct gendisk *pgdisk = NULL;
	MAJOR_NR = register_blkdev(MAJOR_NR, DRIVER_NAME);
	if(MAJOR_NR < 0){
		PRINTK("register block device fail!\n");
		return MAJOR_NR;
	}
	PRINTK("\nMAJOR_NR = %d\n",MAJOR_NR);
	
    for(lp = 0; lp < VRD_COUNT; lp++){
		rdisk[lp].major = MAJOR_NR;
		rdisk[lp].sector_size = VRD_SECTOR_SIZE;
		rdisk[lp].mem_size = VRD_SIZE;
		rdisk[lp].vmem = vmalloc(VRD_SIZE);
		rdisk[lp].sector_count = VRD_SECTOR_COUNT;
		spin_lock_init(&rdisk[lp].splock);			//init the spin lock
	
        rdisk[lp].gdisk = alloc_disk(VRD_PARTITION);
		pgdisk = rdisk[lp].gdisk;
		
		//设置主设备号
		pgdisk->major = rdisk[lp].major;
		pgdisk->first_minor = lp*VRD_PARTITION;
		
		//为块设备命名
		sprintf(pgdisk->disk_name,"ramdisk%c",'a'+lp); 
		
		//初始化请求队列,指定请求处理函数
		pgdisk->queue = blk_init_queue(vrd_request,&rdisk[lp].splock);
	
		set_capacity(pgdisk, rdisk[lp].sector_count*rdisk[lp].sector_size/KERNEL_SECTOR_SIZE); //设置块设备的总扇区数
		
        //设置块设备的处理函数
        pgdisk->fops = &vrd_fops;
        
        pgdisk->private_data  = rdisk+lp; //保存自定义结构体本身
        add_disk(pgdisk);//注册块设备结构     
    }
    return 0;
    
}

static void __exit vrd_exit(void)
{
    int lp;

    for(lp = 0; lp < VRD_COUNT; lp++){
        del_gendisk(rdisk[lp].gdisk); //注销块设备结构,与add_disk()配对使用
        put_disk(rdisk[lp].gdisk); //清除gendisk结构体分配的内存,与alloc_disk()配对使用
		vfree(rdisk[lp].vmem);
    }
	unregister_blkdev(MAJOR_NR, DRIVER_NAME);
}

module_init(vrd_init);
module_exit(vrd_exit);

MODULE_LICENSE("GPL");

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值