2015-04-12 Lover雪儿
记得以前三月份就开始学习块设备,但是一直弄不出来,今天我们接着以前写的块设备驱动,抱着坚定的信心把它实现.
今天,我们再内存中申请一片内存,模拟作为块设备,程序如下:
程序一:简单的一个小程序
1.定义gendisk结构体与request_queue请求队列结构体,以及file-operation结构体
gendisk结构体,主要是用于定义与内核,硬件有关的一些重要信息,还有就是,告诉内核定义请求队列的结构体以及操作函数的结构体。
请求队列:主要是提供读写能力,实现读写请求的存储,然后自己调用do_rambloc_request函数来实现读写操作。
操作函数:如字符设备的操作函数一样,不过此处的操作函数暂时不需定义任何函数,但是必须要有.MODULE属性,否则会报错
2.入口函数实现
如图所示,在入口函数中主要包含了以下几个操作。
1):分般gendisk结构体,并且设备此设备个数为16个
2):分配、初始化队列,并且指定队列读写函数do_ramblock_request函数。
3):设置以下虚拟块设备的一些属性,包括主设备号,次设备号,名字,操作函数,队列,设备容量等。
4):最后就是注册gendisk结构体。
3.读写函数实现
一些都准备就绪之后,当用户对虚拟块设备发出请求时,系统会调用读写函数do_ramblock_request来实现读写功能。但是刚开始,还是简单点,所以此处,我们的函数主要的功能就是打印信息,告诉我们是否进入了这个读写函数。
4.出口函数实现
和入口函数相反,出口函数主要负责的就是释放前面申请的内存,反注册前面注册的一些信息。
5.程序测试
加载成功:
附上驱动程序ramblock1:
1 /* 参考 2 * drivers\block\xd.c 3 * drivers\block\z2ram.c 4 */ 5 #include <linux/module.h> 6 #include <linux/errno.h> 7 #include <linux/interrupt.h> 8 #include <linux/mm.h> 9 #include <linux/fs.h> 10 #include <linux/kernel.h> 11 #include <linux/timer.h> 12 #include <linux/genhd.h> 13 #include <linux/hdreg.h> 14 #include <linux/ioport.h> 15 #include <linux/init.h> 16 #include <linux/wait.h> 17 #include <linux/blkdev.h> 18 #include <linux/blkpg.h> 19 #include <linux/delay.h> 20 #include <linux/io.h> 21 22 #include <asm/system.h> 23 #include <asm/uaccess.h> 24 #include <asm/dma.h> 25 26 static struct gendisk *ramblock_disk; //定义gendisk结构体 27 static struct request_queue *ramblock_queue; //定义请求队列结构体 28 static DEFINE_SPINLOCK(ramblock_lock); //定义一个自旋锁 29 static int major; //主设备号 30 #define RAMBLOCK_SIZE (1024*1024) //块设备的容量 31 32 33 //file_operation结构体 34 static struct block_device_operations ramblock_fops ={ 35 .owner = THIS_MODULE, 36 }; 37 38 //读写处理函数 39 static void do_ramblock_request(struct request_queue *q) 40 { 41 static int cnt = 0; 42 struct request *req; 43 44 printk("enter do_ramblock_request %d\n",++cnt); 45 46 req = blk_fetch_request(q); 47 while(req){ 48 printk("enter while req %d\n",++cnt); 49 break; 50 } 51 52 __blk_end_request_cur(req, 0); 53 printk("leave do_ramblock_request %d\n",++cnt); 54 } 55 56 static int ramblock_init(void) 57 { 58 printk("ramblock_init\n"); 59 /* 1.分配一个gendisk结构体 */ 60 ramblock_disk = alloc_disk(16); //次设备号个数:分区个数,若为1的话,则意思是只有一个分区 61 62 /* 2.设置 */ 63 /* 2.1 分配/设置队列:函数do_ramblock_request提供读写能力 */ 64 ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock); 65 66 /* 2.2 设置其他属性:比如容量 */ 67 major = register_blkdev(0,"ramblock"); //cat /proc/device 动态申请一个主设备号 68 69 ramblock_disk->major = major; //主设备号 70 ramblock_disk->first_minor = 0; //第一个次设备号 71 sprintf(ramblock_disk->disk_name, "ramblock"); 72 ramblock_disk->fops = &ramblock_fops; //操作函数 73 ramblock_disk->queue = ramblock_queue; //队列 74 set_capacity(ramblock_disk,RAMBLOCK_SIZE/512); //设置容量,以扇区为单位 75 76 /* 3.注册 */ 77 add_disk(ramblock_disk); 78 79 return 0; 80 } 81 82 static void ramblock_exit(void) 83 { 84 printk("ramblock_exit\n"); 85 unregister_blkdev(major, "ramblock"); //卸载主设备号 86 del_gendisk(ramblock_disk); 87 put_disk(ramblock_disk); 88 blk_cleanup_queue(ramblock_queue); 89 } 90 91 module_init(ramblock_init); 92 module_exit(ramblock_exit); 93 MODULE_LICENSE("GPL");
ramblock1.c
程序二:增加读写方向,实现挂载等功能
接着前面实现的驱动程序,我们来在它的基础是来实现读写功能以及挂载等功能,
1.分配、释放申请内存
很明显,实现读写的话,那必要要有内存来存储,所以我们必须要入口函数中增加申请内粗的函数。
既然在入口函数中申请了内存,自然就要在出口函数中实现释放内存的操作。
2.在读写函数中实现读写操作。
由于2.6内核的改动,读写函数中的一些对象的名称有点不太一样,不过总体的思路还是一模一样的。参考内核中其他代码的读写函数,
1):实现引入请求队列,并且遍历请求队列
2):当请求队列为真的时候,计算出请求队列的起始地址及长度
3):通过其实地址和长度判读请求是否有效是否超出内存
4):如果以上都通过之后,接下来就是关键了,判断读写方向,接着实现内存的memcpy
5):读写成功后,调用__blk_end_request_cur来返回读写成功与否
3.程序测试
加载驱动:
读写测试:
4.往开发板中增加mkfs命令
接下来就是使用使用mkfs来格式化,但是发现imx257开发板自带的2.6内核里面没有mkfs的命令.
解决办法:使用busybox来创建一个根文件,然后从那个根文件系统中把mkfs命令拷贝到开发板的sbin目录下,就可以了,步骤如下:
1.首先下载busybox-1.23.1.tar.bz2
2.编译busybox
2.1配置busybox
执行命令:make menuconfig ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
Busybox Settings ---> //BusyBox的通用配置,一般采用默认值即可。 ---Applets Archival Utilities ---> //压缩、解压缩相关工具。 Coreutils ---> //最基本的命令,如cat、cp、ls等。 Console Utilities ---> //控制台相关命令。 Debian Utilities ---> //Debian操作系统相关命令。 Editors ---> //编辑工具,如vi、awk、sed等。 Finding Utilities ---> //查找工具,如find、grep、xargs。 Init Utilities ---> //BusyBox init相关命令。 Login/Password Management Utilities ---> //登陆、用户账号/密码等方面的命令。 Linux Ext2 FS Progs ---> //ext2文件系统的一些工具。 Linux Module Utilities ---> //加载/卸载模块等相关的命令。 Linux System Utilities ---> //一些系统命令。 Miscellaneous Utilities ---> //一些不好分类的命令,如crond、crontab。 Networking Utilities ---> //网络相关的命令和工具。 Print Utilities ---> //print spool服务及相关工具。 Mail Utilities ---> //mail相关命令。 Process Utilities ---> //进程相关命令,如ps、kill等。 Runit Utilities ---> //runit程序。 Shells ---> //shell程序。 System Logging Utilities ---> //系统日志相关工具,如syslogd、klogd。
2.2创建文件系统目录
2.2.1.创建文件系统的目录
1 #mkdir /home/study/nfs_home/rootfs_imx257/ 2 #cd /nfs_home/rootfs_imx25 3 #mkdir bin dev etc lib sbin proc sys var mnt tmp usr 4 #mkdir usr/bin usr/lib usr/sbin lib/modules
2.2.2.创建设备节点
#cd dev/ #mknod -m 666 console c 5 1 #mknod -m 666 null c 1 3
2.3配置选项
必须选中和修改的项: 1."Build Busybox as a static binary(no share libs)" 2."Don‘t use /usr" 3."cross compiler prefix"--------->arm-none-linux-gnueabi- 4."Busybox Installation prefix"--->/home/study/nfs_home/rootfs_imx257/ ****注意此处为你的文件系统目录的路径 (1选择的是静态连接库的方式,如果不选就是使用动态连接库的方式) (采用动态连接库的方式,在lib目录中添加应用程序所需的库文件) (Archival Utilities-->gzip这个选项一定不能掉)
找不到的话可以按下/进行搜索.
如图所示:
2.4错误解决
2.4.1错误一
[email protected]雪:/home/study/nfs_home/system/busybox-1.23.1# make CC miscutils/ubi_tools.o miscutils/ubi_tools.c:67:26: error: mtd/ubi-user.h: No such file or directory miscutils/ubi_tools.c: In function ‘ubi_tools_main‘: miscutils/ubi_tools.c:106: error: ‘UBI_DEV_NUM_AUTO‘ undeclared (first use in this function) miscutils/ubi_tools.c:106: error: (Each undeclared identifier is reported only once miscutils/ubi_tools.c:106: error: for each function it appears in.) miscutils/ubi_tools.c:107: error: ‘UBI_VOL_NUM_AUTO‘ undeclared (first use in this function) miscutils/ubi_tools.c:114: error: field ‘attach_req‘ has incomplete type miscutils/ubi_tools.c:115: error: field ‘mkvol_req‘ has incomplete type miscutils/ubi_tools.c:116: error: field ‘rsvol_req‘ has incomplete type miscutils/ubi_tools.c:177: error: ‘UBI_IOCATT‘ undeclared (first use in this function) miscutils/ubi_tools.c:190: error: ‘UBI_IOCDET‘ undeclared (first use in this function) miscutils/ubi_tools.c:233: error: ‘UBI_DYNAMIC_VOLUME‘ undeclared (first use in this function) miscutils/ubi_tools.c:235: error: ‘UBI_STATIC_VOLUME‘ undeclared (first use in this function) miscutils/ubi_tools.c:238: error: ‘UBI_MAX_VOLUME_NAME‘ undeclared (first use in this function) miscutils/ubi_tools.c:243: error: ‘UBI_IOCMKVOL‘ undeclared (first use in this function) miscutils/ubi_tools.c:256: error: ‘UBI_IOCRMVOL‘ undeclared (first use in this function) miscutils/ubi_tools.c:274: error: ‘UBI_IOCRSVOL‘ undeclared (first use in this function) miscutils/ubi_tools.c:290: error: ‘UBI_IOCVOLUP‘ undeclared (first use in this function) scripts/Makefile.build:197: recipe for target ‘miscutils/ubi_tools.o‘ failed make[1]: *** [miscutils/ubi_tools.o] Error 1 Makefile:741: recipe for target ‘miscutils‘ failed make: *** [miscutils] Error 2
解决方法
拷贝linux内核中的ubi-user.h到busybox下的mtd目录中
mkdir ./include/mtd;cp ../linux-2.6.31/include/mtd/ubi-user.h ./include/mtd/
2.4.2错误二:
networking/udhcp/dhcpc.c: In function ‘udhcp_recv_raw_packet‘: networking/udhcp/dhcpc.c:852: error: invalid application of ‘sizeof‘ to incomplete type ‘struct tpacket_auxdata‘ networking/udhcp/dhcpc.c:915: error: ‘PACKET_AUXDATA‘ undeclared (first use in
解决方法:不要编译dhcp模块
2.5编译安装
配置好之后,运行编译命令:make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
安装入文件系统:make install
2.6 利用busybox的命令格式化
不必挂载文件系统,只需要利用nfs进入busybox创建的文件系统中,拷贝格式化的mkfs命令到/sbin/下,然后就可以开始格式化了
2.6.1格式化文件系统
3挂载文件系统到/mnt/blk/上
4读写测试:
建立一个hello.c文件
5退出/mnt/blk/目录之后,卸载/mnt/blk/,运行sync是为了同步读写,防止设备忙
6再次挂载,看看我们刚才建立的hello.c是否还在:可以发现,我们的文件还在,貌似成功了,嘿嘿
7.整体测试流程如下:
8.mount 过程错误解决
mount: mounting /dev/blkdev on /mnt/blk failed: Invalid argument
若你严格照着上面我的程序来写的,这个问题就不会出现,但如果程序是你本人自己写的,其他的步骤现象和我前面的一模一样,可以分区,可以写入,但是就是无法挂载:
一直显示错误,如下:
[email protected] /mnt/nfs/module/24_block_device# mount /dev/blkdev /mnt/blk do_ramblock_request READ 11 leave do_ramblock_request do_ramblock_request READ 12 leave do_ramblock_request do_ramblock_request READ 13 leave do_ramblock_request do_ramblock_request READ 14 leave do_ramblock_request mount: mounting /dev/blkdev on /mnt/blk failed: Invalid argument
可能的一个原因是:保存分配的内存指针格式错误.
当它的格式为下图时.就会出现上面的错误:
理论上,保存地址的数据类型用长整型是没错的,但是当你编译后记载驱动程序后,就会出现上面的问题,其他的都正常,就是不能挂载.
解决方法:
把long 改为char,如图所示:
再次编译加载挂载就可以了.
如果你碰到这种问题,那个我必须恭喜你了,这个问题非常的变态,经过多天的一句一句代码慢慢的调试,我才发现是数据类型错误导致的,所以遇到这种棘手的问题,千万不要去找大神,大神也是很难看出来的,唯一的解决办法就是,足够的耐心慢慢调试了。
附上驱动程序ramblock2.c
1 /* 参考 2 * drivers\block\xd.c 3 * drivers\block\z2ram.c 4 */ 5 #include <linux/module.h> 6 #include <linux/errno.h> 7 #include <linux/interrupt.h> 8 #include <linux/mm.h> 9 #include <linux/fs.h> 10 #include <linux/kernel.h> 11 #include <linux/timer.h> 12 #include <linux/genhd.h> 13 #include <linux/hdreg.h> 14 #include <linux/ioport.h> 15 #include <linux/init.h> 16 #include <linux/wait.h> 17 #include <linux/blkdev.h> 18 #include <linux/blkpg.h> 19 #include <linux/delay.h> 20 #include <linux/io.h> 21 22 #include <asm/system.h> 23 #include <asm/uaccess.h> 24 #include <asm/dma.h> 25 26 #define DEVICE_NAME "ramblock" 27 28 static struct gendisk *ramblock_disk; //定义gendisk结构体 29 static struct request_queue *ramblock_queue; //定义请求队列结构体 30 static DEFINE_SPINLOCK(ramblock_lock); //定义一个自旋锁 31 static int major; //主设备号 32 #define RAMBLOCK_SIZE (1024*1024) //块设备的容量 33 34 static unsigned char *ramblock_buf; //内存的地址 35 #define RAMBLOCK_SIZE (1024*1024) //块设备的容量 36 37 //file_operation结构体 38 static struct block_device_operations ramblock_fops ={ 39 .owner = THIS_MODULE, 40 }; 41 42 //读写处理函数 43 static void do_ramblock_request(struct request_queue *q) 44 { 45 //static int r_cnt = 0; 46 //static int w_cnt = 0; 47 struct request *req; 48 49 //printk("enter do_ramblock_request %d\n",++cnt); 50 /* 51 req = blk_fetch_request(q); 52 while(req){ 53 printk("enter while req %d\n",++cnt); 54 break; 55 } 56 57 __blk_end_request_cur(req, 0); 58 */ 59 60 req = blk_fetch_request(q); //遍历请求队列 61 while (req) { 62 /* 开始实现读写操作 63 * 数据传输三要素: 源,目的,长度 64 * 源/目的 65 */ 66 unsigned long start = blk_rq_pos(req) << 9; //起始地址 67 unsigned long len = blk_rq_cur_bytes(req); //长度 68 unsigned long offset = blk_rq_pos(req) << 9; 69 int err = 0; 70 71 //如果请求地址超出块的大小 72 if (start + len > RAMBLOCK_SIZE) { 73 printk( KERN_ERR DEVICE_NAME ": bad access: block=%lu, count=%u\n", 74 (unsigned long)blk_rq_pos(req), blk_rq_cur_sectors(req)); 75 err = -EIO; 76 goto done; 77 } 78 79 //判断读写方向 80 if (rq_data_dir(req) == READ){ 81 //printk("do_ramblock_request READ %d\n",++r_cnt); 82 memcpy(req->buffer, ramblock_buf+offset, len); 83 }else{ 84 //printk("do_ramblock_request WRITE %d\n",++w_cnt); 85 memcpy(ramblock_buf+offset, req->buffer, len); 86 } 87 done: 88 if (!__blk_end_request_cur(req, err)) 89 req = blk_fetch_request(q); 90 //读写完成后,返回,0成功,1失败 91 //__blk_end_request_cur(req, err); //0表示成功,1表示失败 92 } 93 94 //printk("leave do_ramblock_request \n"); 95 } 96 97 static int ramblock_init(void) 98 { 99 printk("ramblock_init\n"); 100 /* 1.分配一个gendisk结构体 */ 101 ramblock_disk = alloc_disk(16); //次设备号个数:分区个数,若为1的话,则意思是只有一个分区 102 103 /* 2.设置 */ 104 /* 2.1 分配/设置队列:函数do_ramblock_request提供读写能力 */ 105 ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock); 106 107 /* 2.2 设置其他属性:比如容量 */ 108 major = register_blkdev(0,DEVICE_NAME); //cat /proc/device 动态申请一个主设备号 109 110 ramblock_disk->major = major; //主设备号 111 ramblock_disk->first_minor = 0; //第一个次设备号 112 sprintf(ramblock_disk->disk_name, DEVICE_NAME); 113 ramblock_disk->fops = &ramblock_fops; //操作函数 114 ramblock_disk->queue = ramblock_queue; //队列 115 set_capacity(ramblock_disk,RAMBLOCK_SIZE/512); //设置容量,以扇区为单位 116 117 /* 3.硬件相关的操作 */ 118 ramblock_buf = kzalloc(RAMBLOCK_SIZE,GFP_KERNEL); //申请内存 119 120 /* 4.注册 */ 121 add_disk(ramblock_disk); //添加块 122 123 124 return 0; 125 } 126 127 static void ramblock_exit(void) 128 { 129 printk("ramblock_exit\n"); 130 unregister_blkdev(major, DEVICE_NAME); //卸载主设备号 131 del_gendisk(ramblock_disk); 132 put_disk(ramblock_disk); 133 blk_cleanup_queue(ramblock_queue); 134 135 if(ramblock_buf) 136 kfree(ramblock_buf); //释放内存 137 } 138 139 module_init(ramblock_init); 140 module_exit(ramblock_exit); 141 MODULE_LICENSE("GPL"); 142 143 144 /* 145 146 开发板测试: 147 1. insmod ramblock.ko 148 2. 格式化 mkext3 /dev/ramblock 149 3. 挂接 mount /dev/ramblock /tmp/block 150 4. 读写文件 cd /tmp/block; echo 1 > /tmp/block/1.txt 151 5. 卸载磁盘 unmount /dev/ramblock 152 6. 重新挂载,测试文件是否还在 153 154 155 2.6以上的的内核代码,对requst结构体有过更改。 156 请求队列结构体: 157 struct request { 158 struct list_head queuelist; 159 struct call_single_data csd; 160 int cpu; 161 162 struct request_queue *q; 163 164 unsigned int cmd_flags; 165 enum rq_cmd_type_bits cmd_type; 166 unsigned long atomic_flags; 167 168 / * the following two fields are internal, NEVER access directly * / 169 sector_t __sector; / * 源 sector cursor * / 170 unsigned int __data_len; / * 长度 total data len * / 171 172 struct bio *bio; 173 struct bio *biotail; 174 175 struct hlist_node hash; / * merge hash * / 176 / * 177 * The rb_node is only used inside the io scheduler, requests 178 * are pruned when moved to the dispatch queue. So let the 179 * completion_data share space with the rb_node. 180 * / 181 union { 182 struct rb_node rb_node; / * sort/lookup * / 183 void *completion_data; 184 }; 185 186 */
ramblock2.c
程序三:兼容老式fdisk分区工具
如上图所示:当我们想对齐进行分区时,发现,无法进行分区,提示语为unknown valus for cylinders不知道扇区的值,所以,为了能够实现用fdisk进行分区,我们就必须在程序中模拟伪装扇区,利用block_device_opreations结构体中的getgeo函数来人工的造一些扇区信息给系统看.
struct block_device_operations { int (*open) (struct block_device *, fmode_t); int (*release) (struct gendisk *, fmode_t); int (*locked_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); int (*direct_access) (struct block_device *, sector_t, void **, unsigned long *); int (*media_changed) (struct gendisk *); unsigned long long (*set_capacity) (struct gendisk *, unsigned long long); int (*revalidate_disk) (struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); //伪造磁头,扇区,柱面等信息 struct module *owner; };
测试结果:
分区成功:
附上驱动程序ramblock3.c
1 /* 参考 2 * drivers\block\xd.c 3 * drivers\block\z2ram.c 4 */ 5 #include <linux/module.h> 6 #include <linux/errno.h> 7 #include <linux/interrupt.h> 8 #include <linux/mm.h> 9 #include <linux/fs.h> 10 #include <linux/kernel.h> 11 #include <linux/timer.h> 12 #include <linux/genhd.h> 13 #include <linux/hdreg.h> 14 #include <linux/ioport.h> 15 #include <linux/init.h> 16 #include <linux/wait.h> 17 #include <linux/blkdev.h> 18 #include <linux/blkpg.h> 19 #include <linux/delay.h> 20 #include <linux/io.h> 21 22 #include <asm/system.h> 23 #include <asm/uaccess.h> 24 #include <asm/dma.h> 25 26 #define DEVICE_NAME "ramblock" 27 28 static struct gendisk *ramblock_disk; //定义gendisk结构体 29 static struct request_queue *ramblock_queue; //定义请求队列结构体 30 static DEFINE_SPINLOCK(ramblock_lock); //定义一个自旋锁 31 static int major; //主设备号 32 #define RAMBLOCK_SIZE (1024*1024) //块设备的容量 33 34 static unsigned char *ramblock_buf; //内存的地址 35 36 //为了兼容fdisk老分区工具,我们还得伪装磁头等信息 37 static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo) 38 { 39 /* 容量 = header(面) * cylinders(环) * sectors(扇区) *512 */ 40 geo->heads = 2; //假设有两面磁头 41 geo->cylinders = 32; //假设有32环 42 geo->sectors = RAMBLOCK_SIZE / geo->heads / geo->cylinders /512; //扇区数量 43 44 return 0; 45 } 46 47 //file_operation结构体 48 static struct block_device_operations ramblock_fops ={ 49 .owner = THIS_MODULE, 50 .getgeo = ramblock_getgeo, 51 }; 52 53 //读写处理函数 54 static void do_ramblock_request(struct request_queue *q) 55 { 56 //static int r_cnt = 0; 57 //static int w_cnt = 0; 58 struct request *req; 59 60 //printk("enter do_ramblock_request %d\n",++cnt); 61 /* 62 req = blk_fetch_request(q); 63 while(req){ 64 printk("enter while req %d\n",++cnt); 65 break; 66 } 67 68 __blk_end_request_cur(req, 0); 69 */ 70 71 req = blk_fetch_request(q); //遍历请求队列 72 while (req) { 73 /* 开始实现读写操作 74 * 数据传输三要素: 源,目的,长度 75 * 源/目的 76 */ 77 unsigned long start = blk_rq_pos(req) << 9; //起始地址 78 unsigned long len = blk_rq_cur_bytes(req); //长度 79 unsigned long offset = blk_rq_pos(req) << 9; 80 int err = 0; 81 82 //如果请求地址超出块的大小 83 if (start + len > RAMBLOCK_SIZE) { 84 printk( KERN_ERR DEVICE_NAME ": bad access: block=%lu, count=%u\n", 85 (unsigned long)blk_rq_pos(req), blk_rq_cur_sectors(req)); 86 err = -EIO; 87 goto done; 88 } 89 90 //判断读写方向 91 if (rq_data_dir(req) == READ){ 92 //printk("do_ramblock_request READ %d\n",++r_cnt); 93 memcpy(req->buffer, ramblock_buf+offset, len); 94 }else{ 95 //printk("do_ramblock_request WRITE %d\n",++w_cnt); 96 memcpy(ramblock_buf+offset, req->buffer, len); 97 } 98 done: 99 if (!__blk_end_request_cur(req, err)) 100 req = blk_fetch_request(q); 101 //读写完成后,返回,0成功,1失败 102 //__blk_end_request_cur(req, err); //0表示成功,1表示失败 103 } 104 105 //printk("leave do_ramblock_request \n"); 106 } 107 108 static int ramblock_init(void) 109 { 110 printk("ramblock_init\n"); 111 /* 1.分配一个gendisk结构体 */ 112 ramblock_disk = alloc_disk(16); //次设备号个数:分区个数,若为1的话,则意思是只有一个分区 113 114 /* 2.设置 */ 115 /* 2.1 分配/设置队列:函数do_ramblock_request提供读写能力 */ 116 ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock); 117 118 /* 2.2 设置其他属性:比如容量 */ 119 major = register_blkdev(0,DEVICE_NAME); //cat /proc/device 动态申请一个主设备号 120 121 ramblock_disk->major = major; //主设备号 122 ramblock_disk->first_minor = 0; //第一个次设备号 123 sprintf(ramblock_disk->disk_name, DEVICE_NAME); 124 ramblock_disk->fops = &ramblock_fops; //操作函数 125 ramblock_disk->queue = ramblock_queue; //队列 126 set_capacity(ramblock_disk,RAMBLOCK_SIZE/512); //设置容量,以扇区为单位 127 128 /* 3.硬件相关的操作 */ 129 ramblock_buf = kzalloc(RAMBLOCK_SIZE,GFP_KERNEL); //申请内存 130 131 /* 4.注册 */ 132 add_disk(ramblock_disk); //添加块 133 134 135 return 0; 136 } 137 138 static void ramblock_exit(void) 139 { 140 printk("ramblock_exit\n"); 141 unregister_blkdev(major, DEVICE_NAME); //卸载主设备号 142 del_gendisk(ramblock_disk); 143 put_disk(ramblock_disk); 144 blk_cleanup_queue(ramblock_queue); 145 146 if(ramblock_buf) 147 kfree(ramblock_buf); //释放内存 148 } 149 150 module_init(ramblock_init); 151 module_exit(ramblock_exit); 152 MODULE_LICENSE("GPL"); 153 154 155 /* 156 157 开发板测试: 158 1. insmod ramblock.ko 159 2. 格式化 mkext3 /dev/ramblock 160 3. 挂接 mount /dev/ramblock /tmp/block 161 4. 读写文件 cd /tmp/block; echo 1 > /tmp/block/1.txt 162 5. 卸载磁盘 unmount /dev/ramblock 163 6. 重新挂载,测试文件是否还在 164 165 166 2.6以上的的内核代码,对requst结构体有过更改。 167 请求队列结构体: 168 struct request { 169 struct list_head queuelist; 170 struct call_single_data csd; 171 int cpu; 172 173 struct request_queue *q; 174 175 unsigned int cmd_flags; 176 enum rq_cmd_type_bits cmd_type; 177 unsigned long atomic_flags; 178 179 / * the following two fields are internal, NEVER access directly * / 180 sector_t __sector; / * 源 sector cursor * / 181 unsigned int __data_len; / * 长度 total data len * / 182 183 struct bio *bio; 184 struct bio *biotail; 185 186 struct hlist_node hash; / * merge hash * / 187 / * 188 * The rb_node is only used inside the io scheduler, requests 189 * are pruned when moved to the dispatch queue. So let the 190 * completion_data share space with the rb_node. 191 * / 192 union { 193 struct rb_node rb_node; / * sort/lookup * / 194 void *completion_data; 195 }; 196 197 */
ramblock3.c
程序四:增加分配的内存
前面我们的程序是分配1M的内存,但是,如果我们想分配10M或者更大的内存,还能成功么,我们可以来尝试一下,如下图所示,分配10M:
编译,然后加载驱动程序:
很明显,如下图所示,加载驱动后,发现系统奔溃了.
为什么会这样呢,可以发现,我们上面的程序中使用的kzalloc来申请连续的物理内存,而恰恰我们imx257板子上本来就没有多大的内存,当申请失败,程序再运行下面的程序自然会导致系统奔溃.(此处也提醒我们再程序中一定要做好错误处理的代码,但是此处我们是学习,为了简单明了,就省略了错误处理的机制).
此处,为了能够申请更大的内存,我们使用vmaloc来申请零散的内存
包含头文件:#include <linux/vmalloc.h>
修改完毕之后,我们来编译加载:
成功加载,并且格式化:
接下来,我们来挂载,并且查看一下他的内存是否为10M,从下图中我们发现确实是分配了10M的内存:
附上驱动程序ramblock4.c
1 /* 参考 2 * drivers\block\xd.c 3 * drivers\block\z2ram.c 4 */ 5 #include <linux/module.h> 6 #include <linux/errno.h> 7 #include <linux/interrupt.h> 8 #include <linux/mm.h> 9 #include <linux/fs.h> 10 #include <linux/kernel.h> 11 #include <linux/timer.h> 12 #include <linux/genhd.h> 13 #include <linux/hdreg.h> 14 #include <linux/ioport.h> 15 #include <linux/init.h> 16 #include <linux/wait.h> 17 #include <linux/blkdev.h> 18 #include <linux/blkpg.h> 19 #include <linux/delay.h> 20 #include <linux/io.h> 21 #include <linux/vmalloc.h> 22 23 #include <asm/system.h> 24 #include <asm/uaccess.h> 25 #include <asm/dma.h> 26 27 #define DEVICE_NAME "ramblock" 28 29 static struct gendisk *ramblock_disk; //定义gendisk结构体 30 static struct request_queue *ramblock_queue; //定义请求队列结构体 31 static DEFINE_SPINLOCK(ramblock_lock); //定义一个自旋锁 32 static int major; //主设备号 33 #define RAMBLOCK_SIZE (10*1024*1024) //块设备的容量 34 35 static unsigned char *ramblock_buf; //内存的地址 36 37 //为了兼容fdisk老分区工具,我们还得伪装磁头等信息 38 static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo) 39 { 40 /* 容量 = header(面) * cylinders(环) * sectors(扇区) *512 */ 41 geo->heads = 2; //假设有两面磁头 42 geo->cylinders = 32; //假设有32环 43 geo->sectors = RAMBLOCK_SIZE / geo->heads / geo->cylinders /512; //扇区数量 44 45 return 0; 46 } 47 48 //file_operation结构体 49 static struct block_device_operations ramblock_fops ={ 50 .owner = THIS_MODULE, 51 .getgeo = ramblock_getgeo, 52 }; 53 54 //读写处理函数 55 static void do_ramblock_request(struct request_queue *q) 56 { 57 //static int r_cnt = 0; 58 //static int w_cnt = 0; 59 struct request *req; 60 61 //printk("enter do_ramblock_request %d\n",++cnt); 62 /* 63 req = blk_fetch_request(q); 64 while(req){ 65 printk("enter while req %d\n",++cnt); 66 break; 67 } 68 69 __blk_end_request_cur(req, 0); 70 */ 71 72 req = blk_fetch_request(q); //遍历请求队列 73 while (req) { 74 /* 开始实现读写操作 75 * 数据传输三要素: 源,目的,长度 76 * 源/目的 77 */ 78 unsigned long start = blk_rq_pos(req) << 9; //起始地址 79 unsigned long len = blk_rq_cur_bytes(req); //长度 80 unsigned long offset = blk_rq_pos(req) << 9; 81 int err = 0; 82 83 //如果请求地址超出块的大小 84 if (start + len > RAMBLOCK_SIZE) { 85 printk( KERN_ERR DEVICE_NAME ": bad access: block=%lu, count=%u\n", 86 (unsigned long)blk_rq_pos(req), blk_rq_cur_sectors(req)); 87 err = -EIO; 88 goto done; 89 } 90 91 //判断读写方向 92 if (rq_data_dir(req) == READ){ 93 //printk("do_ramblock_request READ %d\n",++r_cnt); 94 memcpy(req->buffer, ramblock_buf+offset, len); 95 }else{ 96 //printk("do_ramblock_request WRITE %d\n",++w_cnt); 97 memcpy(ramblock_buf+offset, req->buffer, len); 98 } 99 done: 100 if (!__blk_end_request_cur(req, err)) 101 req = blk_fetch_request(q); 102 //读写完成后,返回,0成功,1失败 103 //__blk_end_request_cur(req, err); //0表示成功,1表示失败 104 } 105 106 //printk("leave do_ramblock_request \n"); 107 } 108 109 static int ramblock_init(void) 110 { 111 printk("ramblock_init\n"); 112 /* 1.分配一个gendisk结构体 */ 113 ramblock_disk = alloc_disk(16); //次设备号个数:分区个数,若为1的话,则意思是只有一个分区 114 115 /* 2.设置 */ 116 /* 2.1 分配/设置队列:函数do_ramblock_request提供读写能力 */ 117 ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock); 118 119 /* 2.2 设置其他属性:比如容量 */ 120 major = register_blkdev(0,DEVICE_NAME); //cat /proc/device 动态申请一个主设备号 121 122 ramblock_disk->major = major; //主设备号 123 ramblock_disk->first_minor = 0; //第一个次设备号 124 sprintf(ramblock_disk->disk_name, DEVICE_NAME); 125 ramblock_disk->fops = &ramblock_fops; //操作函数 126 ramblock_disk->queue = ramblock_queue; //队列 127 set_capacity(ramblock_disk,RAMBLOCK_SIZE/512); //设置容量,以扇区为单位 128 129 /* 3.硬件相关的操作 */ 130 //ramblock_buf = kzalloc(RAMBLOCK_SIZE,GFP_KERNEL); //申请内存 131 ramblock_buf = vmalloc(RAMBLOCK_SIZE); //申请内存 132 133 /* 4.注册 */ 134 add_disk(ramblock_disk); //添加块 135 136 137 return 0; 138 } 139 140 static void ramblock_exit(void) 141 { 142 printk("ramblock_exit\n"); 143 unregister_blkdev(major, DEVICE_NAME); //卸载主设备号 144 del_gendisk(ramblock_disk); 145 put_disk(ramblock_disk); 146 blk_cleanup_queue(ramblock_queue); 147 148 if(ramblock_buf) 149 // kfree(ramblock_buf); //释放内存 150 vfree(ramblock_buf); //释放内存 151 } 152 153 module_init(ramblock_init); 154 module_exit(ramblock_exit); 155 MODULE_LICENSE("GPL"); 156 157 158 /* 159 160 开发板测试: 161 1. insmod ramblock.ko 162 2. 格式化 mkext3 /dev/ramblock 163 3. 挂接 mount /dev/ramblock /tmp/block 164 4. 读写文件 cd /tmp/block; echo 1 > /tmp/block/1.txt 165 5. 卸载磁盘 unmount /dev/ramblock 166 6. 重新挂载,测试文件是否还在 167 168 169 2.6以上的的内核代码,对requst结构体有过更改。 170 请求队列结构体: 171 struct request { 172 struct list_head queuelist; 173 struct call_single_data csd; 174 int cpu; 175 176 struct request_queue *q; 177 178 unsigned int cmd_flags; 179 enum rq_cmd_type_bits cmd_type; 180 unsigned long atomic_flags; 181 182 / * the following two fields are internal, NEVER access directly * / 183 sector_t __sector; / * 源 sector cursor * / 184 unsigned int __data_len; / * 长度 total data len * / 185 186 struct bio *bio; 187 struct bio *biotail; 188 189 struct hlist_node hash; / * merge hash * / 190 / * 191 * The rb_node is only used inside the io scheduler, requests 192 * are pruned when moved to the dispatch queue. So let the 193 * completion_data share space with the rb_node. 194 * / 195 union { 196 struct rb_node rb_node; / * sort/lookup * / 197 void *completion_data; 198 }; 199 200 */
ramblock4.c
写到这儿,有没有发现其实也并不是想象中的这么难,有木有,明天我们就来实现一个真正的块设备驱动程序.加油!!!