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里:

[cpp] view
plain
?
  1. struct mtd_info *mtd_table[MAX_MTD_DEVICES];

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

[cpp] view
plain
?
  1. struct mtd_part {
  2. struct mtd_info mtd;        /* 分区信息, 大部分由master决定 */
  3. struct mtd_info *master;    /* 分区的主分区 */
  4. uint64_t offset;            /* 分区的偏移地址 */
  5. int index;                  /* 分区号 (Linux3.0后不存在该字段) */
  6. struct list_head list;      /* 将mtd_part链成一个链表mtd_partitons */
  7. int registered;
  8. };

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

[cpp] view
plain
?
  1. struct mtd_info {
  2. u_char type;         /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */
  3. uint32_t flags;      /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */
  4. uint64_t size;       /* mtd设备的大小 */
  5. uint32_t erasesize;  /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
  6. uint32_t writesize;  /* 写大小, 对于norFlash是字节,对nandFlash为一页 */
  7. uint32_t oobsize;    /* OOB字节数 */
  8. uint32_t oobavail;   /* 可用的OOB字节数 */
  9. unsigned int erasesize_shift;   /* 默认为0,不重要 */
  10. unsigned int writesize_shift;   /* 默认为0,不重要 */
  11. unsigned int erasesize_mask;    /* 默认为1,不重要 */
  12. unsigned int writesize_mask;    /* 默认为1,不重要 */
  13. const char *name;               /* 名字,   不重要*/
  14. int index;                      /* 索引号,不重要 */
  15. int numeraseregions;            /* 通常为1 */
  16. struct mtd_erase_region_info *eraseregions; /* 可变擦除区域 */
  17. void *priv;     /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
  18. struct module *owner;   /* 一般设置为THIS_MODULE */
  19. /* 擦除函数 */
  20. int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
  21. /* 读写flash函数 */
  22. int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  23. int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
  24. /* 带oob读写Flash函数 */
  25. int (*read_oob) (struct mtd_info *mtd, loff_t from,
  26. struct mtd_oob_ops *ops);
  27. int (*write_oob) (struct mtd_info *mtd, loff_t to,
  28. struct mtd_oob_ops *ops);
  29. int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
  30. int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  31. int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
  32. int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  33. int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
  34. int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
  35. int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
  36. int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
  37. /* Sync */
  38. void (*sync) (struct mtd_info *mtd);
  39. /* Chip-supported device locking */
  40. int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
  41. int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
  42. /* 电源管理函数 */
  43. int (*suspend) (struct mtd_info *mtd);
  44. void (*resume) (struct mtd_info *mtd);
  45. /* 坏块管理函数 */
  46. int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
  47. int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
  48. void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
  49. unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
  50. unsigned long len,
  51. unsigned long offset,
  52. unsigned long flags);
  53. struct backing_dev_info *backing_dev_info;
  54. struct notifier_block reboot_notifier;  /* default mode before reboot */
  55. /* ECC status information */
  56. struct mtd_ecc_stats ecc_stats;
  57. int subpage_sft;
  58. struct device dev;
  59. int usecount;
  60. int (*get_device) (struct mtd_info *mtd);
  61. void (*put_device) (struct mtd_info *mtd);
  62. };

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

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

[cpp] view
plain
?
  1. int add_mtd_device(struct mtd_info *mtd)
  2. int del_mtd_device (struct mtd_info *mtd)

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

[cpp] view
plain
?
  1. int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
  2. int del_mtd_partitions(struct mtd_info *master)

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

