• 【驱动】MTD子系统分析


    MTD介绍

      MTD,Memory Technology Device即内存技术设备

      字符设备和块设备的区别在于前者只能被顺序读写,后者可以随机访问;同时,两者读写数据的基本单元不同。

      字符设备,以字节为基本单位,在Linux中,字符设备实现的比较简单,不需要缓冲区即可直接读写,内核例程和用户态API一一对应,用户层的Read函数直接对应了内核中的Read例程,这种映射关系由字符设备的file_operations维护。

      块设备,则以块为单位接受输入和返回输出。对这种设备的读写是按块进行的,其接口相对于字符设备复杂,read、write API没有直接到块设备层,而是直接到文件系统层,然后再由文件系统层发起读写请求。 
      同时,由于块设备的IO性能与CPU相比很差,因此,块设备的数据流往往会引入文件系统的Cache机制。

      MTD设备既非块设备也不是字符设备,但可以同时提供字符设备和块设备接口来操作它。


     MTD总概述

      Linux中MTD的所有源码位于/drivers/mtd子目录下,

      

      MTD设备通常可分为四层 
      上到下依次是:设备节点MTD设备层MTD原始设备层硬件驱动层

    Flash硬件驱动层 

      硬件驱动层负责在init时驱动Flash硬件并建立从具体设备到MTD原始设备映射关系

       tip: 映射关系通常包括 分区信息、I/O映射及特定函数的映射 

    drivers/mtd/chips :   CFI/jedec接口通用驱动 
    drivers/mtd/nand :   nand通用驱动和部分底层驱动程序 
    drivers/mtd/maps :   nor flash映射关系相关函数 
    drivers/mtd/devices:   nor flash底层驱动

    MTD原始设备 

      用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。 

    mtdcore.c :  MTD原始设备接口相关实现 
    mtdpart.c :  MTD分区接口相关实现

    MTD设备层 

      基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。 

    mtdchar.c :  MTD字符设备接口相关实现 
    mtdblock.c : MTD块设备接口相关实现

    设备节点 

      通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90) 
      通过访问此设备节点即可访问MTD字符设备和块设备 

    文件系统 

      内核启动后,通过mount 命令可以将flash中的其余分区作为文件系统挂载到mountpoint上。


    MTD数据结构

    重要的数据结构

    1. mtd_info  表示mtd原始设备, 所有mtd_info结构体被存放在mtd_info数组mtd_table中
    2. mtd_part  表示MTD分区,其中包含了 mtd_info,每一个分区都是被看成一个MTD 原始设备 

      在mtd_table中,mtd_part.mtd_info中的大部分数据都从该分区的主分区mtd_part->master中获得 
      tip: master不作为一个mtd原始设备加入mtd_table

      各层之间的交互如下图

      

    mtd_info的主要数据结构

    作用
    type mtd类型, 包括MTD_NORFLASH,MTD_NANDFLASH等(See mtd-abi.h)
    flags 标志位, MTD_WRITEABLE,MTD_NO_ERASE等(See mtd-abi.h)
    size mtd设备的大小
    erasesize 主要的擦除大小, 即Flash的块大小 (tip: mtd设备可能有多个erasesize)
    writesize 写大小, 对于norFlash是字节,对nandFlash为一页
    oobsize 每块oob数据量, eg 16
    oobavail  
    name 命名
    index  
    ecclayout nand_ecclayout结构体指针, 表示的是ecc布局,可参考硬件手册的OOB中ecc布局
    numeraseregions 可变擦除区域的数目, 通常为1
    eraseregions mtd_erase_region_info结构体指针, 可变擦除区域
    erase 擦除Flash函数
    read/write 读写Flash函数
    read_oob/write_oob 带oob读写Flash函数
    suspend/resume Power Management functions
    priv 私有数据, cfi接口flash指向map_info结构, 或指向自定义flash相关结构体


     
    mtd_part的主要数据结构

    作用
    mtd 分区信息, 大部分由master决定
    master 分区的主分区
    offset 分区的偏移地址
    index 分区号 (3.0后不存在该字段)
    list 将mtd_part链成一个链表mtd_partitons

     mtd_part的主要数据结构

    作用
    mtd 分区信息, 大部分由master决定
    master 分区的主分区
    offset 分区的偏移地址
    index 分区号 (3.0后不存在该字段)
    list 将mtd_part链成一个链表mtd_partitons

    mtd_partition的主要数据结构 

    作用
    name  
    size  
    offset  
    mask_flags  
    ecclayout  
    mtdp  

    map_info的主要数据结构

    作用
    name 名称
    size 大小
    phys 物理地址
    bankwidth 总线宽度(in octets)
    virt 虚拟地址,通常通过ioremap将物理地址进行映射得到
    read/copy_from/write/copy_to 读写函数
    map_priv_1/map_priv_2 驱动可用的私有数据

    nand_chip的主要数据结构

    作用
    IO_ADDR_R/IO_ADDR_W 读/写8根io线的地址
    read_byte/read_word 从芯片读一个字节/字
    read_buf/write_buf 读芯片读取内容至缓冲区/将缓冲区内容写入芯片
    verify_buf  
    select_chip  
    block_bad 检查是否坏块
    block_markbad 标识坏块
    cmd_ctrl 硬件相关控制函数
    init_size  
    dev_ready  
    cmdfunc 命令处理函数
    waitfunc  
    erase_cmd 擦除命令
    scan_bbt 扫描坏块
    errstat  
    write_page  
    options 与具体的NAND 芯片相关的选项, 如NAND_USE_FLASH_BBT等(nand.h)
    page_shift  
       
    ecclayout nand_ecclayout类型结构体, ECC布局信息
    ecc nand_ecc_ctrl类型结构体, ECC控制结构

    nand_ecclayout的主要数据结构

    作用
    eccbytes ecc的字节数(For 512B-per-page, eccbytes is 3)
    eccpos ecc数据在oob中的位置
    oobavail oob中可用的字节数, MTD 会根据其它三个变量自动计算得到
    oobfree nand_oobfree类型结构体, 显示定义空闲的oob 字节

    MTD相关层实现

    MTD设备层

    mtd字符设备接口
      mtdchar.c 实现了字符设备接口,通过它,用户可以直接操作Flash 设备。 
      Ø 通过read()、write()系统调用可以读写Flash。 
      Ø 通过一系列IOCTL 命令可以获取Flash 设备信息、擦除Flash、读写NAND 的OOB、获取OOB layout 及检查NAND 坏块等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL) 
      tip: mtd_read和mtd_write直接直接调用mtd_info的read 函数,因此,字符设备接口跳过patition这一层

    mtd块设备接口: 
      主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 
      但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。 
      块设备模拟驱动按照block 号和偏移量来定位文件,因此在Flash 上除了文件数据,基本没有额外的控制数据。

    MTD原始设备层

    MTD硬件驱动层

    NOR Flash驱动结构

      Linux系统实现了针对cfi,jedec等接口的通用NOR Flash驱动 
       在上述接口驱动基础上,芯片级驱动较简单 
         定义具体内存映射结构体map_info,然后通过接口类型后调用do_map_probe()  
      以h720x-flash.c为例(位于drivers/mtd/maps) 
      - 定义map_info结构体, 初始化成员name, size, phys, bankwidth 
      - 通过ioremap映射成员virt(虚拟内存地址) 
      - 通过函数simple_map_init初始化map_info成员函数read,write,copy_from,copy_to 
      - 调用do_map_probe进行cfi接口探测, 返回mtd_info结构体 
      - 通过parse_mtd_partitions, add_mtd_partitions注册mtd原始设备 

     NAND Flash驱动结构 

      Linux实现了通用NAND驱动(drivers/mtd/nand/nand_base.c) 
      tip: For more, check 内核中的NAND代码布局  
      芯片级驱动需要实现nand_chip结构体 
      MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。

    Ø NAND芯片级初始化 
      主要有以下几个步骤: 
      - 分配nand_chip内存,根据目标板及NAND控制器初始化nand_chip中成员函数(若未初始化则使用nand_base.c中的默认函数),将mtd_info中的priv指向nand_chip(或板相关私有结构),设置ecc模式及处理函数 
      - 以mtd_info为参数调用nand_scan()探测NAND FLash。 
        nand_scan()会读取nand芯片ID,并根据mtd->priv即nand_chip中成员初始化mtd_info 
      - 若有分区,则以mtd_info和mtd_partition为参数调用add_mtd_partitions()添加分区信息 

    Ø MTD对NAND芯片的读写 
      主要分三部分: 
      A、struct mtd_info中的读写函数,如read,write_oob等,这是MTD原始设备层与FLASH硬件层之间的接口; 
      B、struct nand_ecc_ctrl中的读写函数,如read_page_raw,write_page等,主要用来做一些与ecc有关的操作; 
      C、struct nand_chip中的读写函数,如read_buf,cmdfunc等,与具体的NAND controller相关,就是这部分函数与硬件交互,通常需要我们自己来实现。 
      tip: nand_chip中的读写函数虽然与具体的NAND controller相关,但是MTD也为我们提供了默认的读写函数,如果NAND controller比较通用(使用PIO模式),那么对NAND芯片的读写与MTD提供的这些函数一致,就不必自己实现这些函数。

    eg:  以读为例 

      MTD上层会调用struct mtd_info中的读page函数,即nand_read函数。 
      接着nand_read函数会调用struct nand_chip中cmdfunc函数,这个cmdfunc函数与具体的NAND controller相关,它的作用是使NAND controller向NAND 芯片发出读命令,NAND芯片收到命令后,就会做好准备等待NAND controller下一步的读取。 
      接着nand_read函数又会调用struct nand_ecc_ctrl中的read_page函数,而read_page函数又会调用struct nand_chip中read_buf函数,从而真正把NAND芯片中的数据读取到buffer中(所以这个read_buf的意思其实应该是read into buffer,另外,这个buffer是struct mtd_info中的nand_read函数传下来的)。 
      read_buf函数返回后,read_page函数就会对buffer中的数据做一些处理,比如校验ecc,以及若数据有错,就根据ecc对数据修正之类的,最后read_page函数返回到nand_read函数中。 
      对NAND芯片的其它操作,如写,擦除等,都与读操作类似 。

    Flash转换层

       Tranlation Layer 

      逻辑块地址(Logical Block Address)对应到Flash存储器的物理位置,使系统能把Flash当作普通的硬盘一样处理

      FTL 
      Nor Flash

      NFTL 
      Nand Flash

      闪存转换层要做下面的操作来完成写请求: 
      将这个扇区所在擦除块的数据读到内存中,放在缓存中。 
      将缓存中与这个扇区对应的内容用新的内容替换。 
      对该擦除块执行擦除操作。    
      将缓冲中的数据写回该擦除块。

     

  • 相关阅读:
    idea常用快捷键
    Spring中<bean>标签之使用p标签配置bean的属性
    Mysql语句转义
    Idea使用(摘抄至java后端技术公众号-孤独烟)
    js中scroll滚动相关
    Flask-wtforms类似django中的form组件
    Flask中的数据连接池
    SQLAlchemy
    博客园美化阅读模式
    [NOIP2003] 提高组 洛谷P1039 侦探推理
  • 原文地址:https://www.cnblogs.com/lcw/p/3313698.html
Copyright © 2020-2023  润新知