/** @file mtd-nand.c @date 2010/05/27 @author 오재경 freefrug@falinux.com FALinux.Co.,Ltd. @brief mtd를 통해 nand 플래시를 제어한다 2011/03/25 오재경 ezboot 1.x 를 지원을 추가 2011/04/13 오재경 배드블럭에 대한 에러를 수정 2011/06/13 오재경 배드블럭에 대한 에러를 수정 @todo MTD_NANDECC_OFF 옵션도 있으니 구현하자 @bug @remark @warning */ // // 저작권 에프에이리눅스(주) // 외부공개 금지 // //---------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char desc_mtd_nand[] = "falinux mtd-nand ver 0.3.4"; //------------------------------------------------------------------------------ /** @brief mtd-nand 생성함수 @param fname mtd 노드파일이름 @return mtdnand_t 구조체 포인터 *///---------------------------------------------------------------------------- mtdnand_t *mtdnand_create( char *fname ) { mtdnand_t *mtd; mtd = malloc( sizeof(mtdnand_t) ); memset( (void *)mtd, 0, sizeof(mtdnand_t) ); sprintf( mtd->node_name, "%s", fname ); mtd->fd = open( mtd->node_name, O_RDWR ); // O_RDONLY if ( 0 > mtd->fd ) { perror( "mtd open error" ); free( mtd ); return NULL; } // 난드플래시의 정보를 얻는다. if ( 0 == ioctl( mtd->fd, MEMGETINFO, &mtd->info) ) { printf( " mtd=%s \n", mtd->node_name ); printf( " * total size %dMB\n", mtd->info.size/(1024*1024) ); printf( " * erase size %d\n" , mtd->info.erasesize ); printf( " * write size %d\n" , mtd->info.writesize ); printf( " * oob size %d\n" , mtd->info.oobsize ); printf( " * ecc type %d\n" , mtd->info.ecctype ); printf( "\n" ); } else { perror( "unable to get mtd-info" ); free( mtd ); return NULL; } return mtd; } //------------------------------------------------------------------------------ /** @brief mtd-nand 해제함수 @param mtd mtdnand_t 구조체 포인터 *///---------------------------------------------------------------------------- void mtdnand_free( mtdnand_t *mtd ) { if ( mtd ) { close( mtd->fd ); free( mtd ); } } //------------------------------------------------------------------------------ /** @brief mtd-nand 의 엑세스 위치를 이동한다. @param mtd mtdnand_t 구조체 포인터 @param offset 이동크기 @param origin 기준위치 @return 파일포인터의 위치, 에러일경우 음수 *///---------------------------------------------------------------------------- int mtdnand_seek( mtdnand_t *mtd, int offset, int origin ) { int pos; pos = lseek( mtd->fd, offset, origin ); //printf( "seek=0x%08x (%d)\n", rtn, rtn ); if ( 0 > pos ) { perror( "mtd seek error" ); return -1; } else { mtd->pos = pos; //switch( origin ) //{ //case SEEK_SET : mtd->pos = offset; break; //case SEEK_CUR : mtd->pos += offset; break; //case SEEK_END : mtd->pos = mtd->info.size + offset; break; //} } return pos; } //------------------------------------------------------------------------------ /** @brief mtd-nand 의 oob 영역을 읽는다. @param mtd mtdnand_t 구조체 포인터 @param buf 데이타를 담을 버퍼 @param count 버퍼의 크기 @return 성공0 *///---------------------------------------------------------------------------- int mtdnand_read_oob( mtdnand_t *mtd, unsigned char *buf, int count ) { struct mtd_oob_buf oob_buf; int rtn; oob_buf.start = mtd->pos; oob_buf.length = (count > mtd->info.oobsize) ? mtd->info.oobsize : count; oob_buf.ptr = buf; rtn = ioctl( mtd->fd, MEMREADOOB, &oob_buf ); if ( 0 > rtn ) { perror( "mtd read-oob error" ); } return rtn; } //------------------------------------------------------------------------------ /** @brief mtd-nand 의 oob 영역을 읽는다. @param mtd mtdnand_t 구조체 포인터 @return -1 : badblock 0:정상블럭 *///---------------------------------------------------------------------------- int mtdnand_is_bad( mtdnand_t *mtd ) { unsigned char buf[256], cmp; cmp = 0x00; if ( 0 == mtdnand_read_oob( mtd, buf, 6 ) ) { if ( 512 == mtd->info.writesize ) { cmp = buf[5]; } else { cmp = buf[0]; } } if ( cmp != 0xff ) { printf( "%s bad block 0x%08x\n", mtd->node_name, mtd->pos ); return -1; } return 0; } //------------------------------------------------------------------------------ /** @brief mtd-nand 의 한블럭을 지운다. @param mtd mtdnand_t 구조체 포인터 @return 성공0 *///---------------------------------------------------------------------------- int mtdnand_erase( mtdnand_t *mtd ) { int rtn; struct erase_info_user erase_info; erase_info.start = (mtd->pos/mtd->info.erasesize)*mtd->info.erasesize; erase_info.length = mtd->info.erasesize; rtn = ioctl( mtd->fd, MEMERASE, &erase_info); if ( 0 != rtn ) { perror( "mtd erase error" ); } return rtn; } //------------------------------------------------------------------------------ /** @brief mtd-nand 에서 데이타를 읽는다. @param mtd mtdnand_t 구조체 포인터 @param buf 데이타를 담을 버퍼 @param count 버퍼의 크기 @return 읽은 데이타의 크기 @remark 배드블럭을 만나면 *///---------------------------------------------------------------------------- int mtdnand_read( mtdnand_t *mtd, unsigned char *buf, int count ) { int rtn; rtn = read( mtd->fd, buf, count ); if ( 0 > rtn ) { perror( "mtd read error" ); } else { mtd->pos += rtn; } return rtn; } //------------------------------------------------------------------------------ /** @brief mtd-nand 에서 데이타를 읽는다. @param mtd mtdnand_t 구조체 포인터 @param buf 데이타를 담을 버퍼 @param count 버퍼의 크기 @return 읽은 데이타의 크기 @remark 배드블럭을 만나면 다음블럭으로 자동으로 점프한다. *///---------------------------------------------------------------------------- int mtdnand_read_skip_bad( mtdnand_t *mtd, unsigned char *buf, int count ) { int rtn, esize, bad_cnt; int rdcnt, remain; esize = mtd->info.erasesize; bad_cnt = 0; remain = count; // 페이지 단위로 정렬한다. mtd->pos = ALIGN_SIZE( mtd->pos, mtd->info.writesize ); mtdnand_seek( mtd, mtd->pos, SEEK_SET ); while( 0 < remain ) { // 블럭의 시작인가? if ( 0 == (mtd->pos % esize) ) { while( mtdnand_is_bad( mtd ) ) { printf( "\n bad block offset-page=%d\n", mtd->pos/mtd->info.writesize ); mtd->pos += esize; mtdnand_seek( mtd, mtd->pos, SEEK_SET ); bad_cnt ++; if ( MAX_BADBLOCK_COUNT <= bad_cnt ) { printf( "mtd read error : many bad block over %d\n", MAX_BADBLOCK_COUNT ); return RDERR_MANY_BADBLOCK ; } } } // 읽을 갯수를 결정한다. rdcnt = (remain > esize) ? esize : remain; // 데이타 읽기 rtn = read( mtd->fd, buf, rdcnt ); if ( 0 > rtn ) { perror( "mtd read error" ); return rtn; } else { mtd->pos += rdcnt; } remain -= rdcnt; buf += rdcnt; } return count; } //------------------------------------------------------------------------------ /** @brief mtd-nand 에 데이타를 쓴다. @param mtd mtdnand_t 구조체 포인터 @param buf 데이타 버퍼 @param count 버퍼의 크기 @return 쓴 데이타의 크기 @remark 데이타의 크기는 writesize 크기의 배수만 쓰여진다. erase 는 자동으로 이루어지며 erasesize 배수의 위치일 경우 지운다. 배드블럭이 있으면 다음블럭으로 넘긴다. *///---------------------------------------------------------------------------- int mtdnand_write( mtdnand_t *mtd, unsigned char *buf, int count ) { int wrcnt, esize, wsize, wr_total, remain; char wbuf[1024*256], *getp; getp = buf; wr_total = 0; remain = count; esize = mtd->info.erasesize; wsize = mtd->info.writesize; // 쓰기의 위치는 항상 writesize의 배수이어야 한다. if ( 0 != ( mtd->pos % wsize ) ) { mtdnand_seek( mtd, (mtd->pos/wsize)*wsize, SEEK_SET ); } while( 0 < remain ) { // 블럭을 지워야 하는가? if ( 0 == (mtd->pos % esize) ) { if ( mtdnand_is_bad( mtd ) ) { mtd->pos += esize; mtdnand_seek( mtd, mtd->pos, SEEK_SET ); continue; } // 지워서 에러가 있다면 배드블럭이다(?) // mtdnand_erase( mtd ); if ( 0 != mtdnand_erase( mtd ) ) { mtd->pos += esize; mtdnand_seek( mtd, mtd->pos, SEEK_SET ); continue; } } // writesize 만큼 쓴다. wrcnt = (remain >= wsize) ? wsize : remain; // writesize 보다 작다면 나머지를 0xff 로 채운다. memcpy( wbuf, getp, wrcnt ); if ( wrcnt < wsize ) { memset( wbuf+wrcnt, 0xff, wsize-wrcnt ); } // 쓰기는 항상 writesize 이어야 한다. wrcnt = write( mtd->fd, wbuf, wsize ); if ( wrcnt < 0 ) { perror( "mtd write error" ); return -1; } mtd->pos += wrcnt; getp += wrcnt; wr_total += wrcnt; remain -= wrcnt; } return wr_total; } //------------------------------------------------------------------------------ /** @brief mtd-nand 를 모두 지운다. @param mtd mtdnand_t 구조체 포인터 *///---------------------------------------------------------------------------- void mtdnand_erase_all( mtdnand_t *mtd ) { int remain, esize; char str[256]; void progress_msg( char *msg ) { printf( "%s", msg ); fflush( stdout ); } sprintf( str, " erase all mtd=%s size=%dMB\n", mtd->node_name, mtd->info.size/(1024*1024) ); progress_msg( str ); // mtd 어프셋을 설정한다. mtdnand_seek( mtd, 0, SEEK_SET ); remain = mtd->info.size; esize = mtd->info.erasesize; while( 0 < remain ) { mtdnand_erase( mtd ); mtdnand_seek( mtd, esize, SEEK_CUR ); remain -= esize; if ( 0 == (remain % (1024*1024)) ) { progress_msg( "." ); } } progress_msg( ".end\n" ); }