FLASH 驱动程序设计 --- 块设备驱动设计

块设备概念

块设备是指只能以块为单位进行访问的设备,块大小一般是512个字节的整数倍。常见的块设备包括硬盘,SD卡,光盘等。

这里讲的512个字节是指,对设备一次访问最少会读出一个块的内容,用户可以对这个块中的某一个字节进行操作,并不是用户不能操作某一单个的字节。

块设备驱动系统架构

块设备的驱动系统架构如下图所示:
这里写图片描述

虚拟文件系统(VFS):
VFS是对各种具体文件系统的一种封装 ,为用户程序访问文件提供统一的接口。如下图所示:
这里写图片描述

磁盘缓存(Cache):
当用户发起文件访问请求的时候,首先会到Disk Cache中寻找文件是否被缓存了,如果在cache中,则直接从cache中读取。如果数据不在缓存中,就必须要到具体的文件系统中读取数据了。

影射层(Mapping Layer):
1、首先确定文件系统的 block size ,然后计算所请求的数据包含多少个block。
2、调用具体文件系统的函数来访问文件的 inode 结构,确定所请求的数据在磁盘上的地址。

通用块层(Generic Block Layer):
Linux内核把把块设备看作是由若干个扇区组成的数据空间。上层的读写请求在通用块层被构造成一个或多个bio结构。

I/O调度层(I/O Scheduler Layer):
I/O调度层负责采用某种算法(如:电梯调度算法)将I/O操作进行排序。
电梯调度算法的基本原则:如果电梯现在朝上运动,如果当前楼层的上方和下方都有请求,则先响应所有上方的请求,然后才向下响应下方的请求;如果电梯向下运动,则刚好相反。

块设备驱动:
在块系统架构的最底层,由块设备驱动根据排序好的请求,对硬件进行数据访问。

块设备驱动设计

块设备初始化:
这里写图片描述

块设备访问请求处理函数:
这里写图片描述

块设备范例代码:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h> /*  printk()     */
#include <linux/slab.h>   /*  kmalloc()    */
#include <linux/fs.h>     /*  everything...*/
#include <linux/errno.h>  /*  error codes  */
#include <linux/timer.h>
#include <linux/types.h>  /*  size_t       */
#include <linux/fcntl.h>  /*  O_ACCMODE    */
#include <linux/hdreg.h>  /*  HDIO_GETGEO  */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>

MODULE_LICENSE("Dual BSD/GPL");

static int major = 0;
static int sect_size = 512;
static int nsectors = 1024; 

// The internal representation of our device.
struct blk_dev{
         int size;                        /* Device size in sectors */
         u8 *data;                        /* The data array */
         struct request_queue *queue;     /* The device request queue */
         struct gendisk *gd;              /* The gendisk structure */
};

struct blk_dev *dev;

// Handle an I/O request, in sectors.
static void blk_transfer(struct blk_dev *dev, unsigned long sector,
   unsigned long nsect, char *buffer, int write)//硬件访问函数
{
    unsigned long offset = sector*sect_size;
    unsigned long nbytes = nsect*sect_size;

    if ((offset + nbytes) > dev->size) {
       printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
       return;
    }
    if (write)
        memcpy(dev->data + offset, buffer, nbytes);
    else
        memcpy(buffer, dev->data + offset, nbytes);
}

// The simple form of the request function.
static void blk_request(struct request_queue *q) //请求处理函数
{
    struct request *req;

    //取出一个要处理的请求
    req = blk_fetch_request(q);
    while (req != NULL) {
        struct blk_dev *dev = req->rq_disk->private_data;

        //根据请求信息访问硬件,获取数据
        blk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));

        //检查是否还有其他请求
        if(!__blk_end_request_cur(req, 0)) 
        {
            req = blk_fetch_request(q);
        }
    }
}

// The device operations structure.
static struct block_device_operations blk_ops = {
    .owner            = THIS_MODULE,
};

