灭顶之灾手机在线观看:MTD原始设备与FLASH硬件驱动的对话-续
来源:百度文库 编辑:中财网 时间:2024/05/21 22:31:12
上一个贴由下到上的介绍了FLASH硬件驱动是如何与MTD原始设备建立联系的,现在再由上到下的研究一下是如何通过MTD原始设备来访问FLASH硬件驱动的。
首先分析一下如何通过MTD原始设备进而通过FLASH硬件驱动来读取FLASH存储器的数据。
引用自<>一文:
"读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, loff_t from, size_t len,
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 = %i\n", (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 device\n");
*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 recommended\n");
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, data_poi, 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, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
this->enable_hwecc(mtd, NAND_ECC_READ);
this->read_buf(mtd, &data_poi[datidx], 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, &data_poi[datidx], &oob_data[i], &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 %d\n", page, chipnr);
ecc_failed++;
}
} else {
this->calculate_ecc(mtd, &data_poi[datidx], &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, &data_poi[datidx], &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%08x\n", 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存储器写数据。
引用自<>一文:
写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()函数中,主要完成如下几项工作:
1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即
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, size_t len, size_t * retlen, const u_char * buf)
{
return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, 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, loff_t to, size_t len,
size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
{
int startpage, page, ret = -EIO, oob = 0, 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, len = %i\n", (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 page\n");
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 data\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
/* 获取设备的控制权 */
nand_get_device (this, mtd, 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, page, &oobbuf[oob], oobsel, (--numpages > 0));
if (ret) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", 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, this, startpage,
page - startpage,
oobbuf, oobsel, chipnr, (eccbuf != NULL));//一页写完,检查数据
if (ret) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", 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, eccbuf, oobsel,
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, this, startpage, totalpages,
oobbuf, oobsel, chipnr, (eccbuf != NULL));
if (!ret)
*retlen = written;
else
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", 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, 0x00, 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 recommended\n");
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, this->data_poi, 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->data_poi[datidx], this->eccsize);
this->calculate_ecc(mtd, &this->data_poi[datidx], 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, this, FL_WRITING);//等待写入完成
/* See if operation failed and additional status checks are available */
if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
status = this->errstat(mtd, this, 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, this, FL_CACHEDRPG);//cached的写操作暂时没用
}
return 0;
}
首先分析一下如何通过MTD原始设备进而通过FLASH硬件驱动来读取FLASH存储器的数据。
引用自<
"读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, loff_t from, size_t len,
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 = %i\n", (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 device\n");
*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 recommended\n");
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, data_poi, 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, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
this->enable_hwecc(mtd, NAND_ECC_READ);
this->read_buf(mtd, &data_poi[datidx], 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, &data_poi[datidx], &oob_data[i], &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 %d\n", page, chipnr);
ecc_failed++;
}
} else {
this->calculate_ecc(mtd, &data_poi[datidx], &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, &data_poi[datidx], &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%08x\n", 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存储器写数据。
引用自<
写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()函数中,主要完成如下几项工作:
1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即
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, size_t len, size_t * retlen, const u_char * buf)
{
return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, 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, loff_t to, size_t len,
size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
{
int startpage, page, ret = -EIO, oob = 0, 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, len = %i\n", (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 page\n");
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 data\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
/* 获取设备的控制权 */
nand_get_device (this, mtd, 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, page, &oobbuf[oob], oobsel, (--numpages > 0));
if (ret) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", 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, this, startpage,
page - startpage,
oobbuf, oobsel, chipnr, (eccbuf != NULL));//一页写完,检查数据
if (ret) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", 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, eccbuf, oobsel,
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, this, startpage, totalpages,
oobbuf, oobsel, chipnr, (eccbuf != NULL));
if (!ret)
*retlen = written;
else
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", 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, 0x00, 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 recommended\n");
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, this->data_poi, 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->data_poi[datidx], this->eccsize);
this->calculate_ecc(mtd, &this->data_poi[datidx], 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, this, FL_WRITING);//等待写入完成
/* See if operation failed and additional status checks are available */
if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
status = this->errstat(mtd, this, 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, this, FL_CACHEDRPG);//cached的写操作暂时没用
}
return 0;
}
笔记本PCMCIA槽出现无法识别的MTD-0002硬件!!
到哪里去下载MTD 803 的网卡驱动?
XP 下怎样安装MTD 803的驱动?
有没有不需要驱动的硬件设备
pcmcia mtd 0002 驱动找不到
什么是签名的硬件设备驱动,它和未签名的硬件设备驱动程序有什么区别?
制作flash需要那些硬件设备?
如何把“与上帝的对话”flash里的英语改编为汉语放到flash里?
与奥运健儿的对话
与鲁迅的对话
为什么我的摄象头用了摄象头万能驱动还是不能用,摄象头插好了,硬件设备里显示未知的设备
"没有找到可用于播放的硬件设备,请重装声卡驱动"
设备管理器哪个是硬件大小和驱动速度的显示?????????????
设备管理器进去没有硬件显示,而且显卡的驱动安装总是提示数据错误
susv的声卡驱动装上去但是该硬件设备不能用
让驱动返回原始的软件
最原始的显卡驱动mx400
原始的驱动盘找不到了怎么办
关于硬件的驱动
李君羡与武媚娘的对话
生与死的对话
与大自然的对话 作文
枭与鸠的对话
蓬与麻的对话