summaryrefslogtreecommitdiff
path: root/nuttx/drivers
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-12-09 14:11:15 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-12-09 14:11:15 -0600
commitc0658f766fa3731fc2f0191d42fa9f6c41d36691 (patch)
tree282e5296409d9cb0edfc637769fe0731099ee493 /nuttx/drivers
parent246ab986d67e177bb4e60046ad486947e1624774 (diff)
downloadnuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.tar.gz
nuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.tar.bz2
nuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.zip
SmartFS: Implements wear-leveling in the SmartFS. From Ken Pettit
Diffstat (limited to 'nuttx/drivers')
-rw-r--r--nuttx/drivers/mtd/Kconfig111
-rw-r--r--[-rwxr-xr-x]nuttx/drivers/mtd/mtd_nand.c0
-rw-r--r--nuttx/drivers/mtd/smart.c3870
3 files changed, 3585 insertions, 396 deletions
diff --git a/nuttx/drivers/mtd/Kconfig b/nuttx/drivers/mtd/Kconfig
index 3cd2da4fa..0f4d0ae0e 100644
--- a/nuttx/drivers/mtd/Kconfig
+++ b/nuttx/drivers/mtd/Kconfig
@@ -394,6 +394,117 @@ config MTD_SMART_READAHEAD
default n
depends on DRVR_READAHEAD
+config MTD_SMART_WEAR_LEVEL
+ bool "Support FLASH wear leveling"
+ depends on MTD_SMART
+ default y
+ ---help---
+ Adds extra logic and RAM to guarantee equal wear leveling of the FLASH
+ device by recording and monitoring erase block operations and selecting
+ sector allocations to ensure all erase blocks are worn evenly. This will
+ evenly wear both dynamic and static data on the device.
+
+if MTD_SMART_WEAR_LEVEL && !SMART_CRC_16
+
+config MTD_SMART_CONVERT_WEAR_FORMAT
+ bool "Convert existing non wear leveling FLASH to wear leveling"
+ default n
+ ---help---
+ Adds a little extra code which detects an existing SMART format on a device
+ that was created prior to the wear leveling implementation. This conversion
+ only works if either no CRC is being used or if CRC-8 is being used as other
+ CRC versions use a different header format and require a mksmartfs on the
+ device even if an existing format is there.
+
+endif
+
+config MTD_SMART_ENABLE_CRC
+ bool "Enable Sector CRC error detection"
+ depends on MTD_SMART
+ default n
+ ---help---
+ Enables logic to compute and validate a CRC for logical sectors. The
+ CRC is calculated for all bytes in the logical sector. The CRC size is
+ selectable (8-bit, 16-bit, 32-bit). For added protection, larger CRCs should
+ be used with larger (2K - 4K) sector sizes. Enabling CRC protection will
+ cause increased sector relocation and increased erase block erasures since
+ directory and wear-level status updates can no longer be performed in-place
+ and mandate re-writing the information to a new sector.
+
+ An 8-bit CRC protection scheme can be added to an existing non-CRC formatted
+ SMART volume without needing to reformat the drive. As sectors are re-written
+ or relocated, they will be converted to CRC protected sectors.
+
+choice
+ prompt "CRC level selection"
+ depends on MTD_SMART_ENABLE_CRC
+ default SMART_CRC_8
+ ---help---
+ Select the level of CRC protection implemented in the SMART MTD layer.
+ Smaller CRC selection uses less overhead per logical sectors, but also has
+ a higher probability of not detecting multiple bit errors. Devices with
+ larger logical sector sizes should use a larger CRC.
+
+config SMART_CRC_8
+ bool "CRC-8"
+
+config SMART_CRC_16
+ bool "CRC-16"
+
+endchoice
+
+config MTD_SMART_MINIMIZE_RAM
+ bool "Minimize SMART RAM usage using logical sector cache"
+ depends on MTD_SMART
+ default 0
+ ---help---
+ Reduces RAM usage in the SMART MTD layer by replacing the 1-for-1 logical to
+ physical sector map with a smaller cache-based structure. This can save a
+ considerable amount of RAM on devices with a large sector count, but at the
+ expense of increased read/write times when a cache miss occurs. If the
+ requested logical sector has not been cached, then the device will need to be
+ scanned to located it on the physical medium.
+
+config MTD_SMART_SECTOR_CACHE_SIZE
+ int "Number of entries in the SMART logical sector cache"
+ depends on MTD_SMART_MINIMIZE_RAM
+ default 512
+ ---help---
+ Sets the size of the cache used for logical to physical sector mapping. A
+ larger number allows larger files to be "seek"ed randomly without encountering
+ cache misses. Any files larger than CACH_SIZE * SECTOR_SIZE that are seeked
+ start to end will cause the cache to flush forcing manual scanning of the
+ MTD device to find the logical to physical mappings.
+
+config MTD_SMART_SECTOR_PACK_COUNTS
+ bool "Pack free and release counts when possible"
+ depends on MTD_SMART_MINIMIZE_RAM
+ default y
+ ---help---
+ For volumes with 16 sectors per erase block or less, this option causes the
+ free sector and released sector counts used for allocation and garbage
+ collection to be packed such that two values are stored per byte. For
+ volumes with 16 sectors per erase block, the 4 LSBs are packed and all of
+ the high-order bits are packed separately (8 per byte). This squeezes even
+ more RAM out.
+
+config MTD_SMART_SECTOR_ERASE_DEBUG
+ bool "Track Erase Block erasure counts"
+ depends on MTD_SMART
+ default n
+ ---help---
+ Allocates an Erase Block erase count array and keeps track of the number
+ of erases per erase block. This data is then presented on the procfs
+ interface.
+
+config MTD_SMART_ALLOC_DEBUG
+ bool "RAM Allocation Debug"
+ depends on MTD_SMART
+ default n
+ ---help---
+ Records all SMART MTD layer allocations for debug purposes and makes them
+ accessible from the ProcFS interface if it is enabled.
+
endif # MTD_SMART
config MTD_RAMTRON
diff --git a/nuttx/drivers/mtd/mtd_nand.c b/nuttx/drivers/mtd/mtd_nand.c
index 3c333dd47..3c333dd47 100755..100644
--- a/nuttx/drivers/mtd/mtd_nand.c
+++ b/nuttx/drivers/mtd/mtd_nand.c
diff --git a/nuttx/drivers/mtd/smart.c b/nuttx/drivers/mtd/smart.c
index ccb11501e..b99bed493 100644
--- a/nuttx/drivers/mtd/smart.c
+++ b/nuttx/drivers/mtd/smart.c
@@ -52,6 +52,9 @@
#include <debug.h>
#include <errno.h>
+#include <crc8.h>
+#include <crc16.h>
+#include <crc32.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
@@ -63,11 +66,21 @@
* Private Definitions
****************************************************************************/
+//#define CONFIG_SMART_LOCAL_CHECKFREE
+
#define SMART_STATUS_COMMITTED 0x80
#define SMART_STATUS_RELEASED 0x40
+#define SMART_STATUS_CRC 0x20
#define SMART_STATUS_SIZEBITS 0x1C
#define SMART_STATUS_VERBITS 0x03
+
+#if defined(CONFIG_SMART_CRC_16)
+#define SMART_STATUS_VERSION 0x02
+#elif defined(CONFIG_SMART_CRC_32)
+#define SMART_STATUS_VERSION 0x03
+#else
#define SMART_STATUS_VERSION 0x01
+#endif
#define SMART_SECTSIZE_256 0x00
#define SMART_SECTSIZE_512 0x04
@@ -94,11 +107,11 @@
#define SMART_FMT_VERSION_POS (SMART_FMT_POS1 + 4)
#define SMART_FMT_NAMESIZE_POS (SMART_FMT_POS1 + 5)
#define SMART_FMT_ROOTDIRS_POS (SMART_FMT_POS1 + 6)
-#define SMARTFS_FMT_AGING_POS 32
-
-#define SMART_FMT_VERSION 1
+#define SMARTFS_FMT_WEAR_POS 36
+#define SMART_WEAR_LEVEL_FORMAT_SIG 32
#define SMART_PARTNAME_SIZE 4
+#define SMART_FIRST_DIR_SECTOR 3 /* First root directory sector */
#define SMART_FIRST_ALLOC_SECTOR 12 /* First logical sector number we will
* use for assignment of requested Alloc
* sectors. All enries below this are
@@ -119,21 +132,103 @@
#define offsetof(type, member) ( (size_t) &( ( (type *) 0)->member))
#endif
+#define SMART_MAX_ALLOCS 6
+//#define CONFIG_MTD_SMART_PACK_COUNTS
+
+#ifndef CONFIG_MTD_SMART_ALLOC_DEBUG
+#define smart_malloc(d, b, n) kmm_malloc(b)
+#define smart_free(d, p) kmm_free(p)
+#endif
+
+#define SMART_WEAR_FULL_RELOCATE_THRESHOLD 8
+#define SMART_WEAR_REORG_THRESHOLD 14
+#define SMART_WEAR_MIN_LEVEL 5
+#define SMART_WEAR_FORCE_REORG_THRESHOLD 1
+#define SMART_WEAR_BIT_DIVIDE 1
+#define SMART_WEAR_ZERO_MASK 0x0F
+#define SMART_WEAR_BLOCK_MASK 0x01
+
+/* Bit mapping for wear level bits */
+/* These are defined to allow updating the wear leveling with the minimum
+ * number of sector relocations / maximum use of 1 --> 0 transitions when
+ * incrementing the wear level.
+ *
+ * 0: 1111 8: 1011
+ * 1: 1110 9: 1010
+ * 2: 1100 10: 0010
+ * 3: 1000 11: 1101
+ * 4: 0111 12: 1001
+ * 5: 0110 13: 0001
+ * 6: 0100 14: 0011
+ * 7: 0000 15: 0101
+ */
+
+static const uint8_t gWearLevelToBitMap4[] =
+{
+ 0x0F, 0x0E, 0x0C, 0x08, /* Single bit erased (x3) */
+ 0x07, 0x06, 0x04, 0x00, /* Single bit erased (x3) */
+ 0x0B, 0x0A, 0x02, /* Single bit erased (x2) */
+ 0x0D, 0x09, 0x01, /* Single bit erased (x2) */
+ 0x03,
+ 0x05
+};
+
+/* Map a Wear Level bit pattern back to the wear level */
+
+static const uint8_t gWearBitToLevelMap4[] =
+{
+ 7, 13, 10, 14, 6, 15, 5, 4,
+ 3, 12, 9, 8, 2, 11, 1, 0
+};
+
/****************************************************************************
* Private Types
****************************************************************************/
+#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM
+struct smart_cache_s
+{
+ uint16_t logical; /* Logical sector number */
+ uint16_t physical; /* Associated physical sector */
+ uint16_t birth; /* The "birthday" of this entry */
+};
+#endif
+
+/* When CRC is enabled, we allocate sectors in memory only and only write
+ * to the device when an actual writesector is performed. If during the
+ * alloc process we do a physical write, we would either have to hold off on
+ * writing the CRC value (which creates an invalid state on the device) or
+ * we would have to perform a write, release re-write every time which would
+ * increase the wear of the device 2x.
+ */
+
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+struct smart_allocsector_s
+{
+ struct smart_allocsector_s *next; /* Pointer to next alloc sector */
+ uint16_t logical; /* Logical sector number */
+ uint16_t physical; /* Associated physical sector */
+};
+#endif
+
struct smart_struct_s
{
FAR struct mtd_dev_s *mtd; /* Contained MTD interface */
struct mtd_geometry_s geo; /* Device geometry */
+
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ uint32_t unusedsectors; /* Count of unused sectors (i.e. free when erased) */
+ uint32_t blockerases; /* Count of unused sectors (i.e. free when erased) */
+#endif
uint16_t neraseblocks; /* Number of erase blocks or sub-sectors */
+ uint16_t lastallocblock; /* Last block we allocated a sector from */
uint16_t freesectors; /* Total number of free sectors */
+ uint16_t releasesectors; /* Total number of released sectors */
uint16_t mtdBlksPerSector; /* Number of MTD blocks per SMART Sector */
uint16_t sectorsPerBlk; /* Number of sectors per erase block */
uint16_t sectorsize; /* Sector size on device */
uint16_t totalsectors; /* Total number of sectors on device */
- FAR uint16_t *sMap; /* Virtual to physical sector map */
+ uint32_t erasesize; /* Size of an erase block */
FAR uint8_t *releasecount; /* Count of released sectors per erase block */
FAR uint8_t *freecount; /* Count of free sectors per erase block */
FAR char *rwbuffer; /* Our sector read/write buffer */
@@ -141,12 +236,44 @@ struct smart_struct_s
uint8_t formatversion; /* Format version on the device */
uint8_t formatstatus; /* Indicates the status of the device format */
uint8_t namesize; /* Length of filenames on this device */
+ uint8_t debuglevel; /* Debug reporting level */
+ uint8_t availSectPerBlk; /* Number of usable sectors per erase block */
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
uint8_t rootdirentries; /* Number of root directory entries */
uint8_t minor; /* Minor number of the block entry */
#endif
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ uint8_t wearflags; /* Indicates force erase of static blocks needed */
+ uint8_t minwearlevel; /* Min level in the wear level bits */
+ uint8_t maxwearlevel; /* Max level in the wear level bits */
+ uint8_t *wearstatus; /* Array of wear leveling bits */
+ uint32_t uneven_wearcount; /* Number of times the the wear level has gone over max */
+#endif
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ FAR struct smart_allocsector_s *allocsector; /* Pointer to first alloc sector */
+#endif
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
+ FAR uint16_t *sMap; /* Virtual to physical sector map */
+#else
+ FAR uint8_t *sBitMap; /* Virtual sector used bit-map */
+ FAR struct smart_cache_s *sCache; /* Sector cache */
+ uint16_t cache_entries; /* Number of valid entries in the cache */
+ uint16_t cache_lastlog; /* Keep track of the last sector accessed */
+ uint16_t cache_lastphys; /* Keep the physical sector number also */
+ uint16_t cache_nextbirth; /* Sector cache aging value */
+#endif
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ FAR uint8_t *erasecounts; /* Number of erases for each erase block */
+#endif
+#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
+ size_t bytesalloc;
+ struct smart_alloc_s alloc[SMART_MAX_ALLOCS]; /* Array of memory allocations */
+#endif
};
+#define SMART_WEARFLAGS_FORCE_REORG 0x01
+#define SMART_WEARFLAGS_WRITE_NEEDED 0x02
+
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
struct smart_multiroot_device_s
{
@@ -155,21 +282,74 @@ struct smart_multiroot_device_s
};
#endif
+/* Format 1 sector header definition */
+
+#if SMART_STATUS_VERSION == 1
+#define SMART_FMT_VERSION 1
+struct smart_sect_header_s
+{
+ uint8_t logicalsector[2]; /* The logical sector number */
+ uint8_t seq; /* Incrementing sequence number */
+ uint8_t crc8; /* CRC-8 or seq number MSB */
+ uint8_t status; /* Status of this sector:
+ * Bit 7: 1 = Not commited
+ * 0 = commited
+ * Bit 6: 1 = Not released
+ * 0 = released
+ * Bit 5: Sector CRC enable
+ * Bit 4-2: Sector size on volume
+ * Bit 1-0: Format version (0x1) */
+};
+typedef uint8_t crc_t;
+
+/* Format 2 sector header definition. This is for a 16-bit CRC */
+
+#elif SMART_STATUS_VERSION == 2
+#define SMART_FMT_VERSION 2
struct smart_sect_header_s
{
uint8_t logicalsector[2]; /* The logical sector number */
- uint8_t seq[2]; /* Incrementing sequence number */
+ uint8_t crc16[2]; /* CRC-16 for this sector */
+ uint8_t status; /* Status of this sector:
+ * Bit 7: 1 = Not commited
+ * 0 = commited
+ * Bit 6: 1 = Not released
+ * 0 = released
+ * Bit 5: Sector CRC enable
+ * Bit 4-2: Sector size on volume
+ * Bit 1-0: Format version (0x2) */
+ uint8_t seq; /* Incrementing sequence number */
+};
+typedef uint16_t crc_t;
+
+/* Format 3 (32-bit) sector header definition. Actually, this format
+ * isn't used yet and will likely be changed to a format to support
+ * NAND devices (possibly with an 18-bit sector size, allowing up to
+ * 256K sectors on a larger NAND device, though this would take a fair
+ * amount of RAM for management).
+ */
+
+#elif SMART_STATUS_VERSION == 3
+#error "32-Bit mode not supported yet"
+#define SMART_FMT_VERSION 3
+struct smart_sect_header_s
+{
+ uint8_t logicalsector[4]; /* The logical sector number */
+ uint8_t crc32[4]; /* CRC-32 for this sector */
uint8_t status; /* Status of this sector:
* Bit 7: 1 = Not commited
* 0 = commited
* Bit 6: 1 = Not released
* 0 = released
- * Bit 5: Reserved - 1
- * Bit 4: Reserved - 1
- * Bit 3: Reserved - 1
- * Bit 2: Reserved - 1
- * Bit 1-0: Format version */
+ * Bit 5: Sector CRC enable
+ * Bit 4-2: Sector size on volume
+ * Bit 1-0: Format version (0x3) */
+ uint8_t seq; /* Incrementing sequence number */
};
+typedef uint32_t crc_t;
+
+#endif
+
/****************************************************************************
* Private Function Prototypes
@@ -188,6 +368,21 @@ static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer,
static int smart_geometry(FAR struct inode *inode, struct geometry *geometry);
static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg);
+static int smart_findfreephyssector(FAR struct smart_struct_s *dev, uint8_t canrelocate);
+
+#ifdef CONFIG_FS_WRITABLE
+static int smart_writesector(FAR struct smart_struct_s *dev, unsigned long arg);
+#endif
+static int smart_readsector(FAR struct smart_struct_s *dev, unsigned long arg);
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+static int smart_read_wearstatus(FAR struct smart_struct_s *dev);
+static int smart_relocate_static_data(FAR struct smart_struct_s *dev, uint16_t block);
+#endif
+
+static int smart_relocate_sector(FAR struct smart_struct_s *dev,
+ uint16_t oldsector, uint16_t newsector);
+
/****************************************************************************
* Private Data
****************************************************************************/
@@ -237,6 +432,292 @@ static int smart_close(FAR struct inode *inode)
}
/****************************************************************************
+ * Name: smart_malloc
+ *
+ * Description: Perform allocations and keep track of amount of allocated
+ * memory for this context.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
+FAR static void* smart_malloc(FAR struct smart_struct_s *dev,
+ size_t bytes, const char *name)
+{
+ void* ret = kmm_malloc(bytes);
+ uint8_t x;
+
+ /* Keep track of the total allocation */
+
+ if (ret != NULL)
+ {
+ dev->bytesalloc += bytes;
+ }
+
+ /* Keep track of individual allocs */
+
+ for (x = 0; x < SMART_MAX_ALLOCS; x++)
+ {
+ if (dev->alloc[x].ptr == NULL)
+ {
+ dev->alloc[x].ptr = ret;
+ dev->alloc[x].size = bytes;
+ dev->alloc[x].name = name;
+ break;
+ }
+ }
+
+ fdbg("SMART alloc: %ld\n", dev->bytesalloc);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_free
+ *
+ * Description: Perform smart memory free operation.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
+static void smart_free(FAR struct smart_struct_s *dev, FAR void* ptr)
+{
+ uint8_t x;
+
+ for (x = 0; x < SMART_MAX_ALLOCS; x++)
+ {
+ if (dev->alloc[x].ptr == ptr)
+ {
+ dev->alloc[x].ptr = NULL;
+ dev->bytesalloc -= dev->alloc[x].size;
+ kmm_free(ptr);
+ break;
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_set_count
+ *
+ * Description: Set either the freecount or releasecount value for the
+ * specified eraseblock (depending on which pointer is passed).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+static void smart_set_count(FAR struct smart_struct_s *dev, FAR uint8_t* pCount,
+ uint16_t block, uint8_t count)
+{
+ if (dev->sectorsPerBlk > 16)
+ {
+ pCount[block] = count;
+ }
+ else
+ {
+ /* Save the lower 4 bits of the count in a shared byte */
+
+ if (block & 0x01)
+ {
+ pCount[block >> 1] = (pCount[block >> 1] & 0xF0) | (count & 0x0F);
+ }
+ else
+ {
+ pCount[block >> 1] = (pCount[block >> 1] & 0x0F) | ((count & 0x0F) << 4);
+ }
+
+ /* If we have 16 sectors per block, then the upper bit (representing 16)
+ * all get packed into shared bytes.
+ */
+
+ if (dev->sectorsPerBlk == 16)
+ {
+ if (count == 16)
+ {
+ pCount[(dev->geo.neraseblocks >> 1) + (block>>3)] |= 1 << (block & 0x07);
+ }
+ else
+ {
+ pCount[(dev->geo.neraseblocks >> 1) + (block>>3)] &= ~(1 << (block & 0x07));
+ }
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_get_count
+ *
+ * Description: Get either the freecount or releasecount value for the
+ * specified eraseblock (depending on which pointer is passed).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+static uint8_t smart_get_count(FAR struct smart_struct_s *dev,
+ FAR uint8_t* pCount, uint16_t block)
+{
+ uint8_t count;
+
+ if (dev->sectorsPerBlk > 16)
+ {
+ count = pCount[block];
+ }
+ else
+ {
+ /* Save the lower 4 bits of the count in a shared byte */
+
+ if (block & 0x01)
+ {
+ count = pCount[block >> 1] & 0x0F;
+ }
+ else
+ {
+ count = pCount[block >> 1] >> 4;
+ }
+
+ /* If we have 16 sectors per block, then the upper bit (representing 16)
+ * all get packed into shared bytes.
+ */
+
+ if (dev->sectorsPerBlk == 16)
+ {
+ if (pCount[(dev->geo.neraseblocks >> 1) + (block>>3)] & (1 << (block & 0x07)))
+ {
+ count |= 0x10;
+ }
+ }
+ }
+
+ return count;
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_add_count
+ *
+ * Description: Add the specified value to and eraseblock count.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+static void smart_add_count(struct smart_struct_s *dev, uint8_t* pCount,
+ uint16_t block, int adder)
+{
+ int16_t value;
+
+ value = smart_get_count(dev, pCount, block) + adder;
+ smart_set_count(dev, pCount, block, value);
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_checkfree
+ *
+ * Description: A debug routine for validating the free sector count used
+ * during development of the wear leveling code.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+int smart_checkfree(FAR struct smart_struct_s *dev, int lineno)
+{
+ uint16_t x, freecount;
+#ifdef CONFIG_DEBUG_FS
+ uint16_t blockfree, blockrelease;
+ static uint16_t prev_freesectors = 0;
+ static uint16_t prev_releasesectors = 0;
+ static uint8_t *prev_freecount = NULL;
+ static uint8_t *prev_releasecount = NULL;
+#endif
+
+ freecount = 0;
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ freecount += smart_get_count(dev, dev->freecount, x);
+#else
+ freecount += dev->freecount[x];
+#endif
+ }
+
+ /* Test if the calculated freesectors equals the reported value */
+
+#ifdef CONFIG_DEBUG_FS
+ if (freecount != dev->freesectors)
+ {
+ fdbg("Free count incorrect in line %d! Calculated=%d, dev->freesectors=%d\n",
+ lineno, freecount, dev->freesectors);
+
+ /* Determine what changed from the last time which caused this error */
+
+ fdbg(" ... Prev freesectors=%d, prev releasesectors=%d\n",
+ prev_freesectors, prev_releasesectors);
+
+ if (prev_freecount)
+ {
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ blockfree = smart_get_count(dev, dev->freecount, x);
+ blockrelease = smart_get_count(dev, dev->releasecount, x);
+#else
+ blockfree = dev->freecount[x];
+ blockrelease = dev->releasecount[x];
+#endif
+ if (prev_freecount[x] != blockfree || prev_releasecount[x] != blockrelease)
+ {
+ /* This block's values are different from the last time ... report it */
+
+ fdbg(" ... Block %d: Old Free=%d, old release=%d, New free=%d, new release = %d\n",
+ x, prev_freecount[x], prev_releasecount[x], blockfree, blockrelease);
+ }
+ }
+ }
+
+ /* Modifiy the freesector count to reflect the actual calculated freecount
+ to get us back in line.
+ */
+
+ dev->freesectors = freecount;
+ return -EIO;
+ }
+
+ /* Make a copy of the freecount and releasecount arrays to compare the
+ * differences between successive calls so we can evaluate what changed
+ * in the event an error is detected.
+ */
+
+ if (prev_freecount == NULL)
+ {
+ prev_freecount = (FAR uint8_t *) smart_malloc(dev, dev->neraseblocks << 1, "Free backup");
+ prev_releasecount = prev_freecount + dev->neraseblocks;
+ }
+
+ if (prev_freecount != NULL)
+ {
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ prev_freecount[x] = smart_get_count(dev, dev->freecount, x);
+ prev_releasecount[x] = smart_get_count(dev, dev->releasecount, x);
+#else
+ prev_freecount[x] = dev->freecount[x];
+ prev_releasecount[x] = dev->releasecount[x];
+#endif
+ }
+ }
+
+ /* Save the previous freesectors count */
+
+ prev_freesectors = dev->freesectors;
+ prev_releasesectors = dev->releasesectors;
+#endif
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
* Name: smart_reload
*
* Description: Read the specified numer of sectors
@@ -259,8 +740,8 @@ static ssize_t smart_reload(struct smart_struct_s *dev, FAR uint8_t *buffer,
/* Read the full erase block into the buffer */
- fdbg("Read %d blocks starting at block %d\n", mtdBlocks, mtdStartBlock);
- nread = MTD_BREAD(dev->mtd, mtdStartBlock, mtdBlocks, buffer);
+ fvdbg("Read %d blocks starting at block %d\n", mtdBlocks, mtdStartBlock);
+ nread = MTD_BREAD(dev->mtd, mtdStartBlock, mtdBlocks, buffer);
if (nread != mtdBlocks)
{
fdbg("Read %d blocks starting at block %d failed: %d\n",
@@ -301,10 +782,11 @@ static ssize_t smart_read(FAR struct inode *inode, unsigned char *buffer,
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
-static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer,
- size_t start_sector, unsigned int nsectors)
+static ssize_t smart_write(FAR struct inode *inode,
+ FAR const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
{
- struct smart_struct_s *dev;
+ FAR struct smart_struct_s *dev;
off_t alignedblock;
off_t mask;
off_t blkstowrite;
@@ -321,9 +803,9 @@ static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer,
DEBUGASSERT(inode && inode->i_private);
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
- dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev;
+ dev = ((FAR struct smart_multiroot_device_s*) inode->i_private)->dev;
#else
- dev = (struct smart_struct_s *)inode->i_private;
+ dev = (FAR struct smart_struct_s *)inode->i_private;
#endif
/* I think maybe we need to lock on a mutex here */
@@ -421,7 +903,7 @@ static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer,
static int smart_geometry(FAR struct inode *inode, struct geometry *geometry)
{
- struct smart_struct_s *dev;
+ FAR struct smart_struct_s *dev;
uint32_t erasesize;
fvdbg("Entry\n");
@@ -430,9 +912,9 @@ static int smart_geometry(FAR struct inode *inode, struct geometry *geometry)
if (geometry)
{
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
- dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev;
+ dev = ((FAR struct smart_multiroot_device_s*) inode->i_private)->dev;
#else
- dev = (struct smart_struct_s *)inode->i_private;
+ dev = (FAR struct smart_struct_s *)inode->i_private;
#endif
geometry->geo_available = true;
geometry->geo_mediachanged = false;
@@ -445,7 +927,7 @@ static int smart_geometry(FAR struct inode *inode, struct geometry *geometry)
erasesize = dev->geo.erasesize;
if (erasesize == 0)
{
- erasesize = 65536;
+ erasesize = 262144;
}
geometry->geo_nsectors = dev->geo.neraseblocks * erasesize /
@@ -471,10 +953,11 @@ static int smart_geometry(FAR struct inode *inode, struct geometry *geometry)
*
****************************************************************************/
-static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size)
+static int smart_setsectorsize(FAR struct smart_struct_s *dev, uint16_t size)
{
uint32_t erasesize;
uint32_t totalsectors;
+ uint32_t allocsize;
/* Validate the size isn't zero so we don't divide by zero below */
@@ -483,6 +966,11 @@ static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size)
size = CONFIG_MTD_SMART_SECTOR_SIZE;
}
+ if (size == dev->sectorsize)
+ {
+ return OK;
+ }
+
erasesize = dev->geo.erasesize;
dev->neraseblocks = dev->geo.neraseblocks;
@@ -492,56 +980,263 @@ static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size)
if (erasesize == 0)
{
- erasesize = 65536;
+ erasesize = 262144;
}
+ dev->erasesize = erasesize;
dev->sectorsize = size;
dev->mtdBlksPerSector = dev->sectorsize / dev->geo.blocksize;
- dev->sectorsPerBlk = erasesize / dev->sectorsize;
+ if (erasesize / dev->sectorsize > 256)
+ {
+ /* We can't throw a dbg message here becasue it is too early.
+ * set the erasesize to zero and exit, then we will detect
+ * it during mksmartfs or mount.
+ */
+
+ dev->erasesize = 0;
+ dev->sectorsPerBlk = 256;
+ dev->availSectPerBlk = 255;
+ }
+ else
+ {
+ /* Set the sectors per erase block and available sectors per erase block */
+
+ dev->sectorsPerBlk = erasesize / dev->sectorsize;
+ if (dev->sectorsPerBlk == 256)
+ {
+ dev->availSectPerBlk = 255;
+ }
+ else
+ {
+ dev->availSectPerBlk = dev->sectorsPerBlk;
+ }
+ }
+
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ dev->unusedsectors = 0;
+ dev->blockerases = 0;
+#endif
/* Release any existing rwbuffer and sMap */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->sMap != NULL)
{
- kmm_free(dev->sMap);
+ smart_free(dev, dev->sMap);
+ dev->sMap = NULL;
+ }
+#else
+ if (dev->sBitMap != NULL)
+ {
+ smart_free(dev, dev->sBitMap);
+ dev->sBitMap = NULL;
}
+ dev->cache_entries = 0;
+ dev->cache_lastlog = 0xFFFF;
+ dev->cache_nextbirth = 0;
+#endif
+
if (dev->rwbuffer != NULL)
{
- kmm_free(dev->rwbuffer);
+ smart_free(dev, dev->rwbuffer);
+ dev->rwbuffer = NULL;
}
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ if (dev->wearstatus != NULL)
+ {
+ smart_free(dev, dev->wearstatus);
+ dev->wearstatus = NULL;
+ }
+#endif
+
/* Allocate a virtual to physical sector map buffer. Also allocate
* the storage space for releasecount and freecounts.
*/
totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
+
+ /* Validate the number of total sectors is small enough for a uint16_t */
+
+ if (totalsectors > 65536)
+ {
+ dbg("Invalid SMART sector count %ld\n", totalsectors);
+ return -EINVAL;
+ }
+ else if (totalsectors == 65536)
+ {
+ /* Special case. We allow 65536 sectors and simply waste 2 sectors
+ * to allow a smaller sector size with almost maximum flash usage.
+ */
+
+ totalsectors -= 2;
+ }
+
dev->totalsectors = (uint16_t) totalsectors;
- dev->sMap = (uint16_t *) kmm_malloc(totalsectors * sizeof(uint16_t) +
- (dev->neraseblocks << 1));
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
+ allocsize = dev->neraseblocks << 1;
+ dev->sMap = (FAR uint16_t *) smart_malloc(dev, totalsectors * sizeof(uint16_t) +
+ allocsize, "Sector map");
if (!dev->sMap)
{
fdbg("Error allocating SMART virtual map buffer\n");
- kmm_free(dev);
- return -EINVAL;
+ goto errexit;
+ }
+
+ dev->releasecount = (FAR uint8_t *) dev->sMap + (totalsectors * sizeof(uint16_t));
+ dev->freecount = dev->releasecount + dev->neraseblocks;
+#else
+ dev->sBitMap = (FAR uint8_t *) smart_malloc(dev, (totalsectors+7) >> 3, "Sector Bitmap");
+ if (dev->sBitMap == NULL)
+ {
+ fdbg("Error allocating SMART sector cache\n");
+ goto errexit;
+ }
+
+ /* Calculate the alloc size of the freesector and release sector arrays */
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ if (dev->sectorsPerBlk > 16)
+ {
+ allocsize = dev->neraseblocks << 1;
}
+ else if (dev->sectorsPerBlk == 16)
+ {
+ allocsize = dev->neraseblocks + (dev->neraseblocks >> 2);
+ }
+ else
+ {
+ allocsize = dev->neraseblocks;
+ }
+
+#else
+ allocsize = dev->neraseblocks << 1;
+#endif
- dev->releasecount = (uint8_t *) dev->sMap + (totalsectors * sizeof(uint16_t));
+ /* Allocate the sector cache */
+
+ if (dev->sCache == NULL)
+ {
+ dev->sCache = (FAR struct smart_cache_s *) smart_malloc(dev,
+ CONFIG_MTD_SMART_SECTOR_CACHE_SIZE * sizeof(struct smart_cache_s) +
+ allocsize, "Sector Cache");
+ }
+
+ if (!dev->sCache)
+ {
+ fdbg("Error allocating SMART sector cache\n");
+ goto errexit;
+ }
+
+ dev->releasecount = (FAR uint8_t *) dev->sCache + (CONFIG_MTD_SMART_SECTOR_CACHE_SIZE *
+ sizeof(struct smart_cache_s));
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ if (dev->sectorsPerBlk > 16)
+ {
+ dev->freecount = dev->releasecount + dev->neraseblocks;
+ }
+ else if (dev->sectorsPerBlk == 16)
+ {
+ dev->freecount = dev->releasecount + (dev->neraseblocks >> 1) + (dev->neraseblocks >> 3);
+ }
+ else
+ {
+ dev->freecount = dev->releasecount + (dev->neraseblocks >> 1);
+ }
+
+#else
dev->freecount = dev->releasecount + dev->neraseblocks;
+#endif
+
+#endif /* CONFIG_MTD_SMART_MINIMIZE_RAM */
+
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ /* Allocate a buffer to hold the erase counts */
+
+ if (dev->erasecounts == NULL)
+ {
+ dev->erasecounts = (FAR uint8_t *) smart_malloc(dev, dev->neraseblocks, "Erase counts");
+ }
+
+ if (!dev->erasecounts)
+ {
+ fdbg("Error allocating erase count array\n");
+ goto errexit;
+ }
+
+ memset(dev->erasecounts, 0, dev->neraseblocks);
+#endif
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ /* Allocate the wear leveling status array */
+
+ dev->wearstatus = (FAR uint8_t *) smart_malloc(dev, dev->neraseblocks >>
+ SMART_WEAR_BIT_DIVIDE, "Wear status");
+ if (!dev->wearstatus)
+ {
+ fdbg("Error allocating wear level status array\n");
+ goto errexit;
+ }
+
+ memset(dev->wearstatus, CONFIG_SMARTFS_ERASEDSTATE, dev->neraseblocks >>
+ SMART_WEAR_BIT_DIVIDE);
+ dev->wearflags = 0;
+ dev->uneven_wearcount = 0;
+#endif
/* Allocate a read/write buffer */
- dev->rwbuffer = (char *) kmm_malloc(size);
+ dev->rwbuffer = (FAR char *) smart_malloc(dev, size, "RW Buffer");
if (!dev->rwbuffer)
{
fdbg("Error allocating SMART read/write buffer\n");
- kmm_free(dev->sMap);
- kmm_free(dev);
- return -EINVAL;
+ goto errexit;
}
return OK;
+
+ /* On error for any allocation, we jump here and free anything that had
+ * previously been allocated.
+ */
+
+errexit:
+
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
+ if (dev->sMap)
+ {
+ smart_free(dev, dev->sMap);
+ }
+#else
+ if (dev->sBitMap)
+ {
+ smart_free(dev, dev->sBitMap);
+ }
+
+ if (dev->sCache)
+ {
+ smart_free(dev, dev->sCache);
+ }
+#endif
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ if (dev->wearstatus)
+ {
+ smart_free(dev, dev->wearstatus);
+ }
+#endif
+
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ if (dev->erasecounts)
+ {
+ smart_free(dev, dev->erasecounts);
+ }
+#endif
+
+ kmm_free(dev);
+ return -ENOMEM;
}
/****************************************************************************
@@ -551,12 +1246,12 @@ static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size)
* MTD device. If the MTD driver supports a direct impl of
* write, then it uses it, otherwise it does a read-modify-write
* and depends on the architecture of the flash to only program
- * bits that acutally changed.
+ * bits that actually changed.
*
****************************************************************************/
-static ssize_t smart_bytewrite(struct smart_struct_s *dev, size_t offset,
- int nbytes, const uint8_t *buffer)
+static ssize_t smart_bytewrite(FAR struct smart_struct_s *dev, size_t offset,
+ int nbytes, FAR const uint8_t *buffer)
{
ssize_t ret;
@@ -587,7 +1282,7 @@ static ssize_t smart_bytewrite(struct smart_struct_s *dev, size_t offset,
/* Do a block read */
- ret = MTD_BREAD(dev->mtd, startblock, nblocks, (uint8_t *) dev->rwbuffer);
+ ret = MTD_BREAD(dev->mtd, startblock, nblocks, (FAR uint8_t *) dev->rwbuffer);
if (ret < 0)
{
fdbg("Error %d reading from device\n", -ret);
@@ -600,7 +1295,7 @@ static ssize_t smart_bytewrite(struct smart_struct_s *dev, size_t offset,
/* Write the data back to the device */
- ret = MTD_BWRITE(dev->mtd, startblock, nblocks, (uint8_t *) dev->rwbuffer);
+ ret = MTD_BWRITE(dev->mtd, startblock, nblocks, (FAR uint8_t *) dev->rwbuffer);
if (ret < 0)
{
fdbg("Error %d writing to device\n", -ret);
@@ -615,6 +1310,451 @@ errout:
}
/****************************************************************************
+ * Name: smart_add_sector_to_cache
+ *
+ * Description: Adds a logical to physical sector maaping to the sector
+ * map cache. The cache is used to minimize RAM by eliminating
+ * a one-to-one mapping of all logical sectors and only keeping
+ * a fixed number of mappings per the
+ * CONFIG_MTD_SMART_SECTOR_CACHE_SIZE parameter. Sectors are
+ * automatically managed and removed based on the time since
+ * they were accessed last.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM
+static int smart_add_sector_to_cache(FAR struct smart_struct_s *dev,
+ uint16_t logical, uint16_t physical, int line)
+{
+ uint16_t index, x;
+ uint16_t oldest;
+
+ /* If we aren't full yet, just add the sector to the end of the list */
+
+ index = 1;
+ if (dev->cache_entries < CONFIG_MTD_SMART_SECTOR_CACHE_SIZE)
+ {
+ index = dev->cache_entries++;
+ }
+ else
+ {
+ /* Cache is full. We must find the least accessed entry and replace it */
+
+ oldest = 0xFFFF;
+ for (x = 0; x < CONFIG_MTD_SMART_SECTOR_CACHE_SIZE; x++)
+ {
+ /* Never replace cache entries for system sectors */
+
+ if (dev->sCache[x].logical < SMART_FIRST_ALLOC_SECTOR)
+ continue;
+
+ /* If the hit count is zero, then choose this entry */
+
+ if (dev->sCache[x].birth < oldest)
+ {
+ oldest = dev->sCache[x].birth;
+ index = x;
+ }
+ }
+ }
+
+ /* Now add the sector at index */
+
+ dev->sCache[index].logical = logical;
+ dev->sCache[index].physical = physical;
+ dev->sCache[index].birth = dev->cache_nextbirth++;
+ dev->cache_lastlog = logical;
+ dev->cache_lastphys = physical;
+ if (dev->debuglevel > 1)
+ {
+ dbg("Add Cache sector: Log=%d, Phys=%d at index %d from line %d\n",
+ logical, physical, index, line);
+ }
+
+ /* Test if the birthdays need to be adjusted */
+
+ if (oldest >= CONFIG_MTD_SMART_SECTOR_CACHE_SIZE + 1024)
+ {
+ for (x = 0; x < dev->cache_entries; x++)
+ {
+ dev->sCache[x].birth -= 1024;
+ }
+
+ dev->cache_nextbirth -= 1024;
+ }
+
+ return index;
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_cache_lookup
+ *
+ * Description: Perform a cache lookup for the requested logical sector.
+ * If the sector is in the cache, then update the hitcount and
+ * return the physical mapping. If a cache miss occurs, then
+ * the routine will scan the volume to find the logical sector
+ * and add / replace a cache entry with the newly located sector.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM
+static uint16_t smart_cache_lookup(FAR struct smart_struct_s *dev, uint16_t logical)
+{
+ int ret;
+ uint16_t block, sector;
+ uint16_t x, physical, logicalsector;
+ struct smart_sect_header_s header;
+ size_t readaddress;
+
+ physical = 0xFFFF;
+
+ /* Test if searching for the last sector used */
+
+ if (logical == dev->cache_lastlog)
+ {
+ return dev->cache_lastphys;
+ }
+
+ /* First search for the entry in the cache */
+
+ for (x = 0; x < dev->cache_entries; x++)
+ {
+ if (dev->sCache[x].logical == logical)
+ {
+ /* Entry found in the cache. Grab the physical mapping. */
+
+ physical = dev->sCache[x].physical;
+ break;
+ }
+ }
+
+ /* If the entry wasn't found in the cache, then we must search the volume
+ * for it and add it to the cache.
+ */
+
+ if (physical == 0xFFFF)
+ {
+ /* Now scan the MTD device. Instead of scanning start to end, we
+ * span the erase blocks and read one sector from each at a time.
+ * this helps speed up the search on volumes that aren't full
+ * because of sector allocation scheme will use the lower sector
+ * numbers in each erase block first.
+ */
+
+ for (sector = 0; sector < dev->sectorsPerBlk && physical == 0xFFFF; sector++)
+ {
+ /* Now scan across each erase block */
+
+ for (block = 0; block < dev->geo.neraseblocks; block++)
+ {
+ /* Calculate the read address for this sector */
+
+ readaddress = block * dev->erasesize +
+ sector * CONFIG_MTD_SMART_SECTOR_SIZE;
+
+ /* 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)
+ {
+ continue;
+ }
+#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;
+ }
+
+ /* Test if this is the sector we are looking for */
+
+ if (logicalsector == logical)
+ {
+ /* This is the sector we are looking for! Add it to the cache */
+
+ physical = block * dev->sectorsPerBlk + sector;
+ smart_add_sector_to_cache(dev, logical, physical, __LINE__ );
+ break;
+ }
+ }
+ }
+ }
+
+ /* Update the last logical sector found variable */
+
+ dev->cache_lastlog = logical;
+ dev->cache_lastphys = physical;
+
+err_out:
+ return physical;
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_update_cache
+ *
+ * Description: Updates a cache entry (if present) replacing the logical
+ * sector's physical sector mapping with the new one provided.
+ * This does not affect the hit count.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM
+static void smart_update_cache(FAR struct smart_struct_s *dev, uint16_t
+ logical, uint16_t physical)
+{
+ uint16_t x;
+
+ /* Scan through all cache entries and find the logical sector entry */
+
+ for (x = 0; x < dev->cache_entries; x++)
+ {
+ if (dev->sCache[x].logical == logical)
+ {
+ /* Entry found. Update it's physical mapping */
+
+ dev->sCache[x].physical = physical;
+
+ /* If we are freeing a sector, then remove the logical entry from
+ the cache.
+ */
+
+ if (physical == 0xFFFF)
+ {
+ dev->sCache[x].logical = dev->sCache[dev->cache_entries-1].logical;
+ dev->sCache[x].physical = dev->sCache[dev->cache_entries-1].physical;
+ dev->cache_entries--;
+ }
+
+ if (dev->debuglevel > 1)
+ {
+ dbg("Update Cache: Log=%d, Phys=%d at index %d\n", logical, physical, x);
+ }
+
+ break;
+ }
+ }
+
+ if (dev->cache_lastlog == logical)
+ {
+ dev->cache_lastphys = physical;
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_get_wear_level
+ *
+ * Description: Gets the wear level of the specified block. Wear levels are
+ * encoded to minimize the number of zero to one transitions,
+ * possibly allowing updates to made on NOR devices that have
+ * no CRC enabled.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+static uint8_t smart_get_wear_level(FAR struct smart_struct_s *dev, uint16_t block)
+{
+ uint8_t bits;
+
+ bits = dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE];
+ if (block & 0x01)
+ {
+ /* Use the upper nibble */
+
+ bits >>= 4;
+ }
+ else
+ {
+ /* Use the lower nibble */
+
+ bits &= 0x0F;
+ }
+
+ /* Lookup and return the level using the BitToLevel map */
+
+ return gWearBitToLevelMap4[bits];
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_find_wear_minmax
+ *
+ * Description: Find the minimum and maximum wear levels. This is used when
+ * we increment the wear level of a minimum value block so that
+ * we can detect if a new minimum exists and perform normalization
+ * of the wear-levels.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+static void smart_find_wear_minmax(FAR struct smart_struct_s *dev)
+{
+ uint16_t x;
+ unsigned char level;
+
+ dev->minwearlevel = 15;
+ dev->maxwearlevel = 0;
+
+ /* Loop through all erase blocks and find min / max level */
+
+ for (x = 0; x < dev->geo.neraseblocks; x++)
+ {
+ /* Find wear level of the minimum worn block */
+
+ level = smart_get_wear_level(dev, x);
+ if (level < dev->minwearlevel)
+ {
+ dev->minwearlevel = level;
+ }
+
+ /* Find wear level of the maximum worn block */
+
+ if (level > dev->maxwearlevel)
+ {
+ dev->maxwearlevel = level;
+ }
+ }
+
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ /* Also adjust the erase counts */
+ level = 255;
+ for (x = 0; x < dev->geo.neraseblocks; x++)
+ {
+ if (dev->erasecounts[x] < level)
+ {
+ level = dev->erasecounts[x];
+ }
+ }
+
+ if (level != 0)
+ {
+ for (x = 0; x < dev->geo.neraseblocks; x++)
+ {
+ dev->erasecounts[x] -= level;
+ }
+ }
+
+#endif
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_set_wear_level
+ *
+ * Description: Sets the wear level of the specified block. The wear level
+ * is a 4-bit field packed 2 entries per byte and is mapped to
+ * a bit field which minimizes the number of 0 to 1 transitions
+ * such that entries can be updated on a NOR flash withough the
+ * need to relocated the format sector (assuming CRC is not
+ * enabled, in which case a relocated is needed for ANY change).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+static int smart_set_wear_level(FAR struct smart_struct_s *dev, uint16_t block,
+ uint8_t level)
+{
+ uint8_t bits, oldlevel;
+
+ /* Get the old wear level to test if we need to update min / max */
+
+ oldlevel = smart_get_wear_level(dev, block);
+
+ /* Get the bit map for this wear level from the static map array */
+
+ if (level > 15)
+ {
+ dbg("Fatal Design Error! Wear level > 15, block=%d\n", block);
+
+ /* This is a design flaw, but we still allow processing, otherwise we
+ * will corrupt the volume. It's better to have a few blocks that are
+ * worn a bit more than to create an error condition on the volume.
+ *
+ * Set the level to the maximum value and add to the un-even wear count
+ * to keep track of the number of times this has happened.
+ */
+
+ level = 15;
+ dev->uneven_wearcount++;
+ }
+
+ bits = gWearLevelToBitMap4[level];
+
+ if (block & 0x01)
+ {
+ /* Use the upper nibble */
+
+ dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] &= 0x0F;
+ dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] |= bits << 4;
+ }
+ else
+ {
+ /* Use the lower nibble */
+
+ dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] &= 0xF0;
+ dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] |= bits;
+ }
+
+ /* Mark wear bits as dirty */
+
+ dev->wearflags |= SMART_WEARFLAGS_WRITE_NEEDED;
+
+ /* Test if min / max need to be updated */
+
+ if (oldlevel + 1 == level)
+ {
+ /* Test if max needs to be updated */
+
+ if (level > dev->maxwearlevel)
+ {
+ dev->maxwearlevel = level;
+ }
+
+ /* Test if this was the min level. If it was, then
+ we need to rescan for min. */
+
+ if (oldlevel == dev->minwearlevel)
+ {
+ smart_find_wear_minmax(dev);
+
+ if (oldlevel != dev->minwearlevel)
+ fvdbg("##### New min wear level = %d\n", dev->minwearlevel);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/****************************************************************************
* Name: smart_scan
*
* Description: Performs a scan of the MTD device searching for format
@@ -623,48 +1763,75 @@ errout:
*
****************************************************************************/
-static int smart_scan(struct smart_struct_s *dev)
+static int smart_scan(FAR struct smart_struct_s *dev)
{
int sector;
int ret;
- int offset;
uint16_t totalsectors;
- uint16_t sectorsize;
+ uint16_t sectorsize, prerelease;
uint16_t logicalsector;
+ uint16_t loser;
+ uint32_t readaddress;
+ uint32_t offset;
uint16_t seq1;
uint16_t seq2;
- size_t readaddress;
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[22];
- struct smart_multiroot_device_s *rootdirdev;
+ FAR struct smart_multiroot_device_s *rootdirdev;
#endif
fvdbg("Entry\n");
- /* Read the 1st header from the device. We always keep the
- * 1st sector's header's sectorsize field accurate, even
- * after we erase an MTD block/sector */
+ /* 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 seach 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.
+ */
- ret = MTD_READ(dev->mtd, 0, sizeof(struct smart_sect_header_s),
- (uint8_t *) &header);
- if (ret != sizeof(struct smart_sect_header_s))
+ sectorsize = 0xFFFF;
+ offset = 16384;
+
+ while (sectorsize == 0xFFFF)
{
- goto err_out;
- }
+ readaddress = 0;
- /* Now set the sectorsize and other sectorsize derived variables */
+ while (readaddress < dev->erasesize * dev->geo.neraseblocks)
+ {
+ /* Read the next sector from the device */
- if (header.status == CONFIG_SMARTFS_ERASEDSTATE)
- {
- sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
- }
- else
- {
- sectorsize = (header.status & SMART_STATUS_SIZEBITS) << 7;
+ ret = MTD_READ(dev->mtd, 0, 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 = (header.status & SMART_STATUS_SIZEBITS) << 7;
+ break;
+ }
+
+ readaddress += offset;
+ }
+
+ offset >>= 1;
+ if (offset < 256 && sectorsize == 0xFFFF)
+ {
+ sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
+ }
}
+ /* Now set the sectorsize and other sectorsize derived variables */
+
ret = smart_setsectorsize(dev, sectorsize);
if (ret != OK)
{
@@ -673,24 +1840,45 @@ static int smart_scan(struct smart_struct_s *dev)
/* Initialize the device variables */
- totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
+ totalsectors = dev->totalsectors;
dev->formatstatus = SMART_FMT_STAT_NOFMT;
- dev->freesectors = totalsectors;
+ dev->freesectors = dev->availSectPerBlk * dev->geo.neraseblocks;
+ dev->releasesectors = 0;
/* Initialize the freecount and releasecount arrays */
for (sector = 0; sector < dev->neraseblocks; sector++)
{
- dev->freecount[sector] = dev->sectorsPerBlk;
- dev->releasecount[sector] = 0;
+ 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 */
@@ -705,7 +1893,7 @@ static int smart_scan(struct smart_struct_s *dev)
/* Read the header for this sector */
ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s),
- (uint8_t *) &header);
+ (FAR uint8_t *) &header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
@@ -713,7 +1901,7 @@ static int smart_scan(struct smart_struct_s *dev)
/* Get the logical sector number for this physical sector */
- logicalsector = *((uint16_t *) header.logicalsector);
+ logicalsector = *((FAR uint16_t *) header.logicalsector);
#if CONFIG_SMARTFS_ERASEDSTATE == 0x00
if (logicalsector == 0)
{
@@ -733,7 +1921,11 @@ static int smart_scan(struct smart_struct_s *dev)
* 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,
@@ -743,7 +1935,16 @@ static int smart_scan(struct smart_struct_s *dev)
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;
}
@@ -772,7 +1973,7 @@ static int smart_scan(struct smart_struct_s *dev)
/* Read the sector data */
ret = MTD_READ(dev->mtd, readaddress, 32,
- (uint8_t*) dev->rwbuffer);
+ (FAR uint8_t*) dev->rwbuffer);
if (ret != 32)
{
fdbg("Error reading physical sector %d.\n", sector);
@@ -792,8 +1993,6 @@ static int smart_scan(struct smart_struct_s *dev)
continue;
}
- /* TODO: May want to validate / save the erase block aging info */
-
/* Mark the volume as formatted and set the sector size */
dev->formatstatus = SMART_FMT_STAT_FORMATTED;
@@ -824,7 +2023,8 @@ static int smart_scan(struct smart_struct_s *dev)
* the SMART device structure and the root directory number.
*/
- rootdirdev = (struct smart_multiroot_device_s*) kmm_malloc(sizeof(*rootdirdev));
+ rootdirdev = (struct smart_multiroot_device_s*) smart_malloc(dev,
+ sizeof(*rootdirdev), "Root Dir");
if (rootdirdev == NULL)
{
fdbg("Memory alloc failed\n");
@@ -836,55 +2036,138 @@ static int smart_scan(struct smart_struct_s *dev)
rootdirdev->dev = dev;
rootdirdev->rootdirnum = x;
- (void)register_blockdriver(dev->rwbuffer, &g_bops, 0, rootdirdev);
+ ret = register_blockdriver(dev->rwbuffer, &g_bops, 0, rootdirdev);
/* Inode private data is a reference to the SMART device structure */
- (void)register_blockdriver(devname, &g_bops, 0, rootdirdev);
+ 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
+ * the same logical sector. Use the sequence number information
* to resolve who wins.
*/
- uint16_t loser;
- seq2 = *((uint16_t *) header.seq);
+#if SMART_STATUS_VERSION == 1
+ if (header.status & 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),
- (uint8_t *) &header);
+ (FAR uint8_t *) &header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
}
- seq1 = *((uint16_t *) header.seq);
+#if SMART_STATUS_VERSION == 1
+ if (header.status & SMART_STATUS_CRC)
+ {
+ seq1 = header.seq;
+ }
+ else
+ {
+ seq1 = *((FAR uint16_t *) &header.seq);
+ }
+#else
+ seq1 = header.seq;
+#endif
/* Now determine who wins */
- if (seq1 > 0xFFF0 && seq2 < 10)
+ if ((seq1 > 0xFFF0 && seq2 < 10) || seq2 > seq1)
{
- /* Seq 2 is the winner ... we assume it wrapped */
-
- loser = dev->sMap[logicalsector];
- dev->sMap[logicalsector] = sector;
- }
- else if (seq2 > seq1)
- {
- /* Seq 2 is bigger, so it's the winner */
+ /* Seq 2 is the winner ... bigger or it wrapped */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
loser = dev->sMap[logicalsector];
dev->sMap[logicalsector] = sector;
+#else
+ loser = dupsector;
+#endif
}
else
{
@@ -897,7 +2180,7 @@ static int smart_scan(struct smart_struct_s *dev)
readaddress = loser * dev->mtdBlksPerSector * dev->geo.blocksize;
ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s),
- (uint8_t *) &header);
+ (FAR uint8_t *) &header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
@@ -917,16 +2200,114 @@ static int smart_scan(struct smart_struct_s *dev)
}
}
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
/* Update the logical to physical sector map */
dev->sMap[logicalsector] = sector;
+#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, sector, __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, (uint8_t *) dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ fdbg("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!!! */
+
+ fdbg("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);
+ smart_add_count(dev, dev->freecount, newsector / dev->sectorsPerBlk, -1);
+ smart_add_count(dev, dev->releasecount, sector / dev->sectorsPerBlk, 1);
+#endif
+
+ }
}
+#endif /* CONFIG_MTD_SMART_CONVERT_WEAR_FORMAT */
+#endif /* CONFIG_MTD_SMART_WEAR_LEVEL && SMART_STATUS_VERSION == 1 */
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ /* Read the wear leveling status bits */
+
+ smart_read_wearstatus(dev);
+#endif
+
fdbg("SMART Scan\n");
fdbg(" Erase size: %10d\n", dev->sectorsPerBlk * dev->sectorsize);
fdbg(" Erase count: %10d\n", dev->neraseblocks);
fdbg(" Sect/block: %10d\n", dev->sectorsPerBlk);
fdbg(" MTD Blk/Sect: %10d\n", dev->mtdBlksPerSector);
+#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
+ fdbg(" Allocations:\n");
+ for (sector = 0; sector < SMART_MAX_ALLOCS; sector++)
+ {
+ if (dev->alloc[sector].ptr != NULL)
+ {
+ fdbg(" %s: %d\n", dev->alloc[sector].name, dev->alloc[sector].size);
+ }
+ }
+#endif
ret = OK;
@@ -943,16 +2324,15 @@ err_out:
****************************************************************************/
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
-static inline int smart_getformat(struct smart_struct_s *dev,
- struct smart_format_s *fmt,
+static inline int smart_getformat(FAR struct smart_struct_s *dev,
+ FAR struct smart_format_s *fmt,
uint8_t rootdirnum)
#else
-static inline int smart_getformat(struct smart_struct_s *dev,
- struct smart_format_s *fmt)
+static inline int smart_getformat(FAR struct smart_struct_s *dev,
+ FAR struct smart_format_s *fmt)
#endif
{
int ret;
- int x;
fvdbg("Entry\n");
DEBUGASSERT(fmt);
@@ -987,7 +2367,8 @@ static inline int smart_getformat(struct smart_struct_s *dev,
fmt->sectorsize = dev->sectorsize;
fmt->availbytes = dev->sectorsize - sizeof(struct smart_sect_header_s);
- fmt->nsectors = dev->neraseblocks * dev->sectorsPerBlk;
+ fmt->nsectors = dev->totalsectors;
+
fmt->nfreesectors = dev->freesectors;
fmt->namesize = dev->namesize;
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
@@ -997,10 +2378,7 @@ static inline int smart_getformat(struct smart_struct_s *dev,
/* Add the released sectors to the reported free sector count */
- for (x = 0; x < dev->neraseblocks; x++)
- {
- fmt->nfreesectors += dev->releasecount[x];
- }
+ fmt->nfreesectors += dev->releasesectors;
/* Subtract the reserved sector count */
@@ -1013,6 +2391,394 @@ err_out:
}
/****************************************************************************
+ * Name: smart_erase_block_if_empty
+ *
+ * Description: Tests the specified erase block if it contains all free or
+ * released sectors and erases it.
+ *
+ ****************************************************************************/
+
+static void smart_erase_block_if_empty(FAR struct smart_struct_s *dev,
+ uint16_t block, uint8_t forceerase)
+{
+ uint16_t freecount, releasecount, prerelease;
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ releasecount = smart_get_count(dev, dev->releasecount, block);
+ freecount = smart_get_count(dev, dev->freecount, block);
+#else
+ releasecount = dev->releasecount[block];
+ freecount = dev->freecount[block];
+#endif
+
+ if ((freecount + releasecount == dev->availSectPerBlk && freecount < 1) || forceerase)
+ {
+ /* Erase the block */
+
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ dev->unusedsectors += freecount;
+ dev->blockerases++;
+#endif
+ MTD_ERASE(dev->mtd, block, 1);
+
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ if (dev->erasecounts)
+ {
+ dev->erasecounts[block]++;
+ }
+#endif
+
+ /* If wear leveling enabled, then we must add one to the wear status */
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ smart_set_wear_level(dev, block, smart_get_wear_level(dev, block) + 1);
+#endif
+
+ /* If we have a device with 65534 sectors, then disallow the last two
+ * physical sector if this is the last erase block on the device.
+ */
+
+ if (block == dev->geo.neraseblocks - 1 && dev->totalsectors == 65534)
+ {
+ prerelease = 2;
+ }
+ else
+ {
+ prerelease = 0;
+ }
+
+ dev->freesectors += dev->availSectPerBlk - prerelease - freecount;
+ dev->releasesectors -= releasecount - prerelease;
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_set_count(dev, dev->releasecount, block, prerelease);
+ smart_set_count(dev, dev->freecount, block, dev->availSectPerBlk-prerelease);
+#else
+ dev->releasecount[block] = prerelease;
+ dev->freecount[block] = dev->availSectPerBlk - prerelease;
+#endif /* CONFIG_MTD_SMART_PACK_COUNTS */
+
+ /* Now that we have erased this block and updated the release / free counts,
+ * if we are in WEAR LEVELING enabled mode, we must check if this erase block's
+ * wear level has reached the threshold to warrant moving a minimum wear level
+ * block's data into it (i.e. relocating static data to this block so it will
+ * be worn less).
+ */
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ if (!forceerase)
+ {
+ smart_relocate_static_data(dev, block);
+ }
+#endif
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
+ {
+ fdbg(" ...while eraseing block %d\n", block);
+ }
+#endif
+ }
+}
+
+/****************************************************************************
+ * Name: smart_relocate_static_data
+ *
+ * Description: Tests if the specified block has reached the wear threshold
+ * for static data relocation and if it has, relocates a less
+ * worn block to it.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+static int smart_relocate_static_data(FAR struct smart_struct_s *dev, uint16_t block)
+{
+ uint16_t freecount, x, sector, minblock;
+ uint16_t nextsector, newsector, mincount;
+ int ret;
+ FAR struct smart_sect_header_s *header;
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ FAR struct smart_allocsector_s *allocsector;
+#endif
+
+ /* Now that we have erased this block and updated the release / free counts,
+ * if we are in WEAR LEVELING enabled mode, we must check if this erase block's
+ * wear level has reached the threshold to warrant moving a minimum wear level
+ * block's data into it (i.e. relocating static data to this block so it will
+ * be worn less).
+ */
+
+ ret = OK;
+ header = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
+ {
+ fdbg(" ...about to relocate static data %d\n", block);
+ }
+#endif
+
+ if (smart_get_wear_level(dev, block) >= SMART_WEAR_FULL_RELOCATE_THRESHOLD)
+ {
+ /* Okay, this block is getting too worn. Move a minimum wear level
+ * block to it in it's entirity.
+ */
+
+ /* Scan all erase blocks (or until we find a minimum wear level block
+ * with no free + released blocks.
+ */
+
+ freecount = dev->sectorsPerBlk + 1;
+ minblock = dev->geo.neraseblocks;
+ mincount = 0;
+ for (x = 0; x < dev->geo.neraseblocks; x++)
+ {
+ if (smart_get_wear_level(dev, x) == dev->minwearlevel)
+ {
+ /* Don't allow the format sector or directory sector to
+ * be moved into a worn block. First get the format and
+ * dir sectors.
+ */
+
+ mincount++;
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ if (smart_get_count(dev, dev->releasecount, x) +
+ smart_get_count(dev, dev->freecount, x) < freecount)
+ {
+ freecount = smart_get_count(dev, dev->releasecount, x) +
+ smart_get_count(dev, dev->freecount, x);
+ minblock = x;
+ }
+#else
+ if (dev->freecount[x] + dev->releasecount[x] < freecount)
+ {
+ freecount = dev->freecount[x] + dev->releasecount[x];
+ minblock = x;
+ }
+#endif
+
+ /* Break if freecount reaches zero */
+
+ if (freecount == 0)
+ {
+ /* We found a minimum wear-level block with no free sectors.
+ * relocate this block to the more highly worn block.
+ */
+
+ break;
+ }
+ }
+ }
+
+ /* Okay, now move block 'x' to block 'block' and erase block 'x' */
+
+ x = minblock;
+
+ /* We are resuing nextsector and newsector variables here simply as
+ * variables for displaying debug data. I have learned through my
+ * years of programming that this is a really good way to create
+ * spaghetti code, but I didn't want to add stack variables just
+ * for debug data, and I *know* these variables aren't being used
+ * yet.
+ */
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ nextsector = smart_get_count(dev, dev->freecount, x);
+ newsector = smart_get_count(dev, dev->releasecount, x);
+#else
+ nextsector = dev->freecount[x];
+ newsector = dev->releasecount[x];
+#endif
+ fvdbg("Moving block %d, wear %d, free %d, released %d to block %d, wear %d\n",
+ x, smart_get_wear_level(dev, x),
+ nextsector, newsector,
+ block, smart_get_wear_level(dev, block));
+
+ nextsector = block * dev->sectorsPerBlk;
+ for (sector = x * dev->sectorsPerBlk; sector <
+ x * dev->sectorsPerBlk + dev->availSectPerBlk; sector++)
+ {
+ /* Read the next sector from this erase block */
+
+ ret = MTD_BREAD(dev->mtd, sector * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ fdbg("Error reading sector %d\n", sector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Test if the block is in use */
+
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+
+ /* Check if there is a temporary alloc for this physical sector */
+
+ allocsector = dev->allocsector;
+ while (allocsector)
+ {
+ if (allocsector->physical == sector)
+ {
+ break;
+ }
+
+ allocsector = allocsector->next;
+ }
+
+ /* If we found a temp allocation, just update the mapped physical
+ * location and move on to the next block ... there is no data to
+ * move yet.
+ */
+
+ if (allocsector)
+ {
+ /* Get next sector from 'block' */
+
+ newsector = nextsector++;
+ if (newsector == 0xFFFF)
+ {
+ /* Unable to find a free sector!!! */
+
+ fdbg("Can't find a free sector for relocation\n");
+ ret = -ENOSPC;
+ goto errout;
+ }
+
+ /* Update the temporary allocation's physical sector */
+
+ allocsector->physical = newsector;
+ *((FAR uint16_t *) header->logicalsector) = allocsector->logical;
+ }
+ else
+#endif
+ {
+ if (((header->status & SMART_STATUS_COMMITTED) ==
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) ||
+ ((header->status & SMART_STATUS_RELEASED) !=
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)))
+ {
+ /* This sector doesn't have live data (free or released).
+ * just continue to the next sector and don't move it.
+ */
+
+ continue;
+ }
+
+ /* Find a new sector where it can live, NOT in this erase block */
+
+ newsector = nextsector++;
+
+ /* Relocate the sector data */
+
+ if ((ret = smart_relocate_sector(dev, sector, newsector)) < 0)
+ {
+ goto errout;
+ }
+ }
+
+ dev->freesectors--;
+
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
+ dev->sMap[*((FAR uint16_t *) header->logicalsector)] = newsector;
+#else
+ smart_update_cache(dev, *((FAR uint16_t *) header->logicalsector), newsector);
+#endif
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_add_count(dev, dev->freecount, block, -1);
+#else
+ dev->freecount[block]--;
+#endif /* CONFIG_MTD_SMART_PACK_COUNTS */
+ }
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
+ {
+ fdbg(" ...about to erase static block %d\n", block);
+ }
+#endif
+
+ /* Now erase the block we just relocated, force erasing it */
+
+ smart_erase_block_if_empty(dev, x, TRUE);
+ }
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
+ {
+ fdbg(" ...done erasing static block %d\n", block);
+ }
+#endif
+
+errout:
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_calc_sector_crc
+ *
+ * Description: Calculate the CRC value for the sector data in the RW buffer
+ * based on the configured CRC size.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+static crc_t smart_calc_sector_crc(FAR struct smart_struct_s *dev)
+{
+ crc_t crc = 0;
+
+#ifdef CONFIG_SMART_CRC_8
+
+ /* Calculate CRC on data region of the sector */
+
+ crc = crc8((uint8_t *) &dev->rwbuffer[sizeof(struct smart_sect_header_s)],
+ dev->mtdBlksPerSector * dev->geo.blocksize - sizeof(struct smart_sect_header_s));
+
+ /* Add logical sector number and seq to the CRC calculation */
+
+ crc = crc8part((uint8_t *) dev->rwbuffer, 3, crc);
+
+ /* Add status to the CRC calculation */
+
+ crc = crc8part((uint8_t *) &dev->rwbuffer[offsetof(struct smart_sect_header_s,
+ status)], 1, crc);
+
+#elif defined(CONFIG_SMART_CRC_16)
+ /* Calculate CRC on data region of the sector */
+
+ crc = crc16((uint8_t *) &dev->rwbuffer[sizeof(struct smart_sect_header_s)],
+ dev->mtdBlksPerSector * dev->geo.blocksize - sizeof(struct smart_sect_header_s));
+
+ /* Add logical sector number to the CRC calculation */
+
+ crc = crc16part((uint8_t *) dev->rwbuffer, 2, crc);
+
+ /* Add status and seq to the CRC calculation */
+
+ crc = crc16part((uint8_t *) &dev->rwbuffer[offsetof(struct smart_sect_header_s,
+ status)], 2, crc);
+
+#elif defined(CONFIG_SMART_CRC_32)
+ /* Calculate CRC on data region of the sector */
+
+ crc = crc32((uint8_t *) &dev->rwbuffer[sizeof(struct smart_sect_header_s)],
+ dev->mtdBlksPerSector * dev->geo.blocksize - sizeof(struct smart_sect_header_s));
+
+ /* Add logical sector number, status and seq to the CRC calculation */
+
+ crc = crc32part((uint8_t *) dev->rwbuffer, 6, crc);
+#else
+#error "Unknown CRC size!"
+#endif
+
+ return crc;
+}
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+
+/****************************************************************************
* Name: smart_llformat
*
* Description: Performs a low-level format of the flash device. This
@@ -1022,17 +2788,38 @@ err_out:
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
-static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg)
+static inline int smart_llformat(FAR struct smart_struct_s *dev, unsigned long arg)
{
- struct smart_sect_header_s *sectorheader;
- size_t wrcount;
- size_t totalsectors;
- int x;
- int ret;
- uint8_t sectsize;
+ FAR struct smart_sect_header_s *sectorheader;
+ size_t wrcount;
+ int x;
+ int ret;
+ uint8_t sectsize, prerelease;
fvdbg("Entry\n");
+ smart_setsectorsize(dev, CONFIG_MTD_SMART_SECTOR_SIZE);
+
+ /* Check for invalid format */
+ if (dev->erasesize == 0)
+ {
+ if (dev->geo.erasesize == 0)
+ {
+ dev->erasesize = 262144;
+ }
+ else
+ {
+ dev->erasesize = dev->geo.erasesize;
+ }
+
+ dbg("ERROR: Invalid geometery ... Sectors per erase block must be 256 or less\n");
+ dbg(" Erase block size = %d\n", dev->erasesize);
+ dbg(" Sector size = %d\n", dev->sectorsize);
+ dbg(" Sectors/erase block = %d\n", dev->erasesize / dev->sectorsize);
+
+ return -EINVAL;
+ }
+
/* Erase the MTD device */
ret = MTD_IOCTL(dev->mtd, MTDIOC_BULKERASE, 0);
@@ -1041,28 +2828,47 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg)
return ret;
}
- /* Now construct a logical sector zero header to write to the device.
- * We fill it with zero so when we add sector aging, all the sector
- * ages will already be initialized to zero without needing special
- * logic to deal with a 0xFF erased-state value.
- */
+ /* Now construct a logical sector zero header to write to the device. */
- sectorheader = (struct smart_sect_header_s *) dev->rwbuffer;
- memset(dev->rwbuffer, 0, dev->sectorsize);
- memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, SMARTFS_FMT_AGING_POS);
- *((uint16_t *) sectorheader->seq) = 0;
+ sectorheader = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+ memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, dev->sectorsize);
+#if SMART_STATUS_VERSION == 1
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ /* CRC enabled. Using an 8-bit sequence number */
+
+ sectorheader->seq = 0;
+#else
+ /* CRC not enabled. Using a 16-bit sequence number */
+
+ *((FAR uint16_t *) &sectorheader->seq) = 0;
+#endif
+#else /* SMART_STATUS_VERSION == 1 */
+ sectorheader->seq = 0;
+#endif /* SMART_STATUS_VERSION == 1 */
+
+ /* Set the sector size of this sector */
sectsize = (CONFIG_MTD_SMART_SECTOR_SIZE >> 9) << 2;
+
+ /* Set the sector logical sector to zero and setup the header status */
+
#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF )
- *((uint16_t *) sectorheader->logicalsector) = 0;
+ *((FAR uint16_t *) sectorheader->logicalsector) = 0;
sectorheader->status = (uint8_t) ~(SMART_STATUS_COMMITTED | SMART_STATUS_VERBITS |
SMART_STATUS_SIZEBITS) | SMART_STATUS_VERSION |
sectsize;
-#else
- *((uint16_t *) sectorheader->logicalsector) = 0xFFFF;
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ sectorheader->status &= ~SMART_STATUS_CRC;
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+
+#else /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */
+ *((FAR uint16_t *) sectorheader->logicalsector) = 0xFFFF;
sectorheader->status = (uint8_t) (SMART_STATUS_COMMITTED | SMART_STATUS_VERSION |
sectsize);
-#endif
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ sectorheader->status |= SMART_STATUS_CRC;
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+#endif /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */
/* Now add the format signature to the sector */
@@ -1078,10 +2884,18 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg)
dev->rwbuffer[SMART_FMT_ROOTDIRS_POS] = (uint8_t) arg;
+#ifdef CONFIG_SMART_CRC_8
+ sectorheader->crc8 = smart_calc_sector_crc(dev);
+#elif defined(CONFIG_SMART_CRC_16)
+ *((uint16_t *) sectorheader->crc16) = smart_calc_sector_crc(dev);
+#elif defined(CONFIG_SMART_CRC_32)
+ *((uint32_t *) sectorheader->crc32) = smart_calc_sector_crc(dev);
+#endif
+
/* Write the sector to the flash */
wrcount = MTD_BWRITE(dev->mtd, 0, dev->mtdBlksPerSector,
- (uint8_t *) dev->rwbuffer);
+ (FAR uint8_t *) dev->rwbuffer);
if (wrcount != dev->mtdBlksPerSector)
{
/* The block is not empty!! What to do? */
@@ -1102,30 +2916,56 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg)
}
dev->formatstatus = SMART_FMT_STAT_UNKNOWN;
- dev->freesectors = dev->neraseblocks * dev->sectorsPerBlk - 1;
+ dev->freesectors = dev->availSectPerBlk * dev->geo.neraseblocks - 1;
+ dev->releasesectors = 0;
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ dev->uneven_wearcount = 0;
+#endif
+
+ /* Initialize the released and free counts */
+
for (x = 0; x < dev->neraseblocks; x++)
{
- /* Initialize the released and free counts */
+ /* Test for a geometry with 65536 sectors. We allow this, though
+ we never use the last two sectors in this mode.
+ */
- dev->releasecount[x] = 0;
- dev->freecount[x] = dev->sectorsPerBlk;
+ if (x == dev->neraseblocks && dev->totalsectors == 65534)
+ {
+ prerelease = 2;
+ }
+ else
+ {
+ prerelease = 0;
+ }
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_set_count(dev, dev->releasecount, x, prerelease);
+ smart_set_count(dev, dev->freecount, x, dev->availSectPerBlk-prerelease);
+#else
+ dev->releasecount[x] = prerelease;
+ dev->freecount[x] = dev->availSectPerBlk-prerelease;
+#endif
}
/* Account for the format sector */
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_set_count(dev, dev->freecount, 0, dev->availSectPerBlk - 1);
+#else
dev->freecount[0]--;
+#endif
/* Now initialize the logical to physical sector map */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->sMap[0] = 0; /* Logical sector zero = physical sector 0 */
-
- totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
- for (x = 1; x < totalsectors; x++)
+ for (x = 1; x < dev->totalsectors; x++)
{
/* Mark all other logical sectors as non-existant */
dev->sMap[x] = -1;
}
+#endif
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
@@ -1143,6 +2983,380 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg)
#endif /* CONFIG_FS_WRITABLE */
/****************************************************************************
+ * Name: smart_relocate_sector
+ *
+ * Description: Relocates the specified sector to the new sector location.
+ *
+ ****************************************************************************/
+
+static int smart_relocate_sector(FAR struct smart_struct_s *dev,
+ uint16_t oldsector, uint16_t newsector)
+{
+ int ret;
+ size_t offset;
+ FAR struct smart_sect_header_s *header;
+ uint8_t newstatus;
+
+ header = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+
+ /* Increment the sequence number and clear the "commit" flag */
+
+#if SMART_STATUS_VERSION == 1
+ if (header->status & SMART_STATUS_CRC)
+ {
+#endif
+ /* Using 8-bit sequence */
+
+ header->seq++;
+ if (header->seq == 0xFF)
+ {
+ header->seq = 1;
+ }
+#if SMART_STATUS_VERSION == 1
+ }
+ else
+ {
+ /* Using 16-bit sequence and no CRC */
+
+ (*((FAR uint16_t *) &header->seq))++;
+ if (*((FAR uint16_t *) &header->seq) == 0xFFFF)
+ {
+ *((FAR uint16_t *) &header->seq) = 1;
+ }
+ }
+#endif
+
+ /* When CRC is enabled, we must pre-commit the sector and also
+ * calculate an updated CRC for the sector prior to writing
+ * since we changed the sequence number.
+ */
+
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+
+ /* First pre-commit the sector */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status &= ~(SMART_STATUS_COMMITTED | SMART_STATUS_CRC);
+#else
+ header->status |= SMART_STATUS_COMMITTED | SMART_STATUS_CRC;
+#endif
+
+ /* Now calculate the new CRC */
+
+#ifdef CONFIG_SMART_CRC_8
+ header->crc8 = smart_calc_sector_crc(dev);
+#elif defined(CONFIG_SMART_CRC_16)
+ *((uint16_t *) header->crc16) = smart_calc_sector_crc(dev);
+#elif defined(CONFIG_SMART_CRC_32)
+ *((uint32_t *) header->crc32) = smart_calc_sector_crc(dev);
+#endif
+
+ /* Write the data to the new physical sector location */
+
+ ret = MTD_BWRITE(dev->mtd, newsector * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer);
+
+#else /* CONFIG_MTD_SMART_ENABLE_CRC */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status |= SMART_STATUS_COMMITTED;
+#else
+ header->status &= ~SMART_STATUS_COMMITTED;
+#endif
+
+ /* Write the data to the new physical sector location */
+
+ ret = MTD_BWRITE(dev->mtd, newsector * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer);
+
+ /* Commit the sector */
+
+ offset = newsector * dev->mtdBlksPerSector * dev->geo.blocksize +
+ offsetof(struct smart_sect_header_s, status);
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ newstatus = header->status & ~SMART_STATUS_COMMITTED;
+#else
+ newstatus = header->status | SMART_STATUS_COMMITTED;
+#endif
+ ret = smart_bytewrite(dev, offset, 1, &newstatus);
+ if (ret < 0)
+ {
+ fdbg("Error %d committing new sector %d\n" -ret, newsector);
+ goto errout;
+ }
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+
+ /* Release the old physical sector */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ newstatus = header->status & ~(SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED);
+#else
+ newstatus = header->status | SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED;
+#endif
+ offset = oldsector * dev->mtdBlksPerSector * dev->geo.blocksize +
+ offsetof(struct smart_sect_header_s, status);
+ ret = smart_bytewrite(dev, offset, 1, &newstatus);
+ if (ret < 0)
+ {
+ fdbg("Error %d releasing old sector %d\n" -ret, oldsector);
+ }
+
+#ifndef CONFIG_MTD_SMART_ENABLE_CRC
+errout:
+#endif
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smart_relocate_block
+ *
+ * Description: Relocates the specified MTD erase block by moving any
+ * active sectors to a different erase block and then erases
+ * the selected block.
+ *
+ ****************************************************************************/
+
+static int smart_relocate_block(FAR struct smart_struct_s *dev, uint16_t block)
+{
+ uint16_t newsector, oldrelease;
+ int x;
+ int ret;
+ FAR struct smart_sect_header_s *header;
+ uint8_t prerelease;
+ uint16_t freecount;
+#if defined(CONFIG_SMART_LOCAL_CHECKFREE) && defined(CONFIG_DEBUG_FS)
+ uint16_t releasecount;
+#endif
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ FAR struct smart_allocsector_s *allocsector;
+#endif
+
+ /* Perform collection on block with the most released sectors.
+ * First mark the block as having no free sectors so we don't
+ * try to move sectors into the block we are trying to erase.
+ */
+
+ header = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
+ {
+ fdbg(" ...while relocating block %d, free=%d\n", block, dev->freesectors);
+ }
+#endif
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ freecount = smart_get_count(dev, dev->freecount, block);
+
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+#if defined(CONFIG_SMART_LOCAL_CHECKFREE) && defined(CONFIG_DEBUG_FS)
+ releasecount = smart_get_count(dev, dev->releasecount, block);
+#endif
+#endif
+
+ /* Ensure we aren't relocating a block containing the only free sectors */
+
+ if (freecount >= dev->freesectors)
+ {
+ fdbg("Program bug! Relocating the only block (%d) with free sectors!\n", block);
+ ret = -EIO;
+ goto errout;
+ }
+
+ smart_set_count(dev, dev->freecount, block, 0);
+
+#else /* CONFIG_MTD_SMART_PACK_COUNTS */
+
+ freecount = dev->freecount[block];
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+#if defined(CONFIG_SMART_LOCAL_CHECKFREE) && defined(CONFIG_DEBUG_FS)
+ releasecount = dev->releasecount[block];
+#endif
+#endif
+ dev->freecount[block] = 0;
+#endif
+
+ /* Next move all live data in the block to a new home. */
+
+ for (x = block * dev->sectorsPerBlk; x <
+ block * dev->sectorsPerBlk + dev->availSectPerBlk; x++)
+ {
+ /* Read the next sector from this erase block */
+
+ ret = MTD_BREAD(dev->mtd, x * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ fdbg("Error reading sector %d\n", x);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Test if the block is in use */
+
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+
+ /* Check if there is a temporary alloc for this physical sector */
+
+ allocsector = dev->allocsector;
+ while (allocsector)
+ {
+ if (allocsector->physical == x)
+ break;
+ allocsector = allocsector->next;
+ }
+
+ /* If we found a temp allocation, just update the mapped physical
+ * location and move on to the next block ... there is no data to
+ * move yet.
+ */
+
+ if (allocsector)
+ {
+ newsector = smart_findfreephyssector(dev, FALSE);
+ if (newsector == 0xFFFF)
+ {
+ /* Unable to find a free sector!!! */
+
+ fdbg("Can't find a free sector for relocation\n");
+ ret = -ENOSPC;
+ goto errout;
+ }
+
+ /* Update the temporary allocation's physical sector */
+
+ allocsector->physical = newsector;
+ *((FAR uint16_t *) header->logicalsector) = allocsector->logical;
+ }
+ else
+#endif
+ {
+ if (((header->status & SMART_STATUS_COMMITTED) ==
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) ||
+ ((header->status & SMART_STATUS_RELEASED) !=
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)))
+ {
+ /* This sector doesn't have live data (free or released).
+ * just continue to the next sector and don't move it.
+ */
+
+ continue;
+ }
+
+ /* Find a new sector where it can live, NOT in this erase block */
+
+ newsector = smart_findfreephyssector(dev, FALSE);
+ if (newsector == 0xFFFF)
+ {
+ /* Unable to find a free sector!!! */
+
+ fdbg("Can't find a free sector for relocation\n");
+ ret = -ENOSPC;
+ goto errout;
+ }
+
+ /* Relocate the sector data */
+
+ if ((ret = smart_relocate_sector(dev, x, newsector)) < 0)
+ goto errout;
+ }
+
+ /* Update the variables */
+
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
+ dev->sMap[*((FAR uint16_t *) header->logicalsector)] = newsector;
+#else
+ smart_update_cache(dev, *((FAR uint16_t *) header->logicalsector), newsector);
+#endif
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_add_count(dev, dev->freecount, newsector / dev->sectorsPerBlk, -1);
+#else
+ dev->freecount[newsector / dev->sectorsPerBlk]--;
+#endif
+ }
+
+ /* Now erase the erase block */
+
+ MTD_ERASE(dev->mtd, block, 1);
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ dev->unusedsectors += freecount;
+ dev->blockerases++;
+#endif
+
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ if (dev->erasecounts)
+ {
+ dev->erasecounts[block]++;
+ }
+#endif
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+
+ /* Update the new wear level count */
+
+ smart_set_wear_level(dev, block, smart_get_wear_level(dev, block) + 1);
+#endif
+
+ /* Update the free and release sectors for this erase block. */
+
+ if (x == dev->neraseblocks && dev->totalsectors == 65534)
+ {
+ /* We can't use the last two sectors on a 65536 sector device,
+ so "pre-release" them so they never get allocated.
+ */
+
+ prerelease = 2;
+ }
+ else
+ {
+ prerelease = 0;
+ }
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ oldrelease = smart_get_count(dev, dev->releasecount, block);
+ dev->freesectors += oldrelease-prerelease;
+ dev->releasesectors -= oldrelease-prerelease;
+ smart_set_count(dev, dev->freecount, block, dev->availSectPerBlk-prerelease);
+ smart_set_count(dev, dev->releasecount, block, prerelease);
+#else
+ oldrelease = dev->releasecount[block];
+ dev->freesectors += oldrelease-prerelease;
+ dev->releasesectors -= oldrelease-prerelease;
+ dev->freecount[block] = dev->availSectPerBlk-prerelease;
+ dev->releasecount[block] = prerelease;
+#endif
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
+ {
+ fdbg(" ...while relocating block %d, free=%d, release=%d, oldrelease=%d\n", block, freecount, releasecount, oldrelease);
+ }
+#endif
+
+ /* Test if this erase causes the block to reach the full relocate
+ * threshold requiring static data relocation.
+ */
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ smart_relocate_static_data(dev, block);
+#endif
+
+ return OK;
+
+errout:
+ /* Restore the block's freecount if error */
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_set_count(dev, dev->freecount, block, freecount);
+#else
+ dev->freecount[block] = freecount;
+#endif
+ return ret;
+}
+
+/****************************************************************************
* Name: smart_findfreephyssector
*
* Description: Finds a free physical sector based on free and released
@@ -1150,12 +3364,17 @@ static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg)
*
****************************************************************************/
-static int smart_findfreephyssector(struct smart_struct_s *dev)
+static int smart_findfreephyssector(FAR struct smart_struct_s *dev,
+ uint8_t canrelocate)
{
- uint16_t allocfreecount;
- uint16_t allocblock;
+ uint16_t count, allocfreecount, allocblock;
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ uint16_t wornfreecount, wornblock;
+ uint8_t wearlevel, wornlevel;
+ uint8_t maxwearlevel;
+#endif
uint16_t physicalsector;
- uint16_t x;
+ uint16_t x, block;
uint32_t readaddr;
struct smart_sect_header_s header;
int ret;
@@ -1164,58 +3383,211 @@ static int smart_findfreephyssector(struct smart_struct_s *dev)
* sector from. This is based on the number of free sectors
* available in each erase block. */
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+retry:
+#endif
allocfreecount = 0;
allocblock = 0xFFFF;
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ wornfreecount = 0;
+ wornblock = 0xFFFF;
+ wornlevel = 15;
+ maxwearlevel = 0;
+#endif
physicalsector = 0xFFFF;
+ if (++dev->lastallocblock >= dev->neraseblocks)
+ {
+ dev->lastallocblock = 0;
+ }
+
+ block = dev->lastallocblock;
for (x = 0; x < dev->neraseblocks; x++)
{
/* Test if this block has more free blocks than the
- * currently selected block */
+ * currently selected block
+ */
- if (dev->freecount[x] > allocfreecount)
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ count = smart_get_count(dev, dev->freecount, block);
+#else
+ count = dev->freecount[block];
+#endif
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ /* Keep track of the block with the max free sectors that is worn */
+
+ wearlevel = smart_get_wear_level(dev, block);
+ if (wearlevel >= SMART_WEAR_FULL_RELOCATE_THRESHOLD)
+ {
+ if (wearlevel > maxwearlevel && count > 0)
+ {
+ maxwearlevel = wearlevel;
+ }
+
+ if (count > wornfreecount || (count > 0 && wearlevel < wornlevel))
+ {
+ /* Keep track of this block. If there are only worn blocks with
+ * free sectors left, then we will use it.
+ */
+
+ if (x < dev->neraseblocks - 1 || !wornfreecount)
+ {
+ wornfreecount = count;
+ wornblock = block;
+ wornlevel = wearlevel;
+ }
+ }
+ }
+ else
+#endif
+
+ if (count > allocfreecount)
{
/* Assign this block to alloc from */
- allocblock = x;
- allocfreecount = dev->freecount[x];
+ if (x < dev->neraseblocks - 1 || !allocfreecount)
+ {
+ allocblock = block;
+ allocfreecount = count;
+ }
}
+ if (++block >= dev->neraseblocks)
+ block = 0;
}
/* Check if we found an allocblock. */
if (allocblock == 0xFFFF)
{
- /* No free sectors found! Bug? */
- return -EIO;
+ /* No un-worn blocks with free sectors */
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+
+ /* If we are allowed to relocate unworn blocks then do so now */
+
+ if (canrelocate && wornfreecount < (dev->sectorsPerBlk >> 2) && wornlevel == maxwearlevel)
+ {
+ /* Relocate up to 8 unworn blocks */
+
+ block = 0;
+ for (x = 0; x < 8; )
+ {
+ if (smart_get_wear_level(dev, block) < SMART_WEAR_FORCE_REORG_THRESHOLD)
+ {
+ if (smart_relocate_block(dev, block) < 0)
+ {
+ fdbg("Error relocating block while finding free phys sector\n");
+ return -1;
+ }
+
+ x++;
+ }
+
+ block++;
+ }
+ if (x > 0)
+ {
+ /* Disable relocate for retry */
+
+ canrelocate = FALSE;
+ goto retry;
+ }
+ }
+ else
+ {
+ dev->wearflags |= SMART_WEARFLAGS_FORCE_REORG;
+ }
+
+ /* Test if we found a worn block with free sectors */
+
+ if (wornblock != 0xFFFF)
+ {
+ allocblock = wornblock;
+ }
+ else
+#endif
+
+ {
+ dbg("Program bug! Expected a free sector, free=%d\n", dev->freesectors);
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+ printf("%d ", dev->freecount[x]);
+ }
+
+ /* No free sectors found! Bug? */
+
+ return -1;
+ }
}
/* Now find a free physical sector within this selected
* erase block to allocate. */
for (x = allocblock * dev->sectorsPerBlk;
- x < (allocblock+1) * dev->sectorsPerBlk; x++)
+ x < allocblock * dev->sectorsPerBlk + dev->availSectPerBlk; x++)
{
- /* Check if this physical sector is available */
+ /* Check if this physical sector is available. */
+
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ /* First check if there is a temporary alloc in place */
+
+ FAR struct smart_allocsector_s* allocsect;
+ allocsect = dev->allocsector;
+
+ while (allocsect)
+ {
+ if (allocsect->physical == x)
+ break;
+ allocsect = allocsect->next;
+ }
+
+ /* If we found this physical sector above, then continue on
+ * to the next physical sector in this block ... this one has
+ * a temporary allocation assigned.
+ */
+
+ if (allocsect)
+ {
+ continue;
+ }
+#endif
+
+ /* Now check on the physical media */
readaddr = x * dev->mtdBlksPerSector * dev->geo.blocksize;
ret = MTD_READ(dev->mtd, readaddr, sizeof(struct smart_sect_header_s),
- (uint8_t *) &header);
+ (FAR uint8_t *) &header);
if (ret != sizeof(struct smart_sect_header_s))
{
- fvdbg("Error reading phys sector %d\n", physicalsector);
- return -EIO;
+ fdbg("Error reading phys sector %d\n", physicalsector);
+ return -1;
}
- if ((*((uint16_t *) header.logicalsector) == 0xFFFF) &&
- (*((uint16_t *) header.seq) == 0xFFFF) &&
+ if ((*((FAR uint16_t *) header.logicalsector) == 0xFFFF) &&
+#if SMART_STATUS_VERSION == 1
+ (*((FAR uint16_t *) &header.seq) == 0xFFFF) &&
+#else
+ (header.seq == CONFIG_SMARTFS_ERASEDSTATE) &&
+#endif
((header.status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)))
{
physicalsector = x;
+ dev->lastallocblock = allocblock;
break;
}
}
+ if (physicalsector == 0xFFFF)
+ {
+ dbg("Program bug! Expected a free sector\n");
+ }
+
+ if (physicalsector >= dev->totalsectors)
+ {
+ dbg("Program bug! Selected sector too big!!!\n");
+ }
+
return physicalsector;
}
@@ -1229,54 +3601,74 @@ static int smart_findfreephyssector(struct smart_struct_s *dev)
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
-static int smart_garbagecollect(struct smart_struct_s *dev)
+static int smart_garbagecollect(FAR struct smart_struct_s *dev)
{
- uint16_t releasedsectors;
uint16_t collectblock;
uint16_t releasemax;
- uint16_t newsector;
bool collect = TRUE;
int x;
int ret;
- size_t offset;
- struct smart_sect_header_s *header;
- uint8_t newstatus;
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ uint8_t count;
+#endif
while (collect)
{
collect = FALSE;
- /* Calculate the number of released sectors on the device */
-
- releasedsectors = 0;
- collectblock = 0xFFFF;
- releasemax = 0;
- for (x = 0; x < dev->neraseblocks; x++)
- {
- releasedsectors += dev->releasecount[x];
- if (dev->releasecount[x] > releasemax)
- {
- releasemax = dev->releasecount[x];
- collectblock = x;
- }
- }
-
/* Test if the released sectors count is greater than the
* free sectors. If it is, then we will do garbage collection.
*/
- if (releasedsectors > dev->freesectors)
- collect = TRUE;
+ if (dev->releasesectors > dev->freesectors && dev->freesectors <
+ (dev->totalsectors >> 5))
+ {
+ collect = TRUE;
+ }
/* Test if we have more reached our reserved free sector limit */
if (dev->freesectors <= (dev->sectorsPerBlk << 0) + 4)
- collect = TRUE;
+ {
+ collect = TRUE;
+ }
/* Test if we need to garbage collect */
if (collect)
{
+ /* Find the block with the most released sectors */
+
+ collectblock = 0xFFFF;
+ releasemax = 0;
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ /* Don't collect blocks that have been worn completely */
+
+ if (smart_get_wear_level(dev, x) >= SMART_WEAR_REORG_THRESHOLD)
+ {
+ continue;
+ }
+#endif
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ count = smart_get_count(dev, dev->releasecount, x);
+ if (count > releasemax)
+ {
+ releasemax = count;
+ collectblock = x;
+ }
+#else
+ if (dev->releasecount[x] > releasemax)
+ {
+ releasemax = dev->releasecount[x];
+ collectblock = x;
+ }
+#endif
+ }
+ //releasemax = smart_get_count(dev, dev->releasecount, collectblock);
+
if (collectblock == 0xFFFF)
{
/* Need to collect, but no sectors with released blocks! */
@@ -1285,167 +3677,402 @@ static int smart_garbagecollect(struct smart_struct_s *dev)
goto errout;
}
- fdbg("Collecting block %d, free=%d released=%d\n",
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
+ {
+ fdbg(" ...before collecting block %d\n", collectblock);
+ }
+#endif
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ fvdbg("Collecting block %d, free=%d released=%d, totalfree=%d, totalrelease=%d\n",
+ collectblock, smart_get_count(dev, dev->freecount, collectblock),
+ smart_get_count(dev, dev->releasecount, collectblock), dev->freesectors, dev->releasesectors);
+#else
+ fvdbg("Collecting block %d, free=%d released=%d\n",
collectblock, dev->freecount[collectblock],
dev->releasecount[collectblock]);
+#endif
- if (dev->freecount[collectblock] == 6)
+ /* Relocate the active data in the collection block */
+
+ ret = smart_relocate_block(dev, collectblock);
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ if (smart_checkfree(dev, __LINE__) != OK)
{
- fdbg("here!\n");
+ fdbg(" ...while collecting block %d\n", collectblock);
}
+#endif
- /* Perform collection on block with the most released sectors.
- * First mark the block as having no free sectors so we don't
- * try to move sectors into the block we are trying to erase.
- */
+ if (ret != OK)
+ {
+ goto errout;
+ }
+ }
+ }
- dev->freecount[collectblock] = 0;
+ return OK;
- /* Next move all live data in the block to a new home. */
+errout:
+ return ret;
+}
+#endif /* CONFIG_FS_WRITABLE */
- for (x = collectblock * dev->sectorsPerBlk; x <
- (collectblock + 1) * dev->sectorsPerBlk; x++)
- {
- /* Read the next sector from this erase block */
+/****************************************************************************
+ * Name: smart_write_wearstatus
+ *
+ * Description: Writes the wear leveling status bits to sector zero (and
+ * possibly others if it doesn't fit) such that is is persisted
+ * across OS reboots.
+ *
+ ****************************************************************************/
- ret = MTD_BREAD(dev->mtd, x * dev->mtdBlksPerSector,
- dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer);
- if (ret != dev->mtdBlksPerSector)
- {
- fdbg("Error reading sector %d\n", x);
- ret = -EIO;
- goto errout;
- }
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+static int smart_write_wearstatus(struct smart_struct_s *dev)
+{
+ uint16_t sector;
+ uint16_t remaining, towrite;
+ struct smart_read_write_s req;
+ int ret;
+ uint8_t buffer[8], write_buffer = 0;
- /* Test if if the block is in use */
+ sector = 0;
+ remaining = dev->geo.neraseblocks >> 1;
+ memset(buffer, 0xFF, sizeof(buffer));
- header = (struct smart_sect_header_s *) dev->rwbuffer;
- if (((header->status & SMART_STATUS_COMMITTED) ==
- (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) ||
- ((header->status & SMART_STATUS_RELEASED) !=
- (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)))
- {
- /* This sector doesn't have live data (free or released).
- * just continue to the next sector and don't move it.
- */
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ if (dev->blockerases > 0)
+ {
+ *((uint32_t *) buffer) = dev->blockerases;
+ write_buffer = 1;
+ }
+#endif
- continue;
- }
+ /* Write the uneven wear count just prior to the wear bits */
- /* Find a new sector where it can live, NOT in this erase block */
+ if (dev->uneven_wearcount != 0)
+ {
+ *((uint32_t *) &buffer[4]) = dev->uneven_wearcount;
+ write_buffer = 1;
+ }
- newsector = smart_findfreephyssector(dev);
- if (newsector == 0xFFFF)
- {
- /* Unable to find a free sector!!! */
+ /* Test if we need to write either total block erase count or
+ uneven wearcount (or both)
+ */
- fdbg("Can't find a free sector for relocation\n");
- ret = -EIO;
- goto errout;
- }
+ if (write_buffer)
+ {
+ req.logsector = sector;
+ req.offset = SMARTFS_FMT_WEAR_POS - 8;
+ req.count = sizeof(buffer);
+ req.buffer = buffer;
+ ret = smart_writesector(dev, (unsigned long) &req);
+ if (ret != OK)
+ {
+ goto errout;
+ }
+ }
- /* Increment the sequence number and clear the "commit" flag */
+ /* Write all wear level bits to logical sector zero, one, two */
- (*((uint16_t *) header->seq))++;
- if (*((uint16_t *) header->seq) == 0xFFFF)
- {
- *((uint16_t *) header->seq) = 1;
- }
-#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
- header->status |= SMART_STATUS_COMMITTED;
-#else
- header->status &= ~SMART_STATUS_COMMITTED;
-#endif
+ while (remaining)
+ {
+ /* Calculate the number of bytes to write to this sector */
- /* Write the data to the new physical sector location */
+ towrite = remaining;
+ if (towrite > dev->sectorsize - SMARTFS_FMT_WEAR_POS)
+ {
+ towrite = dev->sectorsize - SMARTFS_FMT_WEAR_POS;
+ }
- (void)MTD_BWRITE(dev->mtd, newsector * dev->mtdBlksPerSector,
- dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer);
+ /* Setup the sector write request (we are our own client) */
- /* Commit the sector */
+ req.logsector = sector;
+ req.offset = SMARTFS_FMT_WEAR_POS;
+ req.count = towrite;
+ req.buffer = &dev->wearstatus[(dev->geo.neraseblocks >> SMART_WEAR_BIT_DIVIDE) -
+ remaining];
- offset = newsector * dev->mtdBlksPerSector * dev->geo.blocksize +
- offsetof(struct smart_sect_header_s, status);
-#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
- newstatus = header->status & ~SMART_STATUS_COMMITTED;
-#else
- newstatus = header->status | SMART_STATUS_COMMITTED;
-#endif
- ret = smart_bytewrite(dev, offset, 1, &newstatus);
- if (ret < 0)
- {
- fdbg("Error %d committing new sector %d\n" -ret, newsector);
- goto errout;
- }
+ /* Write the sector */
- /* Release the old physical sector */
+ ret = smart_writesector(dev, (unsigned long) &req);
+ if (ret != OK)
+ {
+ goto errout;
+ }
-#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
- newstatus = header->status & ~SMART_STATUS_RELEASED;
-#else
- newstatus = header->status | SMART_STATUS_RELEASED;
-#endif
- offset = x * dev->mtdBlksPerSector * dev->geo.blocksize +
- offsetof(struct smart_sect_header_s, status);
- ret = smart_bytewrite(dev, offset, 1, &newstatus);
- if (ret < 0)
- {
- fdbg("Error %d releasing old sector %d\n" -ret, x);
- goto errout;
- }
+ /* Decrement the remaining count */
- /* Update the variables */
+ remaining -= towrite;
+ if (remaining)
+ {
+ /* Data doesn't fit in a single sector. Use the reserved sectors */
- dev->sMap[*((uint16_t *) header->logicalsector)] = newsector;
- dev->freecount[newsector / dev->sectorsPerBlk]--;
+ sector++;
+ if (sector >= SMART_FIRST_DIR_SECTOR)
+ {
+ /* Error, wear status bit too large! */
+ fdbg("Invalid geometry - wear level status too large\n");
+ ret = -EINVAL;
+ goto errout;
}
+ }
+ }
- /* Now erase the erase block */
+ /* Now clear the NEEDS_WRITE wear status bit */
- MTD_ERASE(dev->mtd, collectblock, 1);
+ dev->wearflags &= ~SMART_WEARFLAGS_WRITE_NEEDED;
+ ret = OK;
- dev->freesectors += dev->releasecount[collectblock];
- dev->freecount[collectblock] = dev->sectorsPerBlk;
- dev->releasecount[collectblock] = 0;
+errout:
+ return ret;
+}
+#endif
- /* If this is block zero, then be sure to write the sector size */
+/****************************************************************************
+ * Name: smart_read_wearstatus
+ *
+ * Description: Reads the wear leveling status bits from sector zero (and
+ * possibly others if it doesn't fit) such that is is persisted
+ * across OS reboots.
+ *
+ ****************************************************************************/
- if (collectblock == 0)
- {
- /* Set the sector size in the 1st header */
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+static inline int smart_read_wearstatus(FAR struct smart_struct_s *dev)
+{
+ uint16_t sector;
+ uint16_t remaining, toread;
+ struct smart_read_write_s req;
+ int ret;
+ uint8_t buffer[8];
+
+ /* Prepare to read the total block erases and uneven wearcount values */
+
+ sector = 0;
+ req.logsector = sector;
+ req.offset = SMARTFS_FMT_WEAR_POS - 8;
+ req.count = sizeof(buffer);
+ req.buffer = buffer;
+ ret = smart_readsector(dev, (unsigned long) &req);
+ if (ret != sizeof(buffer))
+ {
+ goto errout;
+ }
+
+ /* Get the uneven wearcount value */
+
+ dev->uneven_wearcount = *((uint32_t *) &buffer[4]);
+
+ /* Check for erased state */
- uint8_t sectsize = dev->sectorsize >> 7;
#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF )
- newstatus = (uint8_t) ~SMART_STATUS_SIZEBITS | sectsize;
-#else
- newstatus = (uint8_t) sectsize;
+ if (dev->uneven_wearcount == 0xFFFFFFFF)
+ {
+ dev->uneven_wearcount = 0;
+ }
#endif
- /* Write the sector size to the device */
- offset = offsetof(struct smart_sect_header_s, status);
- ret = smart_bytewrite(dev, offset, 1, &newstatus);
- if (ret < 0)
- {
- fdbg("Error %d setting sector 0 size\n", -ret);
- }
- }
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ /* Get the block erases count */
+
+ dev->blockerases = *((uint32_t *) buffer);
+#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF )
+ if (dev->blockerases == 0xFFFFFFFF)
+ {
+ dev->blockerases = 0;
+ }
+#endif
+#endif
+
+ /* Read all wear level bits from the flash */
+
+ remaining = dev->geo.neraseblocks >> 1;
+ while (remaining)
+ {
+ /* Calculate number of bytes to read from this sector */
- /* Update the block aging information in the format signature sector */
+ toread = remaining;
+ if (toread > dev->sectorsize - SMARTFS_FMT_WEAR_POS)
+ {
+ toread = dev->sectorsize - SMARTFS_FMT_WEAR_POS;
}
- else
+
+ /* Setup the sector read request (we are our own client) */
+
+ req.logsector = sector;
+ req.offset = SMARTFS_FMT_WEAR_POS;
+ req.count = toread;
+ req.buffer = &dev->wearstatus[(dev->geo.neraseblocks >> SMART_WEAR_BIT_DIVIDE) -
+ remaining];
+
+ /* Read the sector */
+
+ ret = smart_readsector(dev, (unsigned long) &req);
+ if (ret != toread)
{
- /* Test for aging sectors and push them to a new location
- * so we wear evenly.
- */
+ goto errout;
+ }
+
+ /* Decrement the remaining count */
+
+ remaining -= toread;
+ if (remaining)
+ {
+ /* Data doesn't fit in a single sector. Use the reserved sectors */
+
+ sector++;
+ if (sector >= SMART_FIRST_DIR_SECTOR)
+ {
+ /* Error, wear status bit too large! */
+
+ fdbg("Invalid geometry - wear level status too large\n");
+ ret = -EINVAL;
+ goto errout;
+ }
}
}
- return OK;
+ /* Now interrogate the status bits */
+
+ smart_find_wear_minmax(dev);
+
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ /* Set the erase counts equal to the wear levels */
+
+ for (sector = 0; sector < dev->geo.neraseblocks; sector++)
+ {
+ dev->erasecounts[sector] = smart_get_wear_level(dev, sector);
+ }
+#endif
+
+ ret = OK;
errout:
return ret;
}
-#endif /* CONFIG_FS_WRITABLE */
+#endif
+
+/****************************************************************************
+ * Name: smart_write_alloc_sector
+ *
+ * Description: Writes a newly allocated sector's header to the RW buffer
+ * and updates sector mapping variables. If CRC isn't enabled
+ * it also writes the header to the device.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static int smart_write_alloc_sector(FAR struct smart_struct_s *dev,
+ uint16_t logical, uint16_t physical)
+{
+ int ret = 1;
+ uint8_t sectsize;
+ FAR struct smart_sect_header_s *header;
+
+ memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, dev->sectorsize);
+ header = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+ *((FAR uint16_t *) header->logicalsector) = logical;
+#if SMART_STATUS_VERSION == 1
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ header->seq = 0;
+#else
+ *((FAR uint16_t *) &header->crc8) = 0;
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+#else
+ header->seq = 0;
+#endif
+ sectsize = dev->sectorsize >> 7;
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status = ~(SMART_STATUS_COMMITTED | SMART_STATUS_SIZEBITS |
+ SMART_STATUS_VERBITS) | SMART_STATUS_VERSION | sectsize;
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ header->status &= ~SMART_STATUS_CRC;
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+#else
+ header->status = SMART_STATUS_COMMITTED | SMART_STATUS_VERSION | sectsize;
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ header->status |= SMART_STATUS_CRC;
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+#endif
+
+ /* Write the header to the physical sector location */
+
+#ifndef CONFIG_MTD_SMART_ENABLE_CRC
+ fvdbg("Write MTD block %d\n", physical * dev->mtdBlksPerSector);
+ ret = MTD_BWRITE(dev->mtd, physical * dev->mtdBlksPerSector, 1,
+ (FAR uint8_t *) dev->rwbuffer);
+ if (ret != 1)
+ {
+ /* The block is not empty!! What to do? */
+
+ fdbg("Write block %d failed: %d.\n", physical *
+ dev->mtdBlksPerSector, ret);
+
+ /* Unlock the mutex if we add one */
+
+ return -EIO;
+ }
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: smart_validate_crc
+ *
+ * Description: Validates the CRC data in the sector's header against the
+ * data in the sector. Assumes the entire sector has been
+ * read into the RW buffer already.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+static int smart_validate_crc(FAR struct smart_struct_s *dev)
+{
+ crc_t crc;
+ FAR struct smart_sect_header_s *header;
+
+ /* Calculate CRC on data region of the sector */
+
+ crc = smart_calc_sector_crc(dev);
+ header = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+
+#ifdef CONFIG_SMART_CRC_8
+
+ /* Test 8-bit CRC */
+
+ if (crc != header->crc8)
+ {
+ return -EIO;
+ }
+
+#elif defined(CONFIG_SMART_CRC_16)
+
+ /* Test 16-bit CRC */
+
+ if (crc != *((uint16_t *) header->crc16))
+ {
+ return -EIO;
+ }
+
+#elif defined(CONFIG_SMART_CRC_32)
+
+ if (crc != *((uint32_t *) header->crc32))
+ {
+ return -EIO;
+ }
+
+#endif
+
+ /* CRC checkout out okay */
+
+ return OK;
+}
+#endif
/****************************************************************************
* Name: smart_writesector
@@ -1459,20 +4086,26 @@ errout:
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
-static inline int smart_writesector(struct smart_struct_s *dev, unsigned long arg)
+static int smart_writesector(FAR struct smart_struct_s *dev,
+ unsigned long arg)
{
- int ret;
- uint16_t x;
- bool needsrelocate = FALSE;
- uint32_t mtdblock;
- uint16_t physsector;
- struct smart_read_write_s *req;
- struct smart_sect_header_s *header;
- size_t offset;
- uint8_t byte;
+ int ret;
+ bool needsrelocate = FALSE;
+ uint32_t mtdblock;
+ uint16_t physsector, oldphyssector, block;
+ FAR struct smart_read_write_s *req;
+ FAR struct smart_sect_header_s *header;
+ size_t offset;
+ uint8_t byte;
+#if defined(CONFIG_MTD_SMART_WEAR_LEVEL) || !defined(CONFIG_MTD_SMART_ENABLE_CRC)
+ uint16_t x;
+#endif
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ FAR struct smart_allocsector_s *allocsector;
+#endif
fvdbg("Entry\n");
- req = (struct smart_read_write_s *) arg;
+ req = (FAR struct smart_read_write_s *) arg;
DEBUGASSERT(req->offset <= dev->sectorsize);
DEBUGASSERT(req->offset+req->count <= dev->sectorsize);
@@ -1485,8 +4118,38 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
ret = -EINVAL;
goto errout;
}
+ header = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ /* Test if an adjustement to the wear levels is needed */
+
+ if (dev->minwearlevel >= SMART_WEAR_MIN_LEVEL ||
+ (dev->minwearlevel > 0 && dev->maxwearlevel >= SMART_WEAR_REORG_THRESHOLD))
+ {
+ /* Subtract dev->minwearlevel from all wear levels */
+
+ offset = dev->minwearlevel;
+ fvdbg("Reducing wear level bits by %d\n", offset);
+ for (x = 0; x < dev->geo.neraseblocks; x++)
+ {
+ smart_set_wear_level(dev, x, smart_get_wear_level(dev, x) - offset);
+ }
+
+ dev->minwearlevel -= offset;
+ dev->maxwearlevel -= offset;
+
+ /* Now write the new wear bits to the flash */
+ dev->wearflags &= ~SMART_WEARFLAGS_FORCE_REORG;
+ dev->wearflags |= SMART_WEARFLAGS_WRITE_NEEDED;
+ }
+#endif
+
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
physsector = dev->sMap[req->logsector];
+#else
+ physsector = smart_cache_lookup(dev, req->logsector);
+#endif
if (physsector == 0xFFFF)
{
fdbg("Logical sector %d not allocated\n", req->logsector);
@@ -1497,7 +4160,7 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
/* Read the sector data into our buffer */
mtdblock = physsector * dev->mtdBlksPerSector;
- ret = MTD_BREAD(dev->mtd, mtdblock, dev->mtdBlksPerSector, (uint8_t *)
+ ret = MTD_BREAD(dev->mtd, mtdblock, dev->mtdBlksPerSector, (FAR uint8_t *)
dev->rwbuffer);
if (ret != dev->mtdBlksPerSector)
{
@@ -1508,6 +4171,36 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
/* Test if we need to relocate the sector to perform the write */
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ allocsector = dev->allocsector;
+ while (allocsector)
+ {
+ /* Test if the requested logical sector is a temp alloc */
+
+ if (allocsector->logical == req->logsector)
+ {
+ break;
+ }
+
+ allocsector = allocsector->next;
+ }
+
+ /* When CRC is enabled, then we always have to relocate the sector if
+ * it is not a temporary alloc (i.e. initial alloc before the very first
+ * write operation).
+ */
+
+ if (!allocsector)
+ {
+ needsrelocate = TRUE;
+ }
+
+#else
+ /* When CRC is not enabled, we may be able to simply add the new data to
+ * the sector if it doesn't conflict with existing data on the device.
+ * Test if there is a conflict in the data.
+ */
+
for (x = 0; x < req->count; x++)
{
/* Test if the next byte can be written to the flash */
@@ -1527,12 +4220,20 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
}
#endif
}
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+
+ /* If we are not using CRC and on a device that supports re-writing
+ bits from 1 to 0 without neededing a block erase, such as NOR
+ FLASH, then we can simply update the data in place and don't need
+ to relocate the sector. Test if we need to relocate or not.
+ */
if (needsrelocate)
{
/* Find a new physical sector to save data to */
- physsector = smart_findfreephyssector(dev);
+ oldphyssector = physsector;
+ physsector = smart_findfreephyssector(dev, FALSE);
if (physsector == 0xFFFF)
{
fdbg("Error relocating sector %d\n", req->logsector);
@@ -1542,8 +4243,26 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
/* Update the sequence number to indicate the sector was moved */
- header = (struct smart_sect_header_s *) dev->rwbuffer;
- (*((uint16_t *) header->seq))++;
+#if SMART_STATUS_VERSION == 1
+ if (header->status & SMART_STATUS_CRC)
+ {
+#endif
+ header->seq++;
+ if (header->seq == 0xFF)
+ {
+ header->seq = 0;
+ }
+#if SMART_STATUS_VERSION == 1
+ }
+ else
+ {
+ (*((FAR uint16_t *) &header->seq))++;
+ if (*((FAR uint16_t *) &header->seq) == 0xFFFF)
+ *((FAR uint16_t *) &header->seq) = 1;
+ }
+#else
+ header->seq++;
+#endif
#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
header->status |= SMART_STATUS_COMMITTED;
#else
@@ -1551,11 +4270,82 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
#endif
}
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ /* When CRC is enabled and we have a temp alloc, then fill in the RW buffer
+ * with the header information prior to copying the write data to the buf.
+ */
+
+ if (allocsector)
+ {
+ smart_write_alloc_sector(dev, allocsector->logical, allocsector->physical);
+
+ /* Remove allocsector from the list and free the memory */
+
+ if (dev->allocsector == allocsector)
+ {
+ /* We are the head item. Remove ourselves as head */
+
+ dev->allocsector = allocsector->next;
+ }
+ else
+ {
+ FAR struct smart_allocsector_s *prev;
+
+ /* Start at head and find our entry */
+
+ prev = dev->allocsector;
+ while (prev && prev->next != allocsector)
+ {
+ /* Scan the list until we find this entry */
+
+ prev = prev->next;
+ }
+
+ if (prev)
+ {
+ /* Remove from the list */
+
+ prev->next = allocsector->next;
+ }
+ }
+
+ /* Now free the memory */
+
+ kmm_free(allocsector);
+ }
+
/* Now copy the data to the sector buffer. */
memcpy(&dev->rwbuffer[sizeof(struct smart_sect_header_s) + req->offset],
req->buffer, req->count);
+ /* Commit the sector ahead of time. The CRC will protect us */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status &= ~(SMART_STATUS_COMMITTED | SMART_STATUS_CRC);
+#else
+ header->status |= SMART_STATUS_COMMITTED | SMART_STATUS_CRC;
+#endif
+
+ /* Now calculate the CRC value for the sector */
+
+#ifdef CONFIG_SMART_CRC_8
+ header->crc8 = smart_calc_sector_crc(dev);
+#elif defined(CONFIG_SMART_CRC_16)
+ *((uint16_t *) header->crc16) = smart_calc_sector_crc(dev);
+#elif defined(CONFIG_SMART_CRC_32)
+ *((uint32_t *) header->crc32) = smart_calc_sector_crc(dev);
+#endif
+
+#else /* CONFIG_MTD_SMART_ENABLE_CRC */
+
+ /* Now copy the data to the sector buffer. */
+
+ memcpy(&dev->rwbuffer[sizeof(struct smart_sect_header_s) + req->offset],
+ req->buffer, req->count);
+
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
+
/* Now write the sector buffer to the device. */
if (needsrelocate)
@@ -1563,7 +4353,7 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
/* Write the entire sector to the new physical location, uncommitted. */
ret = MTD_BWRITE(dev->mtd, physsector * dev->mtdBlksPerSector,
- dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer);
+ dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer);
if (ret != dev->mtdBlksPerSector)
{
fdbg("Error writing to physical sector %d\n", physsector);
@@ -1573,6 +4363,8 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
/* Commit the new physical sector */
+#ifndef CONFIG_MTD_SMART_ENABLE_CRC
+
#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
byte = header->status & ~SMART_STATUS_COMMITTED;
#else
@@ -1587,13 +4379,14 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
ret = -EIO;
goto errout;
}
+#endif
/* Release the old physical sector */
#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
- byte = header->status & ~SMART_STATUS_RELEASED;
+ byte = header->status & ~(SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED);
#else
- byte = header->status | SMART_STATUS_RELEASED;
+ byte = header->status | SMART_STATUS_RELEASED | SMART_STATUS_COMMITTED;
#endif
offset = mtdblock * dev->geo.blocksize +
offsetof(struct smart_sect_header_s, status);
@@ -1602,13 +4395,34 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
/* Update releasecount for released sector and freecount for the
* newly allocated physical sector. */
- dev->releasecount[dev->sMap[req->logsector] / dev->sectorsPerBlk]++;
+ block = oldphyssector / dev->sectorsPerBlk;
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_add_count(dev, dev->releasecount, block, 1);
+ smart_add_count(dev, dev->freecount, physsector / dev->sectorsPerBlk, -1);
+#else
+ dev->releasecount[block]++;
dev->freecount[physsector / dev->sectorsPerBlk]--;
+#endif
dev->freesectors--;
+ dev->releasesectors++;
+
+#ifdef CONFIG_SMART_LOCAL_CHECKFREE
+ /* Perform debug free count checking enabled */
+
+ smart_checkfree(dev, __LINE__);
+#endif
/* Update the sector map */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->sMap[req->logsector] = physsector;
+#else
+ smart_update_cache(dev, req->logsector, physsector);
+#endif
+
+ /* Test if releasing the sector created an empty erase block */
+
+ smart_erase_block_if_empty(dev, block, FALSE);
/* Since we performed a relocation, do garbage collection to
* ensure we don't fill up our flash with released blocks.
@@ -1616,14 +4430,47 @@ static inline int smart_writesector(struct smart_struct_s *dev, unsigned long ar
smart_garbagecollect(dev);
}
- else
+ else /* needsrelocate */
{
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ /* Write the entire sector to FLASH when CRC enabled */
+
+ ret = MTD_BWRITE(dev->mtd, physsector * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ fdbg("Error writing to physical sector %d\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Read the sector back and validate the CRC. */
+
+ ret = MTD_BREAD(dev->mtd, physsector * 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)
+ {
+ /* TODO: Mark this as a bad block! */
+
+ fdbg("Error validating physical sector %d\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+#else
/* Not relocated. Just write the portion of the sector that needs
* to be written. */
offset = mtdblock * dev->geo.blocksize +
sizeof(struct smart_sect_header_s) + req->offset;
ret = smart_bytewrite(dev, offset, req->count, req->buffer);
+#endif
}
ret = OK;
@@ -1641,16 +4488,23 @@ errout:
*
****************************************************************************/
-static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg)
+static int smart_readsector(FAR struct smart_struct_s *dev,
+ unsigned long arg)
{
int ret;
- uint32_t readaddr;
uint16_t physsector;
- struct smart_read_write_s *req;
+ FAR struct smart_read_write_s *req;
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+#if SMART_STATUS_VERSION == 1
+ FAR struct smart_sect_header_s *header;
+#endif
+#else
+ uint32_t readaddr;
struct smart_sect_header_s header;
+#endif
fvdbg("Entry\n");
- req = (struct smart_read_write_s *) arg;
+ req = (FAR struct smart_read_write_s *) arg;
DEBUGASSERT(req->offset < dev->sectorsize);
DEBUGASSERT(req->offset+req->count < dev->sectorsize);
@@ -1664,7 +4518,11 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg
goto errout;
}
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
physsector = dev->sMap[req->logsector];
+#else
+ physsector = smart_cache_lookup(dev, req->logsector);
+#endif
if (physsector == 0xFFFF)
{
fdbg("Logical sector %d not allocated\n", req->logsector);
@@ -1672,10 +4530,62 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg
goto errout;
}
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+
+ /* When CRC is enabled, we read the entire sector into RAM so we can
+ * validate the CRC.
+ */
+
+ ret = MTD_BREAD(dev->mtd, physsector * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (FAR uint8_t *) dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ /* TODO: Mark the block bad */
+
+ fdbg("Error reading phys sector %d\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+#if SMART_STATUS_VERSION == 1
+ /* Test if this sector has CRC enabled or not */
+
+ header = (FAR struct smart_sect_header_s *) dev->rwbuffer;
+ if ((header->status & SMART_STATUS_CRC) == (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC))
+ {
+ /* Format VERSION 1 supports either no CRC or 8-bit CRC. Looks like
+ * CRC not enabled for this sector, so skip the CRC test.
+ */
+
+ }
+ else
+#endif
+ {
+ /* Validate the read CRC against the calculated sector CRC */
+
+ ret = smart_validate_crc(dev);
+ if (ret != OK)
+ {
+ /* TODO: Mark the block bad */
+
+ fdbg("Error validating sector %d CRC during read\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+ }
+
+ /* Copy data to the output buffer */
+
+ memmove((FAR char *) req->buffer, &dev->rwbuffer[req->offset +
+ sizeof(struct smart_sect_header_s)], req->count);
+ ret = req->count;
+
+#else /* CONFIG_MTD_SMART_ENABLE_CRC */
+
/* Read the sector header data to validate as a sanity check */
ret = MTD_READ(dev->mtd, physsector * dev->mtdBlksPerSector * dev->geo.blocksize,
- sizeof(struct smart_sect_header_s), (uint8_t *) &header);
+ sizeof(struct smart_sect_header_s), (FAR uint8_t *) &header);
if (ret != sizeof(struct smart_sect_header_s))
{
fvdbg("Error reading sector %d header\n", physsector);
@@ -1685,7 +4595,7 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg
/* Do a sanity check on the header data */
- if (((*(uint16_t *) header.logicalsector) != req->logsector) ||
+ if (((*(FAR uint16_t *) header.logicalsector) != req->logsector) ||
((header.status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)))
{
@@ -1702,7 +4612,7 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg
readaddr = (uint32_t) physsector * dev->mtdBlksPerSector * dev->geo.blocksize +
req->offset + sizeof(struct smart_sect_header_s);;
- ret = MTD_READ(dev->mtd, readaddr, req->count, (uint8_t *)
+ ret = MTD_READ(dev->mtd, readaddr, req->count, (FAR uint8_t *)
req->buffer);
if (ret != req->count)
{
@@ -1711,6 +4621,8 @@ static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg
goto errout;
}
+#endif
+
errout:
return ret;
}
@@ -1724,32 +4636,43 @@ errout:
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
-static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long requested)
+static inline int smart_allocsector(FAR struct smart_struct_s *dev,
+ unsigned long requested)
{
int x;
- int ret;
uint16_t logsector = 0xFFFF; /* Logical sector number selected */
uint16_t physicalsector; /* The selected physical sector */
- uint16_t releasecount;
- struct smart_sect_header_s *header;
- uint8_t sectsize;
+#ifndef CONFIG_MTD_SMART_ENABLE_CRC
+ int ret;
+#endif
/* Validate that we have enough sectors available to perform an
* allocation. We have to ensure we keep enough reserved sectors
* on hand to do released sector garbage collection. */
- releasecount = 0;
- for (x = 0; x < dev->neraseblocks; x++)
- {
- releasecount += dev->releasecount[x];
- }
-
if (dev->freesectors <= (dev->sectorsPerBlk << 0) + 4)
{
- /* We are at our free sector limit. Test if we have
- * sectors we can release */
+ /* Do a garbage collect and then test freesectors again */
+
+ if (dev->releasesectors + dev->freesectors > dev->availSectPerBlk + 4)
+ {
+
+ for (x = 0; x < dev->availSectPerBlk; x++)
+ {
+ smart_garbagecollect(dev);
+
+ if (dev->freesectors > dev->availSectPerBlk + 4)
+ break;
+ }
- if (releasecount == 0)
+ if (dev->freesectors <= (dev->availSectPerBlk << 0) + 4)
+ {
+ /* No space left!! */
+
+ return -ENOSPC;
+ }
+ }
+ else
{
/* No space left!! */
@@ -1764,9 +4687,33 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re
{
/* Validate the sector is not already allocated */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->sMap[requested] == (uint16_t) -1)
+#else
+ if (!(dev->sBitMap[requested >> 3] & (1 << (requested & 0x07))))
+#endif
{
- logsector = requested;
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ FAR struct smart_allocsector_s *allocsect;
+
+ /* Ensure this logical sector doesn't have a temporary alloc */
+ allocsect = dev->allocsector;
+ while (allocsect)
+ {
+ if (allocsect->logical == requested)
+ {
+ break;
+ }
+
+ allocsect = allocsect->next;
+ }
+
+ if (allocsect != NULL)
+ {
+ }
+ else
+#endif
+ logsector = requested;
}
}
@@ -1778,8 +4725,41 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re
for (x = SMART_FIRST_ALLOC_SECTOR; x < dev->totalsectors; x++)
{
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->sMap[x] == (uint16_t) -1)
+#else
+ if (!(dev->sBitMap[x >> 3] & (1 << (x & 0x07))))
+#endif
{
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ FAR struct smart_allocsector_s *allocsect;
+
+ /* Ensure this logical sector doesn't have a temporary alloc
+ * when CRC is enabled. With CRC enabled, when a sector is
+ * allocated, we don't actually update the FLASH until the
+ * very end when we have all data so the CRC can be calculated.
+ * Instead, we keep an in-memory linked list of allocated
+ * sectors until the write sector occurs.
+ */
+
+ allocsect = dev->allocsector;
+ while (allocsect)
+ {
+ if (allocsect->logical == x)
+ {
+ break;
+ }
+
+ allocsect = allocsect->next;
+ }
+
+ if (allocsect != NULL)
+ {
+ /* This logical sector has an in-memory temp alloc */
+
+ continue;
+ }
+#endif
/* Unused logical sector found. Use this one */
logsector = x;
@@ -1805,7 +4785,7 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re
}
/* Check if we need to do garbage collection. We have to
- * ensure we keep enough reserved free sectors to per garbage
+ * ensure we keep enough reserved free sectors to perform garbage
* collection as it involves moving sectors from blocks with
* released sectors into blocks with free sectors, then
* erasing the vacated block. */
@@ -1814,47 +4794,64 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re
/* Find a free physical sector */
- physicalsector = smart_findfreephyssector(dev);
+ physicalsector = smart_findfreephyssector(dev, FALSE);
fvdbg("Alloc: log=%d, phys=%d, erase block=%d, free=%d, released=%d\n",
logsector, physicalsector, physicalsector /
dev->sectorsPerBlk, dev->freesectors, releasecount);
- /* Create a header to assign the logical sector */
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
- memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, dev->sectorsize);
- header = (struct smart_sect_header_s *) dev->rwbuffer;
- *((uint16_t *) header->logicalsector) = logsector;
- *((uint16_t *) header->seq) = 0;
- sectsize = dev->sectorsize >> 7;
+ /* When CRC is enabled, we don't write the header to the device until
+ * the data is written via writesector. Just add the allocation to
+ * our temporary allocsector list and we'll pick it up later.
+ */
-#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
- header->status = ~(SMART_STATUS_COMMITTED | SMART_STATUS_SIZEBITS |
- SMART_STATUS_VERBITS) | SMART_STATUS_VERSION | sectsize;
-#else
- header->status = SMART_STATUS_COMMITTED | SMART_STATUS_VERSION | sectsize;
-#endif
+ {
+ FAR struct smart_allocsector_s *allocsect = (FAR struct smart_allocsector_s *)
+ kmm_malloc(sizeof(struct smart_allocsector_s));
+ if (allocsect == NULL)
+ {
+ fdbg("Out of memory allocting sector\n");
+ return -ENOMEM;
+ }
- /* Write the header to the physical sector location */
+ /* Fill in the struct and add to the list. We are protected by the
+ * smartfs layer's mutex, so no locking required.
+ */
- x = physicalsector * dev->mtdBlksPerSector;
+ allocsect->logical = logsector;
+ allocsect->physical = physicalsector;
+ allocsect->next = dev->allocsector;
+ dev->allocsector = allocsect;
+ }
- fvdbg("Write MTD block %d\n", x);
- ret = MTD_BWRITE(dev->mtd, x, 1, (uint8_t *) dev->rwbuffer);
- if (ret != 1)
- {
- /* The block is not empty!! What to do? */
+#else /* CONFIG_MTD_SMART_ENABLE_CRC */
- fdbg("Write block %d failed: %d.\n", x, ret);
+ /* Write the logical sector to the flash. We will fill it in with data later. */
- /* Unlock the mutex if we add one */
+ ret = smart_write_alloc_sector(dev, logsector, physicalsector);
+ if (ret != 1)
+ {
+ /* Error writing sector, return error */
- return -EIO;
+ return ret;
}
+#endif /* CONFIG_MTD_SMART_ENABLE_CRC */
/* Map the sector and update the free sector counts */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->sMap[logsector] = physicalsector;
+#else
+ dev->sBitMap[logsector >> 3] |= (1 << (logsector & 0x07));
+ smart_add_sector_to_cache(dev, logsector, physicalsector, __LINE__ );
+#endif
+
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_add_count(dev, dev->freecount, physicalsector / dev->sectorsPerBlk, -1);
+#else
dev->freecount[physicalsector / dev->sectorsPerBlk]--;
+#endif
dev->freesectors--;
/* Return the logical sector number */
@@ -1873,8 +4870,8 @@ static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long re
****************************************************************************/
#ifdef CONFIG_FS_WRITABLE
-static inline int smart_freesector(struct smart_struct_s *dev, unsigned long
- logicalsector)
+static inline int smart_freesector(FAR struct smart_struct_s *dev,
+ unsigned long logicalsector)
{
int ret;
int readaddr;
@@ -1889,7 +4886,11 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long
{
/* Validate the sector is actually allocated */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->sMap[logicalsector] == (uint16_t) -1)
+#else
+ if (!(dev->sBitMap[logicalsector >> 3] & (1 << (logicalsector & 0x07))))
+#endif
{
fdbg("Invalid release - sector %d not allocated\n", logicalsector);
ret = -EINVAL;
@@ -1899,10 +4900,14 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long
/* Okay to release the sector. Read the sector header info */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
physsector = dev->sMap[logicalsector];
+#else
+ physsector = smart_cache_lookup(dev, logicalsector);
+#endif
readaddr = physsector * dev->mtdBlksPerSector * dev->geo.blocksize;
ret = MTD_READ(dev->mtd, readaddr, sizeof(struct smart_sect_header_s),
- (uint8_t *) &header);
+ (FAR uint8_t *) &header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto errout;
@@ -1910,7 +4915,7 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long
/* Do a sanity check on the logical sector number */
- if (*((uint16_t *) header.logicalsector) != (uint16_t) logicalsector)
+ if (*((FAR uint16_t *) header.logicalsector) != (uint16_t) logicalsector)
{
/* Hmmm... something is wrong. This should always match! Bug in our code? */
@@ -1933,32 +4938,32 @@ static inline int smart_freesector(struct smart_struct_s *dev, unsigned long
ret = smart_bytewrite(dev, offset, 1, &header.status);
if (ret != 1)
{
- fdbg("Error updating physicl sector %d status\n", physsector);
+ fdbg("Error updating physical sector %d status\n", physsector);
goto errout;
}
/* Update the erase block's release count */
+ dev->releasesectors++;
block = physsector / dev->sectorsPerBlk;
+#ifdef CONFIG_MTD_SMART_PACK_COUNTS
+ smart_add_count(dev, dev->releasecount, block, 1);
+#else
dev->releasecount[block]++;
+#endif
/* Unmap this logical sector */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->sMap[logicalsector] = (uint16_t) -1;
+#else
+ dev->sBitMap[logicalsector >> 3] &= ~(1 << (logicalsector & 0x07));
+ smart_update_cache(dev, logicalsector, 0xFFFF);
+#endif
/* If this block has only released blocks, then erase it */
- if (dev->releasecount[block] + dev->freecount[block] == dev->sectorsPerBlk)
- {
- /* Erase the block */
-
- MTD_ERASE(dev->mtd, block, 1);
-
- dev->freesectors += dev->releasecount[block];
- dev->releasecount[block] = 0;
- dev->freecount[block] = dev->sectorsPerBlk;
- }
-
+ smart_erase_block_if_empty(dev, block, FALSE);
ret = OK;
errout:
@@ -1975,18 +4980,20 @@ errout:
static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
{
- struct smart_struct_s *dev ;
+ FAR struct smart_struct_s *dev ;
int ret;
- uint32_t sector;
- struct mtd_smart_procfs_data_s * procfs_data;
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ FAR struct mtd_smart_procfs_data_s *procfs_data;
+ FAR struct mtd_smart_debug_data_s *debug_data;
+#endif
fvdbg("Entry\n");
DEBUGASSERT(inode && inode->i_private);
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
- dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev;
+ dev = ((FAR struct smart_multiroot_device_s*) inode->i_private)->dev;
#else
- dev = (struct smart_struct_s *)inode->i_private;
+ dev = (FAR struct smart_struct_s *)inode->i_private;
#endif
/* Process the ioctl's we care about first, pass any we don't respond
@@ -2019,10 +5026,10 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
/* Return the format information for the device */
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
- ret = smart_getformat(dev, (struct smart_format_s *) arg,
- ((struct smart_multiroot_device_s*) inode->i_private)->rootdirnum);
+ ret = smart_getformat(dev, (FAR struct smart_format_s *) arg,
+ ((FAR struct smart_multiroot_device_s*) inode->i_private)->rootdirnum);
#else
- ret = smart_getformat(dev, (struct smart_format_s *) arg);
+ ret = smart_getformat(dev, (FAR struct smart_format_s *) arg);
#endif
goto ok_out;
@@ -2059,6 +5066,16 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
/* Write to the sector */
ret = smart_writesector(dev, arg);
+
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ if (dev->wearflags & SMART_WEARFLAGS_WRITE_NEEDED)
+ {
+ /* Write new wear status bits to the device */
+
+ smart_write_wearstatus(dev);
+ }
+#endif
+
goto ok_out;
#endif /* CONFIG_FS_WRITABLE */
@@ -2071,21 +5088,19 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
procfs_data->totalsectors = dev->totalsectors;
procfs_data->sectorsize = dev->sectorsize;
procfs_data->freesectors = dev->freesectors;
- procfs_data->releasesectors = 0;
- for (sector = 0; sector < dev->neraseblocks; sector++)
- {
- procfs_data->releasesectors += dev->releasecount[sector];
- }
-
+ procfs_data->releasesectors = dev->releasesectors;
procfs_data->namelen = dev->namesize;
procfs_data->formatversion = dev->formatversion;
- procfs_data->unusedsectors = 0;
- procfs_data->blockerases = 0;
+ procfs_data->unusedsectors = dev->unusedsectors;
+ procfs_data->blockerases = dev->blockerases;
procfs_data->sectorsperblk = dev->sectorsPerBlk;
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
procfs_data->formatsector = dev->sMap[0];
procfs_data->dirsector = dev->sMap[3];
+#else
+ procfs_data->formatsector = smart_cache_lookup(dev, 0);
+ procfs_data->dirsector = smart_cache_lookup(dev, 3);
#endif
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
@@ -2096,9 +5111,28 @@ static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
procfs_data->allocs = dev->alloc;
procfs_data->alloccount = SMART_MAX_ALLOCS;
#endif
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ procfs_data->uneven_wearcount = dev->uneven_wearcount;
+#endif
ret = OK;
goto ok_out;
#endif
+
+ case BIOC_DEBUGCMD:
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+ debug_data = (FAR struct mtd_smart_debug_data_s *) arg;
+ switch (debug_data->debugcmd)
+ {
+ case SMART_DEBUG_CMD_SET_DEBUG_LEVEL:
+ dev->debuglevel = debug_data->debugdata;
+ dbg("Debug level set to %d\n", dev->debuglevel);
+
+ ret = OK;
+ goto ok_out;
+ }
+#endif
+
+ break;
}
/* No other block driver ioctl commands are not recognized by this
@@ -2133,13 +5167,13 @@ ok_out:
*
****************************************************************************/
-int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname)
+int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, FAR const char *partname)
{
- struct smart_struct_s *dev;
+ FAR struct smart_struct_s *dev;
int ret = -ENOMEM;
uint32_t totalsectors;
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
- struct smart_multiroot_device_s *rootdirdev;
+ FAR struct smart_multiroot_device_s *rootdirdev = NULL;
#endif
/* Sanity check */
@@ -2153,12 +5187,19 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname)
/* Allocate a SMART device structure */
- dev = (struct smart_struct_s *)kmm_malloc(sizeof(struct smart_struct_s));
+ dev = (FAR struct smart_struct_s *)kmm_malloc(sizeof(struct smart_struct_s));
if (dev)
{
/* Initialize the SMART device structure */
dev->mtd = mtd;
+#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
+ dev->bytesalloc = 0;
+ for (totalsectors = 0; totalsectors < SMART_MAX_ALLOCS; totalsectors++)
+ {
+ dev->alloc[totalsectors].ptr = NULL;
+ }
+#endif
/* Get the device geometry. (casting to uintptr_t first eliminates
* complaints on some architectures where the sizeof long is different
@@ -2171,33 +5212,52 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname)
if (ret < 0)
{
fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
- kmm_free(dev);
goto errout;
}
/* Set the sector size to the default for now */
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->sMap = NULL;
+#else
+ dev->sCache = NULL;
+ dev->sBitMap = NULL;
+#endif
dev->rwbuffer = NULL;
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ dev->erasecounts = NULL;
+#endif
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ dev->wearstatus = NULL;
+#endif
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+ dev->allocsector = NULL;
+#endif
+ dev->sectorsize = 0;
ret = smart_setsectorsize(dev, CONFIG_MTD_SMART_SECTOR_SIZE);
if (ret != OK)
{
- kmm_free(dev);
goto errout;
}
/* Calculate the totalsectors on this device and validate */
totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
- if (totalsectors > 65534)
+ if (totalsectors > 65536)
{
fdbg("SMART Sector size too small for device\n");
- kmm_free(dev);
ret = -EINVAL;
goto errout;
}
+ else if (totalsectors == 65536)
+ {
+ totalsectors -= 2;
+ }
- dev->freesectors = (uint16_t) totalsectors;
+ dev->totalsectors = (uint16_t) totalsectors;
+ dev->freesectors = (uint16_t) dev->availSectPerBlk * dev->geo.neraseblocks;
+ dev->lastallocblock = 0;
+ dev->debuglevel = 0;
/* Mark the device format status an unknown */
@@ -2232,13 +5292,11 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname)
* the SMART device structure and the root directory number.
*/
- rootdirdev = (struct smart_multiroot_device_s*) kmm_malloc(sizeof(*rootdirdev));
+ rootdirdev = (FAR struct smart_multiroot_device_s*) smart_malloc(dev,
+ sizeof(*rootdirdev), "Root Dir");
if (rootdirdev == NULL)
{
fdbg("register_blockdriver failed: %d\n", -ret);
- kmm_free(dev->sMap);
- kmm_free(dev->rwbuffer);
- kmm_free(dev);
ret = -ENOMEM;
goto errout;
}
@@ -2267,9 +5325,6 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname)
if (ret < 0)
{
fdbg("register_blockdriver failed: %d\n", -ret);
- kmm_free(dev->sMap);
- kmm_free(dev->rwbuffer);
- kmm_free(dev);
goto errout;
}
@@ -2278,6 +5333,29 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, const char *partname)
smart_scan(dev);
}
+ return OK;
+
errout:
+#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
+ smart_free(dev, dev->sMap);
+#else
+ smart_free(dev, dev->sBitMap);
+ smart_free(dev, dev->sCache);
+#endif
+ smart_free(dev, dev->rwbuffer);
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ smart_free(dev, dev->wearstatus);
+#endif
+#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
+ smart_free(dev, dev->erasecounts);
+#endif
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ if (rootdirdev)
+ {
+ smart_free(dev,rootdirdev);
+ }
+#endif
+
+ kmm_free(dev);
return ret;
}