MTD设备系统介绍 【外文翻译】

今天搜MTD的介绍,发现中文资料真的很残缺,不好理解。决定翻译下外文,为了减少工作量,只进行意译。由于,经过不断地修修补补,有些内容会重复。

概念概述:

MTD原始设备层:外文翻译是MTD Raw Device或者MTD raw Flash。是一个类似于Block device的接口层, 它可以直接给JFFS2和YAFFS文件系统操作,但它缺少坏块管理和负载平衡模块,所以一般不建议直接调用MTD Raw Device。 如果你要通过ext4访问raw flash,那得调用mtdblcok接口,这就是MTD设备层接口,详情看下面的介绍。

MTD设备层:因为块设备接口很常见,且MTD原始设备层有局限,所以需要把MTD原始设备再封装一层,就是这一层,这一层就提供了Block Device接口,可供ext4等常见系统使用。

块设备: block device。包括硬盘、SSD固态硬盘、SD卡、光驱、eMMC、USB内存条。

字符设备:键盘、鼠标之类。

块设备接口: block device interface。常见的接口类型,很多文件系统都调用这个接口,如ext4.

内存控制器:Memory Controller

负载均衡:Wear Leveling。FLash擦除次数有限,所以要进行擦写均衡。

坏块:bad block。由于技术限制,Flash出厂时候就不可避免地有坏块,且写操作多了,容易产生坏块。需要软件或者硬件进行管理,对物理内存进行重映射。

FTLs: Flash Translation Layers。指linux的子系统MTD中的mtdblock。

CFI接口

原理参照这文章
http://blog.csdn.net/xzongyuan/article/details/30036203
源码参照这篇
http://blog.csdn.net/xzongyuan/article/details/29822879

Flash驱动

http://blog.csdn.net/xzongyuan/article/details/28234763

MTD 原始设备驱动

在原始设备层中。貌似map.h的函数都是管理底层驱动的。 提供register、probe、destroy、map_word_equal、map_word_and、map_word_or等。

mtd_chip_driver

struct mtd_chip_driver {

       struct mtd_info *(*probe)(struct map_info *map);     该芯片的probe程序

       void (*destroy)(struct mtd_info *);

       struct module *module;         模块

       char *name;           芯片名

       struct list_head list;

};

MTD原始设备(芯片)驱动器的结构

chip_probe

struct chip_probe {

       char *name;

       int (*probe_chip)(struct map_info *map, __u32 base,

                       struct flchip *chips, struct cfi_private *cfi);   

};

通用探测程序的参数结构,此结构类型将作为参数传进strcut mtd_info *mtd_do_chip_probe()中(Gen_probe.h),mtd_do_chip_probe()将间接调用其中的chip_probe函数.
flashchip.h

flchip

struct flchip {

       unsigned long start; /* Offset within the map */

       //     unsigned long len;          假设所有的Flash chip都是相同的,所以没有定义len

       /* We omit len for now, because when we group them together

          we insist that they're all of the same size, and the chip size

          is held in the next level up. If we get more versatile later,

          it'll make it a damn sight harder to find which chip we want from

          a given offset, and we'll want to add the per-chip length field

          back in.

       */

       flstate_t state;

       flstate_t oldstate;

       spinlock_t *mutex;

       spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */

       wait_queue_head_t wq; /* Wait on here when we're waiting for the chip

                          to be ready */

       int word_write_time;

       int buffer_write_time;

       int erase_time;

};

MTD原始设备

MTD原始设备是什么?看看下面的type就知道。那就是RAM、ROM、ABSENT、PEROM、NORFLASH等

mtd_info.type的取值

#define MTD_ABSENT        0

#define MTD_RAM                     1

#define MTD_ROM                     2

#define MTD_NORFLASH           3

#define MTD_NANDFLASH        4

#define MTD_PEROM          5

#define MTD_OTHER          14

#define MTD_UNKNOWN           15

MTD原始设备的结构体为mtd_info,主要有如下函数(其它难懂的,删掉了):
struct mtd_info {  
       int (*erase) (struct mtd_info *mtd, struct erase_info *instr); 
       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);
 
       int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf);
       int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf);
 
       int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
       int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
       int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
       int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
       int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
       int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
 
       /* Power Management functions */
       int (*suspend) (struct mtd_info *mtd);
       void (*resume) (struct mtd_info *mtd);
}
可见原始设备只有erase、read、write、suspend、lock等基本功能,ecc是校验码,v代表vector,oob是out of band,一般用做坏块管理。我在linux2.26源码看到还有这两个函数
/* Bad block management functions */
	int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

MTD原始设备的源码在mtd.h、mtdcore.h。
mtdcore.c

本文件主要实现了MTD原始设备层的数据结构mtd_table和对其的操作函数,主要包括add_mtd_device()(加入MTD原始设备)、del_mtd_device()(删除MTD原始设备)

 

MTD原始设备分区

MTD系统可以访问没有分区的MTD设备(分区是通过MTD Mapping driver实现),如果MTD设备事先被分区,MTD系统访问时,就会根据不同分区显示为单独的MTD设备,就像一个物理硬盘可以分区为逻辑c盘和d盘那样。

mtdpart.c

       MTD原始设备层分区的实现,mtd_part结构是用于描述分区的,由mtd_partitons将mtd_part中的list成员链成一个链表。每个mtd_part结构中的mtd_info结构用于描述本分区,被加入mtd_table中,其中大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。而主分区(其大小涵盖所有分区)则不作为一个MTD原始设备加入mtd_table。

       本文件向外提供的add_mtd_partitions()del_mtd_partitions()根据参数map_info结构将所有的分区加入mtd_table

       mtdpart.c中还实现了part_read、part_write等函数,这些函数注册在每个分区中,指向主分区的read、write函数,之所以这样做而不直接将主分区的read、write函数连接到每个分区中的原因是因为函数中的参数mtd_info会被调用者置为函数所属的mtd_info,即mtd->read(mtd…),而参数mtd_info其实应该指向主分区。

mtd_partitions

/* Our partition linked list */

static LIST_HEAD(mtd_partitions);                    MTD原始设备分区的链表

分区的操作函数如:

part_read

part_write

part_readv

part_writev

part_erase

part_lock

part_unlock

part_sync

part_suspend

part_resume


MTD设备与MTD设备层

传统的设备层只有块设备和字符设备,后来根据某些flash的特性,增加了MTD设备。字节设备可以读取数据,但其本身不能被seek和拥有大小(字符设备没有大小概念,不能存数据)。块设备有固定块大小——512字节,并可以被seek。Flash与这两种传统设备的特征有很大差异,MTD就是缩小这种差异,例如,封装出了mtdblock,模仿block,但是因为flash本身的特性,导致写性能和block是无法比拟的,读性能应该不差。所以,被封装过后的flash,就命名为MTD设备。


MTD设备是一种有坏块,且需要坏块管理、负载均衡的设备。MTD设备系统是为linux设计的一种内存管理技术,也可以看做是MTD子系统,它提供了一个介于硬件设备到上层调用的抽象层,虽然字符设备和块设备已经很常见(没有flash技术之前就有了),很多Flash就是封装成块设备的,但是块设备接口不是很适合flash内存的操作,因此,linux系统另外设计了针对flash memory 的MTD层和MTD原始设备层。这两层的每个程序对象,分别称为MTD设备和MTD原始设备。所以,经常会把MTD设备和MTD设备层混为一谈,在看文章时候要理解这个区别。 

我的理解:通常说MTD设备,就是指NorFlash和NandFlash。但在MTD出现前,已经有了很多封装成BlockDevice接口的Flash,并在很多主流系统中使用,所以要区分这种被封装的flash和raw flash。虽然MTD设备本身可以被文件系统访问,但为了适应主流文件系统的接口,还会对MTD设备进行封装,封装成mtdblock。


具体对比如下:


Block device

MTD device

固定大小扇区

固定大小的擦除块

Sectors are small (512, 1024 bytes)

Eraseblocks are larger (typically 128KiB)

Maintains 2 main operations:read sectorand write sector

Maintains 3 main operations:read from eraseblock,write to eraseblock, anderase eraseblock

Bad sectors are re-mapped and hidden by hardware (at least in modern LBA hard drives); in case of FTL devices it is the responsibility of FTL to provide this

Bad eraseblocks are not hidden and should be dealt with in software

