static int smart_scan()

in drivers/mtd/smart.c [1946:2618]


static int smart_scan(FAR struct smart_struct_s *dev)
{
  int       sector;
  int       ret;
  uint16_t  totalsectors;
  uint16_t  sectorsize;
  uint16_t  prerelease;
  uint16_t  logicalsector;
  uint16_t  winner;
  uint16_t  loser;
  uint32_t  readaddress;
  uint32_t  offset;
  uint16_t  seq1;
  uint16_t  seq2;
  uint16_t  seqwrap;
  struct    smart_sect_header_s header;
#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM
  int       dupsector;
  uint16_t  duplogsector;
#endif
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
  int       x;
  char      devname[32];
  FAR struct smart_multiroot_device_s *rootdirdev;
#endif
  static const uint16_t sizetbl[8] =
  {
    CONFIG_MTD_SMART_SECTOR_SIZE,
    512, 1024, 4096, 2048, 8192, 16384, 32768
  };

  finfo("Entry\n");

  /* Find the sector size on the volume by reading headers from
   * sectors of decreasing size.  On a formatted volume, the sector
   * size is saved in the header status byte of search sector, so
   * by starting with the largest supported sector size and
   * decreasing from there, we will be sure to find data that is
   * a header and not sector data.
   */

  sectorsize = 0xffff;
  offset = 16384;

  while (sectorsize == 0xffff)
    {
      readaddress = 0;

      while (readaddress < dev->erasesize * dev->geo.neraseblocks)
        {
          /* Read the next sector from the device */

          ret = MTD_READ(dev->mtd, readaddress,
                         sizeof(struct smart_sect_header_s),
                         (FAR uint8_t *)&header);
          if (ret != sizeof(struct smart_sect_header_s))
            {
              goto err_out;
            }

          if (header.status != CONFIG_SMARTFS_ERASEDSTATE)
            {
              sectorsize =
                sizetbl[(header.status & SMART_STATUS_SIZEBITS) >> 2];
              break;
            }

          readaddress += offset;
        }

      if (sectorsize == 0xffff)
        {
          sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
        }

      offset >>= 1;
      if (offset < 256 && sectorsize == 0xffff)
        {
          /* No valid sectors found on device.  Default the
           * sector size to the CONFIG value
           */

          sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
        }
    }

  /* Now set the sectorsize and other sectorsize derived variables */

  ret = smart_setsectorsize(dev, sectorsize);
  if (ret != OK)
    {
      goto err_out;
    }

  /* Initialize the device variables */

  totalsectors        = dev->totalsectors;
  dev->formatstatus   = SMART_FMT_STAT_NOFMT;
  dev->freesectors    = dev->availsectperblk * dev->geo.neraseblocks;
  dev->releasesectors = 0;

  /* Initialize the freecount and releasecount arrays */

  for (sector = 0; sector < dev->neraseblocks; sector++)
    {
      if (sector == dev->neraseblocks - 1 && dev->totalsectors == 65534)
        {
          prerelease = 2;
        }
      else
        {
          prerelease = 0;
        }

#ifdef CONFIG_MTD_SMART_PACK_COUNTS
      smart_set_count(dev, dev->freecount, sector,
                      dev->availsectperblk - prerelease);
      smart_set_count(dev, dev->releasecount, sector, prerelease);
#else
      dev->freecount[sector] = dev->availsectperblk - prerelease;
      dev->releasecount[sector] = prerelease;
#endif
    }

  /* Initialize the sector map */

#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
  for (sector = 0; sector < totalsectors; sector++)
    {
      dev->smap[sector] = -1;
    }
#else
  /* Clear all logical sector used bits */

  memset(dev->sbitmap, 0, (dev->totalsectors + 7) >> 3);
#endif

  /* Now scan the MTD device */

  /* At first, set the loser sector as the invalid value */

  loser = totalsectors;

  for (sector = 0; sector < totalsectors; sector++)
    {
      finfo("Scan sector %d\n", sector);

      winner = sector;

      /* Calculate the read address for this sector */

      readaddress = sector * dev->mtdblkspersector * dev->geo.blocksize;

      /* Read the header for this sector */

      ret = MTD_READ(dev->mtd, readaddress,
                     sizeof(struct smart_sect_header_s),
                     (FAR uint8_t *)&header);
      if (ret != sizeof(struct smart_sect_header_s))
        {
          goto err_out;
        }

      /* Get the logical sector number for this physical sector */

      logicalsector = *((FAR uint16_t *)header.logicalsector);
#if CONFIG_SMARTFS_ERASEDSTATE == 0x00
      if (logicalsector == 0)
        {
          logicalsector = -1;
        }
#endif

      /* Test if this sector has been committed */

      if ((header.status & SMART_STATUS_COMMITTED) ==
              (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))
        {
          continue;
        }

      /* This block is committed, therefore not free.  Update the
       * erase block's freecount.
       */

#ifdef CONFIG_MTD_SMART_PACK_COUNTS
      smart_add_count(dev, dev->freecount, sector / dev->sectorsperblk, -1);
#else
      dev->freecount[sector / dev->sectorsperblk]--;
#endif
      dev->freesectors--;

      /* Test if this sector has been release and if it has,
       * update the erase block's releasecount.
       */

      if ((header.status & SMART_STATUS_RELEASED) !=
              (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))
        {
          /* Keep track of the total number of released sectors and
           * released sectors per erase block.
           */

          dev->releasesectors++;
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
          smart_add_count(dev, dev->releasecount,
                          sector / dev->sectorsperblk, 1);
#else
          dev->releasecount[sector / dev->sectorsperblk]++;
#endif
          continue;
        }

      if ((header.status & SMART_STATUS_VERBITS) != SMART_STATUS_VERSION)
        {
          continue;
        }

      /* Validate the logical sector number is in bounds */

      if (logicalsector >= totalsectors)
        {
          /* Error in logical sector read from the MTD device */

          ferr("ERROR: Invalid logical sector %d at physical %d.\n",
               logicalsector, sector);
          continue;
        }

      /* If this is logical sector zero, then read in the signature
       * information to validate the format signature.
       */

      if (logicalsector == 0)
        {
          /* Read the sector data */

          ret = MTD_READ(dev->mtd, readaddress, 32,
                         (FAR uint8_t *)dev->rwbuffer);
          if (ret != 32)
            {
              ferr("ERROR: Error reading physical sector %d.\n", sector);
              goto err_out;
            }

          /* Validate the format signature */

          if (dev->rwbuffer[SMART_FMT_POS1] != SMART_FMT_SIG1 ||
              dev->rwbuffer[SMART_FMT_POS2] != SMART_FMT_SIG2 ||
              dev->rwbuffer[SMART_FMT_POS3] != SMART_FMT_SIG3 ||
              dev->rwbuffer[SMART_FMT_POS4] != SMART_FMT_SIG4)
            {
              /* Invalid signature on a sector claiming to be sector 0!
               * What should we do?  Release it?
               */

              continue;
            }

          /* Mark the volume as formatted and set the sector size */

          dev->formatstatus = SMART_FMT_STAT_FORMATTED;
          dev->namesize = dev->rwbuffer[SMART_FMT_NAMESIZE_POS];
          dev->formatversion = dev->rwbuffer[SMART_FMT_VERSION_POS];

#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
          dev->rootdirentries = dev->rwbuffer[SMART_FMT_ROOTDIRS_POS];

          /* If rootdirentries is greater than 1, then we need to register
           * additional block devices.
           */

          for (x = 1; x < dev->rootdirentries; x++)
            {
              if (dev->partname[0] != '\0')
                {
                  snprintf(devname, sizeof(devname), "/dev/smart%d%sd%d",
                           dev->minor, dev->partname, x + 1);
                }
              else
                {
                  snprintf(devname, sizeof(devname), "/dev/smart%dd%d",
                           dev->minor, x + 1);
                }

              /* Inode private data is a reference to a struct containing
               * the SMART device structure and the root directory number.
               */

              rootdirdev = (FAR struct smart_multiroot_device_s *)
                smart_malloc(dev, sizeof(*rootdirdev), "Root Dir");
              if (rootdirdev == NULL)
                {
                  ferr("ERROR: Memory alloc failed\n");
                  ret = -ENOMEM;
                  goto err_out;
                }

              /* Populate the rootdirdev */

              rootdirdev->dev = dev;
              rootdirdev->rootdirnum = x;

              /* Inode private data is a reference to the SMART device
               * structure.
               */

              ret = register_blockdriver(devname, &g_bops, 0, rootdirdev);
            }
#endif
        }

      /* Test for duplicate logical sectors on the device */

#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
      if (dev->smap[logicalsector] != 0xffff)
#else
      if (dev->sbitmap[logicalsector >> 3] & (1 << (logicalsector & 0x07)))
#endif
        {
          /* Uh-oh, we found more than 1 physical sector claiming to be
           * the same logical sector.  Use the sequence number information
           * to resolve who wins.
           */

#if SMART_STATUS_VERSION == 1
          if ((header.status & SMART_STATUS_CRC) !=
                  (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC))
            {
              seq2 = header.seq;
            }
          else
            {
              seq2 = *((FAR uint16_t *) &header.seq);
            }
#else
          seq2 = header.seq;
#endif

          /* We must re-read the 1st physical sector to get it's seq number */

#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
          readaddress = dev->smap[logicalsector] * dev->mtdblkspersector *
                        dev->geo.blocksize;
#else
          /* For minimize RAM, we have to rescan to find the 1st sector
           * claiming to be this logical sector.
           */

          for (dupsector = 0; dupsector < sector; dupsector++)
            {
              /* Calculate the read address for this sector */

              readaddress = dupsector * dev->mtdblkspersector *
                            dev->geo.blocksize;

              /* Read the header for this sector */

              ret = MTD_READ(dev->mtd, readaddress,
                             sizeof(struct smart_sect_header_s),
                             (FAR uint8_t *)&header);
              if (ret != sizeof(struct smart_sect_header_s))
                {
                  goto err_out;
                }

              /* Get the logical sector number for this physical sector */

              duplogsector = *((FAR uint16_t *)header.logicalsector);

#if CONFIG_SMARTFS_ERASEDSTATE == 0x00
              if (duplogsector == 0)
                {
                  duplogsector = -1;
                }
#endif

              /* Test if this sector has been committed */

              if ((header.status & SMART_STATUS_COMMITTED) ==
                      (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))
                {
                  continue;
                }

              /* Test if this sector has been release and skip it if it has */

              if ((header.status & SMART_STATUS_RELEASED) !=
                      (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))
                {
                  continue;
                }

              if ((header.status & SMART_STATUS_VERBITS) !=
                  SMART_STATUS_VERSION)
                {
                  continue;
                }

              /* Now compare if this logical sector matches the current
               * sector
               */

              if (duplogsector == logicalsector)
                {
                  break;
                }
            }
#endif

          ret = MTD_READ(dev->mtd, readaddress,
                         sizeof(struct smart_sect_header_s),
                         (FAR uint8_t *)&header);
          if (ret != sizeof(struct smart_sect_header_s))
            {
              goto err_out;
            }

#if SMART_STATUS_VERSION == 1
          if ((header.status & SMART_STATUS_CRC) !=
                  (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC))
            {
              seq1 = header.seq;
              seqwrap = 0xf0;
            }
          else
            {
              seq1 = *((FAR uint16_t *)&header.seq);
              seqwrap = 0xfff0;
            }
#else
          seq1 = header.seq;
          seqwrap = 0xf0;
#endif

          /* Now determine who wins */

          if ((seq1 > seqwrap && seq2 < 10) || seq2 > seq1)
            {
              /* Seq 2 is the winner ... bigger or it wrapped */

              winner = sector;
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
              loser = dev->smap[logicalsector];
#else
              loser = dupsector;
#endif
            }
          else
            {
              /* We keep the original mapping and seq2 is the loser */

              loser = sector;
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
              winner = dev->smap[logicalsector];
#else
              winner = smart_cache_lookup(dev, logicalsector);
#endif
            }

          finfo("Duplicate Sector winner=%d, loser=%d\n", winner, loser);

#ifdef CONFIG_MTD_SMART_ENABLE_CRC
          /* Check CRC of the winner sector just in case */

          ret = MTD_BREAD(dev->mtd, winner * dev->mtdblkspersector,
                          dev->mtdblkspersector,
                          (FAR uint8_t *)dev->rwbuffer);
          if (ret == dev->mtdblkspersector)
            {
              /* Validate the CRC of the read-back data */

              ret = smart_validate_crc(dev);
            }

          if (ret != OK)
            {
              /* The winner sector has CRC error, so we select the loser
               * sector.  After swapping the winner and the loser sector, we
               * will release the loser sector with CRC error.
               */

              if (sector == winner)
                {
                  /* winner: sector(CRC error) -> origin
                   * loser : origin            -> sector(CRC error)
                   */

                  winner = loser;
                  loser = sector;
                }
              else
                {
                  /* winner: origin(CRC error) -> sector
                   * loser : sector            -> origin(CRC error)
                   */

                  loser = winner;
                  winner = sector;
                }

              finfo("Duplicate Sector winner=%d, loser=%d\n", winner, loser);
            }
#endif /* CONFIG_MTD_SMART_ENABLE_CRC */

          /* Now release the loser sector */

          readaddress = loser  * dev->mtdblkspersector * dev->geo.blocksize;
          ret = MTD_READ(dev->mtd, readaddress,
                         sizeof(struct smart_sect_header_s),
                         (FAR uint8_t *)&header);
          if (ret != sizeof(struct smart_sect_header_s))
            {
              goto err_out;
            }

#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
          header.status &= ~SMART_STATUS_RELEASED;
#else
          header.status |= SMART_STATUS_RELEASED;
#endif
          offset = readaddress +
                   offsetof(struct smart_sect_header_s, status);
          ret    = smart_bytewrite(dev, offset, 1, &header.status);
          if (ret < 0)
            {
              ferr("ERROR: Error %d releasing duplicate sector\n", -ret);
              goto err_out;
            }
        }

      /* Test if this sector is loser of duplicate logical sector */

      if (sector == loser)
        {
          continue;
        }

#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
      /* Update the logical to physical sector map */

      dev->smap[logicalsector] = winner;
#else
      /* Mark the logical sector as used in the bitmap */

      dev->sbitmap[logicalsector >> 3] |= 1 << (logicalsector & 0x07);

      if (logicalsector < SMART_FIRST_ALLOC_SECTOR)
        {
          smart_add_sector_to_cache(dev, logicalsector, winner, __LINE__);
        }
#endif
    }

#if defined (CONFIG_MTD_SMART_WEAR_LEVEL) && (SMART_STATUS_VERSION == 1)
#ifdef CONFIG_MTD_SMART_CONVERT_WEAR_FORMAT

  /* We need to check if we are converting an older format with incorrect
   * wear leveling data in sector zero to the new format.  The old format
   * put all zeros in the wear level bit locations, but the new (better)
   * way is to leave them 0xff.
   */

#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
  sector = dev->smap[0];
#else
  sector = smart_cache_lookup(dev, 0);
#endif

  /* Validate the sector is valid ... may be an unformatted device */

  if (sector != 0xffff)
    {
      /* Read the sector data */

      ret = MTD_BREAD(dev->mtd, sector * dev->mtdblkspersector,
                      dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
      if (ret != dev->mtdblkspersector)
        {
          ferr("ERROR: Error reading physical sector %d.\n", sector);
          goto err_out;
        }

      /* Check for old format wear leveling */

      if (dev->rwbuffer[SMART_WEAR_LEVEL_FORMAT_SIG] == 0)
        {
          /* Old format detected.  We must relocate sector zero and fill it
           * in with 0xff.
           */

          uint16_t newsector = smart_findfreephyssector(dev, false);
          if (newsector == 0xffff)
            {
              /* Unable to find a free sector!!! */

              ferr("ERROR: Can't find a free sector for relocation\n");
              ret = -ENOSPC;
              goto err_out;
            }

          memset(&dev->rwbuffer[SMART_WEAR_LEVEL_FORMAT_SIG], 0xff,
              dev->mtdblkspersector * dev->geo.blocksize -
              SMART_WEAR_LEVEL_FORMAT_SIG);

          smart_relocate_sector(dev, sector, newsector);

          /* Update the free and release sector counts */

          dev->freesectors--;
          dev->releasesectors++;

#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
          dev->smap[0] = newsector;
          dev->freecount[newsector / dev->sectorsperblk]--;
          dev->releasecount[sector / dev->sectorsperblk]++;
#else
          smart_update_cache(dev, 0, newsector);
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
          smart_add_count(dev, dev->freecount,
                          newsector / dev->sectorsperblk, -1);
          smart_add_count(dev, dev->releasecount,
                          sector / dev->sectorsperblk, 1);
#endif
#endif
        }
    }

#endif /* CONFIG_MTD_SMART_CONVERT_WEAR_FORMAT */
#endif /* CONFIG_MTD_SMART_WEAR_LEVEL && SMART_STATUS_VERSION == 1 */

#ifdef CONFIG_MTD_SMART_FSCK
  smart_fsck(dev);
#endif
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
  /* Read the wear leveling status bits */

  smart_read_wearstatus(dev);
#endif

  finfo("SMART Scan\n");
  finfo("   Erase size:   %10d\n", dev->sectorsperblk * dev->sectorsize);
  finfo("   Erase count:  %10d\n", dev->neraseblocks);
  finfo("   Sect/block:   %10d\n", dev->sectorsperblk);
  finfo("   MTD Blk/Sect: %10d\n", dev->mtdblkspersector);

  /* Validate the geometry */

  if (dev->mtdblkspersector == 0 || dev->sectorsperblk == 0 ||
      dev->sectorsperblk == 0 || dev->sectorsize == 0)
    {
      ferr("ERROR: Invalid Geometry!\n");
      ret = -EINVAL;
      goto err_out;
    }

#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
  finfo("   Allocations:\n");
  for (sector = 0; sector < SMART_MAX_ALLOCS; sector++)
    {
      if (dev->alloc[sector].ptr != NULL)
        {
          finfo("       %s: %d\n",
                dev->alloc[sector].name, dev->alloc[sector].size);
        }
    }
#endif

  ret = OK;

err_out:
  return ret;
}