加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

MTD原始设备与FLASH硬件驱动的对话-续

发布时间:2020-12-15 17:58:38 所属栏目:百科 来源:网络整理
导读:转自: l uofu chong http://www.cnitblog.com/luofuchong/archive/2007/09/04/32939.html 上一个贴由下到上的介绍了FLASH硬件驱动是如何与MTD原始设备建立联系的,现在再由上到下的研究一下是如何通过MTD原始设备来访问FLASH硬件驱动的。 首先分析一下如何

转自:luofuchong http://www.cnitblog.com/luofuchong/archive/2007/09/04/32939.html

上一个贴由下到上的介绍了FLASH硬件驱动是如何与MTD原始设备建立联系的,现在再由上到下的研究一下是如何通过MTD原始设备来访问FLASH硬件驱动的。


首先分析一下如何通过MTD原始设备进而通过FLASH硬件驱动来读取FLASH存储器的数据。

引用自<<Linux系统移植>>一文:

"读Nand Flash:
当对nand flash的设备文件(nand flash在/dev下对应的文件)执行系统调用read(),或在某个文件系统中对该
设备进行读操作时. 会调用struct mtd_info中的read方法,他们缺省调用函数为nand_read(),在
drivers/mtd/nand/nand_base.c中定义.nand_read()调用nand_do_read_ecc(),执行读操作. 在
nand_do_read_ecc()函数中,主要完成如下几项工作:
1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即
s3c2410_nand_select_chip()选择要操作的MTD芯片.
2. 会调用在struct nand_chip中系统缺省的方法cmdfunc发送读命令到nand flash.
3. 会调用在nand flash驱动中对struct nand_chip重载的read_buf(),即s3c2410_nand_read_buf()
从Nand Flash的控制器的数据寄存器中读出数据.
4. 如果有必要的话,会调用在nand flash驱动中对struct nand_chip重载的
enable_hwecc,correct_data以及calculate_ecc方法,进行数据ECC校验。"

下面研究一下其中的细节:
/**
?* nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
?* @mtd:??? MTD device structure
?* @from:??? offset to read from
?* @len:??? number of bytes to read
?* @retlen:??? pointer to variable to store the number of read bytes
?* @buf:??? the databuffer to put data
?*
?* This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
?* and flags = 0xff
?*/
static int nand_read (struct mtd_info *mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)
{
??? return nand_do_read_ecc (mtd,from,len,retlen,buf,NULL,&mtd->oobinfo,0xff);
}
注:
??? 以参数oob_buf为NULL,flags为0xff调用nand_do_read_ecc函数。

