块设备驱动程序的编写驱动之用内存模拟磁盘

发布者:小悟空111最新更新时间:2016-04-01 来源: eefocus关键字:块设备  驱动程序  内存模拟磁盘 手机看文章 扫描二维码
随时随地手机看文章
// 参考:
// drivers\block\xd.c
// drivers\block\z2ram.c
 
#include "linux/module.h"
#include "linux/errno.h"
#include "linux/interrupt.h"
#include "linux/mm.h"
#include "linux/fs.h"
#include "linux/kernel.h"
#include "linux/timer.h"
#include "linux/genhd.h"
#include "linux/hdreg.h"
#include "linux/ioport.h"
#include "linux/init.h"
#include "linux/wait.h"
#include "linux/blkdev.h"
#include "linux/blkpg.h"
#include "linux/delay.h"
#include "linux/io.h"
 
#include
#include
#include
 
static struct gendisk *ramblock_disk;
static request_queue_t *ramblock_queue;
 
static int major;
 
static DEFINE_SPINLOCK(ramblock_lock);
 
#define RAMBLOCK_SIZE (1024*1024)
static unsigned char *ramblock_buf;
 
static int ramblock_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
        // 容量=heads*cylinders*sectors*512 
        geo->heads     = 2;
        geo->cylinders = 32;
        geo->sectors   = RAMBLOCK_SIZE/2/32/512;
        return 0;
}
 
 
static struct block_device_operations ramblock_fops = {
        .owner = THIS_MODULE,
        .getgeo = ramblock_getgeo,
};
 
static void do_ramblock_request(request_queue_t * q)
{
        static int r_cnt = 0;
        static int w_cnt = 0;
        struct request *req;
        //printk("do_ramblock_request %d\n", ++cnt);
 
        while ((req = elv_next_request(q)) != NULL) {
                // 数据传输三要素: 源,目的,长度 
                // 源/目的: 
                unsigned long offset = req->sector * 512;
 
                // 目的/源: (写的时候buffer是源,读的时候buffer是目的,从扇区里读出来放在buffer里)
                // req->buffer
 
                // 长度:
                unsigned long len = req->current_nr_sectors * 512;
 
                if (rq_data_dir(req) == READ)
                {      
                        //如果是操作硬盘的话在这个位置放置读取硬盘的函数就可以了
                        //printk("do_ramblock_request read %d\n", ++r_cnt);
                        memcpy(req->buffer, ramblock_buf+offset, len);
                }
                else
                {
                        //如果是操作硬盘的话在这个位置放置写硬盘的函数就可以了
                        //printk("do_ramblock_request write %d\n", ++w_cnt);
                        memcpy(ramblock_buf+offset, req->buffer, len);
                }
                
                end_request(req, 1);
        }
}
 
static int ramblock_init(void)
{
        // 1. 分配一个gendisk结构体 
        ramblock_disk = alloc_disk(16); // 次设备号个数: 分区个数+1 ,表示有15个分区
 
        // 2. 设置 
        // 2.1 分配/设置队列: 提供读写能力 
        ramblock_queue = blk_init_queue(do_ramblock_request, &ramblock_lock);//do_ramblock_request队列处理函数
        ramblock_disk->queue = ramblock_queue;
        // 2.2 设置其他属性: 比如容量 
        major = register_blkdev(0, "ramblock");  // cat /proc/devices
        ramblock_disk->major       = major;
        ramblock_disk->first_minor = 0;
        sprintf(ramblock_disk->disk_name, "ramblock");
        ramblock_disk->fops        = &ramblock_fops;
        set_capacity(ramblock_disk, RAMBLOCK_SIZE / 512);   //在内核里面对于文件系统那一层,认为                                                                                                               //扇区永远是512字节,即为扇区数
                                                                                                  //块设备的操作是以扇区为单位的。     
        // 3. 硬件相关操作 
        ramblock_buf = kzalloc(RAMBLOCK_SIZE, GFP_KERNEL);
 
        // 4. 注册 
        add_disk(ramblock_disk);
 
        return 0;
}
 
static void ramblock_exit(void)
{
        unregister_blkdev(major, "ramblock");
        del_gendisk(ramblock_disk);
        put_disk(ramblock_disk);
        blk_cleanup_queue(ramblock_queue);
 
        kfree(ramblock_buf);
}
 
module_init(ramblock_init);
module_exit(ramblock_exit);
 
MODULE_LICENSE("GPL");
 
==============================================================
框架:
 