// Set up our internal device.
static void setup_device()
{
    // Get some memory.
    dev->size = nsectors*sect_size;
    dev->data = vmalloc(dev->size);
    if (dev->data == NULL) {
       printk (KERN_NOTICE "vmalloc failure.\n");
       return;
    }

    //2、初始化请求队列,指明请求处理函数 blk_request
    dev->queue = blk_init_queue(blk_request, NULL);
    if (dev->queue == NULL)
        goto out_vfree;

    //3、指明扇区大小
    blk_queue_logical_block_size(dev->queue, sect_size);
    dev->queue->queuedata = dev;

    // And the gendisk structure. 4、为设备分配gendisk结构
    dev->gd = alloc_disk(1);
    if (! dev->gd) {
       printk (KERN_NOTICE "alloc_disk failure\n");
       goto out_vfree;
    }

    //5、初始化gendisk结构
    dev->gd->major = major;
    dev->gd->first_minor = 0;
    dev->gd->fops = &blk_ops;
    dev->gd->queue = dev->queue;
    dev->gd->private_data = dev;
    sprintf (dev->gd->disk_name, "simp_blk%d", 0);
    set_capacity(dev->gd, nsectors*(sect_size/sect_size));

    //6、出册块设备
    add_disk(dev->gd);
    return;

out_vfree:
    if (dev->data)
        vfree(dev->data);
}

static int __init blk_init(void) //入口函数
{
    // Get registered. 1、注册块设备驱动程序
    major = register_blkdev(major, "blk");
    if (major <= 0) {
       printk(KERN_WARNING "blk: unable to get major number\n");
       return -EBUSY;
    }

    dev = kmalloc(sizeof(struct blk_dev), GFP_KERNEL);
    if (dev == NULL)
       goto out_unregister; 
    setup_device();      
    return 0;

out_unregister:
    unregister_blkdev(major, "sbd");
    return -ENOMEM;
}

static void blk_exit(void) //出口函数
{
    if (dev->gd) {
        del_gendisk(dev->gd);
        put_disk(dev->gd);
    }
    if (dev->queue)
        blk_cleanup_queue(dev->queue);
    if (dev->data)
        vfree(dev->data);

    unregister_blkdev(major, "blk");
    kfree(dev);
}

module_init(blk_init);
module_exit(blk_exit);

MTD系统架构

FLASH在嵌入式系统中是必不可少的,它是 bootloader、linux 内核和文件系统的最佳载体。在Linux内核中引入了 MTD 子系统为 NOR FLASH 和 NAND FLASH 设备提供统一的接口,从而使得FLASH驱动的设计大为简化。

输入命令: cat /proc/mtd 可以查看系统的flash中的分区。

系统架构:
这里写图片描述

Yaffs2文件系统应用

UBoot、Linux内核、yaffs2文件系统按照MTD分区被烧写在Nand Flash上,在系统启动的时候,UBoot将内核从nand flash 中拷贝到内存,然后启动Linux内核,最后内核根据给定的启动参数挂载文件系统,实现嵌入式系统的自启动。

通过Linux内核对Flash进行MTD分区

首先对内核进行配置,如下图所示:
这里写图片描述

在2440开发板的内核代码中的 common-EmbedSky.c 文件中,规定了每个分区的大小。
这里写图片描述

所有种类的内核代码中,保存分区信息的结构体类型都叫作:mtd_partition ,查找该结构体就可以找到分区的信息。

制作yaffs2文件系统
利用制作工具“mkyaffs2image”,如下图所示:
这里写图片描述

Nand Flash 驱动设计

以OK6410为例,参考代码位置:
内核源码\drivers\mtd\nand\s3c_nand.c

nand flash 驱动以 平台驱动 的方式实现。对flash的读写需要调用mtd设备驱动,在nand_read.c 文件中。

flash初始化流程如下图所示:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值