/**
?* nand_do_read_ecc - [MTD Interface] Read data with ECC
?* @mtd:??? MTD device structure
?* @from:??? offset to read from
?* @len:??? number of bytes to read
?* @retlen:??? pointer to variable to store the number of read bytes
?* @buf:??? the databuffer to put data
?* @oob_buf:??? filesystem supplied oob data buffer (can be NULL)
?* @oobsel:??? oob selection structure
?* @flags:??? flag to indicate if nand_get_device/nand_release_device should be preformed
?*??? ??? and how many corrected error bits are acceptable:
?*??? ??? ? bits 0..7 - number of tolerable errors
?*??? ??? ? bit? 8??? - 0 == do not get/release chip,1 == get/release chip
?*
?* NAND read with ECC
?*/
int nand_do_read_ecc (struct mtd_info *mtd,
??? ??? ??? ???? size_t * retlen,u_char * buf,u_char * oob_buf,
??? ??? ??? ???? struct nand_oobinfo *oobsel,int flags)
{

??? int i,j,col,realpage,page,end,ecc,chipnr,sndcmd = 1;
??? int read = 0,oob = 0,ecc_status = 0,ecc_failed = 0;
??? struct nand_chip *this = mtd->priv;
??? u_char *data_poi,*oob_data = oob_buf; //目前oob_data指针为空,以后会去修改它。
??? u_char ecc_calc[32]; //该数组用于存放计算出来的ecc结果
??? u_char ecc_code[32]; //该数组用于存放oob中ecc部分的数据
??? int eccmode,eccsteps; //eccmode存放ecc的类型(ECC_SOFT);
??? ??? ??? ??? ??? ??? ??? eccsteps用于记录一个page所需的ecc校验次数(2)。
??? int??? *oob_config,datidx;
??? int??? blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
??? int??? eccbytes;
??? int??? compareecc = 1; //是否需要ecc标志(如果设置成ECC_NONE,这个标志将被清0)
??? int??? oobreadlen;


??? DEBUG (MTD_DEBUG_LEVEL3,"nand_read_ecc: from = 0x%08x,len = %in",(unsigned int) from,(int) len);

??? /* Do not allow reads past end of device */
??? /* 不允许超越设备容量的读操作 */
??? if ((from + len) > mtd->size) {
??? ??? DEBUG (MTD_DEBUG_LEVEL0,"nand_read_ecc: Attempt read beyond end of devicen");
??? ??? *retlen = 0;
??? ??? return -EINVAL;
??? }

??? /* Grab the lock and see if the device is available */
??? /* 获取自旋锁,等待设备可用并获取其控制权 */
??? if (flags & NAND_GET_DEVICE)
??? ??? nand_get_device (this,mtd,FL_READING);

??? /* Autoplace of oob data ? Use the default placement scheme */
??? if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
??? ??? oobsel = this->autooob;
??? /*
???? * 感觉这一步有点多余,因为nand_scan中已经调用了以下代码:
???? * memcpy(&mtd->oobinfo,this->autooob,sizeof(mtd->oobinfo));
???? * 把this->autooob的内容拷贝到mtd->oobinfo中了
???? */
??? ???
??? eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
??? oob_config = oobsel->eccpos; //记录ecc在oob数据中的位置

??? /* Select the NAND device */
??? chipnr = (int)(from >> this->chip_shift);
??? this->select_chip(mtd,chipnr); //选择nand flash芯片(在s3c2410 nand flash控制器中为空操作)

??? /* First we calculate the starting page */
??? /* 首先,我们计算出开始页码 */
??? realpage = (int) (from >> this->page_shift);
??? page = realpage & this->pagemask;

??? /* Get raw starting column */
??? /* 其次,我们计算页内偏址 */
??? col = from & (mtd->oobblock - 1);

??? end = mtd->oobblock; //页大小(512)
??? ecc = this->eccsize; //ecc保护下的数据大小(256)
??? eccbytes = this->eccbytes; //ecc所占的字节数(3)
???
??? if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
??? ??? compareecc = 0; //如果设置为关闭ECC或写操作才需要ECC,那把ecc给禁用(现在可是读操作^_^)

??? oobreadlen = mtd->oobsize;//16
??? if (this->options & NAND_HWECC_SYNDROME)
??? ??? oobreadlen -= oobsel->eccbytes;

??? /* Loop until all data read */
??? while (read < len) {
??? ???
??? ??? int aligned = (!col && (len - read) >= end);
??? ??? /*
??? ??? ?* If the read is not page aligned,we have to read into data buffer
??? ??? ?* due to ecc,else we read into return buffer direct
??? ??? ?* 如果要读的位置不是页对齐都话,那么只要先把整页读出来,
??? ??? ?* 取出所需要读取的数据,然后修改读位置,那么以后的读操作都是页对齐的了。
??? ??? ?*/
??? ??? if (aligned)
??? ??? ??? data_poi = &buf[read];
??? ??? else
??? ??? ??? data_poi = this->data_buf;
??? ???
??? ??? /* Check,if we have this page in the buffer
??? ??? ?*
??? ??? ?* FIXME: Make it work when we must provide oob data too,
??? ??? ?* check the usage of data_buf oob field
??? ??? ? * 如果我们所需要的数据还存在于缓冲中都话:
??? ??? ?* 1 如果读位置页对齐,我们只要把缓冲中的数据直接拷贝到data_poi(buf[read])中即可(因为数据存在与缓存中,所以也无需要考虑ecc问题)
??? ??? ?* 2 如果读位置不是页对齐,什么读不要作,让其继续留在缓存(data_buf)中,以后会从data_poi(指向缓存data_buf)中提取所需要的数据。
??? ??? ?*/
??? ??? if (realpage == this->pagebuf && !oob_buf) {
??? ??? ??? /* aligned read ? */
??? ??? ??? if (aligned)
??? ??? ??? ??? memcpy (data_poi,this->data_buf,end);
??? ??? ??? goto readdata;
??? ??? }

??? ??? /* Check,if we must send the read command */
??? ??? /* 发送读命令,页地址为page,列地址为0x00 */
??? ??? if (sndcmd) {
??? ??? ??? this->cmdfunc (mtd,NAND_CMD_READ0,0x00,page);
??? ??? ??? sndcmd = 0;
??? ??? }???

??? ??? /* get oob area,if we have no oob buffer from fs-driver */
??? ??? if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
??? ??? ??? oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
??? ??? ??? oob_data = &this->data_buf[end]; //以上情况,oob_data暂存在data_buf缓存中

??? ??? eccsteps = this->eccsteps;//2
??? ???
??? ??? switch (eccmode) {
??? ??? case NAND_ECC_NONE: {??? /* No ECC,Read in a page */
??? ??? ??? static unsigned long lastwhinge = 0;
??? ??? ??? if ((lastwhinge / HZ) != (jiffies / HZ)) {
??? ??? ??? ??? printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommendedn");
??? ??? ??? ??? lastwhinge = jiffies;
??? ??? ??? }
??? ??? ??? this->read_buf(mtd,data_poi,end);
??? ??? ??? break;
??? ??? }
??? ??? ???
??? ??? case NAND_ECC_SOFT:??? /* Software ECC 3/256: Read in a page + oob data */
??? ??? ??? this->read_buf(mtd,end); //读取数据到data_poi
??? ??? ??? for (i = 0,datidx = 0; eccsteps; eccsteps--,i+=3,datidx += ecc)
??? ??? ??? ??? this->calculate_ecc(mtd,&data_poi[datidx],&ecc_calc[i]);
??? ??? ??? /* 计算出读取到data_poi的数据的ecc值,并存放到ecc_calc数组中。
??? ??? ??? ?* 因为读都数据有一页大小(512),需要分别对其上半部和下半部分计算一次ecc值,并分开存放到ecc_calc数组相应都位置中。
??? ??? ??? ?*/
??? ??? ??? break;???

??? ??? default:
??? ??? ??? for (i = 0,i+=eccbytes,datidx += ecc) {
??? ??? ??? ??? this->enable_hwecc(mtd,NAND_ECC_READ);
??? ??? ??? ??? this->read_buf(mtd,ecc);

??? ??? ??? ??? /* HW ecc with syndrome calculation must read the
??? ??? ??? ??? ?* syndrome from flash immidiately after the data */
??? ??? ??? ??? if (!compareecc) {
??? ??? ??? ??? ??? /* Some hw ecc generators need to know when the
??? ??? ??? ??? ??? ?* syndrome is read from flash */
??? ??? ??? ??? ??? this->enable_hwecc(mtd,NAND_ECC_READSYN);
??? ??? ??? ??? ??? this->read_buf(mtd,&oob_data[i],eccbytes);
??? ??? ??? ??? ??? /* We calc error correction directly,it checks the hw
??? ??? ??? ??? ??? ?* generator for an error,reads back the syndrome and
??? ??? ??? ??? ??? ?* does the error correction on the fly */
??? ??? ??? ??? ??? ecc_status = this->correct_data(mtd,&ecc_code[i]);
??? ??? ??? ??? ??? if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
??? ??? ??? ??? ??? ??? DEBUG (MTD_DEBUG_LEVEL0,"nand_read_ecc: "
??? ??? ??? ??? ??? ??? ??? "Failed ECC read,page 0x%08x on chip %dn",chipnr);
??? ??? ??? ??? ??? ??? ecc_failed++;
??? ??? ??? ??? ??? }
??? ??? ??? ??? } else {
??? ??? ??? ??? ??? this->calculate_ecc(mtd,&ecc_calc[i]);
??? ??? ??? ??? }???
??? ??? ??? }
??? ??? ??? break;??? ??? ??? ??? ??? ???
??? ??? }

??? ??? /* read oobdata */
??? ??? this->read_buf(mtd,&oob_data[mtd->oobsize - oobreadlen],oobreadlen);
??? ??? //读取oob_data存放到oob_data[mtd->oobsize - oobreadlen],在这里是data_buf[end]中

??? ??? /* Skip ECC check,if not requested (ECC_NONE or HW_ECC with syndromes) */
??? ??? /* 跳过ecc检测 */
??? ??? if (!compareecc)
??? ??? ??? goto readoob;???
??? ???
??? ??? /* Pick the ECC bytes out of the oob data */
??? ??? /* 从刚读出来都oob_data中取出ecc数据(在这里是前三个字节) */
??? ??? for (j = 0; j < oobsel->eccbytes; j++)
??? ??? ??? ecc_code[j] = oob_data[oob_config[j]];

??? ??? /* correct data,if neccecary */
??? ??? for (i = 0,j = 0,datidx = 0; i < this->eccsteps; i++,datidx += ecc) {
??? ??? ??? ecc_status = this->correct_data(mtd,&ecc_code[j],&ecc_calc[j]);
??? ??? ??? /* 拿前面计算出来都ecc_cal数组都数据与读出来的ecc数据作比较,并尝试修正错误(但不保证能修复,具体看返回值) */
??? ??? ???
??? ??? ??? /* Get next chunk of ecc bytes */
??? ??? ??? j += eccbytes;
??? ??? ???
??? ??? ??? /* Check,if we have a fs supplied oob-buffer,
??? ??? ??? ?* This is the legacy mode. Used by YAFFS1
??? ??? ??? ?* Should go away some day
??? ??? ??? ?*/
??? ??? ??? if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
??? ??? ??? ??? int *p = (int *)(&oob_data[mtd->oobsize]);
??? ??? ??? ??? p[i] = ecc_status;
??? ??? ??? }
??? ??? ??? /* 很不幸,ecc检测发现错误且未能修复,报告错误 */???
??? ??? ??? if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {???
??? ??? ??? ??? DEBUG (MTD_DEBUG_LEVEL0,"nand_read_ecc: " "Failed ECC read,page 0x%08xn",page);
??? ??? ??? ??? ecc_failed++;
??? ??? ??? }
??? ??? }??? ???

