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;
}