uboot中nand flash代码分析(3)

上文中提到第一次调用nand_block_isbad()函数时,会建立BBT。
下面分析该函数的执行流程。
nand_block_isbad(&nand_info[0], offset);
该函数的作用如注释中所说"Check if block at offset is bad"
该函数的第一个参数指向nand_info[0],之前的nand_init()会初始化该数组。


static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
{
	/* Check for invalid offset */
	if (offs > mtd->size)//如果要check的偏移值比整个flash还大,就返回一个错误值
		return -EINVAL;


	return nand_block_checkbad(mtd, offs, 1, 0);
}



/**
 * nand_block_checkbad - [GENERIC] Check if a block is marked bad
 * @mtd:	MTD device structure
 * @ofs:	offset from device start
 * @getchip:	0, if the chip is already selected
 * @allowbbt:	1, if its allowed to access the bbt area
 *
 * Check, if the block is bad. Either by reading the bad block table or
 * calling of the scan function.
 */
static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
			       int allowbbt)
{
	struct nand_chip *chip = mtd->priv;


	if (!(chip->options & NAND_BBT_SCANNED)) {//NAND_BBT_SCANNED表示BBT已经"扫描好",
		chip->options |= NAND_BBT_SCANNED;//没有则设置为"扫描好",并调用扫描函数。
		chip->scan_bbt(mtd);//nand_bbt.c中的nand_default_bbt()函数
	}


	//nand_default_bbt()函数会设置chip->bbt.
	if (!chip->bbt)
	{
		return chip->block_bad(mtd, ofs, getchip);
	}
	/* Return info from the table */
	//通过查询chip->bbt判断是否是坏块
	return nand_isbad_bbt(mtd, ofs, allowbbt);//待分析1
}



int nand_default_bbt(struct mtd_info *mtd)
{
	struct nand_chip *this = mtd->priv;


	/* Default for AG-AND. We must use a flash based
	 * bad block table as the devices have factory marked
	 * _good_ blocks. Erasing those blocks leads to loss
	 * of the good / bad information, so we _must_ store
	 * this information in a good / bad table during
	 * startup
	 */
	if (this->options & NAND_IS_AND) {//一种特殊的nand flash
		/* Use the default pattern descriptors */
		if (!this->bbt_td) {
			this->bbt_td = &bbt_main_descr;
			this->bbt_md = &bbt_mirror_descr;
		}
		this->options |= NAND_USE_FLASH_BBT;
		return nand_scan_bbt(mtd, &agand_flashbased);
	}


	/* Is a flash based bad block table requested ? */
	//本例board_nand_init()函数会设置该选项,表示flash中存放有bbt
	if (this->options & NAND_USE_FLASH_BBT) {
		/* Use the default pattern descriptors */
		if (!this->bbt_td) {
			this->bbt_td = &bbt_main_descr;//注释1
			this->bbt_md = &bbt_mirror_descr;//注释1
		}
		if (!this->badblock_pattern) {//注释2
			this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
		}
	} else {
		this->bbt_td = NULL;
		this->bbt_md = NULL;
		if (!this->badblock_pattern) {
			this->badblock_pattern = (mtd->writesize > 512) ?
			    &largepage_memorybased : &smallpage_memorybased;
		}
	}
	return nand_scan_bbt(mtd, this->badblock_pattern);
}


1.


a.
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
bbt_main_descr.pattern = bbt_pattern;
bbt_mirror_descr.pattern = mirror_pattern;
nand flash存放BBT的块的第一页或第二页的oob区域会存有字符串"Bbt0" 或者"1tbB"。
程序就是根据某块的第一页或第二页中是否含有这些字符串来判断该块(该页)是否存放有BBT。


b.
bbt_main_descr.options = NAND_BBT_LASTBLOCK | ...
NAND_BBT_LASTBLOCK的含义是 The bad block table is in the last good block of the device 
即BBT存放在最后一个好块中,会扫描最后一块好块


