nand_do_write_ops 的一个注释。
这个函数带有ecc的数据的写入。比如yaffs文件系统就需要这个方法来操作。
/**
* nand_do_write_ops - [INTERN] NAND write with ECC
* @mtd: MTD device structure
* @to: offset to write to
* @ops: oob operations description structure
*
* NAND write with ECC. //有ecc的写操作
*/
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int chipnr, realpage, page, column;
struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t writelen = ops->len; //数据写入长度(不包括oob的长度),字节数
uint32_t oobwritelen = ops->ooblen; //oob的长度,字节数
uint32_t oobmaxlen = mtd_oobavail(mtd, ops); //oob的最大字节数
uint8_t *oob = ops->oobbuf; //指向数据的oob缓存首地址
uint8_t *buf = ops->datbuf; //指向数据的缓存首地址,所以在调用这个函数的时候,oob和数据已经被分离开了。
int ret;
int oob_required = oob ? 1 : 0; //oob指针不为空,这等于1,否则等于0
ops->retlen = 0;
if (!writelen) //写入的长度值为0,表示不用写入,退出
return 0;
/* Reject writes, which are not page aligned */
if (NOTALIGNED(to)) { //不是小页512对齐,退出,因为这里有ecc,每个ecc至少需要一个完整的小页
pr_notice("%s: attempt to write non page aligned data\n",
__func__);
return -EINVAL;
}
column = to & (mtd->writesize - 1); //起始位置不是2k页对齐,记录偏移
chipnr = (int)(to >> chip->chip_shift); //多个flash芯片的情况,这个我的应该是返回0,只有一块芯片
chip->select_chip(mtd, chipnr); //选中第0块
/* Check, if it is write protected */
if (nand_check_wp(mtd)) { //检测是不是写保护?
ret = -EIO;
goto err_out;
}
realpage = (int)(to >> chip->page_shift); //计算页开始的位置,2k页,shift为12
page = realpage & chip->pagemask; //防止可能超出整个芯片,pagemask是不是总页数-1?
/* Invalidate the page cache, when we write to the cached page */
if (to <= ((loff_t)chip->pagebuf << chip->page_shift) &&
((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len)) //数据应该没有超过芯片的最大范围?
chip->pagebuf = -1; //==-1?
/* Don't allow multipage oob writes with offset */
if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { //对oob的字节数进行合法性判断
ret = -EINVAL;
goto err_out;
}
while (1) { //开始写入
int bytes = mtd->writesize; //2k页 值为2048
uint8_t *wbuf = buf; //数据的首地址
int use_bufpoi;
int part_pagewr = (column || writelen < mtd->writesize); //要写一页的一部分?
if (part_pagewr)
use_bufpoi = 1; //只需要写一页的一部分。
else if (chip->options & NAND_USE_BOUNCE_BUFFER)
use_bufpoi = !IS_ALIGNED((unsigned long)buf,
chip->buf_align); //缓存的对齐方式,
else
use_bufpoi = 0; //一般情况下为0
WATCHDOG_RESET(); //看门狗喂狗,实际没有开启
/* Partial page write?, or need to use bounce buffer */
if (use_bufpoi) { //对于只有部分写入的情况,还有需要使用bounce buffer
pr_debug("%s: using write bounce buffer for buf@%p\n",
__func__, buf);
if (part_pagewr)
bytes = min_t(int, bytes - column, writelen); //取出一个小值
chip->pagebuf = -1; //==-1?
memset(chip->buffers->databuf, 0xff, mtd->writesize); //缓存填充0xff
memcpy(&chip->buffers->databuf[column], buf, bytes); //拷贝实际的字节数
wbuf = chip->buffers->databuf; //写缓存等于databuf
}
if (unlikely(oob)) { //oob不是空指针,//但是大概率是空指针
size_t len = min(oobwritelen, oobmaxlen); //取出一个小值
oob = nand_fill_oob(mtd, oob, len, ops); //填充oob的内容。
oobwritelen -= len; //字节数减小
} else {
/* We still need to erase leftover OOB data */
memset(chip->oob_poi, 0xff, mtd->oobsize); //填充0xff
}
ret = chip->write_page(mtd, chip, column, bytes, wbuf, //写一页数据,包含oob
oob_required, page,
(ops->mode == MTD_OPS_RAW));
if (ret) //返回值不为0,表示出错了!!,跳出循环
break;
writelen -= bytes;
if (!writelen) //字节数全部写完了,跳出循环
break;
column = 0; //清零
buf += bytes; //buf的指针向后移动写入的字节数
realpage++; //下一页
page = realpage & chip->pagemask; //防止越界
/* Check, if we cross a chip boundary */
if (!page) { //如果page为0,表示回绕了
chipnr++; //换到下一个芯片开始写
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
}
}
ops->retlen = ops->len - writelen; //真正写入的字节数,用来判断是否出错吧
if (unlikely(oob)) //如果指定了oob,返回oob的写入长度
ops->oobretlen = ops->ooblen;
err_out:
chip->select_chip(mtd, -1);
return ret;
}
1.一点小疑惑:
chip->pagebuf = -1; //==-1? 这个地方出现了两次,正常也是一个数字。
表示page的缓存失效!!!!
2.这个函数中真正做写操作的是write_page的指针。
3.对于不满一页(2k)的数据,我看到没有数据的地方是填充了0xff的。
4.调用这个函数的时候,data和oob是分开存放的。用不同的缓存存起来了。