【驱动】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数据结构
重要的数据结构:
- mtd_info 表示mtd原始设备, 所有mtd_info结构体被存放在mtd_info数组mtd_table中
- 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
闪存转换层要做下面的操作来完成写请求:
将这个扇区所在擦除块的数据读到内存中,放在缓存中。
将缓存中与这个扇区对应的内容用新的内容替换。
对该擦除块执行擦除操作。
将缓冲中的数据写回该擦除块。
【驱动】MTD子系统分析的更多相关文章
- Linux MTD子系统 _从模型分析到Flash驱动模板
MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...
- Linux驱动架构之pinctrl子系统分析(一)
1.前言在嵌入式系统中,许多SoC的内部都包含了pin控制器,通过芯片内部的pin控制器,我们可以配置一个或者一组引脚的状态和功能特性,Linux内核为了统一各SoC厂商的引脚管理,提供了pinctr ...
- linux的mtd架构分析【转】
转自:http://blog.csdn.net/column/details/xgbing-linux-mtd.html linux mtd 嵌入式系统的存储有很多不可靠之处.随着使用容量的增大,现在 ...
- SPI子系统分析之一:框架
内核版本:3.9.5 SPI子系统概述: 一个SPI主控制器对应一条SPI总线,当然在系统中有唯一的总线编号. SPI总线上有两类设备: 其一是主控端,通常作为SOC系统的一个子模块出现,很多嵌入式M ...
- Linux MTD 子系统
一.MTD子系统概述 MTD(Memory Technology Device, 内存技术设备)是用于访问memory 设备 (ROM.FLASH)的Linux子系统. 主要目的是为了使新的memor ...
- linux kernel input 子系统分析
Linux 内核为了处理各种不同类型的的输入设备 , 比如说鼠标 , 键盘 , 操纵杆 , 触摸屏 , 设计并实现了一个对上层应用统一的试图的抽象层 , 即是Linux 输入子系统 . 输入子系统的层 ...
- input子系统分析
------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ -------- ...
- Linux input子系统分析
输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分 ...
- input子系统分析(转)
转自:http://www.linuxidc.com/Linux/2011-09/43187.htm 作者:作者:YAOZHENGUO2006 Input子系统处理输入事务,任何输入设备的驱动程序都可 ...
随机推荐
- AWVS扫描工具使用教程
上文AppScan扫描工具-工作原理&操作教程有写到Web安全漏洞扫描工具之一的APPScan,除此,还有另外一款国内使用比较主流的Web安全漏洞扫描工具——AWVS.相较于大容量的AppSc ...
- 内存问题排查工具 --- valgrind
1. 概述 2. Valgrind 3. 内存泄漏监测 3.1. 示例代码 3.2. 编译它 3.3. 用Valgrind监测进程的内存泄漏 4. 悬挂指针 4.1. 示例代码 4.2. Valgri ...
- iOS10 打开APP设置界面和WIFI界面
在iOS10以上,权限这块有了一些变化 首先在info的URL Types 添加 prefs 1.打开APP设置界面 //打开设置 let url:NSURL = NSURL(string: UIA ...
- Nginx防盗链的3种方法 文件防盗链 图片防盗链 视频防盗链 linux防盗链
Nginx 是一个很牛的高性能Web和反向代理服务器, 它具有有很多非常优越的特性: 在高连接并发的情况下,Nginx是Apache服务器不错的替代品,目前Web服务器调查显示Apache下降Ngni ...
- POJ 1862 Stripies (哈夫曼树)
Stripies Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 10263 Accepted: 4971 Descrip ...
- 【java】详解JFrame结构的分层
在这篇博文中,笔者会介绍JFrame窗口的分层.JFrame继承自Frame,同JFrame.JDialog.JApplet都是重量级组件.如果不弄清楚Frame的分层结构,那么在设置组件的某些特效的 ...
- 【Android】Android解析短信操作
目录结构: contents structure [-] 获取短信 发送短信 1.获取短信 在AndroidManifest.xml中,添加权限: <uses-permission androi ...
- thinkphp导航高亮的方法
因为引入了公共的 header.html,所以需要使用 js来实现向 li 加入active的高亮属性,这里我通过url地址和href的地址进行判断 // 这里对当前页面导航高亮 $(function ...
- label 赋值 , 隐藏 , 显示
<label name='by_stages_number' id='by_stages_number'></label> document.getElementById(&q ...
- Oracle 12C -- 删除audit policy
删除之前,必须将policy disable掉:然后再删除 SQL> noaudit policy audit_sysprvi_po01; SQL> drop audit policy a ...