??? readoob:
??? ??? /* check,if we have a fs supplied oob-buffer */
??? ??? if (oob_buf) {
??? ??? ??? /* without autoplace. Legacy mode used by YAFFS1 */
??? ??? ??? switch(oobsel->useecc) {
??? ??? ??? case MTD_NANDECC_AUTOPLACE:
??? ??? ??? case MTD_NANDECC_AUTOPL_USR:
??? ??? ??? ??? /* Walk through the autoplace chunks */
??? ??? ??? ??? for (i = 0; oobsel->oobfree[i][1]; i++) {
??? ??? ??? ??? ??? int from = oobsel->oobfree[i][0];
??? ??? ??? ??? ??? int num = oobsel->oobfree[i][1];
??? ??? ??? ??? ??? memcpy(&oob_buf[oob],&oob_data[from],num);
??? ??? ??? ??? ??? oob += num;
??? ??? ??? ??? }
??? ??? ??? ??? break;
??? ??? ??? case MTD_NANDECC_PLACE:
??? ??? ??? ??? /* YAFFS1 legacy mode */
??? ??? ??? ??? oob_data += this->eccsteps * sizeof (int);
??? ??? ??? default:
??? ??? ??? ??? oob_data += mtd->oobsize;
??? ??? ??? }
??? ??? }
??? readdata:
??? ??? /* Partial page read,transfer data into fs buffer
??? ??? ?* 读位置不是页对齐,从data_poi(data_buf中)提取所需要都数据
??? ??? ?*/
??? ??? if (!aligned) {
??? ??? ??? for (j = col; j < end && read < len; j++)
??? ??? ??? ??? buf[read++] = data_poi[j]; //read自增
??? ??? ??? this->pagebuf = realpage;???
??? ??? } else??? ???
??? ??? ??? read += mtd->oobblock; //整页读取,计数值加上整页的数目(512)

