mtd and /dev/mtd*相關資料

轉載自 http://my.oschina.net/shelllife/blog/123482

如有侵權,將刪除

今天做升级方案用到了mtd-utils中的flash_eraseall和flash_cp两个工具,在进行方案验证的时候,遭遇到各种不解和疑惑,因对MTD的原理不熟悉,所以只能多次尝试,虽然最后把方案搞定了,不过觉得MTD中的mtd和mtdblock区别这块还是值得总结学习一下。这里先说明一下问题现象,然后在进行具体的区别原理解释。

MTD设备(Nor Flash)使用中的问题现象表现

    1. mtd-utils工具对mtd和mtdblock分区设备的区别处理
1
2
3
4
5
6
7
/ $ flash_eraseall /dev/mtdblock/2
flash_eraseall: /dev/mtdblock/2 : unable to get MTD device info
/ $ flash_eraseall /dev/mtdblock/2
flash_eraseall: /dev/mtdblock/2 : unable to get MTD device info
/ $ flash_eraseall /dev/mtd/2
Erasing 128 Kibyte @ 8e0000 -- 98 % complete.
/ $ ls
1
2
3
4
5
6
/ $ flashcp rootfs_version /dev/mtdblock2
This doesn't seem to be a valid MTD flash device!
/ $ flashcp rootfs_version /dev/mtdblock/2
This doesn't seem to be a valid MTD flash device!
/ $ flashcp rootfs_version /dev/mtd2
/ $ ls
    1. mtd和mtdblock分区设备mount时的区别
1
2
3
4
5
6
/ $ mount  -t jffs2 /dev/mtd/2  qqzm/
mount : Mounting /dev/mtd/2  on qqzm/ failed: Invalid argument
/ $ mount  -t jffs2 /dev/mtd2  qqzm/
mount : Mounting /dev/mtd2  on qqzm/ failed: Invalid argument
/ $ mount  -t jffs2 /dev/mtdblock/2  qqzm/
/ $ ls
    1. mtdblock挂载成功,单擦除后卸载失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/ $ flash_eraseall /dev/mtd/2  <span>< /span > Erasing 128 Kibyte @ 8e0000 -- 98 % complete.
/qqzm  $ mount
/dev/root  on / type  jffs2 (rw,noatime)
proc on /proc  type  proc (rw,nodiratime)
sysfs on /sys  type  sysfs (rw)
devfs on /dev  type  devfs (rw)
devpts on /dev/pts  type  devpts (rw)
/dev/mmcblk0p1  on /mnt/sd  type  vfat (rw,nodiratime,fmask=0022,dmask=0022,codepage=cp437,iocharset=iso8859-1)
/dev/mtdblock/2  on /qqzm  type  jffs2 (rw,noatime)
none on /qqzm/www/cgi-bin/tmp  type  ramfs (rw)
/qqzm  $ cd  ..
/ $ umount  /qqzm
umount : Couldn't umount  /qqzm : Inappropriate ioctl for  device
/ $ umount  /dev/mtdblock/2
umount : Couldn't umount  /dev/mtdblock/2 : Inappropriate ioctl for  device
/ $

通过上面的不断尝试和错误反馈,我把方案基本验证通过了,只是对其中的原理不清楚:

  • 为什么mtd和mtdblock明明是同一个设备分区却有不同的操作?
  • mount命令只能挂载块设备吗?
  • 卸载mtdblock设备时,Inappropriate ioctl for device是什么意思?
  • unable to get MTD device info,又是什么意思?

MTD技术的基本原理

MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口,并进行了一个层次划分,层次从上到下大致为:设备文件、MTD设备层、MTD原始设备层、硬件驱动层。MTD的所有源代码在/drivers/mtd子目录下。

