Linux MTD系统剖析

MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。

如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。

Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。

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字符设备和块设备

MTD数据结构:

1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:

struct mtd_info *mtd_table[MAX_MTD_DEVICES];  
  1. struct mtd_info *mtd_table[MAX_MTD_DEVICES];

  2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。

struct mtd_part {
    struct mtd_info mtd;        /* 分区信息, 大部分由master决定 */
    struct mtd_info *master;    /* 分区的主分区 */
    uint64_t offset;            /* 分区的偏移地址 */
    int index;                  /* 分区号 (Linux3.0后不存在该字段) */
    struct list_head list;      /* 将mtd_part链成一个链表mtd_partitons */
    int registered;
};  

mtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。

struct mtd_info {
    u_char type;         /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */
    uint32_t flags;      /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */
    uint64_t size;       /* mtd设备的大小 */
    uint32_t erasesize;  /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
    uint32_t writesize;  /* 写大小, 对于norFlash是字节,对nandFlash为一页 */
    uint32_t oobsize;    /* OOB字节数 */
    uint32_t oobavail;   /* 可用的OOB字节数 */
    unsigned int erasesize_shift;   /* 默认为0,不重要 */
    unsigned int writesize_shift;   /* 默认为0,不重要 */
    unsigned int erasesize_mask;    /* 默认为1,不重要 */
    unsigned int writesize_mask;    /* 默认为1,不重要 */
    const char *name;               /* 名字,   不重要*/
    int index;                      /* 索引号,不重要 */
    int numeraseregions;            /* 通常为1 */
    struct mtd_erase_region_info *eraseregions; /* 可变擦除区域 */  

    void *priv;     /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
    struct module *owner;   /* 一般设置为THIS_MODULE */  

    /* 擦除函数 */
    int (*erase) (struct mtd_info *mtd, struct erase_info *instr);  

    /* 读写flash函数 */
    int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);  

    /* 带oob读写Flash函数 */
    int (*read_oob) (struct mtd_info *mtd, loff_t from,
             struct mtd_oob_ops *ops);
    int (*write_oob) (struct mtd_info *mtd, loff_t to,
             struct mtd_oob_ops *ops);  

    int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);  

    int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
    int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
    /* Sync */
    void (*sync) (struct mtd_info *mtd);  

    /* Chip-supported device locking */
    int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);  

    /* 电源管理函数 */
    int (*suspend) (struct mtd_info *mtd);
    void (*resume) (struct mtd_info *mtd);  

    /* 坏块管理函数 */
    int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
    int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);  

    void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
    unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
                        unsigned long len,
                        unsigned long offset,
                        unsigned long flags);
    struct backing_dev_info *backing_dev_info;
    struct notifier_block reboot_notifier;  /* default mode before reboot */  

    /* ECC status information */
    struct mtd_ecc_stats ecc_stats;
    int subpage_sft;
    struct device dev;
    int usecount;
    int (*get_device) (struct mtd_info *mtd);
    void (*put_device) (struct mtd_info *mtd);
};  

mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。

如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。

int add_mtd_device(struct mtd_info *mtd)
int del_mtd_device (struct mtd_info *mtd)  

如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。

int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
int del_mtd_partitions(struct mtd_info *master)  

其中mtd_partition结构体表示分区的信息

struct mtd_partition {
    char *name;             /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
    uint64_t size;          /* 分区大小 */
    uint64_t offset;        /* 分区偏移值 */
    uint32_t mask_flags;    /* 掩码标识,不重要 */
    struct nand_ecclayout *ecclayout;   /* OOB布局 */
    struct mtd_info **mtdp;     /* pointer to store the MTD object */
};
其中nand_ecclayout结构体:
struct nand_ecclayout {
    __u32 eccbytes;     /* ECC字节数 */
    __u32 eccpos[64];   /* ECC校验码在OOB区域存放位置 */
    __u32 oobavail;
    /* 除了ECC校验码之外可用的OOB字节数 */
    struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
};  

关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。
MTD设备层:

mtd字符设备接口:

/drivers/mtd/mtdchar.c文件实现了MTD字符设备接口,通过它,可以直接访问Flash设备,与前面的字符驱动一样,通过file_operations结构体里面的open()、read()、write()、ioctl()可以读写Flash,通过一系列IOCTL 命令可以获取Flash 设备信息、擦除Flash、读写NAND 的OOB、获取OOB layout 及检查NAND 坏块等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL)

mtd块设备接口:

/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。
MTD硬件驱动层:

Linux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。

MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。

struct nand_chip {
    void  __iomem    *IO_ADDR_R;        /* 读8位I/O线地址 */
    void  __iomem    *IO_ADDR_W;        /* 写8位I/O线地址 */

    /* 从芯片中读一个字节 */
    uint8_t    (*read_byte)(struct mtd_info *mtd);
    /* 从芯片中读一个字 */
    u16        (*read_word)(struct mtd_info *mtd);
    /* 将缓冲区内容写入芯片 */
    void    (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
    /* 读芯片读取内容至缓冲区/ */
    void    (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
    /* 验证芯片和写入缓冲区中的数据 */
    int        (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
    /* 选中芯片 */
    void    (*select_chip)(struct mtd_info *mtd, int chip);
    /* 检测是否有坏块 */
    int        (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
    /* 标记坏块 */
    int        (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
    /* 命令、地址、数据控制函数 */
    void    (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
    /* 设备是否就绪 */
    int        (*dev_ready)(struct mtd_info *mtd);
    /* 实现命令发送 */
    void    (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
    int        (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    /* 擦除命令的处理 */
    void    (*erase_cmd)(struct mtd_info *mtd, int page);
    /* 扫描坏块 */
    int        (*scan_bbt)(struct mtd_info *mtd);
    int        (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
    /* 写一页 */
    int        (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
                      const uint8_t *buf, int page, int cached, int raw);

    int        chip_delay;            /* 由板决定的延迟时间 */
    /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */
    unsigned int    options;    

    /* 用位表示的NAND芯片的page大小,如某片NAND芯片
     * 的一个page有512个字节,那么page_shift就是9
     */
    int         page_shift;
    /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
     * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14
     */
    int         phys_erase_shift;
    /* 用位表示的bad block table的大小,通常一个bbt占用一个block,
     * 所以bbt_erase_shift通常与phys_erase_shift相等
      */
    int         bbt_erase_shift;
    /* 用位表示的NAND芯片的容量 */
    int         chip_shift;
    /* NADN FLASH芯片的数量 */
    int         numchips;
    /* NAND芯片的大小 */
    uint64_t chipsize;
    int         pagemask;
    int         pagebuf;
    int         subpagesize;
    uint8_t     cellinfo;
    int         badblockpos;
    nand_state_t    state;
    uint8_t        *oob_poi;
    struct nand_hw_control  *controller;
    struct nand_ecclayout    *ecclayout;    /* ECC布局 */

    struct nand_ecc_ctrl ecc;    /* ECC校验结构体,里面有大量的函数进行ECC校验 */
    struct nand_buffers *buffers;
    struct nand_hw_control hwcontrol;
    struct mtd_oob_ops ops;
    uint8_t        *bbt;
    struct nand_bbt_descr    *bbt_td;
    struct nand_bbt_descr    *bbt_md;
    struct nand_bbt_descr    *badblock_pattern;
    void        *priv;
};

最后,我们来用图表的形式来总结一下,MTD设备层、MTD原始设备层、FLASH硬件驱动层之间的联系。

Linux MTD系统剖析

时间: 2024-11-07 15:59:31

Linux MTD系统剖析的相关文章

Linux MTD系统剖析【转】

转自:http://blog.csdn.net/lwj103862095/article/details/21545791 MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口.MTD将文件系统与底层FLASH存储器进行了隔离. 如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点.MTD设备层.MTD原始设备层.硬件驱动层. Flash硬件驱动层:Flash硬件驱动层负责对F

linux MTD系统解析(转)

MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口.MTD将文件系统与底层FLASH存储器进行了隔离. 如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点.MTD设备层.MTD原始设备层.硬件驱动层. Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读.写和擦除操作.MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,No

Linux的系统级性能剖析工具-perf

一直在找个靠谱且易用的性能分析工具,perf 貌似是很符合要求的,先给出阿里整理的几篇文档: Linux的系统级性能剖析工具-perf-1.pdf Linux的系统级性能剖析工具-perf-2.pdf Linux的系统级性能剖析工具-perf-3.pdf Perf在Linux性能评估中的应用_v3.pdf Linux的系统级性能剖析工具-perf,布布扣,bubuko.com

《linux 内核完全剖析》 void free_page() 分析

最近在做项目开发时用到了MySql数据库,在看了一些有关MySql的文章后,很快就上手使用了.在使用的过程中还是出现了一些问题,因为使用的是绿色免安装版的MySql所以在配置的时候出现了一些问题,该篇文章就主要针对MySql绿色版的配置及其使用进行讨论. 一.MySql概述 MySql数据库是有瑞典MySql AB公司开发,现在该公司被Oracle收购属于Oracle所有.同SQL Server类似,它也是基于关系型数据库的数据库管理系统,在Web应用方面MySQL是最好的RDBMS之一,因为它

《linux 内核完全剖析》上帝为什么是右移20,而不是22! dir = (unsigned long *) ((from>>20) & 0xffc)

在memory.c里面有这么一段代码.为了其中的一句话,让我内牛满面啊! dir = (unsigned long *) ((from>>20) & 0xffc) int free_page_tables(unsigned long from,unsigned long size) { unsigned long *pg_table; unsigned long * dir, nr; if (from & 0x3fffff) panic("free_page_tabl

linux进程调度机制剖析(基于3.16-rc4)

进程调度所使用到的数据结构: 1.就绪队列 内核为每一个cpu创建一个进程就绪队列,该队列上的进程均由该cpu执行,代码如下(kernel/sched/core.c). 1 DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues); 定义了一个struct rq结构体数组,每个数组元素是一个就绪队列,对应一个cpu.下面看下struct rq结构体(kernel/sched/sched.h): 1 struct rq { 2 /* runqueue

Linux MTD子系统 _从模型分析到Flash驱动模板

MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化硬件驱动框架.本文基于3.14内核,讨论MTD驱动框架. MTD子系统框架 设备节点层:MTD框架可以在/dev下创建字符设备节点(主设备号90)以及块设备节点(主设备号31), 用户通过访问此设备节点即可访问MTD字符设备或块设备. MTD设备层: 基于MTD原始设备, Linux在这一层次定义出

《linux 内核完全剖析》sched.c sched.h 代码分析笔记

sched.c sched.h 代码分析笔记 首先上header file sched.h #ifndef _SCHED_H #define _SCHED_H #define HZ 100 #define NR_TASKS 64 #define TASK_SIZE 0x04000000 #define LIBRARY_SIZE 0x00400000 #if (TASK_SIZE & 0x3fffff) #error "TASK_SIZE must be multiple of 4M&qu

《linux 内核完全剖析》 sys.c 代码分析

sys.c 代码分析 setregid /* * This is done BSD-style, with no consideration of the saved gid, except * that if you set the effective gid, it sets the saved gid too. This * makes it possible for a setgid program to completely drop its privileges, * which i