Chinaunix首页 | 论坛 | 博客
  • 博客访问: 271823
  • 博文数量: 95
  • 博客积分: 2047
  • 博客等级: 大尉
  • 技术积分: 1022
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-14 16:18
文章分类

全部博文(95)

文章存档

2013年(1)

2011年(94)

我的朋友

分类: LINUX

2011-08-13 16:46:50

331

前言:为了深入了解MTD如何处理Nand问题,决定跟一下MTD Nand层代码。

 

mtd.h

重要结构体:

struct erase_info

如果擦除失败,fail_addr将指示坏块地址。

struct mtd_info

mtd层函数指针存放处。

 

nand.h

Nand基本指令:

#define NAND_CMD_READ0          0

#define NAND_CMD_READ1          1

#define NAND_CMD_PAGEPROG   0x10

#define NAND_CMD_READOOB    0x50

#define NAND_CMD_ERASE1         0x60

#define NAND_CMD_STATUS         0x70

#define NAND_CMD_STATUS_MULTI   0x71

#define NAND_CMD_SEQIN           0x80

#define NAND_CMD_READID        0x90

#define NAND_CMD_ERASE2         0xd0

#define NAND_CMD_RESET           0xff

 

K9F1208指令对比

 

重要结构体:

struct nand_chip

具体操作Nand的函数指针都在这个结构体里面。

  struct nand_bbt_descr

Nand坏块表?具体如何使用还不清楚。

 

 

41

nand_base.c

int nand_scan (struct mtd_info *mtd, int maxchips)

{

struct nand_chip *this = mtd->priv;

privmtd_info结构体里面的一个空指针,现在指向this

 

if (this->cmdfunc == NULL)

this->cmdfunc = nand_command;

判断驱动编写者是否提供了command函数,后来几个类似。

 

this->cmdfunc (mtd, NAND_CMD_READID, 0X00, -1);

读取Nand芯片信息,包括厂商信息的芯片ID,对于K9F12080xEC0x76

对应nand_ids.c中的{"NAND 64MiB 3,3V 8-bit",        0x76, 512, 64, 0x4000, 0}

含义:三星的这颗Nand芯片是64MB的,3.3V供电,8bit位宽,ID0x76,每一页大小为512Byte64MB容量,擦除块尺寸为0x4000,操作0

     对擦除块为0x4000的解释:这颗Nand芯片的容量是这样划分的,512Byte x 32 x 4096 = 64MB,一共有4096个块(block),因此每一个块的大小为512Byte x 32 = 16384Byte = 0x4000Byte

这些信息接下来都会被MTD层获得,如果全部没有问题,则在启动时会打印:

printk (KERN_INFO "NAND device: Manufacturer ID:"

                            " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,

                            nand_manuf_ids[maf_id].name , mtd->name);

 

/* Calculate the address shift from the page size */

this->page_shift = ffs(mtd->oobblock) - 1;

this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;

this->chip_shift = ffs(this->chipsize) - 1;

    这一段不太明白,翻译过来是根据页面大小计算地址变化

我在启动时将其打印了出来:

mtd->oobblock is 0x200

mtd->oobsize is 0x10

mtd->erasesize is 0x4000

this->page_shift is 0x9

this->bbt_erase_shift is 0xe

this->chip_shift is 0x1a

ffs函数第一次见到,看看是什么东西:

       #define ffs(x) generic_ffs(x)

继续,蛮有意思的函数:

       static inline int generic_ffs(int x)

{

       int r = 1;

 

       if (!x)

              return 0;

       if (!(x & 0xffff)) {

              x >>= 16;

              r += 16;

       }

       if (!(x & 0xff)) {

              x >>= 8;

              r += 8;

       }

       if (!(x & 0xf)) {

              x >>= 4;

              r += 4;

       }

       if (!(x & 3)) {

              x >>= 2;

              r += 2;

       }

       if (!(x & 1)) {

              x >>= 1;

              r += 1;

       }

       return r;

}

       这函数人如其名,找到第一个bit位(find first bit set),比如0x80,将返回7

      

/* Set the bad block position */

              this->badblockpos = mtd->oobblock > 512 ?

                     NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;

确定坏块标记的位置,如果大于512,在oob区的位置0,否则是在oob区的位置5

 

/* Do not replace user supplied command function ! */

              if (mtd->oobblock > 512 && this->cmdfunc == nand_command)

                     this->cmdfunc = nand_command_lp;

       这一段没有什么意义,因为我们的底层驱动里面提供了命令函数。

 

if (!nand_flash_ids[i].name) {

              printk (KERN_WARNING "No NAND device found!!!\n");

              this->select_chip(mtd, -1);

              return 1;

       }

       如果没有发现芯片,会提示找不到芯片,我刚开始做u-boot驱动时,读不到正确的芯片ID,就报这个错误,并且直接返回1,下面的程序不再执行。

 

       for (i=1; i < maxchips; i++) {

              this->select_chip(mtd, i);

       /* Send the command for reading device ID */

              this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);

              /* Read manufacturer and device IDs */

              if (nand_maf_id != this->read_byte(mtd) ||

                  nand_dev_id != this->read_byte(mtd))

                     break;

       }

