2017年11月13日15:37:34
最近公司大裁员,闹的人心惶惶,不管怎么样,武装好自己才是硬道理,坚持学习,学会那些还没学会的。
今天虚拟机突然打不开了,吓了我一跳,因为代码都还没备份,一定得养成备份代码的习惯!
好了,下面开始进入正题吧,nandflash驱动彻底分析
底层驱动移植完后,执行nand 命令:nand read 0x30000000 0 0x2000
nand read 的命令格式是 nand read addr off | partition size ,总结起来就是 读到哪去? 从哪读? 读多大?
返回的结果是:
NAND read: device 0 offset 0x0, size 0x2000
file is nand_util.c,fun is nand_read_skip_bad,line is 599,NAND read from offset 2000 failed -74
0 bytes read: ERROR
读失败,给出读失败错误码 -74
要分析失败原因,先分析函数执行流程
执行nand read 命令后,其实是执行了nand_read_skip_bad(nand, off, &size,(u_char *)addr);
跳过坏块读函数的参数简单明了,从哪读,读到哪去,读多少,以及一个公共句柄(包含nand的信息,例如有多少个块,块大小等)
1 int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, 2 u_char *buffer) 3 { 4 int rval; 5 size_t left_to_read = *length; 6 size_t len_incl_bad; 7 u_char *p_buffer = buffer; 8 9 len_incl_bad = get_len_incl_bad (nand, offset, *length); /* 函数分析见2017.11.14笔记(往下翻) */ 10 11 if ((offset + len_incl_bad) > nand->size) { 12 printf ("Attempt to read outside the flash arean"); 13 return -EINVAL; 14 } 15 16 if (len_incl_bad == *length) { 17 rval = nand_read (nand, offset, length, buffer); 18 if (!rval || rval == -EUCLEAN) 19 return 0; 20 printf ("NAND read from offset %llx failed %dn", 21 offset, rval); 22 return rval; 23 } 24 25 while (left_to_read > 0) { 26 size_t block_offset = offset & (nand->erasesize - 1); 27 size_t read_length; 28 29 WATCHDOG_RESET (); 30 31 if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { 32 printf ("Skipping bad block 0x%08llxn", 33 offset & ~(nand->erasesize - 1)); 34 offset += nand->erasesize - block_offset; 35 continue; 36 } 37 38 if (left_to_read < (nand->erasesize - block_offset)) 39 read_length = left_to_read; 40 else 41 read_length = nand->erasesize - block_offset; 42 /* read_length 最大不会超过 nand->erasesize (128K) */ 43 /* nand_read 函数是一个按块读函数 */ 44 rval = nand_read (nand, offset, &read_length, p_buffer); 45 if (rval && rval != -EUCLEAN) { 46 printf ("NAND read from offset %llx failed %dn", 47 offset, rval); 48 *length -= left_to_read; 49 return rval; 50 } 51 52 left_to_read -= read_length; 53 offset += read_length; 54 p_buffer += read_length; 55 } 56 57 return 0; 58 }
先看get_len_incl_bad
1 static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset, 2 const size_t length) 3 { 4 size_t len_incl_bad = 0; 5 size_t len_excl_bad = 0; 6 size_t block_len; 7 8 while (len_excl_bad < length) { 9 block_len = nand->erasesize - (offset & (nand->erasesize - 1)); 10 11 if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) 12 len_excl_bad += block_len; 13 14 len_incl_bad += block_len; 15 offset += block_len; 16 17 if (offset >= nand->size) 18 break; 19 } 20 21 return len_incl_bad; 22 }
想要看懂这个函数,先要理解offset的构成
2017年11月14日10:00:40
每天进步一点点
nand->erasesize = 128K = 0x20000
nand->erasesize - 1 = 0x1FFFF
offset & (nand->erasesize - 1) 保留低17位,清高位,因为nand的特性,所以是按块来统计长度
block_len = nand->erasesize - offset & (nand->erasesize - 1) 作用是统计当前块要读的长度
然后判断当前块是不是坏块,如果不是坏块,则让len_excl_bad += block_len,判断是否循环的标准是len_excl_bad < length
不论当前块是不是坏块,都让len_incl_bad += block_len,len_incl_bad 得到的是包含坏块的长度
总结get_len_incl_bad函数的作用为:
①如果偏移offset是从整块开始的,返回的结果是块的整数倍(不用看length,比如length是0x20001,则读两块)
②如果偏移offset不是从整块开始的,返回的结果是块的整数倍+第一块要读的长度
总之,返回的结果是按块补齐的
再继续分析nand_read_skip_bad
1 if (len_incl_bad == *length) { 2 rval = nand_read (nand, offset, length, buffer); 3 if (!rval || rval == -EUCLEAN) 4 return 0; 5 6 return rval; 7 }
什么情况下,len_incl_bad == *length ?
要读的内容里,没有坏块,且长度最后按块对齐(例如,从0x400开始读,读的长度为0x40000-0x400)
如果不满足,则进入while循环读,在while里,按块读,先判断当前块是不是坏块,如果是则跳过,不是则读
下面分析nand_read函数
1 /* 1.从哪读 2.读多少 3.读到哪去 */ 2 static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, 3 size_t *retlen, uint8_t *buf) 4 { 5 struct nand_chip *chip = mtd->priv; 6 int ret; 7 8 /* Do not allow reads past end of device */ 9 if ((from + len) > mtd->size) 10 return -EINVAL; 11 if (!len) 12 return 0; 13 /* 选中芯片 */ 14 nand_get_device(chip, mtd, FL_READING); 15 16 chip->ops.len = len; 17 chip->ops.datbuf = buf; 18 chip->ops.oobbuf = NULL; 19 20 ret = nand_do_read_ops(mtd, from, &chip->ops); 21 22 *retlen = chip->ops.retlen; 23 /* 取消选中芯片 */ 24 nand_release_device(mtd); 25 26 return ret; 27 }
其实真正的读函数是nand_do_read_ops,从哪去,读多少,读到哪去都被装载在chip->ops结构体中
再看nand_do_read_ops函数
1 /** 2 * nand_do_read_ops - [Internal] Read data with ECC 3 * 4 * @mtd: MTD device structure 5 * @from: offset to read from 6 * @ops: oob ops structure 7 * 8 * Internal function. Called with chip held. 9 */ 10 static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, 11 struct mtd_oob_ops *ops) 12 { 13 int chipnr, page, realpage, col, bytes, aligned; 14 struct nand_chip *chip = mtd->priv; 15 struct mtd_ecc_stats stats; 16 int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; 17 int sndcmd = 1; 18 int ret = 0; 19 uint32_t readlen = ops->len;//128K 0x20000 根据输入的长度决定 20 uint32_t oobreadlen = ops->ooblen; // oobreadlen = 0 21 uint8_t *bufpoi, *oob, *buf; 22 23 stats = mtd->ecc_stats; 24 25 chipnr = (int)(from >> chip->chip_shift); 26 chip->select_chip(mtd, chipnr); 27 /* realpage 总页地址(高17位) */ 28 realpage = (int)(from >> chip->page_shift);//pageshift is 11 29 page = realpage & chip->pagemask;//pagemask = 1ffff 30 /* col 低11位 */ 31 col = (int)(from & (mtd->writesize - 1));//writesize = 2048, 2047 = 0x7ff 32 /* 得到页地址和列地址 */ 33 buf = ops->datbuf; 34 oob = ops->oobbuf; 35 36 while(1) { 37 bytes = min(mtd->writesize - col, readlen); //128K 0x20000 根据输入的长度决定 38 aligned = (bytes == mtd->writesize); //aligned = 1; 39 40 /* Is the current page in the buffer ? */ 41 if (realpage != chip->pagebuf || oob) { 42 bufpoi = aligned ? buf : chip->buffers->databuf; 43 /* 对齐与不对齐读到的缓冲是不一样的 */ 44 if (likely(sndcmd)) { 45 chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); 46 sndcmd = 0; 47 } 48 49 /* Now read the page into the buffer */ 50 if (unlikely(ops->mode == MTD_OOB_RAW)) 51 ret = chip->ecc.read_page_raw(mtd, chip,//nand_read_page_raw 52 bufpoi, page);//从哪里读,读到哪里去,读多少 53 else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) 54 ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); 55 /* nand_read_subpage */ 56 else 57 ret = chip->ecc.read_page(mtd, chip, bufpoi,//整页读 bufpoi = buf 58 page); //nand_read_page_swecc 59 if (ret < 0) 60 break; 61 62 /* Transfer not aligned data */ 63 if (!aligned) { 64 if (!NAND_SUBPAGE_READ(chip) && !oob) 65 chip->pagebuf = realpage; 66 memcpy(buf, chip->buffers->databuf + col, bytes); 67 } 68 69 buf += bytes; 70 71 if (unlikely(oob)) { 72 /* Raw mode does data:oob:data:oob */ 73 if (ops->mode != MTD_OOB_RAW) { 74 int toread = min(oobreadlen, 75 chip->ecc.layout->oobavail); 76 if (toread) { 77 oob = nand_transfer_oob(chip, 78 oob, ops, toread); 79 oobreadlen -= toread; 80 } 81 } else 82 buf = nand_transfer_oob(chip, 83 buf, ops, mtd->oobsize); 84 } 85 86 if (!(chip->options & NAND_NO_READRDY)) { 87 /* 88 * Apply delay or wait for ready/busy pin. Do 89 * this before the AUTOINCR check, so no 90 * problems arise if a chip which does auto 91 * increment is marked as NOAUTOINCR by the 92 * board driver. 93 */ 94 if (!chip->dev_ready) 95 udelay(chip->chip_delay); 96 else 97 nand_wait_ready(mtd); 98 } 99 } else { 100 memcpy(buf, chip->buffers->databuf + col, bytes); 101 buf += bytes; 102 } 103 104 readlen -= bytes; 105 106 if (!readlen) 107 break; 108 109 /* For subsequent reads align to page boundary. */ 110 col = 0; 111 /* Increment page address */ 112 realpage++; 113 114 page = realpage & chip->pagemask; 115 /* Check, if we cross a chip boundary */ 116 if (!page) { 117 chipnr++; 118 chip->select_chip(mtd, -1); 119 chip->select_chip(mtd, chipnr); 120 } 121 122 /* Check, if the chip supports auto page increment 123 * or if we have hit a block boundary. 124 */ 125 if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) 126 sndcmd = 1; 127 } 128 129 ops->retlen = ops->len - (size_t) readlen; 130 if (oob) 131 ops->oobretlen = ops->ooblen - oobreadlen; 132 133 if (ret) 134 return ret; 135 136 if (mtd->ecc_stats.failed - stats.failed) 137 return -EBADMSG; 138 139 return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; 140 }
文章来源: 博客园
- 还没有人评论,欢迎说说您的想法!