c.
bbt_main_descr.options = ... | NAND_BBT_2BIT | ...
NAND_BBT_2BIT表示BBT中每2bit代表一个块

d.
bbt_main_descr.maxblocks = 4
读取BBT时,会从最后一个好块开始,递减依次扫描4块,来查询bbt。
e.
bbt_main_descr.offs = 8
表示{'B', 'b', 't', '0'}或者{'1', 't', 'b', 'B' }在oob中的起始地址
f.
bbt_main_descr.len = 4
表示bbt_main_descr.pattern的长度
g.
bbt_main_descr.veroffs = 12
表示bbt version在oob中的偏移值

可见oob中前8字节保留,之后的4字节存放'B', 'b', 't', '0',之后存放版本号


2.
this->badblock_pattern,待分析2中的check_create()中,若没找到bbt,会利用该变量创建bbt,create_bbt()


/**

 * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
 * @mtd:	MTD device structure
 * @bd:		descriptor for the good/bad block search pattern
 *
*/
int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
	struct nand_chip *this = mtd->priv;
	int len, res = 0;
	uint8_t *buf;
	struct nand_bbt_descr *td = this->bbt_td;//见上个函数
	struct nand_bbt_descr *md = this->bbt_md;//见上个函数
	
	//本例中的nandflash共1024块,每块用2bit表示是否是坏块,
	//所以需要256字节空间来存放bbt
	len = mtd->size >> (this->bbt_erase_shift + 2);
	/* Allocate memory (2bit per block) and clear the memory bad block table */
	this->bbt = kzalloc(len, GFP_KERNEL);//	uint8_t		*bbt;给chip->bbt赋值
	if (!this->bbt) {
		printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
		return -ENOMEM;
	}


	/* If no primary table decriptor is given, scan the device
	 * to build a memory based bad block table
	 */
	if (!td) {
		if ((res = nand_memory_bbt(mtd, bd))) {
			printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
			kfree(this->bbt);
			this->bbt = NULL;
		}
		return res;
	}


	//该buf存放bbt描述符所在页的数据和oob,
	//如果flash很大,整个块都是bbt数据,
	//则需要分配块大小+总oob大小的空间
	
	/* Allocate a temporary buffer for one eraseblock incl. oob */
	//一块的大小
	len = (1 << this->bbt_erase_shift);
	//一块中的页数乘以每页对应的oob大小,64*64
	len += (len >> this->page_shift) * mtd->oobsize;


	buf = vmalloc(len);
	if (!buf) {
		printk(KERN_ERR "nand_bbt: Out of memory\n");
		kfree(this->bbt);
		this->bbt = NULL;
		return -ENOMEM;
	}




	/* Is the bbt at a given page ? */
	// The bbt is at the given page, else we must scan for the bbt 
	if (td->options & NAND_BBT_ABSPAGE) {
		res = read_abs_bbts(mtd, buf, td, md);
	} else {
		/* Search the bad block table using a pattern in oob */
		res = search_read_bbts(mtd, buf, td, md);
	}
	if (res)
		res = check_create(mtd, buf, bd);//待分析2


	/* Prevent the bbt regions from erasing / writing */
	//通过修改chip->bbt[]将flash中保存bbt的块设置为坏块,以防
	//程序意外修改该块,破坏bbt数据
	mark_bbt_region(mtd, td);
	if (md)
	{
		mark_bbt_region(mtd, md);//同上
	}
	vfree(buf);
	return res;
}



static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
	/* Search the primary table */
	//以bbt_main_descr作为bbt描述符,开始扫描,当找到某页oob中包含'B', 'b', 't', '0',字符时
	//表示找到bbt所在的块号。该页数据和oob存在buf中
	search_bbt(mtd, buf, td);


	/* Search the mirror table */
	if (md)
	{
		//以bbt_main_descr作为bbt描述符,开始扫描,当找到某页oob中包含'1', 't', 'b', 'B' ,字符时
	  //表示找到备份bbt所在的块号。该页数据和oob存在buf中
		printf("search_bbt:mdmdmd\n");
		search_bbt(mtd, buf, md);
	}
	/* Force result check */
	return 1;
}