系统中的MTD设备文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
~ $ ls  /dev/mtd * -l
crw-rw----    1 root     root      90,   0 Jan  1 00:00 /dev/mtd0
crw-rw----    1 root     root      90,   1 Jan  1 00:00 /dev/mtd0ro
crw-rw----    1 root     root      90,   2 Jan  1 00:00 /dev/mtd1
crw-rw----    1 root     root      90,   3 Jan  1 00:00 /dev/mtd1ro
crw-rw----    1 root     root      90,   4 Jan  1 00:00 /dev/mtd2
crw-rw----    1 root     root      90,   5 Jan  1 00:00 /dev/mtd2ro
crw-rw----    1 root     root      90,   6 Jan  1 00:00 /dev/mtd3
crw-rw----    1 root     root      90,   7 Jan  1 00:00 /dev/mtd3ro
brw-rw----    1 root     root      31,   0 Jan  1 00:00 /dev/mtdblock0
brw-rw----    1 root     root      31,   1 Jan  1 00:00 /dev/mtdblock1
brw-rw----    1 root     root      31,   2 Jan  1 00:00 /dev/mtdblock2
brw-rw----    1 root     root      31,   3 Jan  1 00:00 /dev/mtdblock3
 
/dev/mtd :
crw-rw-rw-    1 root     root      90,   0 Jan  1 00:00 0
cr--r--r--    1 root     root      90,   1 Jan  1 00:00 0ro
crw-rw-rw-    1 root     root      90,   2 Jan  1 00:00 1
cr--r--r--    1 root     root      90,   3 Jan  1 00:00 1ro
crw-rw-rw-    1 root     root      90,   4 Jan  1 00:00 2
cr--r--r--    1 root     root      90,   5 Jan  1 00:00 2ro
crw-rw-rw-    1 root     root      90,   6 Jan  1 00:00 3
cr--r--r--    1 root     root      90,   7 Jan  1 00:00 3ro
 
/dev/mtdblock :
brw-------    1 root     root      31,   0 Jan  1 00:00 0
brw-------    1 root     root      31,   1 Jan  1 00:00 1
brw-------    1 root     root      31,   2 Jan  1 00:00 2
brw-------    1 root     root      31,   3 Jan  1 00:00 3
~ $

可以看到有mtdN和对应的/dev/mtd/N、mtdblockN和对应的/dev/mtdblock/N两类MTD设备,分别是字符设备,主设备号90和块设备,主设备号31。其中/dev/mtd0和/dev/mtd/0是完全等价的,/dev/mtdblock0和/dev/mtdblock/0是完全等价的,而/dev/mtd0和/dev/mtdblock0则是同一个MTD分区的两种不同应用描述,操作上是有区别的。

/dev/mtdN设备

/dev/mtdN 是MTD架构中实现的mtd分区所对应的字符设备(将mtd设备分成多个区,每个区就为一个字符设备),其里面添加了一些ioctl,支持很多命令,如MEMGETINFO,MEMERASE等。

mtd-utils中的flash_eraseall等工具,就是以这些ioctl为基础而实现的工具,实现一些关于Flash的操作。比如,mtd 工具中 flash_eraseall中:

1
2
3
4
5
if  (ioctl(fd, MEMGETINFO, &meminfo) != 0)
{
    fprintf (stderr, "%s: %s: unable to get MTD device info\n" ,exe_name, mtd_device);
    return  1;
}

MEMGETINFO是Linux MTD中的drivers/mtd/mtdchar.c中的ioctl命令,使用mtd字符设备需要加载mtdchar内核模块。该代码解释了上面的第一个现象。

/dev/mtdblockN设备

/dev/mtdblockN,是Flash驱动中用add_mtd_partitions()添加MTD设备分区,而生成的对应的块设备。MTD块设备驱动程序可以让flash器件伪装成块设备,实际上它通过把整块的erase block放到ram里面进行访问,然后再更新到flash,用户可以在这个块设备上创建通常的文件系统。

而对于MTD块设备,MTD设备层是不提供ioctl的实现方法的,也就不会有对应的MEMGETINFO命令之类,因此不能使用nandwrite,flash_eraseall,flash_erase等工具去对/dev/mtdblockN去进行操作,否则就会出现上面的现象一,同时也解释了现象3——用mtd2擦除分区后,在用mtdblock2进行umount就会造成混乱。

