SmartFS: Implements wear-leveling in the SmartFS. From Ken Pettit

This commit is contained in:
Gregory Nutt 2014-12-09 14:11:15 -06:00
parent 69cb752813
commit cdc8fc52d1
12 changed files with 4382 additions and 518 deletions

View File

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

0
drivers/mtd/mtd_nand.c Executable file → Normal file
View File

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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.
4. The released-sector garbage collection process occurs only during a write
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.
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.

View File

@ -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);

View File

@ -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;
}
}
}
@ -695,6 +753,33 @@ static int smartfs_stat(const char *relpath, struct stat *buf)
return ret;
}
/****************************************************************************
* 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
*
@ -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 */

View File

@ -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 */
sf->entry.datlen += readwrite.count;
sf->byteswritten += readwrite.count;
sf->filepos += readwrite.count;
sf->curroffset += readwrite.count;
buflen -= readwrite.count;
byteswritten += readwrite.count;
}
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
/* 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;

View File

@ -116,6 +116,92 @@ void smartfs_semgive(struct smartfs_mountpt_s *fs)
sem_post(fs->fs_sem);
}
/****************************************************************************
* 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
*
@ -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_EMPTY | type | (mode & SMARTFS_DIRENT_MODE));
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));
#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:

View File

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

View File

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

View File

@ -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 */
};
/****************************************************************************