MTD原始设备与FLASH硬件驱动的对话 (二)

来源:互联网 发布:通用的顶级域名 编辑:程序博客网 时间:2024/05/20 07:14
上一个贴由下到上的介绍了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, 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存储器写数据。

引用自<<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()函数中,主要完成如下几项工作:
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;   
}
 
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 摩托车证扣12分怎么办 初中孩孑想扩展单词量怎么办 恒安保险倒闭了怎么办 小麦收割机卸粮筒总是转怎么办 非牛顿体结块了怎么办 非牛顿流体硬了怎么办 玩具枪子弹打到眼睛怎么办 杭州磨床7130噪音大怎么办 步兵太苦还有一年怎么办 检法事业编怎么办最新 河北省事业单位改企编制怎么办 梦飞去虎牙锐雯怎么办 蚂蚁借呗还款钱没到账怎么办 蚂蚁借呗没有按时还款怎么办 新装的win7没有网卡驱动怎么办 3d电影没眼睛怎么办 看vr手机发烫变卡怎么办 vr眼镜头晕想吐怎么办 程序员年龄大了失业怎么办 事业人员编制在机关任职改革怎么办 老公出轨死不承认怎么办 2017年军自考毕业证怎么办 大腿酸痛肌肉萎缩怎么办一 习惯不了军训一起洗澡怎么办 脚着凉了有点痛怎么办 腿着凉了膝盖疼怎么办 下雨了脚着凉了怎么办 军训中被教官训怎么办 命根子根部长豆豆很痒怎么办? 屁股坏了化脓了怎么办 貔貅屁股摔坏了怎么办 当兵身高差4厘米怎么办 查体戴眼镜视力达不到怎么办 公司福利体检查乙肝怎么办 有乙肝每年福利体检怎么办 国企有乙肝每年福利体检怎么办 单位福利体检查乙肝怎么办 事业单位每年福利体检有乙肝怎么办 高考失利后我该怎么办 初三了科学总是考不好怎么办 初三学生三模考的不好怎么办