mtd块设备的大小可以通过proc文件系统进行查看:

1
2
3
4
5
6
7
8
9
10
~ $ cat  /proc/partitions
major minor  #blocks  name
 
   31     0        512 mtdblock0
   31     1       1024 mtdblock1
   31     2       5632 mtdblock2
   31     3       9216 mtdblock3
  254     0   30760960 mmcblk0   
  254     1   30756864 mmcblk0p1
~ $

后面的两个是SD块设备的分区大小。每个block的大小是1KB。

MTD设备分区和总结

通过proc文件系统查看mtd设备的分区情况:

1
2
3
4
5
6
7
~ $ cat  /proc/mtd
dev:    size   erasesize  name
mtd0: 00080000 00020000 "boot"
mtd1: 00100000 00020000 "kernel"
mtd2: 00580000 00020000 "roofs70"
mtd3: 00900000 00020000 "app"
~ $

可以发现,实际上mtdN和mtdblockN描述的是同一个MTD分区,对应同一个硬件分区,两者的大小是一样的,只不过是MTD设备层提供给上层的视图不一样,给上层提供了字符和块设备两种操作视图——为了上层使用的便利和需要,比如mount命令的需求,你只能挂载块设备(有文件系统),而不能对字符设备进行挂载,否则会出现上面的现象2:无效参数。

这里对于mtd和mtdblock设备的使用场景进行简单总结:

  1. mtd-utils工具只能应用与/dev/mtdN的MTD字符设备
  2. mount、umount命令只对/dev/mtdblockN的MTD块设备有效
  3. /dev/mtdN和/dev/mtdblockN是同一个MTD设备的同一个分区(N一样)

================================================================== 


轉載自(英文)

http://www.opensourceforu.com/2012/01/working-with-mtd-devices/


MTD (Memory Technology Devices) are NAND/NOR-based flash memory chips used for storing non-volatile data like boot images and configurations. Readers are cautioned not to get confused with USB sticks, SD cards, etc., which are also called flash devices, but are not MTD devices. The latter are generally found on development boards, used to store boot loaders, an OS, etc.

Even though MTD devices are for data storage, they differ from hard disks and RAM in several aspects. The biggest difference is that while hard disk sectors are rewritable, MTD device sectors must be erased before rewriting — which is why they are more commonly called erase-blocks. Second, hard disk sectors can be rewritten several times without wearing out the hardware, but MTD device sectors have a limited life and are not usable after about 10^3-10^5 erase operations. The worn out erase-blocks are called bad blocks and the software must take care not to use such blocks.

Like hard disks, MTD devices can be partitioned and can therefore act as independent devices. On a system with one or more MTD devices, device and partition information can be obtained from the /proc/mtd file. A typical /proc/mtd file is as follows:

cat /proc/mtd
dev:  size    erasesize name
mtd0: 000a0000 00020000 "misc"
mtd1: 00420000 00020000 "recovery"
mtd2: 002c0000 00020000 "boot"
mtd3: 0fa00000 00020000 "system"
mtd4: 02800000 00020000 "cache"
mtd5: 0af20000 00020000 "userdata"

A partitioned MTD device can be depicted as in Figure 1, which shows the relation between an MTD device, a partition and a sector.

An MTD device

Figure 1: An MTD device

As already said, MTD write operations are different from usual storage devices. Therefore, before we move further, let’s understand how write operations take place on MTD devices. Figure 2 shows a typical write case.

An MTD write operation

Figure 2: An MTD write operation

The left-most part shows a sector that has some data at the end. The rest of the sector has not been written since the last erase. A user wants to write “new data 1″ to this sector at offset 0. Since this part of the sector has already been erased, it is ready to be written and so “new data 1″ can be directly written to the sector. Later, the user may want to write “new data 2″, again at offset 0. To do this, the sector must be erased. Since the sector needs to be erased in entirety, the “old data” must be backed up in a temporary buffer. After erasing the complete sector, the “new data 2″ and “old data” must be written at appropriate offsets.

