Linux驱动开发----块设备驱动(内存模拟)Tiny6410
2013-12-18 00:11
696 查看
写了好久的字符设备驱动,是时候看下块设备驱动程序设计方法了,块设备驱动和字符设备不同,字符设备是直接和虚拟文件系统进行交互,而块设备驱动则是通过块缓冲/调度层间接和虚拟文件系统交互;块设备驱动数据访问都是以块为单位;多个块I/O需要组成一个请求队列,这个功能是块缓冲/调度层提供的,它出于硬件特性和读写性能的考虑,将块I/O进行重新排序,并组成一个请求队列,交给内核,内核则调用请求队列处理函数来逐个处理请求队列。
大致框架:
1.分配gendisk结构,使用alloc_disk函数
2.分配设置请求队列,提供读写能力
3.设置其他信息,例如容量、名称等
4.硬件相关操作
5.注册(add_disk)
在测试块设备驱动的过程中,刚开始使用的是函数kzmalloc申请1M大小的内存来进行测试,可是驱动加载的时候没有问题,可是当使用mkdosfs格式化的时候报错"short write".,并且在导出到文件上时也提示错误,内核直接崩溃掉。后来改成vmalloc函数就可以了。额。。这是为什么??还没弄明白。。
下面是代码:
编译完成后,使用mkdosfs格式化,并挂载到目录test下,读写文件,重新挂载查看文件是否存在,另外,可以导出到文件上,在PC机上测试文件是否正确。
大致框架:
1.分配gendisk结构,使用alloc_disk函数
2.分配设置请求队列,提供读写能力
3.设置其他信息,例如容量、名称等
4.硬件相关操作
5.注册(add_disk)
在测试块设备驱动的过程中,刚开始使用的是函数kzmalloc申请1M大小的内存来进行测试,可是驱动加载的时候没有问题,可是当使用mkdosfs格式化的时候报错"short write".,并且在导出到文件上时也提示错误,内核直接崩溃掉。后来改成vmalloc函数就可以了。额。。这是为什么??还没弄明白。。
下面是代码:
#include <linux/init.h>//module_init/exit #include <linux/module.h>//MODULE_AUTHOR,MODULE_LICENSE等 #include <linux/genhd.h>//alloc_disk #include <linux/blkdev.h>//blk_init_queue #include <linux/fs.h>//register_blkdev,unregister_blkdev #include <linux/types.h>//u_char,u_short #include <linux/vmalloc.h> #include <linux/hdreg.h> //1MB大小空间 #define RAMBLK_SIZE (1024*1024*2) /* //定义驱动私有数据结构 struct ramblk_info{ u_char heads;//磁头数 u_short cylinders;//柱面数 u_char sectors;//扇区数 u_char control; int unit; }; */ static struct gendisk * ramblk_disk = NULL; static struct request_queue * ramblk_request_queue = NULL; static int major = 0;//块设备的主设备号 //static struct ramblk_info *pinfo = NULL; static DEFINE_SPINLOCK(ramblk_spinlock);//定义并初始化一个自旋锁 static char * ramblk_buf = NULL;//申请的内存起始地址 //ramdisk_info初始化函数 /* static int ramblk_info_init(struct ramblk_info *p) { if(!p){ printk("ramblk_info_init p==NULL.\n"); return -1; } p->heads = 4;//4个磁头 p->cylinders = 4;//4个柱面 p->sectors = 128;//128个扇区 return 0; } */ int ramblk_getgeo(struct block_device * blk_Dev, struct hd_geometry * hg) { hg->cylinders = 64; hg->heads = 8; hg->sectors = (RAMBLK_SIZE/8/64/512); return 0; } static const struct block_device_operations ramblk_fops = { .owner = THIS_MODULE, .getgeo = ramblk_getgeo, }; static void do_ramblk_request(struct request_queue *q ) { struct request *req; // static volatile int r_cnt = 0; // static volatile int w_cnt = 0; //printk("ramblk_request_fn %d.\n",cnt++); req = blk_fetch_request(q); while (req) { unsigned long start = blk_rq_pos(req) << 9; unsigned long len = blk_rq_cur_bytes(req); // printk("len=%d.\n",len); if (start + len > RAMBLK_SIZE) { printk("RAMBLK_SIZE< start+len"); goto done; } if (rq_data_dir(req) == READ) memcpy(req->buffer, (char *)(start+ramblk_buf), len); else memcpy((char *)(start+ramblk_buf), req->buffer, len); done: if (!__blk_end_request_cur(req, 0)) req = blk_fetch_request(q); } } static int ramblk_init(void) { // 1.分配gendisk结构体,使用alloc_disk函数 ramblk_disk = alloc_disk(16);//minors=分区+1 // 2.设置 // 2.1 分配/设置队列,提供读写能力.使用函数blk_init_queue(request_fn_proc *rfn,spin_lock_t *lock) ramblk_request_queue = blk_init_queue(do_ramblk_request,&ramblk_spinlock); // 2.2 设置disk的其他信息,比如容量、主设备号等 major = register_blkdev(0,"ramblk");//注册主设备 if(major < 0){//检查是否成功分配一个有效的主设备号 printk(KERN_ALERT "register_blkdev err.\n"); return -1; } //设置主设备号 ramblk_disk->major = major; ramblk_disk->first_minor = 0;//设置第一个次设备号 sprintf(ramblk_disk->disk_name, "ramblk%c", 'a');//设置设备名 ramblk_disk->fops = &ramblk_fops;//设置fops /* //分配一个ramdisk_info结构体,并初始化 pinfo =(struct ramblk_info*)kmalloc(sizeof(struct ramblk_info),GFP_KERNEL); if(!pinfo){ printk("kmalloc pinfo err.\n"); return -1; } ramblk_info_init(pinfo); ramlk_disk->private_data = pinfo;*/ ramblk_disk->queue = ramblk_request_queue;//设置请求队列 set_capacity(ramblk_disk, RAMBLK_SIZE/512);//设置容量 // 3.硬件相关的操作 ramblk_buf = (char*)vmalloc(RAMBLK_SIZE);//申请RAMBLK_SIZE内存 // 4.注册 add_disk(ramblk_disk);//add partitioning information to kernel list printk("ramblk_init.\n"); return 0; } static void ramblk_exit(void) { unregister_blkdev(major,"ramblk");//注销设备驱动 blk_cleanup_queue(ramblk_request_queue);//清除队列 del_gendisk(ramblk_disk); put_disk(ramblk_disk); vfree(ramblk_buf);//释放申请的内存 printk("ramblk_exit.\n"); } module_init(ramblk_init);//入口 module_exit(ramblk_exit);//出口 MODULE_AUTHOR("jefby"); MODULE_LICENSE("Dual BSD/GPL");
编译完成后,使用mkdosfs格式化,并挂载到目录test下,读写文件,重新挂载查看文件是否存在,另外,可以导出到文件上,在PC机上测试文件是否正确。
相关文章推荐
- Linux 字符设备驱动开发--内存读写操作
- Linux驱动程序开发007 - 设备驱动模型初探
- Linux设备驱动开发详解-Note(14)--- Linux 设备驱动中的并发控制(1)
- linux驱动开发:背光设备
- Linux字符设备驱动程序开发(1)-使用字符设备驱动
- Linux 设备驱动开发 —— platform设备驱动应用实例解析
- linux设备驱动开发学习之旅--linux设备驱动中的并发与竞态
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- linux 设备驱动开发详解 code (5)
- 2.linux驱动基础开发1——linux 设备驱动基本概念
- linux驱动开发 主设备号与次设备号
- linux驱动开发--字符设备:自动创建设备节点
- 【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动
- linux驱动开发--I/O内存的访问流程
- Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析
- 浅谈 Linux 内核开发之网络设备驱动
- Linux驱动开发--Linux字符设备驱动模板
- Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用
- Linux 设备驱动开发 —— Tasklets 机制浅析
- Linux设备驱动编程之内存与I/O操作…