如果有多块芯片,这里会去读它们的ID信息。

 

       /* Allocate buffers, if neccecary */

       if (!this->oob_buf) {

              size_t len;

              len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);

              this->oob_buf = kmalloc (len, GFP_KERNEL);

              if (!this->oob_buf) {

                     printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");

                     return -ENOMEM;

              }

              this->options |= NAND_OOBBUF_ALLOC;

 

       }

if (!this->data_buf) {

              size_t len;

              len = mtd->oobblock + mtd->oobsize;

              this->data_buf = kmalloc (len, GFP_KERNEL);

              if (!this->data_buf) {

                     if (this->options & NAND_OOBBUF_ALLOC)

                            kfree (this->oob_buf);

                     printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");

                     return -ENOMEM;

              }

              this->options |= NAND_DATABUF_ALLOC;

      

       }

如果前面没有分配,在这儿分配数据区和oob区的空间。说说这个size_t,是为了方便移植而的设定的,其实就是unsigned intoob区的大小是mtd->oobsize << (this->phys_erase_shift - this->page_shift),数据区的大小是mtd->oobblock + mtd->oobsize。这儿在计算oob区该分配多大时用到了前面定义的this->page_shiftthis->phys_erase_shift

具体计算方法?

这时候用得上前面print出来的内容:

mtd->oobblock is 0x200

mtd->oobsize is 0x10

mtd->erasesize is 0x4000

this->page_shift is 0x9

this->bbt_erase_shift is 0xe

this->chip_shift is 0x1a

 

len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);

       这句话应该是计算oob_buf的长度,计算结果应该是(16 << 5=512,奇怪了,oob区的大小应该是16才对,为何要左移5位变成512呢?

暂且放下,现在还没看到oob_buf的用途,继续看下面的内容。

 

/* Store the number of chips and calc total size for mtd */

this->numchips = i;

mtd->size = i * this->chipsize;

/* Convert chipsize to number of pages per chip -1. */

this->pagemask = (this->chipsize >> this->page_shift) - 1;

/* Preset the internal oob buffer */

memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));

存储芯片的数目并计算mtd的总大小。

将芯片大小换算成页数,这时我才看懂this->page_shift的意思,就是9bit,因为便于移位操作,所以才用ffs函数将512变换为9的。

最后将oob_buf全部填充了0xff

 

/* If no default placement scheme is given, select an

        * appropriate one */

       if (!this->autooob) {

              /* Select the appropriate default oob placement scheme for

               * placement agnostic filesystems */

              switch (mtd->oobsize) {

              case 8:

                     this->autooob = &nand_oob_8;

                     break;

              case 16:

                     this->autooob = &nand_oob_16;

                     break;

              case 64:

                     this->autooob = &nand_oob_64;

                     break;

              default:

                     printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",

                            mtd->oobsize);

                     BUG();

              }

}

       根据oobsize填充autooob,我们的oobsize16,填充的是nand_oob_16这个结构体的内容:

static struct nand_oobinfo nand_oob_16 = {

       .useecc = MTD_NANDECC_AUTOPLACE,