Sectors are devoid of the wear-out property (in FTL devices it is the responsibility of FTL to provide this)

Eraseblocks wear-out and become bad and unusable after about 103(for MLC NAND) - 105(NOR, SLC NAND) erase cycles

 



MTD Device与Block Device的区别 

1.对raw flash的接口(封装)区别

虽然MTD Device是为了Flash而存在,但不是所有的Flash都用MTD设备,有很多Flash设备是使用Block Device Interface(块设备接口)封装的,如USB,SD卡,eMMc,MMC(内存卡)、CompactFlashes。其中,eMMc是嵌入式系统中常用的,在raw flash的基础上,添加了内存控制器(Memory Controller),直接封装成块设备。而MTD本身类似于Block Device,可以提供文件系统API,但同时,又提供了一个模拟block device的转换层FTLs。

虽然USB内存、MMC等很多市面上的设备都叫做Flash,但是他们和MTD设备不是一个概念。如果要在传统的文件系统上如ext4,FAT使用MTD设备,得添加Flash Translation Layers(FTLs)。 

2.文件系统上的区别

 当使用MTD子系统,可以使用JFFS2和YAFFS作为文件系统,这两个文件系统可以直接访问MTD系统,而其他的文件系统得先把MTD转化为Block Device接口才能访问。

ext3、ext4、FAT、JFS、XFS和一些传统的文件系统,都是使用Block Device Interface的。

所以,区分了MTD原始设备和MTD设备,JFFS2可以访问MTD原始设备,而ext4只能访问经过转化的MTD设备mtdblock。据说mtd原始设备没有提供负载均衡和坏块管理标准,所以一般都要经过MTDblock,调用底层的Flash驱动去实现。(这部分我搜到的信息有矛盾,可能是错误的,仅供参考)

MTD设备层(MTDblock and MTDchar)


该设备层是模仿块设备的,mtdblock.h代码中有mtdblock对象,但据说mtd系统中提供的FLTs的模拟效果不好,只是粗糙的模仿。如Nor Flash擦除时都是擦除大面积内存的(128k),而块设备一般擦除大小为512B。所以,一个NorFlash MTD模仿的块设备的写速度是原生块设备的上几十万倍。因此,MTDBlock比较适用于read only的文件系统。不过,你用在读写系统也可以,如果是NandFlash设备,写操作没有那么慢,可以使用Yaffs和Jffs2,这两个系统提供read only只读和read-write读写模式。

我的理解:烧录image时候,会用到yaffs系统,但是linux系统运行起来后,好像用的是ext4系统,所以,即使mtdblock有缺陷,写速度慢,但还是用了啊?估计是MTD设备成本低,所以不直接用block device接口,而是模拟成mtdblock。那些封装成了BlockDevice的,估计是因为通过硬件实现,需要很高的成本,据说内存控制器都比flash本身贵。所以,看来mtdblock应该就是软件实现了flash的坏块管理和均衡功能,节省了硬件实现的成本。

可以通过命令查看mtd设备:

cat /proc/mtd

android平板的分区信息如下:

--------------------------------------------------------

root@android:/proc # cat mtd
cat mtd
dev:       size        erasesize  name
mtd0: 00400000 00004000 "misc"
mtd1: 01000000 00004000 "kernel"
mtd2: 01000000 00004000 "boot"
mtd3: 01000000 00004000 "recovery"
mtd4: 18000000 00004000 "backup"
mtd5: 08000000 00004000 "cache"
mtd6: 20000000 00004000 "userdata"
mtd7: 00400000 00004000 "kpanic"
mtd8: 20000000 00004000 "system"
mtd9: 8a400000 00004000 "user"

MTD设备源码分析

mtdblock.c

       MTD设备层的块设备的相关数据结构和程序,其中定义了MTD块设备的notifier和MTD块设备的结构mtdblk_dev类型的mtdblks数组,该数组中的每个成员与MTD原始设备层的mtd_table数组中的每个成员一一对应。

notifier

       设备层的mtdblcok设备的notifier

static struct mtd_notifier notifier = {   

        mtd_notify_add,

        mtd_notify_remove,

        NULL

}; 

mtdblk_dev

设备层的块设备的结构(and its private , I think),MTD字符设备没有相对应的结构。