static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
	struct nand_chip *this = mtd->priv;
	int i, chips;
	int bits, startblock, block, dir;
	int scanlen = mtd->writesize + mtd->oobsize;
	int bbtblocks;
	int blocktopage = this->bbt_erase_shift - this->page_shift;




	//确定扫描起始块号和扫描方向:从最后一个好块,递减扫描
	/* Search direction top -> down ? */
	if (td->options & NAND_BBT_LASTBLOCK) {
		startblock = (mtd->size >> this->bbt_erase_shift) - 1;
		dir = -1;//递减
	} else {
		startblock = 0;
		dir = 1;
	}


	printf("startblock=0x%x,dir=%d\n",startblock,dir);
	/* Do we have a bbt per chip ? */
	if (td->options & NAND_BBT_PERCHIP) {
		chips = this->numchips;
		bbtblocks = this->chipsize >> this->bbt_erase_shift;
		startblock &= bbtblocks - 1;
	} else {
		chips = 1;
		bbtblocks = mtd->size >> this->bbt_erase_shift;
	}


	/* Number of bits for each erase block in the bbt */
	bits = td->options & NAND_BBT_NRBITS_MSK;


	for (i = 0; i < chips; i++) {
		/* Reset version information */
		td->version[i] = 0;
		td->pages[i] = -1;
		/* Scan the maximum number of blocks */
		for (block = 0; block < td->maxblocks; block++) {


			int actblock = startblock + dir * block;
			loff_t offs = (loff_t)actblock << this->bbt_erase_shift;


			//第一次循环时,读取最后一块中的第一页,offs=0x7fe0000
			//第二次循环时,读取倒数第二块中的第一页,offs=0x7fc0000
			// ... 
			/* Read first page */
			scan_read_raw(mtd, buf, offs, mtd->writesize);
			if (!check_pattern(buf, scanlen, mtd->writesize, td)) {//待分析3
				//此函数返回0,表示找到bbt所在的块,保存块号和版本号				
				td->pages[i] = actblock << blocktopage;
				if (td->options & NAND_BBT_VERSION) {
					td->version[i] = buf[mtd->writesize + td->veroffs];
					printf("td->version[%d] = 0x%x,td->pages[%d] = 0x%x\n",i,td->version[i],i,td->pages[i]);
				}
				break;
			}
		}
		startblock += this->chipsize >> this->bbt_erase_shift;
	}
	/* Check, if we found a bbt for each requested chip */
	for (i = 0; i < chips; i++) {
		if (td->pages[i] == -1)
			printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
		else
			;
			/* JS: silent this driver
			printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
			       td->version[i]);
			*/
	}
	return 0;
}


static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
			 size_t len)
{
	struct mtd_oob_ops ops;


	ops.mode = MTD_OOB_RAW;
	ops.ooboffs = 0;
	ops.ooblen = mtd->oobsize;
	ops.oobbuf = buf;
	ops.datbuf = buf;
	ops.len = len;


	return mtd->read_oob(mtd, offs, &ops); //nand_read_oob()
}


static int nand_read_oob(struct mtd_info *mtd, loff_t from,
			 struct mtd_oob_ops *ops)
{
	struct nand_chip *chip = mtd->priv;
	int ret = -ENOTSUPP;


	ops->retlen = 0;