       .eccbytes = 6,

       .eccpos = {0, 1, 2, 3, 6, 7},

       .oobfree = { {8, 8} }

};

       结构体中规定了ecc校验位的位置。

/* The number of bytes available for the filesystem to place fs dependend

        * oob data */

       mtd->oobavail = 0;

       for (i = 0; this->autooob->oobfree[i][1]; i++)

              mtd->oobavail += this->autooob->oobfree[i][1];

       文件系统的oob数据放在oobfree区里面。

 

/* * check ECC mode, default to software

        * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize

        * fallback to software ECC

       */

       this->eccsize = 256;     /* set default eccsize */

       this->eccbytes = 3;

       switch (this->eccmode) {

       case NAND_ECC_HW12_2048:

              if (mtd->oobblock < 2048) {

                     printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",

                            mtd->oobblock);

                     this->eccmode = NAND_ECC_SOFT;

                     this->calculate_ecc = nand_calculate_ecc;

                     this->correct_data = nand_correct_data;

              } else

                     this->eccsize = 2048;

              break;

       case NAND_ECC_HW3_512:

       case NAND_ECC_HW6_512:

       case NAND_ECC_HW8_512:

              if (mtd->oobblock == 256) {

                     printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");

                     this->eccmode = NAND_ECC_SOFT;

                     this->calculate_ecc = nand_calculate_ecc;

                     this->correct_data = nand_correct_data;

              } else

                     this->eccsize = 512; /* set eccsize to 512 */

              break;

       case NAND_ECC_HW3_256:

              break;

       case NAND_ECC_NONE:

              printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");

              this->eccmode = NAND_ECC_NONE;

              break;

       case NAND_ECC_SOFT:

              this->calculate_ecc = nand_calculate_ecc;

              this->correct_data = nand_correct_data;

              break;

       default:

              printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);

              BUG();

       }

       默认的eccsize256eccbytes3

       开始判断驱动中提供的eccmode,我们以前用的是NAND_ECC_SOFT,现在为了使用yaffs,改用NAND_ECC_NONE,其他硬件的都不用看。如果是NONE的话,直接printk一个warning,如果是SOFT的,需要填充:

       this->calculate_ecc = nand_calculate_ecc;

       this->correct_data = nand_correct_data;

       这是两个函数哦,不是变量,mark下后面要跟。

 

/* Check hardware ecc function availability and adjust number of ecc bytes per

        * calculation step

       */

       switch (this->eccmode) {

       case NAND_ECC_HW12_2048:

              this->eccbytes += 4;

       case NAND_ECC_HW8_512:

              this->eccbytes += 2;

       case NAND_ECC_HW6_512:

              this->eccbytes += 3;

       case NAND_ECC_HW3_512:

       case NAND_ECC_HW3_256:

              if (this->calculate_ecc && this->correct_data && this->enable_hwecc)

                     break;

              printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");

              BUG();

       }

 

       mtd->eccsize = this->eccsize;

       没用到硬件ecc,这儿应该直接跳过了。

 

/* Set the number of read / write steps for one page to ensure ECC generation */

       switch (this->eccmode) {

       case NAND_ECC_HW12_2048:

              this->eccsteps = mtd->oobblock / 2048;

              break;

       case NAND_ECC_HW3_512:

       case NAND_ECC_HW6_512:

       case NAND_ECC_HW8_512:

              this->eccsteps = mtd->oobblock / 512;

              break;

       case NAND_ECC_HW3_256:

       case NAND_ECC_SOFT:

              this->eccsteps = mtd->oobblock / 256;

              break;

 

       case NAND_ECC_NONE:

              this->eccsteps = 1;

              break;

       }

       设置每一页的ecc校验的stepsNAND_ECC_NONE1NAND_ECC_SOFT2

 

       /* Initialize state, waitqueue and spinlock */

       this->state = FL_READY;

       init_waitqueue_head (&this->wq);

       spin_lock_init (&this->chip_lock);

       初始化状态机、等待列队和自旋锁。

 

