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

Working with MTD Devices

By Mohan Lal Jangir on January 31, 2012 in CodingDevelopers · 6 Comments

This article shows how kernel and application developers (in C) can make use of MTD in Linux.

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.

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.

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 theMEMGETINFO command. 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 MEMUNLOCKbefore MEMERASE, 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 theei.state flag. 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.

Working with MTD Devices的更多相关文章

  1. mtd零星记录

    查看Flash分区情况: root@DD-WRT:~# cat /proc/mtd dev: size erasesize name mtd0: "RedBoot" mtd1: 0 ...

  2. MTD NANDFLASH驱动相关知识介绍

    转:http://blog.csdn.net/zhouzhuan2008/article/details/11053877 目录 MTD总概述 MTD数据结构 MTD相关层实现 MTD,Memory ...

  3. Is an MTD device a block device or a char device?

    转:http://www.linux-mtd.infradead.org/faq/general.html#L_mtd_what Note, you can find Ukranian transla ...

  4. mtd工具

    http://daemons.net/linux/storage/mtd.html MTD The Memory Technology Devices (MTD) subsystem provides ...

  5. linux kernel系列四:嵌入式系统中的文件系统以及MTD

    本节介绍File System和MTD技术 一 FS 熟知的FS有ext2,3,4.但是这些都是针对磁盘设备的.而ES中一般的存储设备为Flash,由于Flash的特殊性: Flash存储按照Bloc ...

  6. 【驱动】MTD子系统分析

    MTD介绍 MTD,Memory Technology Device即内存技术设备 字符设备和块设备的区别在于前者只能被顺序读写,后者可以随机访问:同时,两者读写数据的基本单元不同. 字符设备,以字节 ...

  7. Creating and Flashing UBIFS with MTD Utils

    转:http://wiki.atlas-embedded.com/index.php?title=Creating_and_Flashing_UBIFS_with_MTD_Utils Contents ...

  8. linux内核中mtd架构分析

    一. 引言 MTD(memory technology device内存技术设备)是用于访问memory设备(RAM.ROM.flash)的Linux的子系统.MTD的主要目的是为了使新的memory ...

  9. mtd介绍

    转:http://blog.csdn.net/lwj103862095/article/details/21545791 MTD,Memory Technology Device即内存技术设备 字符设 ...

随机推荐

  1. 深入浅出谈存储:如何区别NAS、SAN与DAS

    深入浅出谈存储:如何区别NAS.SAN与DAS 2012年02月17日16:51 来源:新浪博客 作者:林沛满 编辑:曾智强 查看全文 赞(0)评论(1) 分享 标签: DAS , SAN , 存储系 ...

  2. Git 的核心概念解读

    本文不是Git使用教学篇,而是偏向理论方面,旨在更加深刻的理解Git,这样才能更好的使用它,让工具成为我们得力的助手. 版本控制系统 Git 是目前世界上最优秀的分布式版本控制系统.版本控制系统是能够 ...

  3. 第二百零六天 how can I 坚持

    今天爬了趟香山,第三次去了,要征服北京这大大小小的山. 要征服三山五岳,然后...罗娜.哈哈. 爬了趟山好累,人好多. 我的铜钱草. 洗刷睡觉,还是明天给鱼换水吧,好懒.

  4. 如果Apache Spark集群中没有分布式系统,则会?

    若当连接到Spark的master之后,若集群中没有分布式文件系统,Spark会在集群中每一台机器上加载数据,所以要确保Spark集群中每个节点上都有完整数据. 通常可以选择把数据放到HDFS.S3或 ...

  5. 2d网络游戏的延迟补偿(Lag compensation with networked 2D games)

    http://gamedev.stackexchange.com/questions/6645/lag-compensation-with-networked-2d-games ——————————— ...

  6. hdu 5310 Souvenir(BestCoder 1st Anniversary ($))

    http://acm.hdu.edu.cn/showproblem.php?pid=5310 题目大意:要买n个纪念品,可以单个买p元每个,可以成套买q元一套,每套有m个,求最少花费 #include ...

  7. HDU 5437 Alisha’s Party (优先队列模拟)

    题意:邀请k个朋友,每个朋友带有礼物价值不一,m次开门,每次开门让一定人数p(如果门外人数少于p,全都进去)进来,当最后所有人都到了还会再开一次门,让还没进来的人进来,每次都是礼物价值高的人先进.最后 ...

  8. jsp两种包含方式

    jsp中存在两种文件的包含指令 1.<%@include file="xxx.jsp" %> 2.<jsp:include page="xxx.jsp& ...

  9. Activity之间使用intent传递大量数据带来问题总结

    转载:大飞 http://blog.csdn.net/rflyee/article/details/47441405   Activity之间使用Parcel传递大量数据产生的问题. Activity ...

  10. android 自定义adapter和线程结合 + ListView中按钮滑动后状态丢失解决办法

    adapter+线程 1.很多时候自定义adapter的数据都是来源于服务器的,所以在获取服务器的时候就需要异步获取,这里就需要开线程了(线程池)去获取服务器的数据了.但这样有的时候adapter的中 ...