在应用程序中实现对NandFlash的操作

在应用程序中实现对NandFlash的操作

TC58NVG2S3ETA00 为例:

下面是它的一些物理参数:

图一

image

 

 

 

 

 

 

图二

image

 

 

 

 

 

 

 

 

 

 

 

图三

image

 

 

 

 

 

 

 

 

图四

image

 

 

 

 

 

 

 

 

 

 

 

 

 

图五

image

 

 

 

图6-0

image

图6-1

image

 

说明一下,在图6-1中中间的那个布局表可以看做是实际的NandFlash一页数据的布局,其中Data区域用于存放有效的数据,也就是我们可以通过类似read、write、pread、pwrite可以访问的区域,那每页中的64字节的OOB区域是无法通过前面的几个函数访问的,他们会自动跳过OOB区域,访问OOB区域需要借助特殊的命令。

简单说明一下:Data A(512B)对应的ECC校验码存放在ECC for Data A(4 byte)中,OOB A (8byte) 对应的ECC校验码存放在紧接着的下一个ECC for Data A(4 byte)中,虽然用4字节存放ECC,但是对于本例,ECC只占3个字节。在实际使用中如果解决方案中用不到OOB A/B/C/D,可以不用管他们对应的ECC,只需要关心Data区域对应的ECC。如果使能了硬件ECC,硬件会自动把计算生成的ECC写到OOB中。可以参考http://www.cnblogs.com/pengdonglin137/p/3467960.html 

读NandFlash需要按页读,即一次读一页;写NandFlash需要按页写,即每次写一页;擦除NandFlash需要按块擦,即每次要擦除一块。

对与NandFlash等块设备的访问操作,mtd-utils工具集中提供了非常好的支持(可以到http://www.linux-mtd.infradead.org/进行了解),要使用mtd-utils工具集首先需要搞到mtd-utils的源码,并且使用目标设备上的交叉工具编译链进行编译,具体方法可以参考:http://www.cnblogs.com/pengdonglin137/p/3415550.html,其中介绍了如何生成可以再目标板上运行的mtd-utils工具。关于mtd-utils工具的使用可以参考:http://www.cnblogs.com/pengdonglin137/p/3415663.html 其中介绍了mtd-utils中常用的工具。

我们可以参考mtd-utils中工具的实现,从而完成在自己的应用程序中实现对NandFlash的操作。常用的命令如下:

#define MEMGETINFO        _IOR('M', 1, struct mtd_info_user) 
#define MEMERASE        _IOW('M', 2, struct erase_info_user) 
#define MEMWRITEOOB        _IOWR('M', 3, struct mtd_oob_buf) 
#define MEMREADOOB        _IOWR('M', 4, struct mtd_oob_buf) 
#define MEMLOCK            _IOW('M', 5, struct erase_info_user) 
#define MEMUNLOCK        _IOW('M', 6, struct erase_info_user) 
#define MEMGETREGIONCOUNT    _IOR('M', 7, int) 
#define MEMGETREGIONINFO    _IOWR('M', 8, struct region_info_user) 
#define MEMSETOOBSEL        _IOW('M', 9, struct nand_oobinfo) 
#define MEMGETOOBSEL        _IOR('M', 10, struct nand_oobinfo) 
#define MEMGETBADBLOCK        _IOW('M', 11, __kernel_loff_t) 
#define MEMSETBADBLOCK        _IOW('M', 12, __kernel_loff_t) 
#define OTPSELECT        _IOR('M', 13, int) 
#define OTPGETREGIONCOUNT    _IOW('M', 14, int) 
#define OTPGETREGIONINFO    _IOW('M', 15, struct otp_info) 
#define OTPLOCK            _IOR('M', 16, struct otp_info) 
#define ECCGETLAYOUT        _IOR('M', 17, struct nand_ecclayout_user) 
#define ECCGETSTATS        _IOR('M', 18, struct mtd_ecc_stats) 
#define MTDFILEMODE        _IO('M', 19) 
#define MEMERASE64        _IOW('M', 20, struct erase_info_user64) 
#define MEMWRITEOOB64        _IOWR('M', 21, struct mtd_oob_buf64) 
#define MEMREADOOB64        _IOWR('M', 22, struct mtd_oob_buf64) 
#define MEMISLOCKED        _IOR('M', 23, struct erase_info_user)

 

打开设备

这里需要注意的是,打开的设备结点是/dev/mtd?,而不是/dec/mtdblock?,原因可以参考:

http://www.cnblogs.com/pengdonglin137/p/3316523.html,其中介绍了mtd与mtdblock的区别。

fd = open ("/dev/mtd0", O_SYNC | O_RDWR);

 

获取设备信息

#include
复制代码
structmtd_info_user { __u8 type; __u32 flags; __u32 size; // Total size of the MTD
    __u32 erasesize;
    __u32 writesize;
    __u32 oobsize;// Amount of OOB data per block (e.g. 16)
    

    __u32 ecctype;
    __u32 eccsize;
};


struct mtd_info_user mtd;
复制代码
ioctl(fd, MEMGETINFO,&mtd) ;
 
             其中type可以用来区分是NorFlash还是NandFlash。 
            

擦除NandFlash

#include
#include
struct erase_info_user {
    __u32 start;
    __u32 length;
};
typedef struct erase_info_user erase_info_t;
erase_info_t erase;
int isNAND, bbtest = 1;
erase.length = DevInfo->erasesize;
// erase.length 表示的是擦除大小,也就是一块的大小,如128KB
// DevInfo->size 为某个/dev/mtdx的大小
// erasse.start应该是按块对齐递增
isNAND = (DevInfo->typenum== MTD_NANDFLASH) ? 1 : 0;
for (erase.start = 0; erase.start <  DevInfo->size; erase.start += DevInfo->erasesize) 
{
        if (bbtest) 
        {
            loff_t offset = erase.start;
            int ret = ioctl(DevInfo->fd, MEMGETBADBLOCK, &offset); //判断是不是坏块
            if (ret > 0) 
            {
                if (!quiet)
                    DEBUG ("\nSkipping bad block at 0xx\n", erase.start);
                continue;//发现是坏块,应该跳过
            } 
            else if (ret < 0) 
            {
                if (errno == EOPNOTSUPP) 
                {
                    bbtest = 0;
                    if (isNAND) 
                    {
                        fprintf(stderr, "%s: Bad block check not available\n", DevInfo->dir);
                        return 1;
                    }
                } 
                else 
                {
                    fprintf(stderr, "\n%s: MTD get bad block failed: %s\n", DevInfo->dir, strerror(errno));
                    return 1;
                }
            }
        }
        if (!quiet)
        {
            fprintf(stderr, "\rErasing %d Kibyte @ %x -- %2llu %% complete.", \
                (DevInfo->erasesize) / 1024, erase.start,
                (unsigned long long) erase.start * 100 / (DevInfo->size));
        }
        if (ioctl(DevInfo->fd, MEMERASE, &erase) != 0) //执行擦除操作
        {
            fprintf(stderr, "\n%s: MTD Erase failure: %s\n", DevInfo->dir,strerror(errno));
            continue;
        }
}

写NandFlash

这里分为写数据区和写OOB区

写数据区,对于本例一次要写一页,也就是2KB,写OOB区,对于本例可以操作的只有32字节,剩下的32字节用于存放ECC。

复制代码
struct mtd_oob_buf {
    __u32 start;
    __u32 length;
    unsigned char *ptr;
};


int nandwrite(DeviceInfo* meminfo)
{
    int imglen = 0, pagelen;
    bool baderaseblock = false;
    int blockstart = -1;
    loff_t offs;
    int ret, readlen;
    unsigned char tmp_oob[32];//OOB A/B/C/D,一共32字节
    struct mtd_oob_buf OOB_INFO ;
    sourceaddr = meminfo->head->file_offset;   //要读的部分在镜像文件中的偏移量
    sourcelen = meminfo->head->size;           //要读的部分的大小
    int num_to_read = 0;

    OOB_INFO.start = 0;
    OOB_INFO.length = meminfo->head->oob_usr_length;   //32字节,用户可以访问的OOB的大小,也就是OOB A/B/C/D
    OOB_INFO.ptr = tmp_oob;

    pagelen = meminfo->writesize; // 2KB
    imglen = sourcelen;           // 镜像文件的长度

    mtdoffset = meminfo->head->flash_offset;  //要写的部分在/dev/mtdx中的偏移量,以字节为单位
    
    
    if (0 == sourceaddr) {
        DEBUG("Have no sourceaddr return ****************************\n");
        return 1;
    }

    // Check, if length fits into device
    if ( ((imglen / pagelen) * meminfo->writesize) > (meminfo->size - mtdoffset)) {
        fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
                imglen, pagelen, meminfo->writesize, meminfo->size);
        perror ("Input file does not fit into device");
        goto closeall;
    }

    while ((imglen>0) && (mtdoffset < meminfo->size))
    {
        //blockstart 将来存放的是正在写的那块的起始地址,并且是块对齐的
        //mtdoffset 表示的是在某个mtd设备中的整体偏移量,可以按块递增,也可以按页递增
        //设置blockstart的目的是:
        // 假如检测到一个好的块,开始进行写操作,但是在写的过程中发生了写错误,可以认为这块已经
        //是坏块了,需要重新再找一个好的块,然后重新写入之前的数据,因此需要知道刚才那个坏块的起始地址
        // mtdoffset & (~meminfo->erasesize + 1) 这种获取块起始地址的算法值得借鉴
        while (blockstart != (mtdoffset & (~meminfo->erasesize + 1)))
        {
            blockstart = mtdoffset & (~meminfo->erasesize + 1);
            offs = blockstart;
            baderaseblock = false;
            if (!quiet)
            {
                fprintf (stderr, "\rWriting data to block %d at offset 0x%x", \
                        blockstart / meminfo->erasesize, blockstart);
            }

            
            // meminfo->fd 是某个/dev/mtdx的文件描述符
            do {
                    if ((ret = ioctl(meminfo->fd, MEMGETBADBLOCK, &offs)) < 0)
                    {
                        perror("ioctl(MEMGETBADBLOCK)");
                        goto closeall;
                    }
                    if (ret == 1)
                    {
                        baderaseblock = true;
                        if (!quiet)
                        {
                            fprintf (stderr, "Bad block at %x block(s) "
                                    "from %x will be skipped\n",
                                    (int) offs, blockstart);
                        }
                    }
                    if (baderaseblock)
                    {
                        mtdoffset = blockstart + meminfo->erasesize;
                    }
                    offs +=  meminfo->erasesize;
                } while ( offs < blockstart + meminfo->erasesize );

        }

        readlen = meminfo->writesize;  // 2KB

        if (0 != sourceaddr)
        {
            if((meminfo->head->imageType == YAFFS) || (meminfo->head->imageType == OOB_RAW))
            {
                writeoob = true;
            }
            else
            {
                writeoob = false;
            }

             memset(writebuf, 0xff, sizeof(writebuf));
            
            if(imglen <<span style="font-family: 'Courier New' !important; font-size: 12px !important;"> readlen)
            {
                num_to_read = imglen;
            }
            else
            {
                num_to_read = readlen;
            }

            // 从镜像文件中偏移量为sourceaddr处读取num_to_read个字节到writebuf中
            // ALLIMAGEFD 为镜像文件的文件描述符
            if(pread(ALLIMAGEFD, writebuf, num_to_read, sourceaddr) < 0) 
            {
                perror("fail to pread\n");
                return -1;
            }

            sourceaddr += num_to_read;

            if(writeoob)
            {
                memset(tmp_oob, 0xff , OOB_FREE_MAX);
                // 从镜像文件中偏移量为sourceaddr+meminfo->head->oob_usr_offset处读取meminfo->head->oob_usr_length个字节到tmp_oob中,其中meminfo->head->oob_usr_offset是OOB A相对与OOB区域的偏移量,meminfo->head->oob_usr_length 在本例中为32字节
                if(pread(ALLIMAGEFD, tmp_oob, meminfo->head->oob_usr_length, sourceaddr+meminfo->head->oob_usr_offset) < 0)
                {
                    perror("fail to pread\n");
                    return -1;
                }
                sourceaddr += meminfo->oobsize;

            }

        }

    if(-1 == pwrite(meminfo->fd, writebuf, meminfo->writesize, mtdoffset)) //写NandFlash
    {
        
        int rewind_blocks;
        off_t rewind_bytes;
        erase_info_t erase;

        perror("ioctl(MEMEWRITEPAGE)");

        
        rewind_blocks = (mtdoffset - blockstart) / meminfo->writesize; 
        rewind_bytes = (rewind_blocks * meminfo->writesize) + readlen;
        if (writeoob)
        {
            rewind_bytes += (rewind_blocks + 1) * meminfo->oobsize;
        }
        sourceaddr -= rewind_bytes;
        erase.start = blockstart;
        erase.length = meminfo->erasesize;
        fprintf(stderr, "Erasing failed write from lx-lx\n",
                (long)erase.start, (long)erase.start+erase.length-1);
        if (ioctl(meminfo->fd, MEMERASE, &erase) != 0)
        {
            perror("MEMERASE");
            goto closeall;
        }

        if (markbad)
        {
            loff_t bad_addr = mtdoffset & (~meminfo->erasesize + 1);
            fprintf(stderr, "Marking block at lx bad\n", (long)bad_addr);
            if (ioctl(meminfo->fd, MEMSETBADBLOCK, &bad_addr)) {
                perror("MEMSETBADBLOCK");
                
            }
        }
        mtdoffset = blockstart + meminfo->erasesize;
        imglen += rewind_blocks * meminfo->writesize;
        if(writeoob)
        {
            imglen += rewind_blocks * meminfo->oobsize;
        }

        continue;
    }

        imglen -= readlen;
        if(writeoob)
        {
            imglen -= meminfo->oobsize;
            OOB_INFO.start = mtdoffset;
            if (ioctl(meminfo->fd, MEMWRITEOOB, &OOB_INFO))
            {
                 perror("fail to ioctl");
            }
        }
        mtdoffset += meminfo->writesize;
    }

closeall:
    if ((imglen > 0))
    {
        perror ("Data was only partially written due to error\n");
        exit (EXIT_FAILURE);
    }

    return EXIT_SUCCESS;
}
复制代码

 

对于写NandFlash,有的设备支持一次性把data和oob一块写进去。代码如下:

struct mtd_info_user {
    uint8_t type;
    uint32_t flags;
    uint32_t size;     // Total size of the MTD
    uint32_t erasesize;
    uint32_t writesize;
    uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
   
    uint32_t ecctype;
    uint32_t eccsize;
};

struct mtd_epage_buf 
{
    unsigned long long  start;
    unsigned long       data_len;
    unsigned long       oob_len;
    unsigned char     * data_ptr;
    unsigned char     * oob_ptr; 
};


#define MEMEWRITEPAGE _IOWR('M', 23, struct mtd_epage_buf)

#define MAX_PAGE_SIZE 8192
#define MAX_OOB_SIZE 512

unsigned char writebuf[MAX_PAGE_SIZE];
char oobbuf[MAX_OOB_SIZE];
int nandwrite(int argc, char * const argv[])
{
   
    int fd = -1;
   
    int imglen = 0, pagelen;
    bool baderaseblock = false;
    int blockstart = -1;
    struct mtd_info_user meminfo;
    struct mtd_epage_buf eccbuf;
    loff_t offs;
    int ret, readlen;

   

    mtdoffset = 0;
    erase_buffer(oobbuf, sizeof(oobbuf));

    if (pad && writeoob) {
        fprintf(stderr, "Can't pad when oob data is present.\n");
        exit (EXIT_FAILURE);
    }

   
    if ((fd = open(mtd_device, O_RDWR)) == -1) {
        perror(mtd_device);
        exit (EXIT_FAILURE);
    }

   
    if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
        perror("MEMGETINFO");
        close(fd);
        exit (EXIT_FAILURE);
    }

   
    if (NULL == sourceaddr) {
        DEBUG("Have no sourceaddr return ****************************\n");
        return 0;
    }

    pagelen = meminfo.writesize + ((writeoob) ? meminfo.oobsize : 0);

   

    imglen = sourcelen;

    // Check, if file is page-aligned
    if ((!pad) && ((imglen % pagelen) != 0)) {
        fprintf (stderr, "Input file is not page-aligned. Use the padding "
                "option.\n");
        goto closeall;
    }

    // Check, if length fits into device
    if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) {
        fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
                imglen, pagelen, meminfo.writesize, meminfo.size);
        perror ("Input file does not fit into device");
        goto closeall;
    }

    const int allSizeConst = imglen;
   
   
    while (imglen && (mtdoffset < meminfo.size)) 
    {
        // new eraseblock , check for bad block(s)
        // Stay in the loop to be sure if the mtdoffset changes because
        // of a bad block, that the next block that will be written to
        // is also checked. Thus avoiding errors if the block(s) after the
        // skipped block(s) is also bad (number of blocks depending on
        // the blockalign
        while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) 
        {
            blockstart = mtdoffset & (~meminfo.erasesize + 1);
            offs = blockstart;
            baderaseblock = false;
            if (!quiet)
                fprintf (stdout, "Writing data to block %d at offset 0x%x\n",
                        blockstart / meminfo.erasesize, blockstart);

           
            do {
                if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
                    perror("ioctl(MEMGETBADBLOCK)");
                    goto closeall;
                }
                if (ret == 1) {
                    baderaseblock = true;
                    if (!quiet)
                        fprintf (stderr, "Bad block at %x block(s) "
                                "from %x will be skipped\n",
                                (int) offs, blockstart);
                }

                if (baderaseblock) {
                    mtdoffset = blockstart + meminfo.erasesize;
                }
                offs +=  meminfo.erasesize;
            } while ( offs < blockstart + meminfo.erasesize );

        }

        readlen = meminfo.writesize;

        if (NULL != sourceaddr) {
            if (pad && (imglen < readlen))
            {
                readlen = imglen;
                erase_buffer(writebuf + readlen, meminfo.writesize - readlen);
            }

            memcpy(writebuf, sourceaddr, readlen);
            sourceaddr += readlen;
        } 

        if (writeoob) {
            memcpy(oobbuf, sourceaddr, meminfo.oobsize);
            sourceaddr += meminfo.oobsize;
        }

        eccbuf.data_ptr = writebuf;
        eccbuf.data_len = meminfo.writesize;
        eccbuf.oob_ptr  = oobbuf;
        eccbuf.oob_len  = meminfo.oobsize;
        eccbuf.start    = mtdoffset;
        if (ioctl(fd, MEMEWRITEPAGE, &eccbuf) != 0)
        {
            int rewind_blocks;
            off_t rewind_bytes;
            erase_info_t erase;

            perror("ioctl(MEMEWRITEPAGE)");

           
            rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize;
            rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen;
            if (writeoob)
                rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize;
            sourceaddr -= rewind_bytes;
            erase.start = blockstart;
            erase.length = meminfo.erasesize;
            fprintf(stderr, "Erasing failed write from lx-lx\n",
                    (long)erase.start, (long)erase.start+erase.length-1);
            if (ioctl(fd, MEMERASE, &erase) != 0) {
                perror("MEMERASE");
                goto closeall;
            }

            if (markbad) {
                loff_t bad_addr = mtdoffset & (~meminfo.erasesize + 1);
                fprintf(stderr, "Marking block at lx bad\n", (long)bad_addr);
                if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) {
                    perror("MEMSETBADBLOCK");
                   
                }
            }
            mtdoffset = blockstart + meminfo.erasesize;
            imglen += rewind_blocks * meminfo.writesize;

            continue;
        }

        imglen -= (readlen + meminfo.oobsize);
        mtdoffset += meminfo.writesize;

    }

closeall:

    close(fd);

    if ((imglen > 0)) {
        perror ("Data was only partially written due to error\n");
        exit (EXIT_FAILURE);
    }

   
    return EXIT_SUCCESS;
}

 

 

读OOB

读OOB跟写OOB类似,只不过使用的命令是MEMREADOOB。

复制代码
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include <<span style="color: rgb(0, 0, 255); font-family: 'Courier New' !important; font-size: 12px !important;">string.h>
#include 

#define N 32
#define OFS   (0)
#define block_size (128*1024)
#define page_size  (2*1024)

int main(int argc, const char *argv[])
{

     int fd;
     int i, j;

     unsigned char oob_data[32] =
     {
        0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 
        0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 
        0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 
        0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff
     };

      unsigned char oobbuf[N];
     
     struct mtd_oob_buf oob = {0, N, oobbuf};
     struct mtd_oob_buf my_oob = {0, N, oob_data};
     
     fd = open("/dev/mtd0", O_RDWR);
     if(fd < 0)
     {
         perror("fail to open\n");
         exit(-1);
     }

    if(ioctl(fd, MEMWRITEOOB, &my_oob))
     {
         perror("fail to ioctl");
         exit(-1);
     }

        memset(oobbuf, 0, sizeof(oobbuf));
        oob.start = OFS;
        if (ioctl(fd, MEMREADOOB, &oob))
        {
            perror("fail to ioctl");
            exit(-1);
        }
   
        for(i=0; i)
        {
            if(i%8 == 0)
            {
                printf("\n");
            }
            printf("%#x ", oobbuf[i]);
        }
   
        printf("\n\n");

     close (fd);

     return 0;

}


              
              
include sys/ioctl.h
include stdio.h
include mtd/mtd-user.h
include sys/types.h
include sys/stat.h
include fcntl.h
include unistd.h
include string.h
include stdlib.h
#define N 32
#define OFS    (0)
#define block_size (128*1024)
#define page_size   (2*1024)
int main(int argc, const char *argv[])
{
        int fd;
        int i, j;
        unsigned char oob_data[1024*2] =
        {
            0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 
            0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 
            0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 
            0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff
        };
         unsigned char oobbuf[N];
       
        struct mtd_oob_buf oob = {0, N, oobbuf};
        struct mtd_oob_buf my_oob = {0, N, oob_data};
       
        fd = open("/dev/mtd3", O_RDWR);
        if(fd < 0)
        {
              perror("fail to open\n");
              exit(-1);
        }
       
        pwrite(fd, oob_data, 1024*2, 1024*4);//写时必须页对齐,一次写一页(2k)的整数位
        memset(oob_data,0,32);
        pread(fd, oob_data, 32, 1024*4);
       
        for(i=0;i<32;i++)
        {
            if(i%8==0)
                 printf("\n");
            printf("%2x ",oob_data[i]);
        }
        printf("\n");
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值