/* Fill in remaining MTD driver data */

       mtd->type = MTD_NANDFLASH;

       mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;

       mtd->ecctype = MTD_ECC_SW;

       mtd->erase = nand_erase;

       mtd->point = NULL;

       mtd->unpoint = NULL;

       mtd->read = nand_read;

       mtd->write = nand_write;

       mtd->read_ecc = nand_read_ecc;

       mtd->write_ecc = nand_write_ecc;

       mtd->read_oob = nand_read_oob;

       mtd->write_oob = nand_write_oob;

       mtd->readv = NULL;

       mtd->writev = nand_writev;

       mtd->writev_ecc = nand_writev_ecc;

       mtd->sync = nand_sync;

       mtd->lock = NULL;

       mtd->unlock = NULL;

       mtd->suspend = nand_suspend;

       mtd->resume = nand_resume;

       mtd->block_isbad = nand_block_isbad;

       mtd->block_markbad = nand_block_markbad;

       填充MTD结构体的其他成员及函数,我看完nand scan如果没有突破点,就应该一个一个看这里面的内容。

 

/* Check, if we should skip the bad block table scan */

       if (this->options & NAND_SKIP_BBTSCAN)

              return 0;

       这儿比较重要,我正想u-boot在开机能不能跳过scan坏块呢,只要定义了NAND_SKIP_BBTSCAN就可以跳过坏块了。但是这个Linux下的nand_base.c,刚又看了下u-boot里面的nand_base.c,发现没有这个判断,奇怪。

 

/* Build bad block table */

       return this->scan_bbt (mtd);

       虽然返回,但没有结束,跳去执行scan_bbt这个函数了,下一步目标:scan_bbt

       终于终于把一个小小的nand_sacn函数看完了~

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

421

惭愧,没想到隔了这么长时间才继续学习。

前面看到在nand_scan()函数的最后将会跳至scan_bbt()函数,这个函数在nand_scan里面有定义:

2415       if (!this->scan_bbt)

2416       this->scan_bbt = nand_default_bbt;

       nand_default_bbt()位于Nand_bbt.c文件中。

 

1047       /**

             * nand_default_bbt - [NAND Interface] Select a default bad block table for the device

             * @mtd: MTD device structure

             *

             * This function selects the default bad block table

             * support for the device and calls the nand_scan_bbt function

**/

int nand_default_bbt (struct mtd_info *mtd)

{

       struct nand_chip *this = mtd->priv;

       这个函数的作用是建立默认的坏块表。

 

1059       /* Default for AG-AND. We must use a flash based

              * bad block table as the devices have factory marked

              * _good_ blocks. Erasing those blocks leads to loss

              * of the good / bad information, so we _must_ store

             * this information in a good / bad table during

             * startup

              */

              if (this->options & NAND_IS_AND) {

              /* Use the default pattern descriptors */

              if (!this->bbt_td) {

                     this->bbt_td = &bbt_main_descr;

                     this->bbt_md = &bbt_mirror_descr;

              }

                     this->options |= NAND_USE_FLASH_BBT;

 

                     return nand_scan_bbt (mtd, &agand_flashbased);

              }

       如果Flash的类型是AG-AND(这种Flash类型比较特殊,既不是MLC又不是SLC,因此不去深究了,而且好像瑞萨要把它淘汰掉),需要使用默认的模式描述符,最后再进入nand_scan_bbt()函数。

      

1078       /* Is a flash based bad block table requested ? */

              if (this->options & NAND_USE_FLASH_BBT) {

              /* Use the default pattern descriptors */

              if (!this->bbt_td) {

                     this->bbt_td = &bbt_main_descr;

                     this->bbt_md = &bbt_mirror_descr;

      

              }

              if (!this->badblock_pattern) {

                     this->badblock_pattern = (mtd->oobblock > 512) ?

                            &largepage_flashbased : &smallpage_flashbased;

              }

              } else {

              this->bbt_td = NULL;

              this->bbt_md = NULL;

              if (!this->badblock_pattern) {

                     this->badblock_pattern = (mtd->oobblock > 512) ?

                            &largepage_memorybased : &smallpage_memorybased;

              }

              }

             

              return nand_scan_bbt (mtd, this->badblock_pattern);

       如果Flash芯片需要使用坏块表,对于1208芯片来说是使用smallpage_memorybased

 

985         static struct nand_bbt_descr smallpage_memorybased = {

              .options = NAND_BBT_SCAN2NDPAGE,

              .offs = 5,

              .len = 1,

              .pattern = scan_ff_pattern

};

       暂时没看到如何使用这些赋值,先放着。后面检测坏块时用得着。

 

1099       return nand_scan_bbt (mtd, this->badblock_pattern);

       最后将badblock_pattern作为参数,调用nand_can_bbt函数。

 

844         /**

* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)

      * @mtd: MTD device structure

      * @bd:          descriptor for the good/bad block search pattern

      *

      * The function checks, if a bad block table(s) is/are already

      * available. If not it scans the device for manufacturer

      * marked good / bad blocks and writes the bad block table(s) to

      * the selected place.

      *

      * The bad block table memory is allocated here. It must be freed

      * by calling the nand_free_bbt function.

      *

*/

int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)

{    

       检测、寻找、读取甚至建立坏块表。函数检测是否已经存在一张坏块表,否则建立一张。坏块表的内存分配也在这个函数中。

 

860  struct nand_chip *this = mtd->priv;

       int len, res = 0;

       uint8_t *buf;

       struct nand_bbt_descr *td = this->bbt_td;

       struct nand_bbt_descr *md = this->bbt_md;

 

       len = mtd->size >> (this->bbt_erase_shift + 2);

       /* Allocate memory (2bit per block) */

       this->bbt = kmalloc (len, GFP_KERNEL);

       if (!this->bbt) {

              printk (KERN_ERR "nand_scan_bbt: Out of memory\n");

              return -ENOMEM;

       }

       /* Clear the memory bad block table */

       memset (this->bbt, 0x00, len);

       一些赋值、变量声明、内存分配,每个block分配2bit的空间。12084096block,应该分配4096*2bit的空间。

877  /* If no primary table decriptor is given, scan the device

        * to build a memory based bad block table

        */

       if (!td) {

              if ((res = nand_memory_bbt(mtd, bd))) {

printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");

                     kfree (this->bbt);

                     this->bbt = NULL;

              }

              return res;

       }

       如果没有提供ptd,就扫描设备并建立一张。这里调用了nand_memory_bbt()这个内联函数。

 

653  /**

      * nand_memory_bbt - [GENERIC] create a memory based bad block table

      * @mtd: MTD device structure

      * @bd:          descriptor for the good/bad block search pattern

      *

      * The function creates a memory based bbt by scanning the device

      * for manufacturer / software marked good / bad blocks

*/

static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)

{

              struct nand_chip *this = mtd->priv;

      

              bd->options &= ~NAND_BBT_SCANEMPTY;

              return create_bbt (mtd, this->data_buf, bd, -1);

 

}

       函数的作用是建立一张基于memory的坏块表。

       将操作符的NAND_BBT_SCANEMPTY清除,并继续调用creat_bbt()函数。

 

271  /**

* create_bbt - [GENERIC] Create a bad block table by scanning the device

      * @mtd: MTD device structure

      * @buf:  temporary buffer

      * @bd:          descriptor for the good/bad block search pattern

      * @chip: create the table for a specific chip, -1 read all chips.

      *            Applies only if NAND_BBT_PERCHIP option is set

      *

      * Create a bad block table by scanning the device

      * for the given good/bad block identify pattern

      */

static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)

{

       真正的建立坏块表函数。chip参数是-1表示读取所有的芯片。

 

284 struct nand_chip *this = mtd->priv;

       int i, j, numblocks, len, scanlen;

       int startblock;

       loff_t from;

       size_t readlen, ooblen;

 

       printk (KERN_INFO "Scanning device for bad blocks\n");

       一些变量声明,开机时那句话就是在这儿打印出来的。

 

292 if (bd->options & NAND_BBT_SCANALLPAGES)

       len = 1 << (this->bbt_erase_shift - this->page_shift);

 

       else {

              if (bd->options & NAND_BBT_SCAN2NDPAGE)

                     len = 2;

              else

                     len = 1;

       }

       在前面我们定义了smallpage_memorybased这个结构体,现在里面NAND_BBT_SCANALLPAGES的终于用上了,对于1208芯片来说,len=2

      

304 if (!(bd->options & NAND_BBT_SCANEMPTY)) {

              /* We need only read few bytes from the OOB area */

              scanlen = ooblen = 0;

              readlen = bd->len;

 

       } else {

              /* Full page content should be read */

              scanlen   = mtd->oobblock + mtd->oobsize;

              readlen = len * mtd->oobblock;

              ooblen = len * mtd->oobsize;

       }

       前面已经将NAND_BBT_SCANEMPTY清除了,这里肯定执行else的内容。需要将一页内容都读取出来。

 

316  if (chip == -1) {

              /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it

               * makes shifting and masking less painful */

              numblocks = mtd->size >> (this->bbt_erase_shift - 1);

              startblock = 0;

              from = 0;

       } else {

              if (chip >= this->numchips) {

                     printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",

                            chip + 1, this->numchips);

                     return -EINVAL;

              }

              numblocks = this->chipsize >> (this->bbt_erase_shift - 1);

              startblock = chip * numblocks;

              numblocks += startblock;

              from = startblock << (this->bbt_erase_shift - 1);

       }

       前面提到chip-1,实际上我们只有一颗芯片,numblocks这儿是4096*2

 

 

335 for (i = startblock; i < numblocks;) {

              int ret;

              if (bd->options & NAND_BBT_SCANEMPTY)

                     if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))

                            return ret;

              for (j = 0; j < len; j++) {

                     if (!(bd->options & NAND_BBT_SCANEMPTY)) {

                            size_t retlen;

                            /* Read the full oob until read_oob is fixed to

                             * handle single byte reads for 16 bit buswidth */

                            ret = mtd->read_oob(mtd, from + j * mtd->oobblock,

                                                 mtd->oobsize, &retlen, buf);

                            if (ret)

                                   return ret;

                            if (check_short_pattern (buf, bd)) {

                                          this->bbt[i >> 3] |= 0x03 << (i & 0x6);

                                   printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",

                                          i >> 1, (unsigned int) from);

                                                 break;

                            }

                     } else {

                            if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {

                                                        this->bbt[i >> 3] |= 0x03 << (i & 0x6);

                                   printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",

                                          i >> 1, (unsigned int) from);

                                                        break;

                            }

                     }

              }

              i += 2;

              from += (1 << this->bbt_erase_shift);

       }

       return 0;

       检测这4096block,刚开始的nand_read_raw肯定不会执行。len2,在j循环要循环2次。

       每次循环真正要做的事情是下面的内容:

       ret = mtd->read_oob(mtd, from + j * mtd->oobblock,     mtd->oobsize, &retlen, buf);

       read_oob()函数在nand_scan()里被指向nand_read_oob(),这个函数在Nand_base.c文件中,看来得回Nand_base.c看看了。

      

1397 /**

      * nand_read_oob - [MTD Interface] NAND read out-of-band

      * @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

      *

      * NAND read out-of-band data from the spare area

      */

static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)

{

       才发现oob全称是out-of-band, from是偏移量,len是读取的长度,retlen是存储指针。

      

1409 int i, col, page, chipnr;

       struct nand_chip *this = mtd->priv;

       int   blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;

       DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);

       /* Shift to get page */

       page = (int)(from >> this->page_shift);

       chipnr = (int)(from >> this->chip_shift);

       /* Mask to get column */

       col = from & (mtd->oobsize - 1);

       /* Initialize return length value */

       *retlen = 0;

       一些初始化,blockcheck对于1208应该是(1<<(0xe-0x9)-1=31。然后通过偏移量计算出要读取oob区的pagechipnrcol

 

1425 /* Do not allow reads past end of device */

       if ((from + len) > mtd->size) {

              DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");

              *retlen = 0;

              return -EINVAL;

       }

       /* Grab the lock and see if the device is available */

       nand_get_device (this, mtd , FL_READING);

       /* Select the NAND device */

       this->select_chip(mtd, chipnr);

     /* Send the read command */

       this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);

       不允许非法的读取,获取芯片控制权,发送读取OOB命令,这儿会调用具体硬件驱动中相关的Nand控制函数。

 