??? ??? /* 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 (!this->dev_ready)
??? ??? ??? udelay (this->chip_delay);
??? ??? else
??? ??? ??? nand_wait_ready(mtd);
??? ??? ???
??? ??? if (read == len) //所需数据读完都情况,退出读循环
??? ??? ??? break;???

??? ??? /* For subsequent reads align to page boundary. */
??? ??? col = 0; //对于读位置不是页对齐都情况,前面已对其进行林相应都处理,现在读位置变得页对齐了。
??? ??? /* Increment page address */
??? ??? realpage++; //页地址加1,读取下一页。

??? ??? page = realpage & this->pagemask;
??? ??? /* Check,if we cross a chip boundary */
??? ??? if (!page) {
??? ??? ??? chipnr++;
??? ??? ??? this->select_chip(mtd,-1);
??? ??? ??? this->select_chip(mtd,chipnr);
??? ??? }
??? ??? /* Check,if the chip supports auto page increment
??? ??? ?* or if we have hit a block boundary.
??? ??? ? * 如果芯片支持页自增操作,且未到block boundary(15)的话,不用再发送读命令
??? ??? */
??? ??? if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
??? ??? ??? sndcmd = 1;??? ??? ??? ???
??? }

??? /* Deselect and wake up anyone waiting on the device */
??? if (flags & NAND_GET_DEVICE)
??? ??? nand_release_device(mtd); //放弃对设备都控制权,好让其它进程获取并占有它

