oob相关:
nand_chip->oob_poi 用来oob数据的buff,buff大小为mtd_info->oobsize
ecc相关:
ecc数据存放在以下两个地方,也就是以下两个地方可以找到。
1)存放在nand_chip->oob_poi数组中
存放在nand_chip->oob_poi数组中nand_chip->ecc.layout指定的地方,访问方式如下:
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
2)存放在nand_chip->buffers中
ecc数据长度在nand_chip->ecc->total中指定。
bbt相关:
建立BBT的过程,
管理BBT的数据区不能超过1个Block
1、一个新的nand flash在每个block的第1和第2个页面或最后一个页面的oob区域中存放了这个block的坏块标志,
那到底是存放在第1和第2个页面的oob区域还是存放在最后一个页面的oob区域中是由下面两个宏来标示的。
/* Search good / bad pattern on the first and the second page */
#define NAND_BBT_SCAN2NDPAGE 0x00008000
/* Search good / bad pattern on the last page of the eraseblock */
#define NAND_BBT_SCANLASTPAGE 0x00010000
2、当一个产品第一次开机时,kernel会遍历上面据说的每个block 的oob区域,生成bbt,保存在nand_chip->bbt 所指向的内存中,并将bbt写入到flash的某两个block中,(两个block是因为有一个是备份),写到哪两个block呢.可以写到这个flash可用的前两个block中,也可以写到这个flash的最后两个可用的block中.具体看td->options中有没有NAND_BBT_LASTBLOCK标示.如果有则表示是在最后一个可用的块中.下面代码是找存放bbt的那个block.
if (td->options & NAND_BBT_LASTBLOCK) {//如果定义了NAND_BBT_LASTBLOCK,就从后向前找
startblock = numblocks * (chip + 1) - 1;
dir = -1;
} else {//没有定义NAND_BBT_LASTBLOCK,就从前向后找.
startblock = chip * numblocks;
dir = 1;
}
子页只有SLC nand flash才可能会有。MLC的nand flash没有子页。
struct mtd_info {
u_char type;
uint32_t flags;
uint64_t size; // Total size of the MTD
/* "Major" erase size for the device. Na茂ve users may take this
* to be the only erase size available, or may use the more detailed
* information below if they desire
*/
uint32_t erasesize;
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
* it is of ECC block size, etc. It is illegal to have writesize = 0.
* Any driver registering a struct mtd_info must ensure a writesize of
* 1 or larger.
*/
uint32_t writesize;
/*
* Size of the write buffer used by the MTD. MTD devices having a write
* buffer can write multiple writesize chunks at a time. E.g. while
* writing 4 * writesize bytes to a device with 2 * writesize bytes
* buffer the MTD driver can (but doesn't have to) do 2 writesize
* operations, but not 4. Currently, all NANDs have writebufsize
* equivalent to writesize (NAND page size). Some NOR flashes do have
* writebufsize greater than writesize.
*/
uint32_t writebufsize;
uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
uint32_t oobavail; // Available OOB bytes per block
/*
* If erasesize is a power of 2 then the shift is stored in
* erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
*/
unsigned int erasesize_shift;
unsigned int writesize_shift;
/* Masks based on erasesize_shift and writesize_shift */
unsigned int erasesize_mask;
unsigned int writesize_mask;
/*
* read ops return -EUCLEAN if max number of bitflips corrected on any
* one region comprising an ecc step equals or exceeds this value.
* Settable by driver, else defaults to ecc_strength. User can override
* in sysfs. N.B. The meaning of the -EUCLEAN return code has changed;
* see Documentation/ABI/testing/sysfs-class-mtd for more detail.
*/
unsigned int bitflip_threshold;
// Kernel-only stuff starts here.
const char *name;
int index;
/* ECC layout structure pointer - read only! */
struct nand_ecclayout *ecclayout;
/* the ecc step size. */
unsigned int ecc_step_size;
/* max number of correctible bit errors per ecc step */
unsigned int ecc_strength;
/* Data for variable erase regions. If numeraseregions is zero,
* it means that the whole device has erasesize as given above.
*/
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
/*
* Do not call via these pointers, use corresponding mtd_*()
* wrappers instead.
*/
int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);
int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags);
//读取数据,长度随意。
//如果不满1页,函数内部会找到数据所在的那一页,并读出完整的1页数据,并将长度的数据copy到buf中
//如果len长度大于1页,则会连续读多个页。
//在这个函数里都会去读取oob数据,然后进行ecc校验。
int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf);
//可以只读Data区的数据(这时候与mtd_info->_read函数功能相同),
//也可以只读oob区的数据,还可以两者都读出来。
int (*_read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int (*_write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf);
int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf);
int (*_get_user_prot_info) (struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf);
int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf);
int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
size_t len, size_t *retlen, u_char *buf);
int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
size_t len);
int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen);
void (*_sync) (struct mtd_info *mtd);
int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);
int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
int (*_suspend) (struct mtd_info *mtd);
void (*_resume) (struct mtd_info *mtd);
void (*_reboot) (struct mtd_info *mtd);
/*
* If the driver is something smart, like UBI, it may need to maintain
* its own reference counting. The below functions are only for driver.
*/
int (*_get_device) (struct mtd_info *mtd);
void (*_put_device) (struct mtd_info *mtd);
/* Backing device capabilities for this device
* - provides mmap capabilities
*/
struct backing_dev_info *backing_dev_info;
struct notifier_block reboot_notifier; /* default mode before reboot */
//ecc_stats这个名字其实不太准确,看名字它是用来表示ecc状态的,但实际上它有两个用途,
//一个是用来表示ecc状态(corrected,与failed),
//另一个是用来表示坏块的情况(badblocks与bbtblocks).mtd_ecc_stats结构体如下:
//struct mtd_ecc_stats {
// __u32 corrected;--表示此flash 在ecc校验时,发现bit反转,但已经被修正的bit数量.
// __u32 failed;--表示此flash 在ecc校验时,发现bit反转,但无法被修正的bit数量.
// __u32 badblocks;--表示此flash 坏块的数量.
// __u32 bbtblocks;--表示此flash 保留块的数量.(保留块应该是用别的用途吧)
//};
/* ECC status information */
struct mtd_ecc_stats ecc_stats;
//subpage_sft表示这个nand flash 有多少个子页的shift形式表示,
//如果有2个子页,subpage_sft=1,4个子页则subpage_sft=2。
//subpage_sft是在nand_scan_tail函数中赋值的。如下:
//if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
// switch (ecc->steps) {
// case 2:
// mtd->subpage_sft = 1;
// break;
// case 4:
// case 8:
// case 16:
// mtd->subpage_sft = 2;
// break;
// }
//}
/* Subpage shift (NAND) */
int subpage_sft;
void *priv;
struct module *owner;
struct device dev;
int usecount;
};
struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
//以下这此都是最小粒度的与flash 控制寄存器打交道的函数。
uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
u8 *id_data);
int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
int page_addr);
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
int (*erase)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
//这个函数很奇怪,整个kernel代码中都没有为这个函数指针赋值。
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
int status, int page);
//这个write_page函数一般指向nand_write_page函数,而此函数又会调用
//nand_ecc_ctrl.write_page_raw函数,而nand_ecc_ctrl.
//write_page_raw函数又会调到nand_chip->write_buf函数。如下:
//if (unlikely(raw))
// status = chip->ecc.write_page_raw(mtd, chip, buf,oob_required);
//else if (subpage)
// status = chip->ecc.write_subpage(mtd, chip, offset, data_len,buf, oob_required);
//else
// status = chip->ecc.write_page(mtd, chip, buf, oob_required);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw);
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para);
//当读取数据核验失败时(,也就是读数据时发生了不可修正的bit反转时)
//并且重试次数小于read_retries次数时,可重新读取,但在重新读取之前需要调用此函数。
//如果想要某个nand flash 不支持重试,则让setup_read_retry函数指针等于NULL即可。
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
int chip_delay;
unsigned int options;
unsigned int bbt_options;
//这个也很简单
int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
//表示这个nand flash有多少个chip
int numchips;
//表示一个chip有多少个字节.mtd->size=nand_chip->chipsize*nand_chip->numchips
uint64_t chipsize;
//页掩码,比如一个页大小是2K,则pagemask=0x3ff
int pagemask;
//nand_chip->buffers中存放的buff数据是哪一页的buff。
int pagebuf;
//在读取一个page或subpage时,执行的几个step读取中被修正的bit最多的一次修正了多少个bit。
unsigned int pagebuf_bitflips;
//一个子页的大小,如512 Byte
int subpagesize;
//此变量表明一个cell存放几个bit,在nand flash中数据都是存放在cell中的,
//对于SLC型flash一般一个cell存放一个bit,而对于MLC则有可能存放2个或4个bit。
uint8_t bits_per_cell;
uint16_t ecc_strength_ds;
uint16_t ecc_step_ds;
int onfi_timing_mode_default;
//在每个block的第1和第2个页面或最后一个页面的oob区域中存放了这个block的坏块标志,
//但具体这个坏块标志是在oob区中的哪个偏移量开始的,是由badblockpos指定的。
//见nand_create_badblock_pattern函数中以下这句:
//bd->offs = this->badblockpos;
int badblockpos;
int badblockbits;
int onfi_version;
int jedec_version;
union {
struct nand_onfi_params onfi_params;
struct nand_jedec_params jedec_params;
};
//当读取数据核验失败时(,也就是读数据时发生了不可修正的bit反转时)重试的次数。
int read_retries;
//指明nand flash当前的操作状态,如正在读(FL_READING)正在写(FL_WRITING),
//正在擦除(FL_ERASING)等,一般最好通过nand_get_device函数来设置flash状态。
flstate_t state;
//oob_poi 用来oob数据的buff,buff大小为mtd_info.oobsize
uint8_t *oob_poi;
struct nand_hw_control *controller;
// nand flash ecc相关数据与操作。
struct nand_ecc_ctrl ecc;
//nand flash 一个页面的data 区的buff,struct nand_buffers
//中带有此buff的读出来的ecc与计算出来的ecc,见下文介绍
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
//bbt用来指向内存中坏块表的
uint8_t *bbt;
//linux 会将flash 坏块表bbt存放在flash的某个block中,这样开机时不用每次遍历一
//遍所有的block的oob区去建立bbt了。而只需要读入这一个block数据,进行解析就可以得到bbt。
//为了保险起见,坏块表bbt在flash上还存放了两份,这样就算一份坏了,还要以使用另一份。
//bbt_td就是用来指向正常情况下读取使用的bbt所在的block的,而bbt_md是用来指向备份的那个block
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
//当第一次使用一个flash时,flash上还没有哪个block是存放bbt信息的,
//这时需要遍历所有的block的oob区去建立bbt,如何遍历呢,就是根据badblock_pattern这个
//模式去查找oob区中指定的位置是否存在与badblock_pattern->pattern相匹配的字节,
//匹配的字节长度为badblock_pattern->len,(在nand_bbt.c文件中
//nand_create_badblock_pattern函数为默认实现,pattern为FF FF,len为两个字节)
struct nand_bbt_descr *badblock_pattern;
void *priv;
};
struct nand_ecc_ctrl {
//ecc计算模式,有几模式,如硬件ecc,软件ecc
nand_ecc_modes_t mode;
//nand flash一页数据中,有多少个ecc核验段,因为一个page可能有2K,但一次ecc校验只能管理256或
//512Byte,steps=page size/nand_ecc_ctrl->size
int steps;
//一次ecc对应data区的多少byte
int size;
//一次ecc对应oob区的多少byte
int bytes;
//nand中一页data数据对应多少个ecc数据。total=bytes*steps
int total;
//在一次ecc中最大可以修正的bit数量,一般是1,当一个bit出错时,可以修正。
int strength;
int prepad;
//ecc数据在oob区的具体存放位置。见网上另一篇文章,链接如下:
//http://blog.csdn.net/lanmanck/article/details/5813361
struct nand_ecclayout *layout;
void *priv;
//用来控制ecc相关的寄存器操作,如
//NAND_ECC_WRITE是用来将ecc写入ecc寄存器,主要用在数据写入时使用。
//NAND_ECC_READ是用来reset ecc寄存器,方便接下来读数据时,硬件从新开始计算ecc,
//并填充寄存器,如下面的使用例子。
//for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
// chip->ecc.hwctl(mtd, NAND_ECC_READ);
// chip->read_buf(mtd, p, eccsize);
// chip->ecc.calculate(mtd, p, &ecc_calc[i]);
//}
void (*hwctl)(struct mtd_info *mtd, int mode);
//计算ecc值,dat是要计算的数据,ecc_code是存放计算后的结果,
//当读取一个页数据时要调用nand_ecc_ctrl->steps次此函数才能计算完一页的ecc出来。
//另外对于带硬件ecc功能的nand flash 控制器而言这个函数就是读ecc 的寄存器,见hwctl函数上的例子。
//而且对应硬件ecc来说,必须每读一个step后立刻去读ecc寄存器。
//对于软件ecc,则可以先把一页数据都读出来后,再针对每一个step长度的数据一一计算ecc。
int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code);
//根据计算出来的ecc与读出来的ecc修正dat中的数据反转位,返回值表示修正了多少个bit。
int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
uint8_t *calc_ecc);
//读取一页最原始的数据,不进行任何核验和处理,见下面的nand_read_page_raw函数,
//此函数在nand_base.c文件实现,当nand flash驱动没有实现read_page_raw函数时,
//将会默认设置为这个函数。
//static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
// uint8_t *buf, int oob_required, int page)
//{
// chip->read_buf(mtd, buf, mtd->writesize);
// if (oob_required)
// chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
// return 0;
//}
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required);
//读取一页最原始的数据,带核验与修正bit功能。返回值表示在读此页的过程中某个step读取过程中修正的bit最多的一次修正了几个bit。
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required);
//读一个子页,有些nand flash支持子页的功能,一个页可以分为几个子页,一般比如一个512Byte的页可以分为2个256Byte的子页。此函数除了读的是而不是页外,其它功能与nand_ecc_ctrl->read_page功能是相同的,也带有核验与修正bit功能。
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf, int page);
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, uint32_t data_len,
const uint8_t *data_buf, int oob_required);
//一般情况下,与nand_ecc_ctrl->read_oob函数功能相同,
//这一点可以从nand_base.c文件中nand_scan_tail函数中以下代码看出
// if (!ecc->read_oob_raw)
// ecc->read_oob_raw = ecc->read_oob;
//if (!ecc->write_oob_raw)
// ecc->write_oob_raw = ecc->write_oob;
//即然是一般情况下与nand_ecc_ctrl->read_oob函数功能相同,那什么时候不同呢,
//我想应该是在当nand_ecc_ctrl->read_oob在读取oob数据时提供核验功能或修正功能时,
//这个功能只负责读取不做其它的事情。
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
int page);
int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
int page);
//只读取oob区的数据,不读data区数据。
int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page);
int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
int page);
};
struct nand_buffers表示nand flash 一个页面的data 区的buff,带有此buff的读出来的ecc与计算出来的ecc,ecccalc表示计算出来的ecc,ecccode表示从oob区读出来的ecc
struct nand_buffers {
uint8_t *ecccalc;
uint8_t *ecccode;
uint8_t *databuf;
};
感觉nand_bbt_descr是一个神奇的结构体,它有两个用途,先说一下它用在哪个地方吧,它在3个地方出现了,而且是在同一个结构体中出现3个nand_bbt_descr指针,那就是nand_chip。见下:
struct nand_chip {
....
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
....
}
bbt_td与bbt_md用来搜索与存放bbt在哪个blocak
badblock_pattern用来搜索oob区域中的坏块标志
struct nand_bbt_descr {
int options;
int pages[NAND_MAX_CHIPS];
int offs;
int veroffs;
uint8_t version[NAND_MAX_CHIPS];
int len;
int maxblocks;
int reserved_block_code;
uint8_t *pattern;
};