为什么需要MTD子系统
嵌入式系统使用Flash作为存储设备,Flash类别有Nand、Nor等。Flash的上层是文件系统。直觉上,系统中使用这些Flash时,我们需要为每种Flash编写驱动。同时在调用Flash的文件系统做接口对接。这样,每使用一种新的Flash类型甚至型号,都得修改文件系统的编码来做适配。显然,这会造成代码的爆炸,同时也不方便大家各司其职(例如:厂商A做Flash,添加一个新的Flash需要厂商A的驱动开发人员去改写所有文件系统的接口,这显然不现实)。
几乎所有的现代操作系统都不会允许以上事情的发生。通用的做法是,抽象出上下层对接的方式,厂商驱动开发人员只需要按照接口进行匹配节课。
MTD(Memory Technology Devices)便是Linux系统下处理以上问题的方式。
架构与代码目录
MTD在系统中的结构如下图。
- 构成MTD的部分有MTD核心、MTD字符设备和MTD块设备层。成为文件系统和底层硬件驱动的沟通桥梁
- MTD核心建立在Flash驱动(位于drivers/mtd/)之上,为Flash驱动提供一系列的API抽象
- MTD为上层提供统一的操作抽象接口如
dev/mtd0
,/dev/mtd1
(例如擦除、读写等),同时提供/proc/mtd
供上层读取MTD系统相关信息 - MTD提供一系列的API,为基于Flash的文件系统提供控制操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20+-----------------+
| 使用文件系统的应用 |
+-----------------+
+------------+ +------------+
|Char Dev节点 | |Block Dev节点| 用户空间层
+------------+ +------------+
+---------------------------------+
+---------------+-----------+
| MTD字 符 设 备| MTD块 设 备|
+---------------+-----------+
+---------------------------+
| MTD Core | 内核层
+---------------------------+
+---------------------------+
| Flash驱 动 |
+---------------------------+
+---------------------------------+
+---------------------------+
| 各 种 Flash | 硬件层
+---------------------------+
MTD代码在Linux内核源码树的位置:
- _include/linux/mtd/_:定义所有MTD相关头文件
- drivers/mtd: 定义MTD核心,以及Flash驱动
数据结构
MTD的核心结构定义在内核源码树的_include/linux/mtd/mtd.h_。先看核心数据结构mtd_info:
1 | struct mtd_info { |
另外一个比较重要的数据结构为mtd_partition,顾名思义,对一块flash进行分区:
1 | struct mtd_partition { |
通常每种类型的Flash芯片定义了这样一个数据结构,用于对Flash进行操作。可参考drivers/mtd/devices下相关使用(例如lpddr2_nvm.c)
mtdcore.h/mtdcore.c下定义了一系列使用MTD设备的API:
1 | struct mtd_info *__mtd_next_device(int i); |
MTD字符设备
MTD字符设备定义在mtdchar.c,其中定义了字符设备的相关操作:
1 | static const struct file_operations mtd_fops = { |
MTD块设备
MTD块设备定义在mtdblock.c/mtdblock_ro.c,定义了块设备相关操作:
1 | static struct mtd_blktrans_ops mtdblock_tr = { |
分区
在嵌入式设备,一个flash往往会被划分做不同的功能分区。例如,升级分区、文件系统分区、bootloader分区、配置分区、备份分区等等。代码树中的drivers/mtd/mtdpart.c实现了分区的相关操作:
以下全局变量定义了MTD分区的链表:
1 | static LIST_HEAD(mtd_partitions); |
链表元素为:
1 | struct mtd_part { |
相关操作为:
1 | static struct mtd_part *allocate_partition(struct mtd_info *master, const struct mtd_partition *part, int partno, uint64_t cur_offset) |