1442 /*

        * Read the data, if we read more than one page

        * oob data, let the device transfer the data !

        */

       i = 0;

       while (i < len) {

              int thislen = mtd->oobsize - col;

              thislen = min_t(int, thislen, len);

              this->read_buf(mtd, &buf[i], thislen);

              i += thislen;

              /* Read more ? */

              if (i < len) {

                     page++;

                     col = 0;

                     /* Check, if we cross a chip boundary */

                     if (!(page & this->pagemask)) {

                            chipnr++;

                            this->select_chip(mtd, -1);

                            this->select_chip(mtd, chipnr);

                     }

                     /* 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);

                     /* Check, if the chip supports auto page increment

                      * or if we have hit a block boundary.

                     */

                     if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {

                            /* For subsequent page reads set offset to 0 */

                             this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);

                     }

              }

       }

       /* Deselect and wake up anyone waiting on the device */

       nand_release_device(mtd);

       /* Return happy */

       *retlen = len;

       return 0;

       开始读取数据,while循环只要获取到oob区大小的数据即可。注意,read_buf才是最底层的读写Nand的函数,在我们的驱动中根据参数可以实现读取528byte全部内容,或者16byteoob区。

       如果一次没读完,就要继续再读,根据我们实际使用经验好像没出现过这种问题。

       最后Return Happy~回到Nand_bbt.ccreat_bbt()函数,348行,好像都快忘记我们还没出creat_bbt()函数呢,我再把他贴一遍吧:

346         /* Read the full oob until read_oob is fixed to

              * handle single byte reads for 16 bit buswidth */

              ret = mtd->read_oob(mtd, from + j * mtd->oobblock,

                                                 mtd->oobsize, &retlen, buf);

                            if (ret)

                                   return ret;

                            if (check_short_pattern (buf, bd)) {

                                          this->bbt[i >> 3] |= 0x03 << (i & 0x6);

                                   printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",

                                          i >> 1, (unsigned int) from);

                                                 break;

                            }

                     } else {

                            if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {

                                                        this->bbt[i >> 3] |= 0x03 << (i & 0x6);

                                   printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",

                                          i >> 1, (unsigned int) from);

                                                        break;

                            }

                     }

              }

              i += 2;

              from += (1 << this->bbt_erase_shift);

       }

       return 0;

}

       刚刚如果不是Ruturn Happy,下面的352行就会返回错误了。接着会调用check_short_pattern()这个函数。

 

113  /**

      * check_short_pattern - [GENERIC] check if a pattern is in the buffer

      * @buf:  the buffer to search

      * @td:           search pattern descriptor

      *

      * Check for a pattern at the given place. Used to search bad block

      * tables and good / bad block identifiers. Same as check_pattern, but

      * no optional empty check

      *

*/

static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)

{

       int i;

       uint8_t *p = buf;

       /* Compare the pattern */

       for (i = 0; i < td->len; i++) {

              if (p[td->offs + i] != td->pattern[i])

                     return -1;

       }

       return 0;

}

       检查读到的oob区是不是坏块就靠这个函数了。前面放了好久的struct nand_bbt_descr smallpage_memorybased终于用上了,挨个对比,有一个不一样直接返回-1,坏块就这样产生了。下面会将坏块的位置打印出来,并且将坏块记录在bbt表里面,在nand_scan_bbt()函数的开始我们就为bbt申请了空间。

       this->bbt[i >> 3] |= 0x03 << (i & 0x6);

       为啥要右移3bit呢?首先i要右移1bit,因为前面乘以了2。由于没个block占用2bit的空间,一个char变量8bit,所以还再要右移2bit吧。

下面的check_pattern()函数调用不到的。

       依次检测完所有blockcreat_bbt()函数也顺利返回。

       这样nand_memory_bbt()函数也正确返回。

       接着是nand_scan_bbt()同样顺利结束。

       最后nand_default_bbt()完成。

       整个nand_scan()的工作终于完成咯,好长。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

阅读(1180) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~