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;??? } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |