zoukankan      html  css  js  c++  java
  • IMX257实现Ramblock驱动程序编写

    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  * driverslockxd.c
     3  * driverslockz2ram.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
    ",++cnt);
    45     
    46     req = blk_fetch_request(q);
    47     while(req){
    48         printk("enter while req %d
    ",++cnt);
    49         break;
    50     }
    51 
    52     __blk_end_request_cur(req, 0);
    53     printk("leave do_ramblock_request %d
    ",++cnt);
    54 }
    55 
    56 static int ramblock_init(void)
    57 {
    58     printk("ramblock_init
    ");
    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
    ");
    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  * driverslockxd.c
      3  * driverslockz2ram.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
    ",++cnt);
     50 /*    
     51     req = blk_fetch_request(q);
     52     while(req){
     53         printk("enter while req %d
    ",++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
    ",
     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
    ",++r_cnt);
     82             memcpy(req->buffer, ramblock_buf+offset, len);
     83         }else{
     84             //printk("do_ramblock_request WRITE %d
    ",++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 
    ");
     95 }
     96 
     97 static int ramblock_init(void)
     98 {
     99     printk("ramblock_init
    ");
    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
    ");
    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  * driverslockxd.c
      3  * driverslockz2ram.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
    ",++cnt);
     61 /*    
     62     req = blk_fetch_request(q);
     63     while(req){
     64         printk("enter while req %d
    ",++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
    ",
     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
    ",++r_cnt);
     93             memcpy(req->buffer, ramblock_buf+offset, len);
     94         }else{
     95             //printk("do_ramblock_request WRITE %d
    ",++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 
    ");
    106 }
    107 
    108 static int ramblock_init(void)
    109 {
    110     printk("ramblock_init
    ");
    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
    ");
    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  * driverslockxd.c
      3  * driverslockz2ram.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
    ",++cnt);
     62 /*    
     63     req = blk_fetch_request(q);
     64     while(req){
     65         printk("enter while req %d
    ",++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
    ",
     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
    ",++r_cnt);
     94             memcpy(req->buffer, ramblock_buf+offset, len);
     95         }else{
     96             //printk("do_ramblock_request WRITE %d
    ",++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 
    ");
    107 }
    108 
    109 static int ramblock_init(void)
    110 {
    111     printk("ramblock_init
    ");
    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
    ");
    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

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

  • 相关阅读:
    HDOJ 1069_大二写
    HDOJ 2037:今年暑假不AC_大二写
    第二次HDOJ:1058
    HDOJ 1019
    第二次 HDOJ 1003
    第二次做HDOJ 1051
    memset()的用法
    数据结构中的频度含义
    程序框图的书写
    python课堂整理22----装饰器进阶
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4428533.html
Copyright © 2011-2022 走看看