	/* Do not allow reads past end of device */
	if (ops->datbuf && (from + ops->len) > mtd->size) {
		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
		          "Attempt read beyond end of device\n");
		return -EINVAL;
	}


	nand_get_device(chip, mtd, FL_READING);


	switch(ops->mode) {
	case MTD_OOB_PLACE:
	case MTD_OOB_AUTO:
	case MTD_OOB_RAW:
		break;


	default:
		goto out;
	}


	if (!ops->datbuf)
	{
		ret = nand_do_read_oob(mtd, from, ops);
	}
	else
	{
		ret = nand_do_read_ops(mtd, from, ops);//nand_do_read_ops
	}
 out:
	nand_release_device(mtd);
	return ret;
}

static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
			    struct mtd_oob_ops *ops)
{
	int chipnr, page, realpage, col, bytes, aligned;
	struct nand_chip *chip = mtd->priv;
	struct mtd_ecc_stats stats;
	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
	int sndcmd = 1;
	int ret = 0;
	uint32_t readlen = ops->len;
	uint32_t oobreadlen = ops->ooblen;
	uint8_t *bufpoi, *oob, *buf;




	//将保存ecc状态值保存到临时变量中,本函数在读取数据时,如检测到ECC错误,就会更新mtd->ecc_stats
	//最后mtd->ecc_stats - stats,判断是否有ECC错误发送,若有,就返回错误值
	
	stats = mtd->ecc_stats;
	//要读取的地址处于第几个flash芯片内,本例只有一个flash,所以chipnr=0
	chipnr = (int)(from >> chip->chip_shift); 
	chip->select_chip(mtd, chipnr);


  //计算要读取的地址处于第几页,最后一块的第一页的页号:每块页数64*(总块数1024-1)= 0xffc0
	realpage = (int)(from >> chip->page_shift);
	page = realpage & chip->pagemask;


	//计算要读取的地址在页中的偏移,因为要读取第一个整页,所以col=0
	col = (int)(from & (mtd->writesize - 1));


	buf = ops->datbuf;
	oob = ops->oobbuf;


	while(1) {
		bytes = min(mtd->writesize - col, readlen);
		aligned = (bytes == mtd->writesize);//读取整页,所以bytes==mtd->writesize==readlen,aligned=1




		/* Is the current page in the buffer ? */
		if (realpage != chip->pagebuf || oob) {//要读取的数据是否在缓存cache buffer里
			bufpoi = aligned ? buf : chip->buffers->databuf;//读取到的数据的存放地址


			if (likely(sndcmd)) {//读之前是否发送读命令
				chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
				sndcmd = 0;
			}


			/* Now read the page into the buffer */
			if (unlikely(ops->mode == MTD_OOB_RAW))
				ret = chip->ecc.read_page_raw(mtd, chip,
						bufpoi, page);// 读取整页+oob,不做ECC校验
			else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
				ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);//读取子页,即读取一页中的一部分
			else
				ret = chip->ecc.read_page(mtd, chip, bufpoi,
						page);;//读取整页,并做ECC校验
			if (ret < 0)
				break;
			printk(KERN_WARNING "2mode=0x%x,oob=0x%x,ecc.mode=0x%x,page=0x%x,aligned=0x%x\n",ops->mode ,oob,chip->ecc.mode,page,aligned);


			/* Transfer not aligned data */
			//aligned=0时,上面的read函数会将数据存放在chip->buffers->databuf中,此时会将其copy到buf中
			//一般大页flash都支持读取subpage,所以 chip->pagebuf = realpage;不会执行,chip->pagebuf 无什么作用			
			if (!aligned) {
				if (!NAND_SUBPAGE_READ(chip) && !oob)
					chip->pagebuf = realpage;
				memcpy(buf, chip->buffers->databuf + col, bytes);
			}


			buf += bytes;//调整存放数据的地址


			if (unlikely(oob)) {
				/* Raw mode does data:oob:data:oob */
				if (ops->mode != MTD_OOB_RAW) {
					int toread = min(oobreadlen,
						chip->ecc.layout->oobavail);
					if (toread) {
						oob = nand_transfer_oob(chip,
							oob, ops, toread);
						oobreadlen -= toread;
					}
				} else
					buf = nand_transfer_oob(chip,
						buf, ops, mtd->oobsize);//存放oob到buf中
			}
			//判断是否延时一段时间
			if (!(chip->options & NAND_NO_READRDY)) {
				/*
				 * Apply delay or wait for ready/busy pin. Do
				 * this before the AUTOINCR check, so no
				 * problems arise if a chip which does auto
				 * increment is marked as NOAUTOINCR by the
				 * board driver.
				 */
				if (!chip->dev_ready)
				{
					udelay(chip->chip_delay);
				}
				else
				{
					nand_wait_ready(mtd);
				}
			}
		} else {//若要读取的数据已经在cache buffer里,则直接从buffer复制
			memcpy(buf, chip->buffers->databuf + col, bytes);
			buf += bytes;
		}


		readlen -= bytes; 
		printk(KERN_WARNING "readlen=0x%x\n",readlen);


		if (!readlen)//若读取完,则跳出读循环
			break;


		//没读完,则读取下一页
		/* For subsequent reads align to page boundary. */
		col = 0;
		/* Increment page address */
		realpage++;


		page = realpage & chip->pagemask;
		//如果要读取下一个芯片内的数据,则...
		/* Check, if we cross a chip boundary */
		if (!page) {
			chipnr++;
			chip->select_chip(mtd, -1);
			chip->select_chip(mtd, chipnr);
		}


		/* Check, if the chip supports auto page increment
		 * or if we have hit a block boundary.
		 */
		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
			sndcmd = 1;//下次读操作之前是否需要发送命令
		printk(KERN_WARNING "realpage=0x%x,page=0x%x,chipnr=0x%x,blkcheck=0x%x,sndcmd=0x%x,options=0x%x\n",realpage,page,chipnr,blkcheck,sndcmd,chip->options);
	}


	ops->retlen = ops->len - (size_t) readlen;//此时readlen==0,所以ops->retlen == ops->len,即读到的数据长度等于想要读取的数据长度


	if (oob)
		ops->oobretlen = ops->ooblen - oobreadlen;//oobreadlen==0




	if (ret)
		return ret;
	if (mtd->ecc_stats.failed - stats.failed)//检测是否有ECC错误
		return -EBADMSG;
	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}


static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
			      uint8_t *buf, int page)
{
	chip->read_buf(mtd, buf, mtd->writesize);//读取数据放到buf里
	//读取oob放到oob_poi里,之后会在nand_transfer_oob()函数中,将其移到buf里
	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
	return 0;
}






static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
				  struct mtd_oob_ops *ops, size_t len)
{
	switch(ops->mode) {


	case MTD_OOB_PLACE:
	case MTD_OOB_RAW:
		//将oob数据放到buf中,此时buf指针已经调整到数据部分之后。即oob是紧跟着数据,一起存放在buf中
		memcpy(oob, chip->oob_poi + ops->ooboffs, len);
		return oob + len;


	case MTD_OOB_AUTO: {
		struct nand_oobfree *free = chip->ecc.layout->oobfree;
		uint32_t boffs = 0, roffs = ops->ooboffs;
		size_t bytes = 0;


		for(; free->length && len; free++, len -= bytes) {
			/* Read request not from offset 0 ? */
			if (unlikely(roffs)) {
				if (roffs >= free->length) {
					roffs -= free->length;
					continue;
				}
				boffs = free->offset + roffs;
				bytes = min_t(size_t, len,
					      (free->length - roffs));
				roffs = 0;
			} else {
				bytes = min_t(size_t, len, free->length);
				boffs = free->offset;
			}
			memcpy(oob, chip->oob_poi + boffs, bytes);
			oob += bytes;
		}
		return oob;
	}
	default:
		BUG();
	}
	return NULL;
}



