Lover雪儿
想念时,就看看天空,无论距离有多远,我们总在同一片天空下!

IMX257实现Ramblock驱动程序编写

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错误一

root@Lover雪:/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

若你严格照着上面我的程序来写的,这个问题就不会出现,但如果程序是你本人自己写的,其他的步骤现象和我前面的一模一样,可以分区,可以写入,但是就是无法挂载:

一直显示错误,如下:

root@EasyARM-iMX257 /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

 

写到这儿,有没有发现其实也并不是想象中的这么难,有木有,明天我们就来实现一个真正的块设备驱动程序.加油!!!

posted on 2015-04-15 14:49  Lover雪儿  阅读(785)  评论(0编辑  收藏  举报