[cpp] view
plain
?
  1. struct mtd_partition {
  2. char *name;             /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
  3. uint64_t size;          /* 分区大小 */
  4. uint64_t offset;        /* 分区偏移值 */
  5. uint32_t mask_flags;    /* 掩码标识,不重要 */
  6. struct nand_ecclayout *ecclayout;   /* OOB布局 */
  7. struct mtd_info **mtdp;     /* pointer to store the MTD object */
  8. };
  9. 其中nand_ecclayout结构体:
  10. struct nand_ecclayout {
  11. __u32 eccbytes;     /* ECC字节数 */
  12. __u32 eccpos[64];   /* ECC校验码在OOB区域存放位置 */
  13. __u32 oobavail;
  14. /* 除了ECC校验码之外可用的OOB字节数 */
  15. struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
  16. };

关于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模式,硬件控制等一系列底层机制。

[cpp] view
plain
?
  1. struct nand_chip {
  2. void  __iomem   *IO_ADDR_R;     /* 读8位I/O线地址 */
  3. void  __iomem   *IO_ADDR_W;     /* 写8位I/O线地址 */
  4. /* 从芯片中读一个字节 */
  5. uint8_t (*read_byte)(struct mtd_info *mtd);
  6. /* 从芯片中读一个字 */
  7. u16     (*read_word)(struct mtd_info *mtd);
  8. /* 将缓冲区内容写入芯片 */
  9. void    (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
  10. /* 读芯片读取内容至缓冲区/ */
  11. void    (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
  12. /* 验证芯片和写入缓冲区中的数据 */
  13. int     (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
  14. /* 选中芯片 */
  15. void    (*select_chip)(struct mtd_info *mtd, int chip);
  16. /* 检测是否有坏块 */
  17. int     (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
  18. /* 标记坏块 */
  19. int     (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
  20. /* 命令、地址、数据控制函数 */
  21. void    (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
  22. /* 设备是否就绪 */
  23. int     (*dev_ready)(struct mtd_info *mtd);
  24. /* 实现命令发送 */
  25. void    (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
  26. int     (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
  27. /* 擦除命令的处理 */
  28. void    (*erase_cmd)(struct mtd_info *mtd, int page);
  29. /* 扫描坏块 */
  30. int     (*scan_bbt)(struct mtd_info *mtd);
  31. int     (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
  32. /* 写一页 */
  33. int     (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
  34. const uint8_t *buf, int page, int cached, int raw);
  35. int     chip_delay;         /* 由板决定的延迟时间 */
  36. /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */
  37. unsigned int    options;
  38. /* 用位表示的NAND芯片的page大小,如某片NAND芯片
  39. * 的一个page有512个字节,那么page_shift就是9
  40. */
  41. int      page_shift;
  42. /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
  43. * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14
  44. */
  45. int      phys_erase_shift;
  46. /* 用位表示的bad block table的大小,通常一个bbt占用一个block,
  47. * 所以bbt_erase_shift通常与phys_erase_shift相等
  48. */
  49. int      bbt_erase_shift;
  50. /* 用位表示的NAND芯片的容量 */
  51. int      chip_shift;
  52. /* NADN FLASH芯片的数量 */
  53. int      numchips;
  54. /* NAND芯片的大小 */
  55. uint64_t chipsize;
  56. int      pagemask;
  57. int      pagebuf;
  58. int      subpagesize;
  59. uint8_t  cellinfo;
  60. int      badblockpos;
  61. nand_state_t    state;
  62. uint8_t     *oob_poi;
  63. struct nand_hw_control  *controller;
  64. struct nand_ecclayout   *ecclayout; /* ECC布局 */
  65. struct nand_ecc_ctrl ecc;   /* ECC校验结构体,里面有大量的函数进行ECC校验 */
  66. struct nand_buffers *buffers;
  67. struct nand_hw_control hwcontrol;
  68. struct mtd_oob_ops ops;
  69. uint8_t     *bbt;
  70. struct nand_bbt_descr   *bbt_td;
  71. struct nand_bbt_descr   *bbt_md;
  72. struct nand_bbt_descr   *badblock_pattern;
  73. void        *priv;
  74. };

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

Linux MTD系统剖析的更多相关文章

  1. Linux MTD系统剖析【转】

    转自:http://blog.csdn.net/lwj103862095/article/details/21545791 版权声明:本文为博主原创文章,未经博主允许不得转载. MTD,Memory ...

  2. linux MTD系统解析(转)

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

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

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

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

    MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...

  5. [转帖]Linux内核系统体系概述

    Linux内核系统体系概述 https://www.cnblogs.com/alantu2018/p/8447369.html Linux 内核主要由 5 个模块构成,它们分别是: 进程调度模块 用来 ...

  6. MTD系统架构和yaffs2使用、Nandflash驱动设计

    一.MTD系统架构 1.MTD设备体验 FLASH在嵌入式系统中是必不可少的,它是bootloader.linux内核和文件系统的最佳载体. 在Linux内核中引入了MTD子系统为NORFLASH和N ...

  7. 【原创】Linux RCU原理剖析(二)-渐入佳境

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  8. Linux查看系统状态命令

    Linux查看系统状态命令       iostat iostat 命令详细地显示了存储子系统方面的情况.你通常用iostat来监控存储子系统总体上运行状况如何,并且在用户注意到服务器运行缓慢之前提早 ...

  9. 82 fsck-检查与修复 Linux 档案系统

    Linux fsck命令用于 检查与修复 Linux 档案系统,可以同时检查一个或多个 Linux 档案系统. 语法 fsck [-sACVRP] [-t fstype] [--] [fsck-opt ...

随机推荐

  1. R语言和中国地图

    上图是R语言绘制的按地域分布的数据图.更科学,更严谨,也更有质感的样子. 今天瞎写点东西,我在想数据分析的意义是什么,也许就是研究事物存在的形式.而事物存在的形式是什么样子呢,从最初的三维空间,爱因斯 ...

  2. 复制文件夹的方法 .net

    void CopyDirectory(string SourcePath,string DestinationPath) { //创建所有目录 foreach(string dirPath inDir ...

  3. Django进阶Model篇003 - 数据库同步技巧

    一.认识一个目录 目录名:migrations 作用:用来存放通过makemigrations命令生成的数据库脚本,不熟悉的情况下,里面生成的脚本不要轻易修改.app目录下必须要有migrations ...

  4. 更改当前启动项,开关Hyper-V

    switch-HyperV.bat @echo off "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\ ...

  5. CSS3的transition和transform

    CSS3中的transition和transform是制作HTML5动画一定要使用到的两个属性. 注:这篇文章不考虑兼容性,只讨论webkit核心的浏览器.所以本文的所有例子请用chrome,safa ...

  6. https://www.adminsub.net/tcp-udp-port-finder/14000 ——查找tcp端口对应的服务 可以看某些端口是否让恶意软件开启

    效果: TCP/UDP Port FinderPort number or name:Enter port number (e.g. 21), service (e.g. ssh, ftp) or t ...

  7. 转:oracle驱动表

    以一个比较两本字典来做例子:       一本字典有索引目录(dict a), 一本没有(dict b)       现在要找出所有a开头的单词的异同       那么比较的时候,你会怎么比较?    ...

  8. vsftpd的530 Login incorrect错误解决方法 vsftpd登录错误

    530 Login incorrect只有用匿名anonymous才可登录,其余所有用户都报530 Login incorrect错 复制代码 代码如下: local_enable=YESwrite_ ...

  9. Kotlin 第一弹:自定义 ViewGroup 实现流式标签控件

    古人学问无遗力, 少壮工夫老始成.纸上得来终觉浅, 绝知此事要躬行. – 陆游 <冬夜读书示子聿> 上周 Google I/O 大会的召开,宣布了 Kotlin 语言正式成为了官方开发语言 ...

  10. unity 事件顺序及功能说明

    unity3d中所有控制脚本的基类MonoBehaviour有一些虚函数用于绘制中事件的回调,也可以直接理解为事件函数,例如大家都很清楚的Start,Update等函数,以下做个总结. Awake 当 ...