您的位置:首页 > 其它

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

2018-02-23 17:14 337 查看


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

TC58NVG2S3ETA00 为例:下面是它的一些物理参数:图一      图二           图三        图四             图五   图6-0图6-1 说明一下,在图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); 


获取设备信息

#includestructmtd_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
#includestruct
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; // 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 <<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 512unsigned
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
;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;}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: