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);
}
可见oob中前8字节保留,之后的4字节存放'B', 'b', 't', '0',之后存放版本号
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;
}
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;
}
/**
* 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;
}
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);
...
}
/**
* 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;
}
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;
}