??? /*
??? ?* Return success,if no ECC failures,else -EBADMSG
??? ?* fs driver will take care of that,because
??? ?* retlen == desired len and result == -EBADMSG
??? ?*/
??? *retlen = read;
??? return ecc_failed ? -EBADMSG : 0;
}

好的,接着研究一下如何通过MTD原始设备进而通过FLASH硬件驱动向FLASH存储器写数据。

引用自<<Linux系统移植>>一文:

写Nand Flash
当对nand flash的设备文件(nand flash在/dev下对应的文件)执行系统调用write(),或在某个文件系统中对该设备
进行读操作时,会调用struct mtd_info中write方法,他们缺省调用函数为nand_write(),这两个函数在
drivers/mtd/nand/nand_base.c中定义. nand_write()调用nand_write_ecc(),执行写操作.在
nand_do_write_ecc()函数中,即
s3c2410_nand_select_chip()选择要操作的MTD芯片.
2. 调用nand_write_page()写一个页.
3. 在nand_write_page()中,会调用在struct nand_chip中系统缺省的方法cmdfunc发送写命令
到nand flash.
4. 在nand_write_page()中,会调用在nand flash驱动中对struct nand_chip重载的
write_buf(),即s3c2410_nand_write_buf()从Nand Flash的控制器的数据寄存器中写入数据.
5. 在nand_write_page()中,会调用在nand flash驱动中对struct nand_chip重载waitfunc方法,
该方法调用系统缺省函数nand_wait(),该方法获取操作状态,并等待nand flash操作完成.等
待操作完成,是调用nand flash驱动中对struct nand_chip中重载的dev_ready方法,即
s3c2410_nand_devready()函数.

下面研究一下其中的细节:
/**
?* nand_write - [MTD Interface] compability function for nand_write_ecc
?* @mtd:??? MTD device structure
?* @to:??? ??? offset to write to
?* @len:??? number of bytes to write
?* @retlen:??? pointer to variable to store the number of written bytes
?* @buf:??? the data to write
?*
?* This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
?*
*/
static int nand_write (struct mtd_info *mtd,loff_t to,const u_char * buf)
{
??? return (nand_write_ecc (mtd,to,NULL));
}
注:
??? 以参数eccbuf、oobsel为NULL,调用nand_write_ecc函数。