struct mtdblk_dev {                    

       struct mtd_info *mtd; /* Locked */      下层原始设备层的MTD设备结构

       int count;

       struct semaphore cache_sem;

       unsigned char *cache_data;                 缓冲区(与MTD设备块对齐)

       unsigned long cache_offset;                缓冲区内数据在原始设备层MTD设备内的偏移

       unsigned int cache_size;                      缓冲区大小(通常被设置为MTD设备的erasesize)

       enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;        缓冲区状态

}            

mtdblks

设备层(块)设备数组(与原始设备层的MAX_MTD_DEVICES个MTD设备一一对应)

static struct mtdblk_dev *mtdblks[MAX_MTD_DEVICES];      

erase_callback

格式:

       static void erase_callback(struct erase_info *done)

其它相关的函数


MTD字符设备的意义

MTD设备分为块设备和字符设备,块设备一般是存储设备,最小读写单位一般是512byte,擦除的单位还会更大,适合传输数据量大的设备。而键盘、鼠标等控制设备,传输数据量小,没必要一次写入512byte这么多数据,或者删除128k的数据,所以需要另外一种对应的设备系统,所以设计了字符设备。字符设备可以线性地寻址,操作单位可以是byte(我猜的,反正肯定是比块更小的单位了)。

但是字符设备没有规定擦除的标准接口,因此字符设备是不能erase内存的。那如果要通过设备写入一个很大的文件,会发生什么事?如NandFlash,默认内存单位是1,写入数据后,mos管变为0,如果要让mos从0变为1,必须擦除,把128k的数据都置1后,才能对某个内存区域进行写操作。而字符设备本身不具有这样的功能,所以,必须使用别的API对flash进行擦除。这个API就是ioctl——这个东西,我之前学习编程经常看到,就是不知道为啥要设计这个API

,原来就是为了应对特殊的情况啊。

在代码中,如果要进行擦除等操作,必须知道擦除块的基本单位等内存信息,需要用到下面的函数。

MEMGETINFO:Get layout and capabilities                      struct mtd_info_user *
MEMERASE    :Erase flash blocks struct                         erase_info_user *
MEMLOCK     : Lock flash blocks to disallow changes     struct erase_info_user *
MEMUNLOCK: Unlock flash to allow changes                struct erase_info_user *
MEMGETREGIONCOUNT  :Return number of erase      block regions int *
MEMGETREGIONINFO    :                                         struct region_info_user *

MEMWRITEOOB NAND only:   write out-of-band info (ECC)   struct mtd_oob_buf *
MEMREADOOB NAND only:     read out-of-band info (ECC)    struct mtd_oob_buf *
MEMSETOOBSEL NAND only:   set default OOB info              struct nand_oobinfo*

command操作的例子:

flash_eraseall /dev/mtdchar0
cat /dev/mtdchar0 < new.bin


Mapping Driver映射驱动

In the kernel source, the drivers/mtd/maps directory contains the mapping drivers. You may be able to use the generic physmap.c driver. Specify the base address, chip size and bus width in the kernel configuration, and the physmap.c driver will probe the flash type. Generic memory accesses are used to read the flash. The physmap.c driver can even handle several flash chips in the contiguous memory area.
内核源码中 drivers/mtd/maps 目录有mapping driver,你可以使用通用的physmap.c,该文件可以设置base address,chip size,bus width,还可以探测flash type。通用的内存接入信息可以用来read flash。
有些flash,由于地址线有线,或者你有多块flash,其映射地址不连续,这种情况则不能直接使用该文件,需要参照它进行修改。

疑问

翻译后,发现还是有些地方没搞明白:MTD设备为啥要转成Block Device?我猜测是BlockDevice接口很通用,很多主流文件系统,包括linux的ext4都用这个接口,所以像android平板,使用nandflash,是MTD设备,就要转化为BlockDevice接口,而且NorFlash也是MTD设备,出厂时不会封装成blockdevice。市面上已经有了很多性能强劲的SD卡,u盘,都是直接提供了Block Device Interface的,为啥不直接使用?难道是因为MTD设备便宜? 

参考文章

http://blog.csdn.net/allen6268198/article/details/7280821

http://en.wikipedia.org/wiki/Memory_Technology_Device


  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值