app:      open,read,write "1.txt"
---------------------------------------------  文件的读写
文件系统: vfat, ext2, ext3, yaffs2, jffs2      (把文件的读写转换为扇区的读写)
-----------------ll_rw_block-----------------  扇区的读写
                       1. 不像字符设备那样提供读写函数,而是把"读写"放入队列
                       2. 调用队列的处理函数(优化/调顺序/合并)后再执行
            块设备驱动程序     
---------------------------------------------
硬件:        硬盘,flash
 
 
 
分析ll_rw_block
        for (i = 0; i < nr; i++) {
            struct buffer_head *bh = bhs[i];
            submit_bh(rw, bh);
                struct bio *bio; // 使用bh来构造bio (block input/output)
                submit_bio(rw, bio);
                    // 通用的构造请求: 使用bio来构造请求(request)
                    generic_make_request(bio);
                        __generic_make_request(bio);
                            request_queue_t *q = bdev_get_queue(bio->bi_bdev); // 找到队列  
                            
                            // 调用队列的"构造请求函数"
                            ret = q->make_request_fn(q, bio);
                                    // 默认的函数是__make_request
                                    __make_request
                                        // 先尝试合并
                                        elv_merge(q, &req, bio);
                                        
                                        // 如果合并不成,使用bio构造请求
                                        init_request_from_bio(req, bio);
                                        
                                        // 把请求放入队列
                                        add_request(q, req);
                                        
                                        // 执行队列
                                        __generic_unplug_device(q);
                                                // 调用队列的"处理函数"
                                                q->request_fn(q);
            
怎么写块设备驱动程序呢?
1. 分配gendisk: alloc_disk
2. 设置
2.1 分配/设置队列: request_queue_t  // 它提供读写能力
    blk_init_queue
2.2 设置gendisk其他信息             // 它提供属性: 比如容量
3. 注册: add_disk
 
参考:
drivers\block\xd.c
drivers\block\z2ram.c
 
测试3th,4th:
在开发板上:
1. insmod ramblock.ko
2. 格式化: mkdosfs /dev/ramblock
3. 挂接: mount /dev/ramblock /tmp/
4. 读写文件: cd /tmp, 在里面vi文件
5. cd /; umount /tmp/
6.再次挂接: mount /dev/ramblock /tmp/,后查看文件还依然存在
7. cat /dev/ramblock > /mnt/ramblock.bin
8. 在PC上查看ramblock.bin
   sudo mount -o loop ramblock.bin /mnt
   
测试5th:
1. insmod ramblock.ko
2. ls /dev/ramblock*
3. fdisk /dev/ramblock
 
注:
1、对块设备进行读写操作时可能不会立即响应,先放入队列一段时间后一起执行,如果想让读写操作立即执行可以运行:sync命令,即同步命令进行同步,此时会执行没有执行的相关操作。
2、对于磁盘容量=磁头数*柱面数*扇区数*512,柱面数就是有多少环,每个扇区512字节
     对于flash是有多少块,每块有多少扇区,每个扇区可以存多少字节

 

关键字:块设备  驱动程序  内存模拟磁盘 引用地址:块设备驱动程序的编写驱动之用内存模拟磁盘

上一篇:USB设备驱动程序
下一篇:触摸屏驱动程序(输入子系统)

推荐阅读最新更新时间:2024-03-16 14:48

8255A驱动程序的设计
  8255A的驱动程序主要是涉及对端口A、B、C以及控制字的设置,8255A具体的驱动程序主要包括以下代码及函数。   (1)管脚定义及函数声明。   管脚定义是指端口A、端口B、端口C和控制字的地址说明以及状态标志位的定义;函数的声明包括端口A、端口B、端口C的读写函数和控制字以及C口配置函数,具体代码如下:   (2)端口A、B、C读写函数。   端口A、B、C读写函数完成8255A端口A、B、C的数据读写,程序代码如下:   (3)端口C配置函数。   端口C配置函数可实现PC口具体某一位的输入/输出设置,程序代码如下:   (4)写控制字函数。   写控制字函数完成对控制字的写,从而实现对端
[单片机]
8255A<font color='red'>驱动程序</font>的设计
Linux的MISC类设备AD7859L的驱动程序开发
1 引言 在嵌入式系统中基于ARM微核的嵌入式处理器已经成为市场主流。随着ARM技术的广泛应用,建立面向ARM构架的嵌入式操作系统成为测量行业的热点问题。在LINUX操作系统中添加新的外部设备时,只需为其添加对应的驱动程序即可。介绍另一种驱动程序的编写方式,即采用 MISC类 设备。其实质也是一个字符设备。可将用户各种不同的驱动设备类型合成到一种类型中,共用一个主设备号,通过不同的次设备号和设备节点名来区分。可方便管理这些驱动模块。字符型的驱动设备模块在挂载时都要分配主设备号、次设备号和创建设备节点名,在卸载驱动设备时还必须同时删掉设备节点名。通过采用MISC类设备,在挂载设备驱动时无须再用到mknod命令分配主设备号、次设备号和
[单片机]
Linux的MISC类<font color='red'>设备</font>AD7859L的<font color='red'>驱动程序</font>开发
基于AVR C++写的4位数码管驱动程序
我在这里用C++向大家展示一个实用的程序,如果你会C,相信C++对你来说不会很难。而且在我们平时的编程中,又要兼顾PCB,又要记住接线方法,是否会让你感到很棘手呢?经过我多年的编程经验,我把这个程序和大家分享,让大家领略到一些编程的技巧。这个程序需要将数码管的脚全部连接到MCU的IO上,可以任意连接. 首先我们需要定义一个mydef.h保存在你的工作目录下,输入以下内容,当然是由您自己的喜欢而定义,但是请记住需要对号入座。 #define seLED_QW() (clPB0()) #define seLED_BW() (clPB0()) #define seLED_SW() (clPB0()) #define seLED
[单片机]
S3C2440 字符设备驱动程序之LED驱动程序_测试改进(三)
在加载驱动之前,先来看一下/proc/devices,里面是现在内核所支持的设备。 第一列是主设备号,对应的是内核数组chrdevs的下标,第二列是它的名字。 加载驱动的命令: insmod first_drv.ko at /proc/devices 写个测试驱动程序来测试它。 mknod /dev/xxx c 主设备号 次设备号 register_chrdev(111, first_drv ,&first_drv_fops); 怎么确定这设备号111? 1、先cat /proc/devices,有哪一个空缺项,然后选取那个空缺项。(1~255)。 2、也可以写为0,系统会自动给我们分配一个
[单片机]
模拟串口--基于STM8普通IO口的模拟串口驱动程序
标准串口通讯数据的格式为:起始位(1) + 数据位(8) + 校验位(1) + 停止位(1) 串口通讯另外一个重要的的部分是设置波特率,波特率就是1秒钟内串口所传输的Bit(位)数。 关于采样频率:为了较小读取或者发送串行数据的误差,我们采取了在N(我用的是4次)次中断中,取固定位置的读取的数据。 我以stm8中9600波特率计算的过程为例:(1秒钟传输9600位) 可以计算出传输1位所需要的时间 T1 = 1/9600 约为104us 由此可知,发送一位数据,定时器中断的时间间隔应为 104/4 = 26us(4倍采样频率) stm8 内部晶振频率为16M,我采用8分频也就是2M,故MCU震荡周期为 1/
[单片机]
1602LCD模块的C51驱动程序
手上有一片1602的LCD,ks0066的驱动芯片,带LED背光.参考网站上的一些资料,写了个驱动程序,写完了才发现,跟网上流传的代码基本上差不多 /*======================================= 1602lcd模块驱动程序 ========================================== 说明:1.晶体:11.0592MHz 2.1602驱动:ks0066 ******************************************/ #i nclude reg51.h #i nclude string.h #define
[单片机]
1602LCD模块的C51<font color='red'>驱动程序</font>
OLED 3.12寸 256*64 SSD132的stm32f103单片机驱动程序
OLED 3.12寸 256*64 驱动程序 stm32f103 单片机源程序如下: /* MOD:25664 (dots:256*64) IC : SSD1322 (加入图片测试功能) MCU: W78E516D 12MHZ //============================ 说明:6800:BS0=1,BS1=1; 8080:BS0=0,BS1=1; (默认) 3-SPI:BS0=1,BS1=0; 4-SPI:BS0=0,BS1=0; */ /* 接口定义: 1. VSS--------------GND 2. VDD--------------+3.3V 3. NC 4
[单片机]
OLED 3.12寸 256*64 SSD132的stm32f103单片机<font color='red'>驱动程序</font>
基于PCI总线的双DSP系统及WDM驱动程序设计
摘要:介绍了PCI总线控制芯片PCI2040的功能及内部结构,分析了基于PCI总线的双DSP通信的硬件结构及实现方法,并描述了利用Windows2000 DDK开发WDM设备驱动程序的方法及PCI双DSP通信驱动程序主要模块的设计方法和编程注意要点。 关键词:PCI总线 PCI2040 DSP DDK WDM TI公司专门推出了PCI2040桥芯片是专门针对PCI总线和DSP接口用的,本文利用它和DSP来处理视频信号,并用双端口RAM实现DSP之间的数据传输。 1 硬件设计 1.1 PCI总线控制芯片PCI2040 PCI总线是一种不依附于某个具体处理器的局部总线,它支持32位或64位的总线宽度,频率通常是33MHz,
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved