在应用程序中实现对NandFlash的操作
以TC58NVG2S3ETA00?為例:
下面是它的一些物理參數(shù):
圖一
?
?
?
?
?
?
圖二
?
?
?
?
?
?
?
?
?
?
?
圖三
?
?
?
?
?
?
?
?
圖四
?
?
?
?
?
?
?
?
?
?
?
?
?
圖五
?
?
?
圖6-0
圖6-1
?
說(shuō)明一下,在圖6-1中中間的那個(gè)布局表可以看做是實(shí)際的NandFlash一頁(yè)數(shù)據(jù)的布局,其中Data區(qū)域用于存放有效的數(shù)據(jù),也就是我們可以通過(guò)類(lèi)似read、write、pread、pwrite可以訪問(wèn)的區(qū)域,那每頁(yè)中的64字節(jié)的OOB區(qū)域是無(wú)法通過(guò)前面的幾個(gè)函數(shù)訪問(wèn)的,他們會(huì)自動(dòng)跳過(guò)OOB區(qū)域,訪問(wèn)OOB區(qū)域需要借助特殊的命令。
簡(jiǎn)單說(shuō)明一下:Data A(512B)對(duì)應(yīng)的ECC校驗(yàn)碼存放在ECC for Data A(4 byte)中,OOB A (8byte) 對(duì)應(yīng)的ECC校驗(yàn)碼存放在緊接著的下一個(gè)ECC for Data A(4 byte)中,雖然用4字節(jié)存放ECC,但是對(duì)于本例,ECC只占3個(gè)字節(jié)。在實(shí)際使用中如果解決方案中用不到OOB A/B/C/D,可以不用管他們對(duì)應(yīng)的ECC,只需要關(guān)心Data區(qū)域?qū)?yīng)的ECC。如果使能了硬件ECC,硬件會(huì)自動(dòng)把計(jì)算生成的ECC寫(xiě)到OOB中。可以參考http://www.cnblogs.com/pengdonglin137/p/3467960.html 。
讀NandFlash需要按頁(yè)讀,即一次讀一頁(yè);寫(xiě)NandFlash需要按頁(yè)寫(xiě),即每次寫(xiě)一頁(yè);擦除NandFlash需要按塊擦,即每次要擦除一塊。
對(duì)與NandFlash等塊設(shè)備的訪問(wèn)操作,mtd-utils工具集中提供了非常好的支持(可以到http://www.linux-mtd.infradead.org/進(jìn)行了解),要使用mtd-utils工具集首先需要搞到mtd-utils的源碼,并且使用目標(biāo)設(shè)備上的交叉工具編譯鏈進(jìn)行編譯,具體方法可以參考:http://www.cnblogs.com/pengdonglin137/p/3415550.html,其中介紹了如何生成可以再目標(biāo)板上運(yùn)行的mtd-utils工具。關(guān)于mtd-utils工具的使用可以參考:http://www.cnblogs.com/pengdonglin137/p/3415663.html 其中介紹了mtd-utils中常用的工具。
我們可以參考mtd-utils中工具的實(shí)現(xiàn),從而完成在自己的應(yīng)用程序中實(shí)現(xiàn)對(duì)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)
?
打開(kāi)設(shè)備
這里需要注意的是,打開(kāi)的設(shè)備結(jié)點(diǎn)是/dev/mtd?,而不是/dec/mtdblock?,原因可以參考:
http://www.cnblogs.com/pengdonglin137/p/3316523.html,其中介紹了mtd與mtdblock的區(qū)別。
fd = open ("/dev/mtd0", O_SYNC | O_RDWR);?
獲取設(shè)備信息
#include <linux/types.h> 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)/* The below two fields are obsolete and broken, do not use them * (TODO: remove at some point) */ __u32 ecctype;__u32 eccsize; };struct mtd_info_user mtd; ioctl(fd, MEMGETINFO,&mtd) ; 其中type可以用來(lái)區(qū)分是NorFlash還是NandFlash。擦除NandFlash
#include <mtd/mtd-abi.h> #include <linux/types.h>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 為某個(gè)/dev/mtdx的大小 // erasse.start應(yīng)該是按塊對(duì)齊遞增 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 0x%08x\n", erase.start);continue;//發(fā)現(xiàn)是壞塊,應(yīng)該跳過(guò) } 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) //執(zhí)行擦除操作 {fprintf(stderr, "\n%s: MTD Erase failure: %s\n", DevInfo->dir,strerror(errno));continue;} }寫(xiě)NandFlash
這里分為寫(xiě)數(shù)據(jù)區(qū)和寫(xiě)OOB區(qū)
寫(xiě)數(shù)據(jù)區(qū),對(duì)于本例一次要寫(xiě)一頁(yè),也就是2KB,寫(xiě)OOB區(qū),對(duì)于本例可以操作的只有32字節(jié),剩下的32字節(jié)用于存放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字節(jié)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字節(jié),用戶可以訪問(wèn)的OOB的大小,也就是OOB A/B/C/DOOB_INFO.ptr = tmp_oob;pagelen = meminfo->writesize; // 2KBimglen = sourcelen; // 鏡像文件的長(zhǎng)度 mtdoffset = meminfo->head->flash_offset; //要寫(xiě)的部分在/dev/mtdx中的偏移量,以字節(jié)為單位/* Determine if we are reading from standard input or from a file. */if (0 == sourceaddr) {DEBUG("Have no sourceaddr return ****************************\n");return 1;}// Check, if length fits into deviceif ( ((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 將來(lái)存放的是正在寫(xiě)的那塊的起始地址,并且是塊對(duì)齊的//mtdoffset 表示的是在某個(gè)mtd設(shè)備中的整體偏移量,可以按塊遞增,也可以按頁(yè)遞增//設(shè)置blockstart的目的是:// 假如檢測(cè)到一個(gè)好的塊,開(kāi)始進(jìn)行寫(xiě)操作,但是在寫(xiě)的過(guò)程中發(fā)生了寫(xiě)錯(cuò)誤,可以認(rèn)為這塊已經(jīng)//是壞塊了,需要重新再找一個(gè)好的塊,然后重新寫(xiě)入之前的數(shù)據(jù),因此需要知道剛才那個(gè)壞塊的起始地址// 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);}/* Check all the blocks in an erase block for bad blocks */// meminfo->fd 是某個(gè)/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; // 2KBif (0 != sourceaddr){if((meminfo->head->imageType == YAFFS) || (meminfo->head->imageType == OOB_RAW)){writeoob = true;}else{writeoob = false;}memset(writebuf, 0xff, sizeof(writebuf));if(imglen < readlen){num_to_read = imglen;}else{num_to_read = readlen;}// 從鏡像文件中偏移量為sourceaddr處讀取num_to_read個(gè)字節(jié)到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個(gè)字節(jié)到tmp_oob中,其中meminfo->head->oob_usr_offset是OOB A相對(duì)與OOB區(qū)域的偏移量,meminfo->head->oob_usr_length 在本例中為32字節(jié)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)) //寫(xiě)NandFlash {/*下面這段程序所完成的就是剛才所說(shuō)的在寫(xiě)之前檢測(cè)到是好塊,但是在寫(xiě)的過(guò)程出現(xiàn)了寫(xiě)錯(cuò)誤,這個(gè)時(shí)候需要完成?如下流程:1、計(jì)算已經(jīng)在當(dāng)前塊上寫(xiě)入多少內(nèi)容,比如下面的rewind_blocks是為了計(jì)算在當(dāng)前塊上已經(jīng)寫(xiě)了多少頁(yè),這里需要注意的是;rewind_bytes又加了一個(gè)readlen,也就是一頁(yè)的大小,目的是保證sourceaddr的可以回退到剛開(kāi)始寫(xiě)當(dāng)前塊是sourceaddr的值,可以看到在上面的程序中每次將要寫(xiě)的內(nèi)容讀到writebuf后,sourceaddr已經(jīng)進(jìn)行了自增操作,并沒(méi)有保證剛讀到writebuf中的內(nèi)容可以成功寫(xiě)入。但是mtdoffset進(jìn)行自增的前提是偏移量為mtdoffset的頁(yè)寫(xiě)成功。其實(shí)程序可以這么改進(jìn):將sourceaddr的自增操作跟mtdoffset的自增操作放在一起,此時(shí)rewind_bytes就不需要再加readlen了。對(duì)于oob,一般只有yaffs鏡像中有oob,而向cramfs、jffs2、ubifs這沒(méi)有,如果有oob也需要對(duì)rewind_byte進(jìn)行處理2、對(duì)當(dāng)前塊進(jìn)行擦除3、如果需要進(jìn)行壞塊標(biāo)記,則將當(dāng)前塊標(biāo)記為壞塊4、將mtdoffset指向當(dāng)前塊的下一塊起始地址5、恢復(fù)imglen為剛開(kāi)始處理當(dāng)前塊時(shí)的值,由于imglen也是保證當(dāng)前頁(yè)成功寫(xiě)入后才自減,所以只需要加上rewind_blocks即可*/int rewind_blocks;off_t rewind_bytes;erase_info_t erase;perror("ioctl(MEMEWRITEPAGE)");/* Must rewind to blockstart if we can */rewind_blocks = (mtdoffset - blockstart) / meminfo->writesize; /* Not including the one we just attempted */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 %08lx-%08lx\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 %08lx bad\n", (long)bad_addr);if (ioctl(meminfo->fd, MEMSETBADBLOCK, &bad_addr)) {perror("MEMSETBADBLOCK");/* But continue anyway */}}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; }?
對(duì)于寫(xiě)NandFlash,有的設(shè)備支持一次性把data和oob一塊寫(xiě)進(jìn)去。代碼如下:
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)/* The below two fields are obsolete and broken, do not use them* (TODO: remove at some point) */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
/*
* Buffer array used for writing data
*/
unsigned char writebuf[MAX_PAGE_SIZE];
char oobbuf[MAX_OOB_SIZE];
}closeall:close(fd);if ((imglen > 0)) {perror ("Data was only partially written due to error\n");exit (EXIT_FAILURE);}/* Return happy */return EXIT_SUCCESS; }
?
?
讀OOB
讀OOB跟寫(xiě)OOB類(lèi)似,只不過(guò)使用的命令是MEMREADOOB。
#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[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<N; i++){if(i%8 == 0){printf("\n");}printf("%#x ", oobbuf[i]);}printf("\n\n");close (fd);return 0;}?
以上只是本人在工作中遇到的,僅供參考。
轉(zhuǎn)載于:https://www.cnblogs.com/pengdonglin137/p/3468953.html
總結(jié)
以上是生活随笔為你收集整理的在应用程序中实现对NandFlash的操作的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【原创】mysql数据库异常:data
- 下一篇: (转载)9个主流的开源许可协议[整理]