This procedure is the reason there are specific file systems for MTD devices, like JFFS2 and YAFFFS, and flash translation layers (FTL) like NFTL, INFTL, etc. These FTLs and file systems take special care of MTD device properties to hide complexity from the user.

In the first section that follows, we will look at how to access, read/write and erase MTD devices from Linux applications. The second section describes the same things in kernel space, so that this article can be useful to both application as well as kernel developers.

Accessing MTDs from applications

The user must know the device partition to work upon, which can be found from /proc/mtd as shown earlier. Assuming users want to work on the “userdata” partition, they must use the/dev/mtd5 device.

The first thing to do is to get information about the MTD device. Use the MEMGETINFO ioctlcommand, as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
 
int main()
{
     mtd_info_t mtd_info;
     int fd = open( "/dev/mtd5" , O_RDWR);
ioctl(fd, MEMGETINFO, &mtd_info);
 
     printf ( "MTD type: %u\n" , mtd_info.type);
     printf ( "MTD total size : %u bytes\n" , mtd_info.size);
     printf ( "MTD erase size : %u bytes\n" , mtd_info.erasesize);
 
     return 0;
}

Error handling has been omitted for brevity. The mtd_info_t structure is used with the MEMGETINFOcommand. The MTD type can be MTD_ABSENTMTD_RAMMTD_ROMMTD_NANDMTD_NOR, etc., which are defined in the mtd/mtd-abi.h header file. The mtd_info.size indicates the size of the whole device (i.e., the partition, in this case). Finally, mtd_info.erasesize indicates the sector size. During an erase operation, this is the minimum size that can be erased, as we’ll see later.

Reading MTD devices is similar to ordinary devices:

/* read something from last sector */
unsigned char buf[64];
lseek(fd, -mtd_info.erasesize, SEEK_END);
read(fd, buf, sizeof (buf));

A write operation can be performed in the same way, provided the sector has been erased previously. Finally, we come to the erase operation. Here is an example of erasing a partition, sector by sector:

void erase_partition(mtd_info_t *mtd_info, int fd) {
     erase_info_t ei;
     ei.length = mtd_info->erasesize;
  
     for (ei.start = 0; ei.start < mtd_info->size; ei.start += mtd_info->erasesize) {
         ioctl(fd, MEMUNLOCK, &ei);
         ioctl(fd, MEMERASE, &ei);
     }
}

All sectors of the device are writeable after this erase operation. Notice the use of MEMUNLOCK beforeMEMERASE, which is essential to allow the erase operation.

Accessing MTDs from kernel space

This section will repeat the functions explained in the previous section, but in kernel space. This needs a separate section since the erase operation is more complex here  –  the erase operation may sleep and therefore the kernel programmer has to wait until the operation is completed. This is the case for applications too, but the sleep is transparently taken care of by the scheduler.

As explained earlier, the first MTD information is the mtd_info structure. This is retrieved by iterating through all registered MTD devices:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/err.h>
 
static struct mtd_info *mtd_info = NULL;
  
int init_module( void ) {
     int num;
     for (num = 0; num < 64; num++) {
         mtd_info = get_mtd_device(NULL, num);
         if (IS_ERR(mtd_info)) {
             printk( "No device for num %d\n" , num);
             continue ;
         }
         if (mtd_info->type == MTD_ABSENT) {
             put_mtd_device(mtd_info);
             continue ;
         }
         if ( strcmp (mtd_info->name, "userdata" )) {
             put_mtd_device(mtd_info);
             continue ;
         }
         printk( "MTD type: %u\n" , mtd_info->type);
         printk( "MTD total size : %u bytes\n" , mtd_info->size);
         printk( "MTD erase size : %u bytes\n" , mtd_info->erasesize);
         return 0;
     }
     mtd_info = NULL;
     return 0;
}
  