待分析3
此时最后一个好块的第一页数据和oob都存放在buf中,总长度为len(2048+64byte),页大小是2048byte,bbt描述符是td
此函数返回0,表示找到bbt所在的块
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{
	int i, end = 0;
	uint8_t *p = buf;




	end = paglen + td->offs;
	//若设置了NAND_BBT_SCANEMPTY,会判断数据部分是否都是0xff
	if (td->options & NAND_BBT_SCANEMPTY) {
		for (i = 0; i < end; i++) {
			if (p[i] != 0xff)
				return -1;
		}
	}
	p += end;
	//判断是否是td里面所设置的pattern 'B', 'b', 't', '0'
	/* Compare the pattern */
	for (i = 0; i < td->len; i++) {
		if (p[i] != td->pattern[i])
			return -1;
	}
	//若设置了NAND_BBT_SCANEMPTY,会判断pattern之后的数据是否都是0xff,本例没设置,并且pattern之后
	//要存放version值
	if (td->options & NAND_BBT_SCANEMPTY) {
		p += td->len;
		end += td->len;
		for (i = end; i < len; i++) {
			if (*p++ != 0xff)
				return -1;
		}
	}
	return 0;
}

//待分析2
/**
 * check_create - [GENERIC] create and write bbt(s) if necessary
 **/
static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
{
	int i, chips, writeops, chipsel, res;
	struct nand_chip *this = mtd->priv;
	struct nand_bbt_descr *td = this->bbt_td;
	struct nand_bbt_descr *md = this->bbt_md;
	struct nand_bbt_descr *rd, *rd2;






	/* Do we have a bbt per chip ? */
	if (td->options & NAND_BBT_PERCHIP)
		chips = this->numchips;
	else
		chips = 1;
	//chips: flash 数量 ,本例等于1 
	for (i = 0; i < chips; i++) {
		writeops = 0;
		rd = NULL;
		rd2 = NULL;
		/* Per chip or per device ? */
		chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;


		/* Mirrored table avilable ? */
		if (md) {
			//td->pages[0]  md->pages[0]中存放bbt和Mirrored bbt所在的页号
			//如果都等于-1,则表示没找到bbt,之后会create bbt
			if (td->pages[i] == -1 && md->pages[i] == -1) {
				writeops = 0x03;
				goto create;
			}


			if (td->pages[i] == -1) {
				rd = md;
				td->version[i] = md->version[i];
				writeops = 1;//将md找到的bbt写到td中
				goto writecheck;
			}


			if (md->pages[i] == -1) {
				rd = td;
				md->version[i] = td->version[i];
				writeops = 2;//将td找到的bbt写到md中
				goto writecheck;
			}
			//td->version[0] = md->version[0]
			if (td->version[i] == md->version[i]) {
				rd = td;
				if (!(td->options & NAND_BBT_VERSION))
					rd2 = md;//空
				goto writecheck;
			}


			if (((int8_t) (td->version[i] - md->version[i])) > 0) {
				rd = td;
				md->version[i] = td->version[i];
				writeops = 2;
			} else {
				rd = md;
				td->version[i] = md->version[i];
				writeops = 1;
			}


			goto writecheck;


		} else {
			if (td->pages[i] == -1) {
				writeops = 0x01;
				goto create;
			}
			rd = td;
			goto writecheck;
		}
	create:
		/* Create the bad block table by scanning the device ? */
		if (!(td->options & NAND_BBT_CREATE))
			continue;


		/* Create the table in memory by scanning the chip(s) */
		create_bbt(mtd, buf, bd, chipsel);


		td->version[i] = 1;
		if (md)
			md->version[i] = 1;
	writecheck:
		/* read back first ? */
		//读取bbt
		if (rd)
			read_abs_bbt(mtd, buf, rd, chipsel);
		/* If they weren't versioned, read both. */
		if (rd2)
			read_abs_bbt(mtd, buf, rd2, chipsel);


		//writeops = 0,
		/* Write the bad block table to the device ? */
		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
			res = write_bbt(mtd, buf, td, md, chipsel);
			if (res < 0)
				return res;
		}


	
		/* Write the mirror bad block table to the device ? */
		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
			res = write_bbt(mtd, buf, md, td, chipsel);
			if (res < 0)
				return res;
		}
	}
	return 0;
}


/*
 * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
 */
static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
{
	struct nand_chip *this = mtd->priv;
	int res = 0, i;
	int bits;


	bits = td->options & NAND_BBT_NRBITS_MSK;
	if (td->options & NAND_BBT_PERCHIP) {
		int offs = 0;
		for (i = 0; i < this->numchips; i++) {
			if (chip == -1 || chip == i)
				res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
			if (res)
				return res;
			offs += this->chipsize >> (this->bbt_erase_shift + 2);
		}
	} else {
		res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
		if (res)
			return res;
	}
	return 0;
}


/**
 * read_bbt - [GENERIC] Read the bad block table starting from page
 * @mtd:	MTD device structure
 * @buf:	temporary buffer
 * @page:	the starting page
 * @num:	the number of bbt descriptors to read
 * @bits:	number of bits per block
 * @offs:	offset in the memory table
 * @reserved_block_code:	Pattern to identify reserved blocks
 *
 * Read the bad block table starting from page.
 *
 */
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
		    int bits, int offs, int reserved_block_code)
{
	int res, i, j, act = 0;
	struct nand_chip *this = mtd->priv;
	size_t retlen, len, totlen;
	loff_t from;
	uint8_t msk = (uint8_t) ((1 << bits) - 1);//0x00000011


	//num是flash的总块数,每块用2bit表示是否是坏块,再右移3位,转换为byte数。totlen=256
	totlen = (num * bits) >> 3;
	//由页数计算开始读写的地址
	from = ((loff_t) page) << this->page_shift;


	while (totlen) {
		len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
		//读取256字节,比一页大小要小,会用nand_read_subpage()读取
		res = mtd->read(mtd, from, len, &retlen, buf);
		if (res < 0) {
			if (retlen != len) {
				printk(KERN_INFO "nand_bbt: Error reading bad block table\n");
				return res;
			}
			printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
		}


		printf("\n");
		/* Analyse data */
		//读取到bbt数据,对每2bit进行分析,
		for (i = 0; i < len; i++) {
			uint8_t dat = buf[i];
			for (j = 0; j < 8; j += bits, act += 2) {
				uint8_t tmp = (dat >> j) & msk;
				if (tmp == msk)//0x11表示该块是好块
					continue;
				
				//reserved_block_code没有初始化,默认值是0x00
				if (reserved_block_code && (tmp == reserved_block_code)) {
					printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n",
						(loff_t)((offs << 2) +
						(act >> 1)) <<
						this->bbt_erase_shift);
					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
					mtd->ecc_stats.bbtblocks++;
					continue;
				}
				/* Leave it for now, if its matured we can move this
				 * message to MTD_DEBUG_LEVEL0 */
				//其他值表明该块是坏块,act>>1表示块号,乘以块的大小,就得到坏块的地址
				printk(KERN_WARNING "nand_read_bbt: Bad block at 0x%012llx\n",
					(loff_t)((offs << 2) + (act >> 1)) <<
					this->bbt_erase_shift);
				/* Factory marked bad or worn out ? */
				//设置this->bbt[],好块设置为0,坏块设置为1或者3,仍是2bit代表一块
				//1byte可以代表4块,act>>1是块号,(act >> 3)就是该块在this->bbt中byte位置
				//(act & 0x06)表示该块在该byte中的偏移
				if (tmp == 0)
					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
				else
					this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
					
				mtd->ecc_stats.badblocks++;
			}
		}
		totlen -= len;
		from += len;
	}
	return 0;
}


下面看下读取bbt所用的函数
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
		     size_t *retlen, uint8_t *buf)
{
	struct nand_chip *chip = mtd->priv;
	int ret;


	/* Do not allow reads past end of device */
	if ((from + len) > mtd->size)
		return -EINVAL;
	if (!len)
		return 0;


	nand_get_device(chip, mtd, FL_READING);


	chip->ops.len = len;
	chip->ops.datbuf = buf;
	chip->ops.oobbuf = NULL;


	ret = nand_do_read_ops(mtd, from, &chip->ops);


	*retlen = chip->ops.retlen;


	nand_release_device(mtd);


	return ret;
}


static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
			    struct mtd_oob_ops *ops)
{
		...
		if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
			ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
		...
}


该函数会进行ecc校验,读取到的数据,每256bytes,会计算3bytes的ECC,并和从oob区中读取到的ECC进行比较。
/**
 * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
 */
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
{
	int start_step, end_step, num_steps;
	uint32_t *eccpos = chip->ecc.layout->eccpos;
	uint8_t *p;
	int data_col_addr, i, gaps = 0;
	int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
	int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;

	//如果读取的起始地址不是256bytes对齐,则会多读取一些
	//比如从10开始读取的话,则会从0开始读
	//结束地址不是256bytes对齐,则一样会多读取些
	/* Column address wihin the page aligned to ECC size (256bytes). */
	start_step = data_offs / chip->ecc.size;
	end_step = (data_offs + readlen - 1) / chip->ecc.size;
	num_steps = end_step - start_step + 1;


	/* Data size aligned to ECC ecc.size*/
	datafrag_len = num_steps * chip->ecc.size;
	eccfrag_len = num_steps * chip->ecc.bytes;


	data_col_addr = start_step * chip->ecc.size;




	//判断是否发送NAND_CMD_RNDOUT命令
	/* If we read not a page aligned data */
	if (data_col_addr != 0)
		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);


	//读取数据
	p = bufpoi + data_col_addr;
	chip->read_buf(mtd, p, datafrag_len);


	//计算ECC,存放在chip->buffers->ecccalc
	/* Calculate  ECC */
	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
		chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);


	//判断oob中存放ecc的区域是否是连续的,即ecc之间是否有间隙
	/* The performance is faster if to position offsets
	   according to ecc.pos. Let make sure here that
	   there are no gaps in ecc positions */
	for (i = 0; i < eccfrag_len - 1; i++) {
		if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
			eccpos[i + start_step * chip->ecc.bytes + 1]) {
			gaps = 1;
			break;
		}
	}


	if (gaps) {
		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
	} else {
		/* send the command to read the particular ecc bytes */
		/* take care about buswidth alignment in read_buf */
		aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
		aligned_len = eccfrag_len;
		if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
			aligned_len++;
		if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
			aligned_len++;


		//读取oob中的ecc,aligned_pos表示要读取的ecc所在的位置,aligned_len表示要读取的长度
		//并不是读取全部的ECC
		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
		chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
	}


	for (i = 0; i < eccfrag_len; i++)
		chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];


	//判断是否有ecc错误
	p = bufpoi + data_col_addr;
	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
		int stat;


		stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
		if (stat == -1)
			mtd->ecc_stats.failed++;
		else
			mtd->ecc_stats.corrected += stat;
	}
	return 0;
}


//待分析1
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
{
	struct nand_chip *this = mtd->priv;
	int block;
	uint8_t res;




	/* Get block number * 2 */
	//block实际是块号的2倍 
	//每块对应2bit,1byte对应4块,
	//所以block >> 3表示块在bbt中的下标值
	//(block & 0x06)表示在元素中bit位置
	//另一种计算方式是(块号 & 0x03)*2
	//因为第一块占bit0-bit1,第二块占bit2-bit3,...
	block = (int)(offs >> (this->bbt_erase_shift - 1));
	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
	
	MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: "
	          "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1);


	switch ((int)res) {
	case 0x00:
		return 0;
	case 0x01:
		return 1;
	case 0x02:
		return allowbbt ? 0 : 1;
	}
	return 1;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值