summaryrefslogtreecommitdiff
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
parent246ab986d67e177bb4e60046ad486947e1624774 (diff)
downloadnuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.tar.gz
nuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.tar.bz2
nuttx-c0658f766fa3731fc2f0191d42fa9f6c41d36691.zip
SmartFS: Implements wear-leveling in the SmartFS. From Ken Pettit
-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
-rw-r--r--nuttx/fs/smartfs/Kconfig11
-rw-r--r--nuttx/fs/smartfs/README.txt201
-rw-r--r--nuttx/fs/smartfs/smartfs.h47
-rw-r--r--nuttx/fs/smartfs/smartfs_procfs.c115
-rw-r--r--nuttx/fs/smartfs/smartfs_smart.c181
-rw-r--r--nuttx/fs/smartfs/smartfs_utils.c268
-rw-r--r--nuttx/include/nuttx/fs/ioctl.h6
-rw-r--r--nuttx/include/nuttx/fs/smart.h14
-rw-r--r--nuttx/include/nuttx/mtd/smart.h16
12 files changed, 4352 insertions, 488 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;
}
diff --git a/nuttx/fs/smartfs/Kconfig b/nuttx/fs/smartfs/Kconfig
index 73910d91d..4ee7a37eb 100644
--- a/nuttx/fs/smartfs/Kconfig
+++ b/nuttx/fs/smartfs/Kconfig
@@ -52,4 +52,15 @@ config SMARTFS_MULTI_ROOT_DIRS
Default: y.
+config SMARTFS_ALIGNED_ACCESS
+ bool "Ensure 16 and 32 bit accesses are alined"
+ default n
+ ---help---
+ Performs little endian byte accesses to 16 and 32 values
+ within the directory list to ensure processors that can't
+ handle un-aligned accesses don't bomb out. Default should
+ probably by 'Y', but set to 'N' in case any existing Big
+ Endian instances of SmartFS exist that already have
+ directories with data stored in big endian mode.
+
endif
diff --git a/nuttx/fs/smartfs/README.txt b/nuttx/fs/smartfs/README.txt
index 8d4db1c93..211e245bf 100644
--- a/nuttx/fs/smartfs/README.txt
+++ b/nuttx/fs/smartfs/README.txt
@@ -20,11 +20,12 @@ Features
This implementation is a full-feature file system from the perspective of
file and directory access (i.e. not considering low-level details like the
- lack of wear-leveling, etc.). The SMART File System was designed specifically
+ lack of bad block management). The SMART File System was designed specifically
for small SPI based FLASH parts (1-8 Mbyte for example), though this is not
a limitation. It can certainly be used for any size FLASH and can work with
- any MTD device by binding it with the SMART MTD layer. The FS includes
- support for:
+ any MTD device by binding it with the SMART MTD layer and has been tested with
+ devices as large as 128MByte (using a 2048 byte sector size with 65534 sectors).
+ The FS includes support for:
- Multiple open files from different threads.
- Open for read/write access with seek capability.
- Appending to end of files in either write, append or read/write
@@ -32,6 +33,9 @@ Features
- Directory support.
- Support for multiple mount points on a single volume / partition (see
details below).
+ - Selectable FLASH Wear leveling algorithym
+ - Selectable CRC-8 or CRC-16 error detection for sector data
+ - Reduced RAM model for FLASH geometries with large number of sectors (16K-64K)
General operation
=================
@@ -68,7 +72,7 @@ General operation
erase state to non-erase state) to indicate the sector data is valid. When
a sector's data needs to be deleted, the RELEASED flag will be "set" to
indicate the sector is no longer in use. This is done because the erase
- block continaing the sector cannot necessarly be erased until all sectors
+ block containing the sector cannot necessarily be erased until all sectors
in that block have been "released". This allows sectors in the erase
block to remain active while others are inactive until a "garbage collection"
operation is needed on the volume to reclaim released sectors.
@@ -85,10 +89,12 @@ General operation
The SMART MTD block layer reserves some logical sector numbers for internal
use, including
- Sector 0: The Format Sector. Has a format signture, format version, etc.
- Sector 1: The 1st (or only) Root Directory entry
- Sector 2-8: Additional root directories when Multi-Mount points are supported.
- Sector 9-11: Reserved (maybe for sector wear-leveling, etc.)
+ Sector 0: The Format Sector. Has a format signature, format version, etc.
+ Also contains wear leveling information if enabled.
+ Sector 1-2: Additional wear-leveling info storage if needed.
+ Sector 3: The 1st (or only) Root Directory entry
+ Sector 4-10: Additional root directories when Multi-Mount points are supported.
+ Sector 11-12: Reserved
To perform allocations, the SMART MTD block layer searches each erase block
on the device to identify the one with the most free sectors. Free sectors
@@ -97,7 +103,8 @@ General operation
sectors on the device can be allocated ... the SMART MTD block driver must
reserve at least one erase-block worth of unused sectors to perform
garbage collection, which will be performed automatically when no free
- sectors are available.
+ sectors are available. When wear leveling is enabled, the allocator also takes
+ into account the erase block erasure status to maintain level wearing.
Garbage collection is performed by identifying the erase block with the most
"released" sectors (those that were previously allocated but no longer being
@@ -132,6 +139,133 @@ General operation
perform a full page read-modify-write operation on a 256 or even 512
byte page.
+ Wear Leveling
+ =============
+
+ When wear leveling is enabled, the code automatically writes data across
+ the entire FLASH device in a manner that causes each erase block to be
+ worn (i.e. erased) evenly. This is accomplished by maintaining a 4-bit
+ wear level count for each erase block and forcing less worn blocks to be
+ used for writing new data. The code maintains each block's erase count
+ to be within 16 erases of each other, though through testing, the span
+ so far was never greater than 10 erases of each other.
+
+ As the data in a block is modified repeatedly, the erase count will
+ increase. When the wear level reaches a value of 8 or higher, and the block
+ needs to be erased (because the data in it has been modified, etc.) the code
+ will select an erase block with the lowest wear count and relocate it to
+ this block (with the higher wear count). The idea being that a block with
+ the lowest wear count contains more "static" data and should require fewer
+ additional erase operations. This relocation process will continue on the
+ block (only when it needs to be erased again).
+
+
+ When the wear level of all erase blocks has increased to a level of
+ SMART_WEAR_MIN_LEVEL (currently set to 5), then the wear level counts
+ will all be reduced by this value. This keeps the wear counts normalized
+ so they fit in a 4-bit value. Note that theoretically, it *IS* possible to
+ write data to the flash in a manner that causes the wear count of a single
+ erase block to increment beyond it's maximum value of 15. This would have
+ to be a very, very, very specific and un-predictable write sequence though
+ as data is always spread out across the sectors and relocated dynamically.
+ In the extremely rare event this does occur, the code will automatically
+ cap the maximum wear level at 15 an increment an "uneven wear count"
+ variable to indicate the number times this event has occurred. So far, I
+ have not been able to get the wear count above 10 though my testing.
+
+ The wear level status bits are saved in the format sector (logical sector
+ number zero) with overflow saved in the reserved logical sectors one and
+ two. Additionally, the uneven wear count (and total block erases if
+ PROCFS is enabled) are stored in the format sector. When the PROCFS file
+ system is enabled and a SMARTFS volume is mounted, the SMART block driver
+ details and / or wear level details can be viewed with a command such as:
+
+ cat /proc/fs/smartfs/smart0/status
+ Format version: 1
+ Name Len: 16
+ Total Sectors: 2048
+ Sector Size: 512
+ Format Sector: 1487
+ Dir Sector: 8
+ Free Sectors: 67
+ Released Sectors: 572
+ Unused Sectors: 817
+ Block Erases: 5680
+ Sectors Per Block: 8
+ Sector Utilization:98%
+ Uneven Wear Count: 0
+
+ cat /proc/fs/smartfs/smart0/erasemap
+ DDDCGCCDDCDCCDCBDCCDDGBBDBCDCCDDDCDDDDCCDDCCCGCGDCCDBCDDGBDBDCDD
+ BCCCDDCCDDDCBCCDGCCCBDDCCGBBCBCCGDCCDCBDBCCCDCDDCDDGCDCGDCBCDBDG
+ BCDDCDCBGCCCDDCGBCCGBCCBDDBDDCGDCDDDCGCDDBCDCBDDBCDCGDDCCBCGBCCC
+ GCBCCGCCCDDDBGCCCCGDCCCCCDCDDGBBDACABDBBABCAABCCCDAACBADADDDAECB
+
+ Enabling wear leveling can increase the total number of block erases on the
+ device in favor of even wearing (erasing). This is caused by writing /
+ moving sectors that otherwise don't need to be written to move static data
+ to the more highly worn blocks. This additional write requirement is known
+ as write amplification. To get an idea of the amount of write amplification
+ incurred by enabling wear leveling, I conducted the smart_test example using
+ four different configurations (wear, no wear, CRC-8, no CRC) and the results
+ are shown below. This was done on a 1M Byte simulated FLASH with 4K erase
+ block size, 512 sectors per byte. The smart_test creates a 700K file and
+ then performs 20,000 random seek, write, verify tests. The seek write forces
+ a multitude of sector relocation operations (with or without CRC enabled),
+ causing a boatload of block erases.
+
+ Enabling wear leveling actually decreased the number of erase operations
+ with CRC enabled or disabled. This is only a single test point based one
+ testing method ... results will likely vary based on the method the data
+ is written, the amount of static vs. dynamic data, the amount of free space
+ on the volume, and the volume geometry (erase block size, sector size, etc.).
+
+ The results of the tests are:
+
+ Case Total Block erases
+ ================================================
+ No wear leveling CRC-8 6632
+ Wear leveling CRC-8 5585
+
+ No wear leveling no CRC 6658
+ Wear leveling no CRC 5398
+
+
+ Reduced RAM model
+ =================
+
+ On devices with a larger number of logical sectors (i.e. a lot of erase
+ blocks with a small selected sector size), the RAM requirement can become
+ fairly significant. This is caused by the in-memory sector map which
+ keeps track of the logical to physical mapping of all sectors. This is
+ a RAM array which is 2 * totalsectors in size. For a device with 64K
+ sectors, this means 128K of RAM is required just for the sector map, not
+ counting RAM for read/write buffers, erase block management, etc.
+
+ So a reduced RAM model has been added which only keeps track of which
+ logical sectors have been used (a table which is totalsectors / 8 in size)
+ and a configurable sized sector map cache. Each entry in the sector map
+ cache is 6 bytes (logical sector, physical sector and cache entry age).
+ ON DEVICES WITH SMALLER TOTAL SECTOR COUNT, ENABLING THIS OPTION COULD
+ ACTUALLY INCREASE THE RAM FOOTPRINT INSTEAD OF REDUCE IT.
+
+ The sector map cache size should be selected to balance the desired RAM
+ usage and the file system performance. When a logical to physical sector
+ mapping is not found in the cache, the code must perform a physical search
+ of the FLASH to find the requested logical sector. This involves reading
+ the 5-byte header from each sector on the device until the sector is
+ found. Performing a full read, seek or open for append on a large file
+ can cause the sector map cache to flush completely if the file is larger
+ than (cache entries * sector size). For example, in a configuration with
+ 256 cache entries and a 512 byte sector size, a full read, seek or open for
+ append on a 128K file will flush the cache.
+
+ An additional RAM savings is realized on FLASH parts that contain 16 or
+ fewer logical sectors per erase block by packing the free and released
+ sector counts into a single byte (plus a little extra for 16 sectors per
+ erase block). A device with a 64K erase block size can benefit from this
+ savings by selecting a 4096 or 8192 byte logical sector size, for example.
+
SMART FS Layer
==============
@@ -139,7 +273,7 @@ General operation
logical sectors, create and destroy sector chains, and perform directory and
file I/O operations. Each directory and file on the volume is represented
as a chain or "linked list" of logical sectors. Thus the actual physical
- sectors that a give file or directory uses does not need to be contigous
+ sectors that a give file or directory uses does not need to be contiguous
and in fact can (and will) move around over time. To manage the sector
chains, the SMARTFS layer adds a "chain header" after the sector's "sector
header". This is a 5-byte header which contains the chain type (file or
@@ -287,35 +421,37 @@ SMARTFS Limitations
This implementation has several limitations that you should be aware
before opting to use SMARTFS:
-1. No wear leveling has been implemented. The allocation scheme has a
- bit of inherent wear-leveling since it automatically distributes
- sector allocations across the device, but no provisions exist to
- guarantee equal wearing.
-
-2. There is no CRC or checksum calculations performed on the data stored
- to FLASH, so no error detection has been implemented. This could be
- added by "stealing" one of the sequence number bytes in the sector
- header and incrementing the sector version number.
-
-3. There is currently no FLASH bad-block management code. The reason for
+1. There is currently no FLASH bad-block management code. The reason for
this is that the FS was geared for Serial NOR FLASH parts. To use
- SMARTFS with a NAND FLASH, bad block management would need to be added.
+ SMARTFS with a NAND FLASH, bad block management would need to be added,
+ along with a few minor changes to eliminate single bit writes to release
+ a sector, etc.
+
+2. The implementation can support CRC-8 or CRC-16 error detection, and can
+ relocate a failed write operation to a new sector. However with no bad
+ block management implementation, the code will continue it attempts at
+ using failing block / sector, reducing efficiency and possibly successfully
+ saving data in a block with questionable integrity.
-4. The released-sector garbage collection process occurs only during a write
+3. The released-sector garbage collection process occurs only during a write
when there are no free FLASH sectors. Thus, occasionally, file writing
- may take a long time. This typically isn't noticable unless the volume
+ may take a long time. This typically isn't noticeable unless the volume
is very full and multiple copy / erase cycles must be performed to
complete the garbage collection.
-5. The total number of logical sectors on the device must be less than 65534.
+4. The total number of logical sectors on the device must be 65534 or less.
The number of logical sectors is based on the total device / partition
size and the selected sector size. For larger flash parts, a larger
- sector size would need to be used to meet this requirement. This
- restriction exists because:
+ sector size would need to be used to meet this requirement. Creating a
+ geometry which results in 65536 sectors (a 32MByte FLASH with 512 byte
+ logical sector, for example) will cause the code to automatically reduce
+ the total sector count to 65534, thus "wasting" the last two logical
+ sectors on the device (they will never be used).
+
+ This restriction exists because:
a. The logical sector number is a 16-bit field (i.e. 65535 is the max).
- b. The SMART MTD layer reserves 1 logical sector for a format sector.
- c. Logical sector number 65535 (0xFFFF) is reerved as this is typically
+ b. Logical sector number 65535 (0xFFFF) is reserved as this is typically
the "erased state" of the FLASH.
ioctls
@@ -339,7 +475,7 @@ ioctls
it's erase block.
BIOC_READSECT
- Reads data from a logial sector. This uses a structure to identify
+ Reads data from a logical sector. This uses a structure to identify
the offset and count of data to be read.
BIOC_WRITESECT
@@ -356,8 +492,3 @@ Things to Do
- Add reporting of actual FLASH usage for directories (each directory
occupies one or more physical sectors, yet the size is reported as
zero for directories).
-- Add sector aging to provide some degree of wear-leveling.
-- Possibly steal a byte from the sector header's sequence number and
- implement a sector data verification scheme using a 1-byte CRC.
-
-
diff --git a/nuttx/fs/smartfs/smartfs.h b/nuttx/fs/smartfs/smartfs.h
index eb90b8c7d..ac83a0c0c 100644
--- a/nuttx/fs/smartfs/smartfs.h
+++ b/nuttx/fs/smartfs/smartfs.h
@@ -198,9 +198,14 @@
#define SMARTFS_SECTOR_TYPE_FILE 2
#ifndef CONFIG_SMARTFS_DIRDEPTH
-#define CONFIG_SMARTFS_DIRDEPTH 8
+# define CONFIG_SMARTFS_DIRDEPTH 8
#endif
+/* Buffer flags (when CRC enabled) */
+
+#define SMARTFS_BFLAG_DIRTY 0x01 /* Set if data changed in the sector */
+#define SMARTFS_BFLAG_NEWALLOC 0x02 /* Set if sector not written since alloc */
+
#define SMARTFS_ERASEDSTATE_16BIT (uint16_t) ((CONFIG_SMARTFS_ERASEDSTATE << 8) | \
CONFIG_SMARTFS_ERASEDSTATE)
@@ -211,6 +216,10 @@
#define SMARTFS_NEXTSECTOR(h) ( *((uint16_t *) h->nextsector))
#define SMARTFS_USED(h) ( *((uint16_t *) h->used))
+#ifdef CONFIG_MTD_SMART_ENABLE_CRC
+#define CONFIG_SMARTFS_USE_SECTOR_BUFFER
+#endif
+
/****************************************************************************
* Public Types
****************************************************************************/
@@ -252,12 +261,28 @@ struct smartfs_entry_header_s
* sector. It manages the sector chain and used bytes in the sector.
*/
+#if defined(CONFIG_MTD_SMART_ENABLE_CRC) && defined(CONFIG_SMART_CRC_32)
+struct smartfs_chain_header_s
+{
+ uint8_t nextsector[4];/* Next logical sector in the chain */
+ uint8_t used[4]; /* Number of bytes used in this sector */
+ uint8_t type; /* Type of sector entry (file or dir) */
+};
+#elif defined(CONFIG_MTD_SMART_ENABLE_CRC) && defined(CONFIG_SMART_CRC_16)
struct smartfs_chain_header_s
{
uint8_t type; /* Type of sector entry (file or dir) */
uint8_t nextsector[2];/* Next logical sector in the chain */
uint8_t used[2]; /* Number of bytes used in this sector */
};
+#else
+struct smartfs_chain_header_s
+{
+ uint8_t type; /* Type of sector entry (file or dir) */
+ uint8_t nextsector[2];/* Next logical sector in the chain */
+ uint8_t used[2]; /* Number of bytes used in this sector */
+};
+#endif
/* This structure describes the state of one open file. This structure
* is protected by the volume semaphore.
@@ -266,6 +291,10 @@ struct smartfs_chain_header_s
struct smartfs_ofile_s
{
struct smartfs_ofile_s *fnext; /* Supports a singly linked list */
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ uint8_t* buffer; /* Sector buffer to reduce writes */
+ uint8_t bflags; /* Buffer flags */
+#endif
int16_t crefs; /* Reference count */
mode_t oflags; /* Open mode */
struct smartfs_entry_s entry; /* Describes the SMARTFS inode entry */
@@ -331,7 +360,7 @@ int smartfs_createentry(struct smartfs_mountpt_s *fs,
uint16_t parentdirsector, const char* filename,
uint16_t type,
mode_t mode, struct smartfs_entry_s *direntry,
- uint16_t sectorno);
+ uint16_t sectorno, FAR struct smartfs_ofile_s *sf);
int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
struct smartfs_entry_s *entry);
@@ -340,7 +369,19 @@ int smartfs_countdirentries(struct smartfs_mountpt_s *fs,
struct smartfs_entry_s *entry);
int smartfs_truncatefile(struct smartfs_mountpt_s *fs,
- struct smartfs_entry_s *entry);
+ struct smartfs_entry_s *entry, FAR struct smartfs_ofile_s *sf);
+
+uint16_t smartfs_rdle16(FAR const void *val);
+
+void smartfs_wrle16(void *dest, uint16_t val);
+
+uint32_t smartfs_rdle32(FAR const void *val);
+
+void smartfs_wrle32(uint8_t *dest, uint32_t val);
+
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+struct smartfs_mountpt_s* smartfs_get_first_mount(void);
+#endif
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
struct smartfs_mountpt_s* smartfs_get_first_mount(void);
diff --git a/nuttx/fs/smartfs/smartfs_procfs.c b/nuttx/fs/smartfs/smartfs_procfs.c
index e74d65944..6153cde80 100644
--- a/nuttx/fs/smartfs/smartfs_procfs.c
+++ b/nuttx/fs/smartfs/smartfs_procfs.c
@@ -108,6 +108,7 @@ struct smartfs_procfs_entry_s
{
const char *name; /* Name of the directory entry */
size_t (*read)(FAR struct file *filep, FAR char *buffer, size_t buflen);
+ ssize_t (*write)(FAR struct file *filep, FAR const char *buffer, size_t buflen);
uint8_t type;
};
@@ -121,6 +122,8 @@ static int smartfs_open(FAR struct file *filep, FAR const char *relpath,
static int smartfs_close(FAR struct file *filep);
static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
+static ssize_t smartfs_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
static int smartfs_dup(FAR const struct file *oldp,
FAR struct file *newp);
@@ -132,6 +135,8 @@ static int smartfs_rewinddir(FAR struct fs_dirent_s *dir);
static int smartfs_stat(FAR const char *relpath, FAR struct stat *buf);
+static ssize_t smartfs_debug_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
static size_t smartfs_status_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
@@ -153,13 +158,14 @@ static size_t smartfs_files_read(FAR struct file *filep, FAR char *buffer,
static const struct smartfs_procfs_entry_s g_direntry[] =
{
+ { "debuglevel", NULL, smartfs_debug_write, DTYPE_FILE },
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
- { "erasemap", smartfs_erasemap_read, DTYPE_FILE },
+ { "erasemap", smartfs_erasemap_read, NULL, DTYPE_FILE },
#endif
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
- { "mem", smartfs_mem_read, DTYPE_FILE },
+ { "mem", smartfs_mem_read, NULL, DTYPE_FILE },
#endif
- { "status", smartfs_status_read, DTYPE_FILE }
+ { "status", smartfs_status_read, NULL, DTYPE_FILE }
};
static const uint8_t g_direntrycount = sizeof(g_direntry) /
@@ -181,7 +187,7 @@ const struct procfs_operations smartfs_procfsoperations =
smartfs_read, /* read */
/* No write supported */
- NULL, /* write */
+ smartfs_write, /* write */
smartfs_dup, /* dup */
@@ -430,7 +436,50 @@ static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer,
{
if (priv->level1.direntry < g_direntrycount)
{
- ret = g_direntry[priv->level1.direntry].read(filep, buffer, buflen);
+ if (g_direntry[priv->level1.direntry].read)
+ {
+ ret = g_direntry[priv->level1.direntry].read(filep, buffer, buflen);
+ }
+ }
+ }
+
+ /* Update the file offset */
+
+ if (ret > 0)
+ {
+ filep->f_pos += ret;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_write
+ ****************************************************************************/
+
+static ssize_t smartfs_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen)
+{
+ FAR struct smartfs_file_s *priv;
+ ssize_t ret;
+
+ /* Recover our private data from the struct file instance */
+
+ priv = (FAR struct smartfs_file_s *)filep->f_priv;
+ DEBUGASSERT(priv);
+
+ /* Perform the write based on the directory entry */
+
+ ret = 0;
+
+ if (priv->level1.base.level == 3)
+ {
+ if (priv->level1.direntry < g_direntrycount)
+ {
+ if (g_direntry[priv->level1.direntry].write)
+ {
+ ret = g_direntry[priv->level1.direntry].write(filep, buffer, buflen);
+ }
}
}
@@ -675,6 +724,8 @@ static int smartfs_stat(const char *relpath, struct stat *buf)
}
else
{
+ /* The entry being stat'ed is lowest level */
+
if (g_direntry[level1.direntry].type == DTYPE_DIRECTORY)
{
buf->st_mode |= S_IFDIR;
@@ -683,6 +734,13 @@ static int smartfs_stat(const char *relpath, struct stat *buf)
{
buf->st_mode |= S_IFREG;
}
+
+ /* Test if the entry is writable */
+
+ if (g_direntry[level1.direntry].write != NULL)
+ {
+ buf->st_mode |= S_IWOTH | S_IWGRP | S_IWUSR;
+ }
}
}
@@ -696,6 +754,33 @@ static int smartfs_stat(const char *relpath, struct stat *buf)
}
/****************************************************************************
+ * Name: smartfs_debug_write
+ *
+ * Description: Performs the write operation for the "debug" file
+ *
+ ****************************************************************************/
+
+static ssize_t smartfs_debug_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen)
+{
+ struct mtd_smart_debug_data_s debug_data;
+ FAR struct smartfs_file_s *priv;
+
+ priv = (FAR struct smartfs_file_s *) filep->f_priv;
+
+ /* Populate the debug_data structure */
+
+ debug_data.debugcmd = SMART_DEBUG_CMD_SET_DEBUG_LEVEL;
+ debug_data.debugdata = atoi(buffer);
+
+ priv->level1.mount->fs_blkdriver->u.i_bops->ioctl(
+ priv->level1.mount->fs_blkdriver, BIOC_DEBUGCMD,
+ (unsigned long) &debug_data);
+
+ return buflen;
+}
+
+/****************************************************************************
* Name: smartfs_status_read
*
* Description: Performs the read operation for the "status" dir entry.
@@ -709,9 +794,7 @@ static size_t smartfs_status_read(FAR struct file *filep, FAR char *buffer,
FAR struct smartfs_file_s *priv;
int ret;
size_t len;
-#if 0 /* Not used */
int utilization;
-#endif
priv = (FAR struct smartfs_file_s *) filep->f_priv;
@@ -731,7 +814,6 @@ static size_t smartfs_status_read(FAR struct file *filep, FAR char *buffer,
if (ret == OK)
{
-#if 0 /* Not used */
/* Calculate the sector utilization percentage */
if (procfs_data.blockerases == 0)
@@ -744,7 +826,6 @@ static size_t smartfs_status_read(FAR struct file *filep, FAR char *buffer,
procfs_data.unusedsectors) / (procfs_data.blockerases *
procfs_data.sectorsperblk);
}
-#endif
/* Format and return data in the buffer */
@@ -752,18 +833,22 @@ static size_t smartfs_status_read(FAR struct file *filep, FAR char *buffer,
"Total Sectors: %d\nSector Size: %d\n"
"Format Sector: %d\nDir Sector: %d\n"
"Free Sectors: %d\nReleased Sectors: %d\n"
- "Sectors Per Block: %d\n",
- //"Unused Sectors: %d\nBlock Erases: %d\n"
- //"Sectors Per Block: %d\nSector Utilization:%d%%\n",
+ "Unused Sectors: %d\nBlock Erases: %d\n"
+ "Sectors Per Block: %d\nSector Utilization:%d%%\n"
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ "Uneven Wear Count: %d\n"
+#endif
+ ,
procfs_data.formatversion, procfs_data.namelen,
procfs_data.totalsectors, procfs_data.sectorsize,
procfs_data.formatsector, procfs_data.dirsector,
procfs_data.freesectors, procfs_data.releasesectors,
- procfs_data.sectorsperblk);
-#if 0 /* Not used */
procfs_data.unusedsectors, procfs_data.blockerases,
- procfs_data.sectorsperblk, utilization);
+ procfs_data.sectorsperblk, utilization
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ , procfs_data.uneven_wearcount
#endif
+ );
}
/* Indicate we have already provided all the data */
diff --git a/nuttx/fs/smartfs/smartfs_smart.c b/nuttx/fs/smartfs/smartfs_smart.c
index 0b9a373b1..bf1490ee0 100644
--- a/nuttx/fs/smartfs/smartfs_smart.c
+++ b/nuttx/fs/smartfs/smartfs_smart.c
@@ -196,11 +196,27 @@ static int smartfs_open(FAR struct file *filep, const char *relpath,
goto errout_with_semaphore;
}
+ /* Allocate a sector buffer if CRC enabled in the MTD layer */
+
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ sf->buffer = (uint8_t *) kmm_malloc(fs->fs_llformat.availbytes);
+ if (sf->buffer == NULL)
+ {
+ /* Error ... no memory */
+
+ kmm_free(sf);
+ ret = -ENOMEM;
+ goto errout_with_semaphore;
+ }
+
+ sf->bflags = 0;
+#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
+
sf->entry.name = NULL;
ret = smartfs_finddirentry(fs, &sf->entry, relpath, &parentdirsector,
&filename);
- /* Three possibililities: (1) a node exists for the relpath and
+ /* Three possibilities: (1) a node exists for the relpath and
* dirinfo describes the directory entry of the entity, (2) the
* node does not exist, or (3) some error occurred.
*/
@@ -240,7 +256,7 @@ static int smartfs_open(FAR struct file *filep, const char *relpath,
{
/* Truncate the file as part of the open */
- ret = smartfs_truncatefile(fs, &sf->entry);
+ ret = smartfs_truncatefile(fs, &sf->entry, sf);
if (ret < 0)
{
goto errout_with_buffer;
@@ -268,7 +284,7 @@ static int smartfs_open(FAR struct file *filep, const char *relpath,
ret = smartfs_createentry(fs, parentdirsector, filename,
SMARTFS_DIRENT_TYPE_FILE, mode,
- &sf->entry, 0xFFFF);
+ &sf->entry, 0xFFFF, sf);
if (ret != OK)
{
goto errout_with_buffer;
@@ -426,6 +442,14 @@ static int smartfs_close(FAR struct file *filep)
kmm_free(sf->entry.name);
sf->entry.name = NULL;
}
+
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ if (sf->buffer)
+ {
+ kmm_free(sf->buffer);
+ }
+#endif
+
kmm_free(sf);
okout:
@@ -573,6 +597,39 @@ static int smartfs_sync_internal(struct smartfs_mountpt_s *fs,
struct smartfs_chain_header_s *header;
int ret = OK;
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ if (sf->bflags & SMARTFS_BFLAG_DIRTY)
+ {
+ /* Update the header with the number of bytes written */
+
+ header = (struct smartfs_chain_header_s *) sf->buffer;
+ if (*((uint16_t *) header->used) == SMARTFS_ERASEDSTATE_16BIT)
+ {
+ *((uint16_t *) header->used) = sf->byteswritten;
+ }
+ else
+ {
+ *((uint16_t *) header->used) += sf->byteswritten;
+ }
+
+ /* Write the entire sector to FLASH */
+
+ readwrite.logsector = sf->currsector;
+ readwrite.offset = 0;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = sf->buffer;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d writing used bytes for sector %d\n", ret, sf->currsector);
+ goto errout;
+ }
+
+ sf->byteswritten = 0;
+ sf->bflags = 0;
+ }
+#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
+
/* Test if we have written bytes to the current sector that
* need to be recorded in the chain header's used bytes field. */
@@ -617,6 +674,7 @@ static int smartfs_sync_internal(struct smartfs_mountpt_s *fs,
sf->byteswritten = 0;
}
+#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
errout:
return ret;
@@ -761,6 +819,17 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
* the current sector first.
*/
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ readwrite.count = fs->fs_llformat.availbytes - sf->curroffset;
+ if (readwrite.count > buflen)
+ {
+ readwrite.count = buflen;
+ }
+
+ memcpy(&sf->buffer[sf->curroffset], &buffer[byteswritten], readwrite.count);
+ sf->bflags |= SMARTFS_BFLAG_DIRTY;
+
+#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
readwrite.offset = sf->curroffset;
readwrite.logsector = sf->currsector;
readwrite.buffer = (uint8_t *) &buffer[byteswritten];
@@ -782,24 +851,74 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
fdbg("Error %d writing sector %d data\n", ret, sf->currsector);
goto errout_with_semaphore;
}
+ }
- /* Update our control variables */
+#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
- sf->entry.datlen += readwrite.count;
- sf->byteswritten += readwrite.count;
- sf->filepos += readwrite.count;
- sf->curroffset += readwrite.count;
- buflen -= readwrite.count;
- byteswritten += readwrite.count;
- }
+ /* Update our control variables */
+
+ sf->entry.datlen += readwrite.count;
+ sf->byteswritten += readwrite.count;
+ sf->filepos += readwrite.count;
+ sf->curroffset += readwrite.count;
+ buflen -= readwrite.count;
+ byteswritten += readwrite.count;
/* Test if we wrote a full sector of data */
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ if (sf->curroffset == fs->fs_llformat.availbytes && buflen)
+ {
+ /* First get a new chained sector */
+
+ ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF);
+ if (ret < 0)
+ {
+ fdbg("Error %d allocating new sector\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Copy the new sector to the old one and chain it */
+
+ header = (struct smartfs_chain_header_s *) sf->buffer;
+ *((uint16_t *) header->nextsector) = (uint16_t) ret;
+
+ /* Now sync the file to write this sector out */
+
+ ret = smartfs_sync_internal(fs, sf);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Record the new sector in our tracking variables and
+ * reset the offset to "zero".
+ */
+
+ if (sf->currsector == SMARTFS_NEXTSECTOR(header))
+ {
+ /* Error allocating logical sector! */
+
+ fdbg("Error - duplicate logical sector %d\n", sf->currsector);
+ }
+
+ sf->bflags = SMARTFS_BFLAG_DIRTY;
+ sf->currsector = SMARTFS_NEXTSECTOR(header);
+ sf->curroffset = sizeof(struct smartfs_chain_header_s);
+ memset(sf->buffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes);
+ header->type = SMARTFS_DIRENT_TYPE_FILE;
+ }
+#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
+
if (sf->curroffset == fs->fs_llformat.availbytes)
{
/* Sync the file to write this sector out */
- smartfs_sync_internal(fs, sf);
+ ret = smartfs_sync_internal(fs, sf);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
/* Allocate a new sector if needed */
@@ -833,10 +952,18 @@ static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
* reset the offset to "zero".
*/
+ if (sf->currsector == SMARTFS_NEXTSECTOR(header))
+ {
+ /* Error allocating logical sector! */
+
+ fdbg("Error - duplicate logical sector %d\n", sf->currsector);
+ }
+
sf->currsector = SMARTFS_NEXTSECTOR(header);
sf->curroffset = sizeof(struct smartfs_chain_header_s);
}
}
+#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
}
ret = byteswritten;
@@ -968,6 +1095,27 @@ static off_t smartfs_seek_internal(struct smartfs_mountpt_s *fs,
sf->filepos += SMARTFS_USED(header);
}
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+
+ /* When using sector buffering, we must read in the last buffer to our
+ * sf->buffer in case any changes are made.
+ */
+
+ if (sf->currsector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ readwrite.logsector = sf->currsector;
+ readwrite.offset = 0;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = (uint8_t *) sf->buffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d reading sector %d header\n", ret, sf->currsector);
+ goto errout;
+ }
+ }
+#endif
+
/* Now calculate the offset */
sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos - sf->filepos;
@@ -1465,6 +1613,11 @@ static int smartfs_statfs(struct inode *mountpt, struct statfs *buf)
buf->f_namelen = fs->fs_llformat.namesize;
buf->f_bsize = fs->fs_llformat.sectorsize;
buf->f_blocks = fs->fs_llformat.nsectors;
+ if (buf->f_blocks == 65535)
+ {
+ buf->f_blocks++;
+ }
+
buf->f_bfree = fs->fs_llformat.nfreesectors;
buf->f_bavail = fs->fs_llformat.nfreesectors;
buf->f_files = 0;
@@ -1596,7 +1749,7 @@ static int smartfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode
/* Create the directory */
ret = smartfs_createentry(fs, parentdirsector, filename,
- SMARTFS_DIRENT_TYPE_DIR, mode, &entry, 0xFFFF);
+ SMARTFS_DIRENT_TYPE_DIR, mode, &entry, 0xFFFF, NULL);
if (ret != OK)
{
goto errout_with_semaphore;
@@ -1840,7 +1993,7 @@ int smartfs_rename(struct inode *mountpt, const char *oldrelpath,
mode = oldentry.flags & SMARTFS_DIRENT_MODE;
type = oldentry.flags & SMARTFS_DIRENT_TYPE;
ret = smartfs_createentry(fs, newparentdirsector, newfilename,
- type, mode, &newentry, oldentry.firstsector);
+ type, mode, &newentry, oldentry.firstsector, NULL);
if (ret != OK)
{
goto errout_with_semaphore;
diff --git a/nuttx/fs/smartfs/smartfs_utils.c b/nuttx/fs/smartfs/smartfs_utils.c
index a7bb21e8e..23d0cff13 100644
--- a/nuttx/fs/smartfs/smartfs_utils.c
+++ b/nuttx/fs/smartfs/smartfs_utils.c
@@ -117,6 +117,92 @@ void smartfs_semgive(struct smartfs_mountpt_s *fs)
}
/****************************************************************************
+ * Name: smartfs_rdle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Value:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ ****************************************************************************/
+
+uint16_t smartfs_rdle16(FAR const void *val)
+{
+ return (uint16_t)((FAR const uint8_t *)val)[1] << 8 |
+ (uint16_t)((FAR const uint8_t *)val)[0];
+}
+
+/****************************************************************************
+ * Name: smartfs_wrle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void smartfs_wrle16(FAR void *dest, uint16_t val)
+{
+ ((FAR uint8_t *) dest)[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
+ ((FAR uint8_t *) dest)[1] = val >> 8;
+}
+
+/****************************************************************************
+ * Name: smartfs_rdle32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Value:
+ * A uint32_t representing the whole 32-bit integer value
+ *
+ ****************************************************************************/
+
+uint32_t smartfs_rdle32(FAR const void *val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ return (uint32_t)smartfs_rdle16(&((FAR const uint8_t *)val)[2]) << 16 |
+ (uint32_t)smartfs_rdle16(val);
+}
+
+/****************************************************************************
+ * Name: smartfs_wrle32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void smartfs_wrle32(uint8_t *dest, uint32_t val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ smartfs_wrle16(dest, (uint16_t)(val & 0xffff));
+ smartfs_wrle16(dest+2, (uint16_t)(val >> 16));
+}
+
+/****************************************************************************
* Name: smartfs_mount
*
* Desciption: This function is called only when the mountpoint is first
@@ -132,7 +218,7 @@ int smartfs_mount(struct smartfs_mountpt_s *fs, bool writeable)
FAR struct inode *inode;
struct geometry geo;
int ret = OK;
-#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+#if defined(CONFIG_SMARTFS_MULTI_ROOT_DIRS)
struct smartfs_mountpt_s *nextfs;
#endif
@@ -235,10 +321,11 @@ int smartfs_mount(struct smartfs_mountpt_s *fs, bool writeable)
g_mounthead = fs;
#endif
+#endif /* CONFIG_SMARTFS_MULTI_ROOT_DIRS */
+
fs->fs_rwbuffer = (char *) kmm_malloc(fs->fs_llformat.availbytes);
fs->fs_workbuffer = (char *) kmm_malloc(256);
fs->fs_rootsector = SMARTFS_ROOT_DIR_SECTOR;
-#endif /* CONFIG_SMARTFS_MULTI_ROOT_DIRS */
/* We did it! */
@@ -545,10 +632,17 @@ int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
{
/* Test if this entry is valid and active */
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ if (((smartfs_rdle16(&entry->flags) & SMARTFS_DIRENT_EMPTY) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
+ ((smartfs_rdle16(&entry->flags) & SMARTFS_DIRENT_ACTIVE) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#else
if (((entry->flags & SMARTFS_DIRENT_EMPTY) ==
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
((entry->flags & SMARTFS_DIRENT_ACTIVE) !=
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#endif
{
/* This entry isn't valid, skip it */
@@ -576,9 +670,15 @@ int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
/* Fill in the entry */
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ direntry->firstsector = smartfs_rdle16(&entry->firstsector);
+ direntry->flags = smartfs_rdle16(&entry->flags);
+ direntry->utc = smartfs_rdle32(&entry->utc);
+#else
direntry->firstsector = entry->firstsector;
direntry->flags = entry->flags;
direntry->utc = entry->utc;
+#endif
direntry->dsector = readwrite.logsector;
direntry->doffset = offset;
direntry->dfirst = dirstack[depth];
@@ -595,9 +695,17 @@ int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
* a rudimentary check.
*/
- if ((entry->flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE)
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ if ((smartfs_rdle16(&entry->flags) & SMARTFS_DIRENT_TYPE) ==
+ SMARTFS_DIRENT_TYPE_FILE)
+ {
+ dirsector = smartfs_rdle16(&entry->firstsector);
+#else
+ if ((entry->flags & SMARTFS_DIRENT_TYPE) ==
+ SMARTFS_DIRENT_TYPE_FILE)
{
dirsector = entry->firstsector;
+#endif
readwrite.count = sizeof(struct smartfs_chain_header_s);
readwrite.buffer = (uint8_t *)fs->fs_rwbuffer;
readwrite.offset = 0;
@@ -634,8 +742,13 @@ int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
{
/* Validate it's a directory */
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ if ((smartfs_rdle16(&entry->flags) & SMARTFS_DIRENT_TYPE) !=
+ SMARTFS_DIRENT_TYPE_DIR)
+#else
if ((entry->flags & SMARTFS_DIRENT_TYPE) !=
SMARTFS_DIRENT_TYPE_DIR)
+#endif
{
/* Not a directory! Report the error */
@@ -653,7 +766,11 @@ int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
goto errout;
}
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ dirstack[++depth] = smartfs_rdle16(&entry->firstsector);
+#else
dirstack[++depth] = entry->firstsector;
+#endif
segment = ptr + 1;
break;
}
@@ -727,7 +844,7 @@ int smartfs_createentry(struct smartfs_mountpt_s *fs,
uint16_t parentdirsector, const char* filename,
uint16_t type,
mode_t mode, struct smartfs_entry_s *direntry,
- uint16_t sectorno)
+ uint16_t sectorno, FAR struct smartfs_ofile_s *sf)
{
struct smart_read_write_s readwrite;
int ret;
@@ -784,8 +901,13 @@ int smartfs_createentry(struct smartfs_mountpt_s *fs,
{
/* Check if this entry is available */
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ if ((smartfs_rdle16(&entry->flags) == SMARTFS_ERASEDSTATE_16BIT) ||
+ ((smartfs_rdle16(&entry->flags) &
+#else
if ((entry->flags == SMARTFS_ERASEDSTATE_16BIT) ||
((entry->flags &
+#endif
(SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE) ) ==
(~SMARTFS_ERASEDSTATE_16BIT &
(SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE) )))
@@ -848,11 +970,24 @@ int smartfs_createentry(struct smartfs_mountpt_s *fs,
/* We found an insertion point. Create the entry at sector,offset */
#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
- entry->flags = (uint16_t) (SMARTFS_DIRENT_ACTIVE | SMARTFS_DIRENT_DELETING |
- SMARTFS_DIRENT_RESERVED | type | (mode & SMARTFS_DIRENT_MODE));
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ smartfs_wrle16(&entry->flags, (uint16_t) (SMARTFS_DIRENT_ACTIVE |
+ SMARTFS_DIRENT_DELETING | SMARTFS_DIRENT_RESERVED | type | (mode &
+ SMARTFS_DIRENT_MODE)));
+#else
+ entry->flags = (uint16_t) (SMARTFS_DIRENT_ACTIVE |
+ SMARTFS_DIRENT_DELETING | SMARTFS_DIRENT_RESERVED | type | (mode &
+ SMARTFS_DIRENT_MODE));
+#endif
+#else /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ smartfs_wrle16(&entry->flags, (uint16_t) (SMARTFS_DIRENT_EMPTY | type |
+ (mode & SMARTFS_DIRENT_MODE)));
#else
- entry->flags = (uint16_t) (SMARTFS_DIRENT_EMPTY | type | (mode & SMARTFS_DIRENT_MODE));
+ entry->flags = (uint16_t) (SMARTFS_DIRENT_EMPTY | type |
+ (mode & SMARTFS_DIRENT_MODE));
#endif
+#endif /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */
if (sectorno == 0xFFFF)
{
@@ -868,24 +1003,40 @@ int smartfs_createentry(struct smartfs_mountpt_s *fs,
/* Set the newly allocated sector's type (file or dir) */
- if ((type & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR)
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ if (sf)
{
- chainheader->type = SMARTFS_SECTOR_TYPE_DIR;
+ /* Using sector buffer and we have an open file context. Just update
+ * the sector buffer in the open file context.
+ */
+
+ memset(sf->buffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes);
+ chainheader = (struct smartfs_chain_header_s *) sf->buffer;
+ chainheader->type = SMARTFS_SECTOR_TYPE_FILE;
+ sf->bflags = SMARTFS_BFLAG_DIRTY | SMARTFS_BFLAG_NEWALLOC;
}
else
+#endif
{
- chainheader->type = SMARTFS_SECTOR_TYPE_FILE;
- }
+ if ((type & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR)
+ {
+ chainheader->type = SMARTFS_SECTOR_TYPE_DIR;
+ }
+ else
+ {
+ chainheader->type = SMARTFS_SECTOR_TYPE_FILE;
+ }
- readwrite.count = 1;
- readwrite.offset = offsetof(struct smartfs_chain_header_s, type);
- readwrite.buffer = (uint8_t *) &chainheader->type;
- readwrite.logsector = nextsector;
- ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
- if (ret < 0)
- {
- fdbg("Error %d setting new sector type for sector %d\n",ret, nextsector);
- goto errout;
+ readwrite.count = 1;
+ readwrite.offset = offsetof(struct smartfs_chain_header_s, type);
+ readwrite.buffer = (uint8_t *) &chainheader->type;
+ readwrite.logsector = nextsector;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d setting new sector type for sector %d\n",ret, nextsector);
+ goto errout;
+ }
}
}
else
@@ -897,8 +1048,13 @@ int smartfs_createentry(struct smartfs_mountpt_s *fs,
/* Create the directory entry to be written in the parent's sector */
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ smartfs_wrle16(&entry->firstsector, nextsector);
+ smartfs_wrle16(&entry->utc, time(NULL));
+#else
entry->firstsector = nextsector;
- entry->utc = 0;
+ entry->utc = time(NULL);
+#endif
memset(entry->name, 0, fs->fs_llformat.namesize);
strncpy(entry->name, filename, fs->fs_llformat.namesize);
@@ -919,8 +1075,13 @@ int smartfs_createentry(struct smartfs_mountpt_s *fs,
direntry->firstsector = nextsector;
direntry->dsector = psector;
direntry->doffset = offset;
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ direntry->flags = smartfs_rdle16(&entry->flags);
+ direntry->utc = smartfs_rdle32(&entry->utc);
+#else
direntry->flags = entry->flags;
- direntry->utc = 0;
+ direntry->utc = entry->utc;
+#endif
direntry->datlen = 0;
if (direntry->name == NULL)
{
@@ -1006,7 +1167,7 @@ int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
- fdbg("Error reading directory info at sector %s\n", entry->dsector);
+ fdbg("Error reading directory info at sector %d\n", entry->dsector);
goto errout;
}
@@ -1014,10 +1175,18 @@ int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[entry->doffset];
#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ smartfs_wrle16(&direntry->flags, smartfs_rdle16(&direntry->flags) & ~SMARTFS_DIRENT_ACTIVE);
+#else
direntry->flags &= ~SMARTFS_DIRENT_ACTIVE;
+#endif
+#else /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ smartfs_wrle16(&direntry->flags, smartfs_rdle16(&direntry->flags) | SMARTFS_DIRENT_ACTIVE);
#else
direntry->flags |= SMARTFS_DIRENT_ACTIVE;
#endif
+#endif /* CONFIG_SMARTFS_ERASEDSTATE == 0xFF */
/* Write the updated flags back to the sector */
@@ -1027,7 +1196,7 @@ int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
if (ret < 0)
{
- fdbg("Error marking entry inactive at sector %s\n", entry->dsector);
+ fdbg("Error marking entry inactive at sector %d\n", entry->dsector);
goto errout;
}
@@ -1046,10 +1215,17 @@ int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
/* Test the next entry */
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ if (((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_EMPTY) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
+ ((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_ACTIVE) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#else
if (((direntry->flags & SMARTFS_DIRENT_EMPTY) !=
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
((direntry->flags & SMARTFS_DIRENT_ACTIVE) ==
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#endif
{
/* Count this entry */
count++;
@@ -1190,10 +1366,17 @@ int smartfs_countdirentries(struct smartfs_mountpt_s *fs,
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
while (offset + entrysize < readwrite.count)
{
+#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
+ if (((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_EMPTY) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
+ ((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_ACTIVE) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#else
if (((direntry->flags & SMARTFS_DIRENT_EMPTY) !=
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
((direntry->flags & SMARTFS_DIRENT_ACTIVE) ==
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+#endif
{
/* Count this entry */
count++;
@@ -1223,7 +1406,7 @@ errout:
****************************************************************************/
int smartfs_truncatefile(struct smartfs_mountpt_s *fs,
- struct smartfs_entry_s *entry)
+ struct smartfs_entry_s *entry, FAR struct smartfs_ofile_s *sf)
{
int ret;
uint16_t nextsector;
@@ -1263,6 +1446,15 @@ int smartfs_truncatefile(struct smartfs_mountpt_s *fs,
if (nextsector == entry->firstsector)
{
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+
+ /* When we have a sector buffer in use, simply skip the first sector */
+
+ nextsector = sector;
+ continue;
+
+#else
+
/* Fill our buffer with erased data */
memset(fs->fs_rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes);
@@ -1281,6 +1473,7 @@ int smartfs_truncatefile(struct smartfs_mountpt_s *fs,
/* Set the entry's data length to zero ... we just truncated */
entry->datlen = 0;
+#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
}
else
{
@@ -1299,6 +1492,31 @@ int smartfs_truncatefile(struct smartfs_mountpt_s *fs,
nextsector = sector;
}
+ /* Now deal with the first sector in the event we are using a sector buffer
+ like we would be if CRC is enabled.
+ */
+
+#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
+ if (sf)
+ {
+ /* Using sector buffer and we have an open file context. Just update
+ * the sector buffer in the open file context.
+ */
+
+ readwrite.logsector = entry->firstsector;
+ readwrite.offset = 0;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+
+ memset(sf->buffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes);
+ header = (struct smartfs_chain_header_s *) sf->buffer;
+ header->type = SMARTFS_SECTOR_TYPE_FILE;
+ sf->bflags = SMARTFS_BFLAG_DIRTY;
+ entry->datlen = 0;
+ }
+#endif
+
ret = OK;
errout:
diff --git a/nuttx/include/nuttx/fs/ioctl.h b/nuttx/include/nuttx/fs/ioctl.h
index 491aed3c5..6a55d7975 100644
--- a/nuttx/include/nuttx/fs/ioctl.h
+++ b/nuttx/include/nuttx/fs/ioctl.h
@@ -200,6 +200,12 @@
* ProcFS data.
* OUT: None (ioctl return value provides
* success/failure indication). */
+#define BIOC_DEBUGCMD _BIOC(0x000B) /* Send driver specific debug command /
+ * data to the block device.
+ * IN: Pointer to a struct defined for
+ * the block with specific debug
+ * command and data.
+ * OUT: None. */
/* NuttX MTD driver ioctl definitions ***************************************/
diff --git a/nuttx/include/nuttx/fs/smart.h b/nuttx/include/nuttx/fs/smart.h
index 50ae2c038..2621dd77f 100644
--- a/nuttx/include/nuttx/fs/smart.h
+++ b/nuttx/include/nuttx/fs/smart.h
@@ -89,6 +89,20 @@ struct smart_read_write_s
const uint8_t *buffer; /* Pointer to the data to write */
};
+/* The following defines the procfs data exchange interface between the
+ * SMART MTD and FS layers.
+ */
+
+#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
+struct smart_procfs_data_s
+{
+#ifdef CONFIG_MTD_SMART_ERASE_DEBUG
+ const uint16_t *erasecounts; /* Pointer to the erase counts array */
+ uint16_t erasesize; /* Number of entries in the erase counts array */
+#endif
+};
+#endif
+
/****************************************************************************
* Public Data
****************************************************************************/
diff --git a/nuttx/include/nuttx/mtd/smart.h b/nuttx/include/nuttx/mtd/smart.h
index b2239b99e..4bdcecd31 100644
--- a/nuttx/include/nuttx/mtd/smart.h
+++ b/nuttx/include/nuttx/mtd/smart.h
@@ -50,6 +50,9 @@
* Pre-Processor Definitions
****************************************************************************/
+#define SMART_DEBUG_CMD_SET_DEBUG_LEVEL 1
+#define SMART_DEBUG_CMD_SHOW_LOGMAP 2
+
/****************************************************************************
* Public Types
****************************************************************************/
@@ -91,6 +94,19 @@ struct mtd_smart_procfs_data_s
FAR const struct smart_alloc_s *allocs; /* Array of allocations */
uint16_t alloccount; /* Number of items in the array */
#endif
+#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
+ uint32_t uneven_wearcount; /* Number of uneven block erases */
+#endif
+};
+
+/* The following defines debug command data passed from the procfs layer to
+ the SMART MTD layer for debug purposes.
+ */
+
+struct mtd_smart_debug_data_s
+{
+ uint8_t debugcmd; /* Debug command */
+ uint32_t debugdata; /* Debug data */
};
/****************************************************************************