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

[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

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

时间: 2024-10-10 04:59:02

IMX257实现Ramblock驱动程序编写的相关文章

20150409 IMX257 USB鼠标驱动程序编写

20150409 IMX257 USB鼠标驱动程序编写 2015-03-14 Lover雪儿 USB驱动程序包括为USB总线驱动程序以及USB设备驱动程序. USB总线驱动程序的功能是: 1.识别 2.找到匹配的设备驱动程序 3.提供USB读写函数(不知道数据的含义) USB设备驱动程序功能是: 分析USB的数据,上报相应的事件 今天,我们来实现一个USB鼠标的设备驱动程序,让鼠标的左键模拟为键盘的L键,鼠标的右键模拟为键盘的S键,鼠标的中键模拟为键盘的ENTER键,接下来我们先来实现一个简单程

20150429 S3C实现DMA驱动程序编写

20150429 S3C实现DMA驱动程序编写 2015-04-29 Lover雪儿 在IMX257上只有SDMA,SDMA比DMA的功能更加强大,但是为了学习的目的,如果直接学习SDMA,可能会不能消化, 所以,此处,我们从简单到复杂,从S3C2440的DMA驱动程序开始学习,等学懂它之后,我们再进军IMX257的SDMA. 一.一个简单的程序框架 1.定义一些指针 1 static int major = 0; 2 3 #define MEM_CPY_NO_DMA 0 4 #define M

ok6410之led驱动程序编写

led驱动程序编写 本文主要包含三部分,led驱动程序led.c编写,编译驱动程序的makefile的编写,以及使用驱动程序的应用程序led_app的编写 一.led.c编写 1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/cdev.h> 4 #include <linux/fs.h> 5 #include <linux/io.h> 6 #includ

转:Linux网卡驱动程序编写

Linux网卡驱动程序编写 [摘自 LinuxAID] 工作需要写了我们公司一块网卡的Linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微软垄断的强有力武器,日益受到大家的喜爱.真希望她能在中国迅速成长.把程序文档贴出来,希望和大家探讨Linux技术和应用,促进Linux在中国的普及. Linux操作系统网络驱动程序编写 一.Linux系统设备驱动程序概述 1.1 Linux设备驱动程序分类 1.2 编写驱动程序的一些基本概念 二.Linux系统网络设备驱动程序

20150424 adapter实现i2c驱动程序编写

2015-04-24 Lover雪儿 i2c的驱动程序可以由旧探测方法(adapt)和新探测方法(probe)这两种方法实现. 在i2c_driver中,adapt老方法使用的是attach_adapter作为探测函数,而在新方法中使用的是probe作为探测函数. 1 I2C设备驱动结构: 2 struct i2c_driver { 3 unsigned int class; 4 5 /* 添加和卸载I2C设备,老版本*/ 6 int (*attach_adapter)(struct i2c_a

DM9000C网卡驱动程序编写与测试

一般网卡驱动程序厂商会给我们提供一份模板驱动,我们的工作就是需要根据自己的需要更改这个模板驱动 1.DM9000C的硬件连接 硬件连接图如下所示:它接在S3C2440的BANK4内存控制器上,它只占用8个字节的长度,并且是16bit的位宽. 下面介绍一下DM9000C的主要引脚的功能:SD0-SD15位16bit的数据引脚接口:IOR为读使能信号,低电平有效:IOW为写使能信号,低电平有效:CS为片选信号,低电平有效:CMD为数据与索引选择信号,高电平表数据,低电平表索引,它连接到S3C2440

Linux中断处理驱动程序编写

本章节我们一起来探讨一下Linux中的中断: http://blog.csdn.net/gotosola/article/details/7422072 中断处理 http://www.cnblogs.com/tianshuai11/archive/2012/04/20/2477168.html 里边有自动探测中断号: http://blog.csdn.net/en_wang/article/details/6726421 当目标设备有能力告知驱动它要使用的中断号时,自动探测中断号只是意味着探测

Linux下编写驱动程序(VFS)

转:http://hi.baidu.com/firstm25/item/8fe022155e1fa78988a9568f 摘要:设备驱动程序是操作系统内核与机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节.那么驱动程序如何书写实现这一接口功能是本文讨论的重点,并以一简单的驱动程序介绍书写细节. 在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.(应用程序一般是在用户态下进行)也就是说系统必须在驱动程序的子函数返回后才能进行其它的工作,即驱动程序不能进入死循环. 字符型设备

20150503 imx257下实现I2C驱动的四种方法

20150503 imx257下实现I2C驱动的四种方法 2015-05-3 Lover雪儿 时间过得好快,转眼间五一假期就即将结束了,假期期间,大家都潇洒的去玩了,徒留辛辛苦苦的程序员还是窝在宿舍乖乖的敲着代码... 好啦,开开玩笑,辛酸史每家都有一大本,还是要积极的面对生活!!! 今天我们的任务是简单的入门linux内核下i2c设备驱动分离的四种写法. 一.一个简单的i2c驱动 和以前的驱动程序不同,i2c驱动分为drv驱动和dev设备驱动两个文件,不懂的可以参考我以前写的<20150313