目录
字符节点分层调用流程
open
擦除
读
写
示例:nanddump -p -c -l 0x800 /dev/mtd8
open(“/dev/mtd8”...) = 3 mtdchar_open(struct inode *inode, struct file *file) //mtdchar.c int minor = iminor(inode); int devnum = minor >> 1; struct mtd_info *mtd; struct mtd_file_info *mfi;
mtd = get_mtd_device(NULL, devnum); struct mtd_info *ret = NULL ret = idr_find(&mtd_idr, num); __get_mtd_device(ret); return ret;
mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); mfi->mtd = mtd; file->private_data = mfi;
ioctl
ioctl(3, MIXER_READ(18) or ECCGETSTATS, ...) struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd;
检查坏块:MEMSETBADBLOCK 擦除:MEMERASE、MEMERASE64
mtd_erase(mtd, erase); //mtdcore.c mtd->_erase(mtd, instr) //nand_base.c=>nand_scan_tail中指定的 nand_erase(mtd, instr); //nand_base.c nand_erase_nand(mtd, instr, 0) //nand_base.c chip->select_chip(mtd, chipnr); //控制器驱动或nand_base.c=>nand_scan_ident中指定 nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, allowbbt) //若是坏块,则不擦除 if (!chip->bbt) //否则 return nand_isbad_bbt(mtd, ofs, allowbbt); nand_bbt.c chip->block_bad(mtd, ofs); //nand_base.c=> nand_scan_ident=> nand_set_defaults nand_block_bad(mtd, ofs) //nand_base.c 如果(chip->bbt_options & NAND_BBT_SCANLASTPAGE) ofs += mtd->erasesize - mtd->writesize; chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); bad = chip->read_byte(mtd); res = bad != 0xFF; 如果(chip->bbt_options & NAND_BBT_SCAN2NDPAGE) 会再读下一页的bb数据 return res; chip->erase(mtd, page & chip->pagemask) //nand_base.c=> nand_get_flash_type中指定 single_erase(mtd, page & chip->pagemask) //控制器驱动或nand_base.c=>nand_scan_ident中指定 chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); 发送页地址 chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
read(3, ...) //应用层
mtdchar_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) //mtdchar.c //一般走switch的默认路径 struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; ret = mtd_read(mtd, *ppos, len, &retlen, kbuf); //mtdcore.c ret_code = mtd->_read(mtd, from, len, retlen, buf); //mtdpart.c=> allocate_partition指定 part_read(mtd, from, len, retlen, buf); //mtdpart.c stats = part->master->ecc_stats; res = part->master->_read(part->master, from + part->offset, len, retlen, buf); //nand_base.c=>nand_scan_tail指定 nand_read(mtd, from, len, retlen, buf) //nand_base.c ops.mode = MTD_OPS_PLACE_OOB; nand_do_read_ops(mtd, from, &ops); //nand_base.c uint8_t *oob; oob = ops->oobbuf; ecc_failures = mtd->ecc_stats.failed; chip->select_chip(mtd, chipnr); //控制器驱动或nand_base.c=>nand_scan_ident => nand_set_defaults chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); //控制器驱动或nand_base.c=>nand_scan_ident => nand_set_defaults 发送地址、读命令 如果ops->mode是MTD_OPS_RAW ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, oob_required, page); //nand_base.c=>nand_scan_tail函数中设置 若没对齐 且 chip->options有NAND_SUBPAGE_READ 且 ops->oobbuf是NULL ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi, page); 其他 //一般走此分支 ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); //nand_base.c=> nand_scan_tail指定 nand_read_page_raw(mtd, chip, bufpoi, oob_required, page) //nand_base.c //此函数返回值一定为0 chip->read_buf(mtd, buf, mtd->writesize); //控制器驱动或nand_base.c=>nand_scan_ident => //nand_set_defaults指定 只是把host->buf数据拷贝到buf hisi_fmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) 如果读失败了:mtd->ecc_stats.failed++; 设置ECC纠正的位数:mtd->ecc_stats.corrected += ..
max_bitflips = max_t(unsigned int, max_bitflips, ret); if(oob) oob = nand_transfer_oob(mtd, oob, ops, toread); //如果ecc出错了,重传 if (mtd->ecc_stats.failed - ecc_failures) //如果还有剩余尝试次数,重传 if (retry_mode + 1 < chip->read_retries){ mtd->ecc_stats.failed = ecc_failures; goto read_retry; }else{ ecc_fail = true; } } if (ret < 0) return ret;
if (ecc_fail) return -EBADMSG;
return max_bitflips; if ((mtd_is_eccerr(res))) mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; else mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; 若ret_code < 0,直接返回 若mtd->ecc_strength == 0,直接返回0 若ret_code大于mtd->bitflip_threshold 返回-EUCLEAN 否则 返回0 如果读出错了(ret != 0) ret = mtd_is_bitflip_or_eccerr(ret) //mtd.h return mtd_is_bitflip(err) || mtd_is_eccerr(err) 如果err是-EUCLEAN,是位反转(但被校正过来了);如果err是-EBADMSG,是ECC错误 如果ret != 0,拷贝数据给用户
write(3, ...) mtdchar_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) //mtdchar.c //默认走default流程 mtd_write(mtd, *ppos, len, &retlen, kbuf); //mtdcore.c mtd->_write(mtd, to, len, retlen, buf); //nand_base.c=>nand_scan_tail中指定的 nand_write(mtd, to, len, retlen, buf); //nand_base.c ops.mode = MTD_OPS_PLACE_OOB; nand_do_write_ops(mtd, to, &ops); //nand_base.c chip->select_chip(mtd, chipnr); //控制器驱动或nand_base.c=>nand_scan_ident => nand_set_defaults中指定 chip->write_page(mtd, chip, column, ..., (ops->mode == MTD_OPS_RAW)) //控制器驱动或nand_base.c=>nand_scan_tail指定。的) nand_write_page //nand_base.c chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); 设置写的地址 如果是raw,走chip->ecc.write_page_raw; 如果支持subpage,走chip->ecc.write_subpage; 其他:(一般走此分支) chip->ecc.write_page(mtd, chip, buf, oob_required, page); //nand_base.c=>nand_scan_tail指定 nand_write_page_raw(mtd, chip, buf, oob_required, page); //nand_base.c chip->write_buf(mtd, buf, mtd->writesize); //控制器驱动或nand_base.c=>nand_scan_ident => nand_set_defaults 只是将数据复制到host->buffer chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); //控制器驱动或nand_base.c=>nand_scan_ident => nand_set_defaults指定 执行烧写命令