void cleanup_module( void )
 
{
 
if (mtd_info)
         put_mtd_device(mtd_info);
}

The above kernel module searches for the “userdata” partition. The function get_mtd_device(), when invoked with the first argument NULL, returns the MTD device associated with the minor number specified in the second argument. On a successful search, it increments the reference count of the device. That’s why, before exiting, a call to put_mtd_device() must be made to release (decrement) the reference count.

Additionally, the module uses the flag MTD_ABSENT (which is available to applications too). This check is required to function correctly with some probing device drivers used to allocate placeholder MTD devices on systems that have socketed or removable media.

Having retrieved the mtd_info structure, reading is relatively simple:

/* read something from last sector */
u_char buf[64];
 
mtd_info->read(mtd_info, mtd_info.size-mtd_info.erasesize, sizeof (buf), buf);

The second argument of the read function specifies the read offset, and the third the length to read. Note that the read operation too may sleep and, therefore, it must not be performed in an interrupt context. The write operation can be performed as follows (assuming the sector has been previously erased):

/* write something to last sector */
mtd_info->write(mtd_info, mtd_info.size-mtd_info.erasesize, sizeof (buf), buf);

As mentioned before, the read, write and erase operations may sleep. Therefore, kernel code must wait for the operation to finish. Here is an example of erasing the partition and waiting to finish the operation:

#include <linux/sched.h>
 
void erase_partition( struct mtd_info *mtd_info) {
     unsigned int start;
     for (start = 0; start < mtd_info->size; start += mtd_info->erasesize)
         erase_sector(mtd_info, start, mtd_info->erasesize);
}
  
void erase_sector( struct mtd_info *mtd_info, unsigned int start, unsigned int len)
 
{
     int ret;
     struct erase_info ei = {0};
     wait_queue_head_t waitq;
     DECLARE_WAITQUEUE(wait, current);
     
     init_waitqueue_head(&waitq);
     ei.addr = start;
     ei.len = mtd_info->erasesize;
     ei.mtd = mtd_info;
     ei.callback = erase_callback;
     ei.priv = (unsigned long )&waitq;
     ret = mtd_info->erase(mtd_info, &ei);
     if (!ret)     {
         set_current_state(TASK_UNINTERRUPTIBLE);
         add_wait_queue(&waitq, &wait);
         if (ei.state != MTD_ERASE_DONE && ei.state != MTD_ERASE_FAILED)
             schedule();
         remove_wait_queue(&waitq, &wait);
         set_current_state(TASK_RUNNING);
  
         ret = (ei.state == MTD_ERASE_FAILED)?-EIO:0;
     }
}
  
void erase_callback ( struct erase_info *instr) {
     wake_up((wait_queue_head_t *)instr->priv);
}

The erase_partition() function iterates over all sectors, and erases them with erase_sector(). At the core of erase_sector() is the mtd_info->erase call, which (as mentioned previously) may sleep. Therefore, erase_sector() prepares a wait queue and a wait queue head.

After a call to mtd_info->erase, the function prepares itself to relinquish the CPU (presuming thatmtd_info->erase will sleep) by changing task state to TASK_UNINTERRUPTIBLE and adding itself to the wait queue head. Before relinquishing the CPU, it checks if erase is done, through the ei.stateflag. If erase is done successfully, this flag will be set to MTD_ERASE_DONE.

If the erase operation is not complete, the task relinquishes the CPU by calling schedule(). Later, when the erase operation is complete, the driver calls the callback function provided inei.callback. Here the task wakes up to itself, then removes itself from the wait queue, changes the task state to TASK_RUNNING and finally, the erase_sector() function returns.

MTD devices have many more features that can be used by application programmers. ECC (error correction codes) and OOB (out of band) data are some of them. The MTD framework is integrated into the Linux kernel — therefore it makes working with MTD devices very simple, as we have seen in this article.


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值