/**
?* nand_write_ecc - [MTD Interface] NAND write with ECC
?* @mtd:??? MTD device structure
?* @to:??? ??? offset to write to
?* @len:??? number of bytes to write
?* @retlen:??? pointer to variable to store the number of written bytes
?* @buf:??? the data to write
?* @eccbuf:??? filesystem supplied oob data buffer
?* @oobsel:??? oob selection structure
?*
?* NAND write with ECC
?*/
static int nand_write_ecc (struct mtd_info *mtd,
??? ??? ???? size_t * retlen,const u_char * buf,u_char * eccbuf,struct nand_oobinfo *oobsel)
{
??? int startpage,ret = -EIO,written = 0,chipnr;
??? int autoplace = 0,numpages,totalpages;
??? struct nand_chip *this = mtd->priv;
??? u_char *oobbuf,*bufstart;
??? int??? ppblock = (1 << (this->phys_erase_shift - this->page_shift));//page/block

??? DEBUG (MTD_DEBUG_LEVEL3,"nand_write_ecc: to = 0x%08x,(unsigned int) to,(int) len);

??? /* Initialize retlen,in case of early exit */
??? *retlen = 0;

??? /* Do not allow write past end of device */
??? /* 超越nand flash容量的写操作是不允许的 */
??? if ((to + len) > mtd->size) {
??? ??? DEBUG (MTD_DEBUG_LEVEL0,"nand_write_ecc: Attempt to write past end of pagen");
??? ??? return -EINVAL;
??? }

??? /* reject writes,which are not page aligned */
??? /* 不按页对齐的写操作同样是不允许的 */ ?

??? if (NOTALIGNED (to) || NOTALIGNED(len)) {
??? ??? printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned datan");
??? ??? return -EINVAL;
??? }

??? /* Grab the lock and see if the device is available */
??? /* 获取设备的控制权 */
??? nand_get_device (this,FL_WRITING);

??? /* Calculate chipnr */
??? /*
???? * 存在多片flash的情况下,计算出所要写的是哪片flash?
???? * (当然,像我的板,只用一片nand flash,所以这个操作是不必要的)
???? */
??? chipnr = (int)(to >> this->chip_shift);

??? /* Select the NAND device */
??? /* 片选操作 */
??? this->select_chip(mtd,chipnr);

??? /* Check,if it is write protected */
??? /* 如果nand flash写保护,当然不能再写了 */
??? if (nand_check_wp(mtd))
??? ??? goto out;

??? /* if oobsel is NULL,use chip defaults */
??? if (oobsel == NULL)
??? ??? oobsel = &mtd->oobinfo;??? ???
??? ???
??? /* Autoplace of oob data ? Use the default placement scheme */
??? if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
??? ??? oobsel = this->autooob;
??? ??? autoplace = 1;
??? }???
??? if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
??? ??? autoplace = 1;

??? /* Setup variables and oob buffer */
??? totalpages = len >> this->page_shift;//计算所要读取的数据长度共有多少页
??? page = (int) (to >> this->page_shift);//计算数据所要写到的开始页码
??? /* Invalidate the page cache,if we write to the cached page */
??? /* 如果缓存保存的数据在我们要写数据的范围内,把缓存里的数据设置为不可用???? */
??? if (page <= this->pagebuf && this->pagebuf < (page + totalpages))?
??? ??? this->pagebuf = -1;
???
??? /* Set it relative to chip */
??? page &= this->pagemask;
??? startpage = page;
??? /* Calc number of pages we can write in one go */
??? numpages = min (ppblock - (startpage? & (ppblock - 1)),totalpages);//计算出本block中允许被写的页数
??? oobbuf = nand_prepare_oobbuf (mtd,eccbuf,oobsel,autoplace,numpages);//先不深入研究~_~
??? bufstart = (u_char *)buf;//获取所要写数据的地址

??? /* Loop until all data is written */
??? /* 循环进行写操作 */
??? while (written < len) {

??? ??? this->data_poi = (u_char*) &buf[written];//先把所要写的数据缓冲到data_poi下
??? ??? /* Write one page. If this is the last page to write
??? ??? ?* or the last page in this block,then use the
??? ??? ?* real pageprogram command,else select cached programming
??? ??? ?* if supported by the chip.
??? ???? * 如果这是所写数据的最后一个页或许这是所写block的最后一个页,调用nand flash的
??? ?????
???????? * pageprogram指令,真正把数据写入nand flash中(nand flash的最小擦除单元为block)

??? ??? ?*/
??? ??? ret = nand_write_page (mtd,this,&oobbuf[oob],(--numpages > 0));
??? ??? if (ret) {
??? ??? ??? DEBUG (MTD_DEBUG_LEVEL0,"nand_write_ecc: write_page failed %dn",ret);
??? ??? ??? goto out;
??? ??? }???
??? ??? /* Next oob page */
??? ??? oob += mtd->oobsize;
??? ??? /* Update written bytes count */
??? ??? /* 更新写入计数值 */
??? ??? written += mtd->oobblock;
??? ??? if (written == len)//写入完毕,退出
??? ??? ??? goto cmp;
??? ???
??? ??? /* Increment page address */
??? ??? page++;//下一页

??? ??? /* Have we hit a block boundary ? Then we have to verify and
??? ??? ?* if verify is ok,we have to setup the oob buffer for
??? ??? ?* the next pages.
??? ???? *
暂时不是很明白, 需要先搞明白nand_prepare_oobbuf函数的作用
??? ??? */
??? ??? if (!(page & (ppblock - 1))){
??? ??? ??? int ofs;
??? ??? ??? this->data_poi = bufstart;
//怀疑nand_verify_pages用到
??? ??? ??? ret = nand_verify_pages (mtd,startpage,
??? ??? ??? ??? page - startpage,
??? ??? ??? ??? oobbuf,(eccbuf != NULL));//一页写完,检查数据
??? ??? ??? if (ret) {
??? ??? ??? ??? DEBUG (MTD_DEBUG_LEVEL0,"nand_write_ecc: verify_pages failed %dn",ret);
??? ??? ??? ??? goto out;
??? ??? ??? }???
??? ??? ??? *retlen = written;

??? ??? ??? ofs = autoplace ? mtd->oobavail : mtd->oobsize;
??? ??? ??? if (eccbuf)
??? ??? ??? ??? eccbuf += (page - startpage) * ofs;
??? ??? ??? totalpages -= page - startpage;//更新需要写的页数
??? ??? ??? numpages = min (totalpages,ppblock);//更新可以写的页数
??? ??? ??? page &= this->pagemask;//更新页码
??? ??? ??? startpage = page;//更新开始页码
??? ??? ??? oobbuf = nand_prepare_oobbuf (mtd,
??? ??? ??? ??? ??? autoplace,numpages);
??? ??? ??? /* Check,if we cross a chip boundary */
??? ??? ??? if (!page) {
??? ??? ??? ??? chipnr++;
??? ??? ??? ??? this->select_chip(mtd,-1);
??? ??? ??? ??? this->select_chip(mtd,chipnr);
??? ??? ??? }
??? ??? }
??? }
??? /* Verify the remaining pages */
cmp:
??? this->data_poi = bufstart;//怀疑nand_verify_pages用到
???? ret = nand_verify_pages (mtd,totalpages,
??? ??? oobbuf,(eccbuf != NULL));
??? if (!ret)
??? ??? *retlen = written;
??? else???
??? ??? DEBUG (MTD_DEBUG_LEVEL0,ret);

out:
??? /* Deselect and wake up anyone waiting on the device */
??? nand_release_device(mtd);//放弃对设备的控制权

??? return ret;
}

/**
?* nand_write_page - [GENERIC] write one page
?* @mtd:??? MTD device structure
?* @this:??? NAND chip structure
?* @page: ??? startpage inside the chip,must be called with (page & this->pagemask)
?* @oob_buf:??? out of band data buffer
?* @oobsel:??? out of band selecttion structre
?* @cached:??? 1 = enable cached programming if supported by chip
?*
?* Nand_page_program function is used for write and writev !
?* This function will always program a full page of data
?* If you call it with a non page aligned buffer,you're lost :)
?*
?* Cached programming is not supported yet.
?*/
static int nand_write_page (struct mtd_info *mtd,struct nand_chip *this,int page,
??? u_char *oob_buf,? struct nand_oobinfo *oobsel,int cached)
{
??? int ??? i,status;
??? u_char??? ecc_code[32];
??? int??? eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
??? int? ??? *oob_config = oobsel->eccpos;
??? int??? datidx = 0,eccidx = 0,eccsteps = this->eccsteps;
??? int??? eccbytes = 0;
???
??? /* FIXME: Enable cached programming */
??? cached = 0;//在高版本的内核下找到这样的解释:
??? /*
??? ?* Cached progamming disabled for now,Not sure if its worth the
??? ?* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
??? ?*/
???
??? /* Send command to begin auto page programming */
??? /* 发送页编程指令 */
??? this->cmdfunc (mtd,NAND_CMD_SEQIN,page);

??? /* Write out complete page of data,take care of eccmode */
??? switch (eccmode) {
??? /* No ecc,write all */
??? case NAND_ECC_NONE:
??? ??? printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommendedn");
??? ??? this->write_buf(mtd,this->data_poi,mtd->oobblock);
??? ??? break;
??? ???
??? /* Software ecc 3/256,write all */
??? case NAND_ECC_SOFT:
??? ??? for (; eccsteps; eccsteps--) {
??? ??? ??? this->calculate_ecc(mtd,&this->data_poi[datidx],ecc_code);//计算出一页的ecc数据
??? ??? ??? for (i = 0; i < 3; i++,eccidx++)
??? ??? ??? ??? oob_buf[oob_config[eccidx]] = ecc_code[i];//存放到ecc_code数组中
??? ??? ??? datidx += this->eccsize;
??? ??? }
??? ??? this->write_buf(mtd,mtd->oobblock);//调用FLASH硬件驱动层进行写操作
??? ??? break;
??? default:
??? ??? eccbytes = this->eccbytes;
??? ??? for (; eccsteps; eccsteps--) {
??? ??? ??? /* enable hardware ecc logic for write */
??? ??? ??? this->enable_hwecc(mtd,NAND_ECC_WRITE);
??? ??? ??? this->write_buf(mtd,this->eccsize);
??? ??? ??? this->calculate_ecc(mtd,ecc_code);
??? ??? ??? for (i = 0; i < eccbytes; i++,eccidx++)
??? ??? ??? ??? oob_buf[oob_config[eccidx]] = ecc_code[i];
??? ??? ??? /* If the hardware ecc provides syndromes then
??? ??? ??? ?* the ecc code must be written immidiately after
??? ??? ??? ?* the data bytes (words) */
??? ??? ??? if (this->options & NAND_HWECC_SYNDROME)
??? ??? ??? ??? this->write_buf(mtd,ecc_code,eccbytes);
??? ??? ??? datidx += this->eccsize;
??? ??? }
??? ??? break;
??? }
??? ??? ??? ??? ??? ??? ??? ??? ??? ???
??? /* Write out OOB data */
??? if (this->options & NAND_HWECC_SYNDROME)
??? ??? this->write_buf(mtd,&oob_buf[oobsel->eccbytes],mtd->oobsize - oobsel->eccbytes);
??? else
??? ??? this->write_buf(mtd,oob_buf,mtd->oobsize);//写oob data,主要把上面计算的ecc值写进去

??? /* Send command to actually program the data */
??? this->cmdfunc (mtd,cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG,-1,-1);

??? if (!cached) {
??? ??? /* call wait ready function */
??? ??? status = this->waitfunc (mtd,FL_WRITING);//等待写入完成

??? ??? /* See if operation failed and additional status checks are available */
??? ??? if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
??? ??? ??? status = this->errstat(mtd,FL_WRITING,status,page);
??? ??? }

??? ??? /* See if device thinks it succeeded */
??? ??? if (status & NAND_STATUS_FAIL) {
??? ??? ??? DEBUG (MTD_DEBUG_LEVEL0,"%s: " "Failed write,page 0x%08x,",__FUNCTION__,page);
??? ??? ??? return -EIO;
??? ??? }
??? } else {
??? ??? /* FIXME: Implement cached programming ! */
??? ??? /* wait until cache is ready*/
??? ??? // status = this->waitfunc (mtd,FL_CACHEDRPG);//cached的写操作暂时没用 ??? } ??? return 0;??? }

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读