Linux内核在MTD的下层实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c)因此芯片级的驱动实现不再需要我们关心mtd中的那些成员函数了主题转移到nand_chip数据结构中
先了解了解nand_chip结构体
struct nand_chip {
void __iomem *IO_ADDR_R; //读8位I/O线的地址
void __iomem *IO_ADDR_W; //写8位I/O线的地址
uint8_t (*read_byte)(struct mtd_info *mtd);//从芯片读一个字节
u16 (*read_word)(struct mtd_info *mtd);//从芯片读一个字
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);//将芯片中的数据独到缓冲区中
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); //验证芯片和写入缓冲区中的数据
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);//检查是否坏块
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);//标记坏块
void (*select_chip)(struct mtd_info *mtd, int chip); //实现选中芯片
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl);//控制ALE/CLE/nCE,也用于写命令和地址
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);
void (*erase_cmd)(struct mtd_info *mtd, int page);//擦除命令的处理
int (*scan_bbt)(struct mtd_info *mtd);//扫描坏块
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw);//写一页
};
这上面有一个与ECC相关的结构体 struct nand_ecc_ctrl
struct nand_ecc_ctrl {
nand_ecc_modes_t mode;
int steps;
int size;
int bytes;
int total;
int prepad;
int postpad;
struct nand_ecclayout *layout;
void (*hwctl)(struct mtd_info *mtd, int mode);/**控制硬件ECC*/
int (*calculate)(struct mtd_info *mtd,
const uint8_t *dat,
uint8_t *ecc_code);/**根据data计算ecc值**/
int (*correct)(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc,
uint8_t *calc_ecc);
int (*read_page_raw)(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int page);/**向NANDFLASH芯片读一个页的原始数据*/
void (*write_page_raw)(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf);
int (*read_page)(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int page);/**读一个页但包含ecc校验*/
int (*read_subpage)(struct mtd_info *mtd,
struct nand_chip *chip,
uint32_t offs, uint32_t len,
uint8_t *buf);
void (*write_page)(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf);
int (*read_oob)(struct mtd_info *mtd,
struct nand_chip *chip,
int page,
int sndcmd);/**读OOB但不包含MAIN部分**/
int (*write_oob)(struct mtd_info *mtd,
struct nand_chip *chip,
int page);
};
下面这个结构体用来ECC在oob中布局的一个结构体。
struct nand_ecclayout {
__u32 eccbytes;//ecc字节数,对于512字节/page的NANDflash,eccbytes=3,如果需要额外用到oob中的数据,那么也可以大于3.
__u32 eccpos[64];ECC数据在oob中的位置,这里之所以是个64字节的数组,是因为对于2K/页的NAND来说,它的oob有64个字节。而对于512字节/page的NAND来说,可以而且只可以定义它的前16个字节。
__u32 oobavail;OOB中可用的字节数,不需要对该成员赋值,MTD会根据其他三个成员计算出来
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];//显示定义空闲的OOB字节
};
大家都知道OOB但是OOB里面究竟存了些什么,OOB主要用来存储两种信息:坏块信息和ECC数据,对于小页的NANDFLASH一般坏块占据一个字节(并且是在第6个字节),ECC占3个字节,上面这个结构体就是起到了这个作用告诉那些与操作ECC无关的函数,在OOB区域里那部分是来存储ECC的(不可他用),那些字节是空闲的。