* drivers/mtd/smart.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/crc8.h>
#include <nuttx/crc16.h>
#include <nuttx/crc32.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd/mtd.h>
#include <nuttx/mtd/smart.h>
#include <nuttx/fs/smart.h>
* Pre-processor Definitions
****************************************************************************/
#if 0
# define CONFIG_SMART_LOCAL_CHECKFREE
#endif
#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
#define SMART_SECTSIZE_1024 0x08
#define SMART_SECTSIZE_2048 0x0c
#define SMART_SECTSIZE_4096 0x10
#define SMART_SECTSIZE_8192 0x14
#define SMART_SECTSIZE_16384 0x18
#define SMART_FMT_STAT_UNKNOWN 0
#define SMART_FMT_STAT_FORMATTED 1
#define SMART_FMT_STAT_NOFMT 2
#define SMART_FMT_POS1 sizeof(struct smart_sect_header_s)
#define SMART_FMT_POS2 (SMART_FMT_POS1 + 1)
#define SMART_FMT_POS3 (SMART_FMT_POS1 + 2)
#define SMART_FMT_POS4 (SMART_FMT_POS1 + 3)
#define SMART_FMT_SIG1 'S'
#define SMART_FMT_SIG2 'M'
#define SMART_FMT_SIG3 'R'
#define SMART_FMT_SIG4 'T'
#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_WEAR_POS 36
#define SMART_WEAR_LEVEL_FORMAT_SIG 32
#define SMART_PARTNAME_SIZE 4
#define SMART_FIRST_DIR_SECTOR 3
#define SMART_FIRST_ALLOC_SECTOR 12
* we will use for assignment
* of requested alloc sectors.
* All entries below this are
* reserved (some for root dir
* entries other for our use
* such as format, sector,
* etc.) */
#if defined(CONFIG_MTD_SMART_READAHEAD) || (defined(CONFIG_DRVR_WRITABLE) && \
defined(CONFIG_MTD_SMART_WRITEBUFFER))
# define SMART_HAVE_RWBUFFER 1
#endif
#ifndef CONFIG_MTD_SMART_SECTOR_SIZE
# define CONFIG_MTD_SMART_SECTOR_SIZE 1024
#endif
#ifndef offsetof
#define offsetof(type, member) ( (size_t) &( ( (type *) 0)->member))
#endif
#define SMART_MAX_ALLOCS 10
#ifndef CONFIG_MTD_SMART_ALLOC_DEBUG
#define smart_malloc(d, b, n) kmm_malloc(b)
#define smart_zalloc(d, b, n) kmm_zalloc(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
#define SMART_WEARFLAGS_FORCE_REORG 0x01
#define SMART_WEARFLAGS_WRITE_NEEDED 0x02
#define SET_BITMAP(m, n) do { (m)[(n) / 8] |= 1 << ((n) % 8); } while (0)
#define CLR_BITMAP(m, n) do { (m)[(n) / 8] &= ~(1 << ((n) % 8)); } while (0)
#define ISSET_BITMAP(m, n) ((m)[(n) / 8] & (1 << ((n) % 8)))
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
# define SMARTFS_NEXTSECTOR(h) \
(uint16_t)((FAR const uint8_t *)(h)->nextsector)[1] << 8 | \
(uint16_t)((FAR const uint8_t *)(h)->nextsector)[0]
# define SMARTFS_SET_NEXTSECTOR(h, v) \
do \
{ \
((FAR uint8_t *)(h)->nextsector)[0] = (v) & 0xff; \
((FAR uint8_t *)(h)->nextsector)[1] = (v) >> 8; \
} while (0)
#else
# define SMARTFS_NEXTSECTOR(h) (*((FAR uint16_t *)(h)->nextsector))
# define SMARTFS_SET_NEXTSECTOR(h, v) \
do \
{ \
((*((FAR uint16_t *)(h)->nextsector)) = (uint16_t)(v)); \
} while (0)
#endif
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
* Private Data
****************************************************************************/
* 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 g_wearlevel_to_bitmap4[] =
{
0x0f, 0x0e, 0x0c, 0x08,
0x07, 0x06, 0x04, 0x00,
0x0b, 0x0a, 0x02,
0x0d, 0x09, 0x01,
0x03,
0x05
};
static const uint8_t g_wearbit_to_levelmap4[] =
{
7, 13, 10, 14, 6, 15, 5, 4,
3, 12, 9, 8, 2, 11, 1, 0
};
#endif
* Private Types
****************************************************************************/
#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM
struct smart_cache_s
{
uint16_t logical;
uint16_t physical;
uint16_t birth;
};
#endif
* 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;
uint16_t logical;
uint16_t physical;
};
#endif
struct smart_struct_s
{
FAR struct mtd_dev_s *mtd;
struct mtd_geometry_s geo;
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
uint32_t unusedsectors;
uint32_t blockerases;
#endif
uint16_t neraseblocks;
uint16_t lastallocblock;
uint16_t freesectors;
uint16_t releasesectors;
uint16_t mtdblkspersector;
uint16_t sectorsperblk;
uint16_t sectorsize;
uint16_t totalsectors;
uint32_t erasesize;
FAR uint8_t *releasecount;
FAR uint8_t *freecount;
FAR char *rwbuffer;
char partname[SMART_PARTNAME_SIZE];
uint8_t formatversion;
uint8_t formatstatus;
uint8_t namesize;
uint8_t debuglevel;
uint8_t availsectperblk;
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
uint8_t rootdirentries;
uint8_t minor;
#endif
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
uint8_t wearflags;
uint8_t minwearlevel;
uint8_t maxwearlevel;
uint8_t *wearstatus;
uint32_t uneven_wearcount;
#endif
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
FAR struct smart_allocsector_s *allocsector;
#endif
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
FAR uint16_t *smap;
#else
FAR uint8_t *sbitmap;
FAR struct smart_cache_s *scache;
uint16_t cache_entries;
uint16_t cache_lastlog;
uint16_t cache_lastphys;
uint16_t cache_nextbirth;
#endif
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
FAR uint8_t *erasecounts;
#endif
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
size_t bytesalloc;
struct smart_alloc_s alloc[SMART_MAX_ALLOCS];
#endif
};
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
struct smart_multiroot_device_s
{
FAR struct smart_struct_s *dev;
uint8_t rootdirnum;
};
#endif
#if SMART_STATUS_VERSION == 1
#define SMART_FMT_VERSION 1
struct smart_sect_header_s
{
uint8_t logicalsector[2];
uint8_t seq;
uint8_t crc8;
uint8_t status;
* Bit 7: 1 = Not committed
* 0 = committed
* 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;
#elif SMART_STATUS_VERSION == 2
#define SMART_FMT_VERSION 2
struct smart_sect_header_s
{
uint8_t logicalsector[2];
uint8_t crc16[2];
uint8_t status;
* Bit 7: 1 = Not committed
* 0 = committed
* 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;
};
typedef uint16_t crc_t;
* 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];
uint8_t crc32[4];
uint8_t status;
* Bit 7: 1 = Not committed
* 0 = committed
* Bit 6: 1 = Not released
* 0 = released
* Bit 5: Sector CRC enable
* Bit 4-2: Sector size on volume
* Bit 1-0: Format version (0x3) */
uint8_t seq;
};
typedef uint32_t crc_t;
#endif
* Because needed to search chain_header and entry_header.
*/
#if defined(CONFIG_MTD_SMART_ENABLE_CRC) && defined(CONFIG_SMART_CRC_32)
struct smart_chain_header_s
{
uint8_t nextsector[4];
uint8_t used[4];
uint8_t type;
};
#else
struct smart_chain_header_s
{
uint8_t type;
uint8_t nextsector[2];
uint8_t used[2];
};
#endif
struct smart_entry_header_s
{
uint16_t flags;
* 15: Empty entry
* 14: Active entry
* 12-0: Permissions bits */
int16_t firstsector;
uint32_t utc;
};
* Private Function Prototypes
****************************************************************************/
static int smart_open(FAR struct inode *inode);
static int smart_close(FAR struct inode *inode);
static ssize_t smart_reload(struct smart_struct_s *dev, FAR uint8_t *buffer,
off_t startblock, size_t nblocks);
static ssize_t smart_read(FAR struct inode *inode, FAR unsigned char *buffer,
blkcnt_t start_sector, unsigned int nsectors);
static ssize_t smart_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t start_sector, unsigned int nsectors);
static int smart_geometry(FAR struct inode *inode,
FAR 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,
bool canrelocate);
static int smart_writesector(FAR struct smart_struct_s *dev,
unsigned long arg);
static inline int smart_allocsector(FAR struct smart_struct_s *dev,
unsigned long requested);
static int smart_readsector(FAR struct smart_struct_s *dev,
unsigned long arg);
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
static int smart_validate_crc(FAR struct smart_struct_s *dev);
#endif
#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);
#ifdef CONFIG_MTD_SMART_FSCK
static int smart_fsck(FAR struct smart_struct_s *dev);
#endif
#ifdef CONFIG_SMART_DEV_LOOP
static ssize_t smart_loop_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t smart_loop_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static int smart_loop_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
#endif
* Private Data
****************************************************************************/
static const struct block_operations g_bops =
{
smart_open,
smart_close,
smart_read,
smart_write,
smart_geometry,
smart_ioctl
};
#ifdef CONFIG_SMART_DEV_LOOP
static const struct file_operations g_fops =
{
NULL,
NULL,
smart_loop_read,
smart_loop_write,
NULL,
smart_loop_ioctl,
};
#endif
* Private Functions
****************************************************************************/
* Name: smart_open
*
* Description: Open the block device
*
****************************************************************************/
static int smart_open(FAR struct inode *inode)
{
finfo("Entry\n");
return OK;
}
* Name: smart_close
*
* Description: close the block device
*
****************************************************************************/
static int smart_close(FAR struct inode *inode)
{
finfo("Entry\n");
return OK;
}
* 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, FAR const char *name)
{
FAR void *ret = kmm_malloc(bytes);
uint8_t x;
if (dev == NULL)
{
dev = ret;
dev->bytesalloc = 0;
for (x = 0; x < SMART_MAX_ALLOCS; x++)
{
dev->alloc[x].ptr = NULL;
}
}
if (ret != NULL)
{
dev->bytesalloc += bytes;
}
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;
}
}
finfo("SMART alloc: %ld\n", dev->bytesalloc);
return ret;
}
#endif
* Name: smart_zalloc
*
* Description: Perform allocations and keep track of amount of allocated
* memory for this context.
*
****************************************************************************/
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
FAR static void *smart_zalloc(FAR struct smart_struct_s *dev,
size_t bytes, FAR const char *name)
{
FAR void *mem;
mem = smart_malloc(dev, bytes, name);
if (mem != NULL)
{
memset(mem, 0, bytes);
}
return mem;
}
#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
{
if (block & 0x01)
{
pcount[block >> 1] = (pcount[block >> 1] & 0xf0) |
(count & 0x0f);
}
else
{
pcount[block >> 1] = (pcount[block >> 1] & 0x0f) |
((count & 0x0f) << 4);
}
* 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
{
if (block & 0x01)
{
count = pcount[block >> 1] & 0x0f;
}
else
{
count = pcount[block >> 1] >> 4;
}
* 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(FAR struct smart_struct_s *dev,
FAR 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;
uint16_t freecount;
#ifdef CONFIG_DEBUG_FS
uint16_t blockfree;
uint16_t 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
}
#ifdef CONFIG_DEBUG_FS
if (freecount != dev->freesectors)
{
fwarn("WARNING: Free count incorrect in line %d! Calculated=%d, "
"dev->freesectors=%d\n",
lineno, freecount, dev->freesectors);
fwarn(" ... 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)
{
* report it.
*/
fwarn(" ... Block %d: Old Free=%d, old release=%d, "
"New free=%d, new release = %d\n",
x, prev_freecount[x], prev_releasecount[x],
blockfree, blockrelease);
}
}
}
* freecount to get us back in line.
*/
dev->freesectors = freecount;
return -EIO;
}
* 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
}
}
prev_freesectors = dev->freesectors;
prev_releasesectors = dev->releasesectors;
#endif
return OK;
}
#endif
* Name: smart_reload
*
* Description: Read the specified number of sectors
*
****************************************************************************/
static ssize_t smart_reload(struct smart_struct_s *dev, FAR uint8_t *buffer,
off_t startblock, size_t nblocks)
{
ssize_t nread;
ssize_t mtdblocks;
ssize_t mtdstartblock;
mtdblocks = nblocks * dev->mtdblkspersector;
mtdstartblock = startblock * dev->mtdblkspersector;
finfo("Read %zu blocks starting at block %zu\n",
mtdblocks, mtdstartblock);
nread = MTD_BREAD(dev->mtd, mtdstartblock, mtdblocks, buffer);
if (nread != mtdblocks)
{
ferr("ERROR: Read %zd blocks starting at block %" PRIdOFF
" failed: %zd\n", nblocks, startblock, nread);
}
return nread;
}
* Name: smart_read
*
* Description: Read the specified number of sectors
*
****************************************************************************/
static ssize_t smart_read(FAR struct inode *inode, FAR unsigned char *buffer,
blkcnt_t start_sector, unsigned int nsectors)
{
FAR struct smart_struct_s *dev;
finfo("SMART: sector: %" PRIuOFF " nsectors: %u\n",
start_sector, nsectors);
DEBUGASSERT(inode->i_private);
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
dev = ((FAR struct smart_multiroot_device_s *)inode->i_private)->dev;
#else
dev = inode->i_private;
#endif
return smart_reload(dev, buffer, start_sector, nsectors);
}
* Name: smart_write
*
* Description: Write (or buffer) the specified number of sectors
*
****************************************************************************/
static ssize_t smart_write(FAR struct inode *inode,
FAR const unsigned char *buffer,
blkcnt_t start_sector, unsigned int nsectors)
{
FAR struct smart_struct_s *dev;
off_t alignedblock;
off_t mask;
off_t blkstowrite;
off_t offset;
off_t nextblock;
off_t mtdblkspererase;
off_t eraseblock;
size_t remaining;
size_t nxfrd;
int ret;
off_t mtdstartblock;
off_t mtdblockcount;
finfo("sector: %" PRIuOFF " nsectors: %u\n", start_sector, nsectors);
DEBUGASSERT(inode->i_private);
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
dev = ((FAR struct smart_multiroot_device_s *)inode->i_private)->dev;
#else
dev = inode->i_private;
#endif
* per erase block is a power of 2, and (2) the erase begins with that same
* alignment.
*/
mask = dev->sectorsperblk - 1;
alignedblock = ((start_sector + mask) & ~mask) * dev->mtdblkspersector;
mtdstartblock = start_sector * dev->mtdblkspersector;
mtdblockcount = nsectors * dev->mtdblkspersector;
mtdblkspererase = dev->mtdblkspersector * dev->sectorsperblk;
finfo("mtdsector: %" PRIdOFF " mtdnsectors: %" PRIdOFF "\n",
mtdstartblock, mtdblockcount);
remaining = mtdblockcount;
nextblock = mtdstartblock;
offset = 0;
while (remaining > 0)
{
if (alignedblock == nextblock)
{
eraseblock = alignedblock / mtdblkspererase;
ret = MTD_ERASE(dev->mtd, eraseblock, 1);
if (ret < 0)
{
ferr("ERROR: Erase block=%" PRIdOFF " failed: %d\n",
eraseblock, ret);
return ret;
}
}
blkstowrite = mtdblkspererase;
if (nextblock != alignedblock)
{
blkstowrite = alignedblock - nextblock;
}
if (blkstowrite > remaining)
{
blkstowrite = remaining;
}
finfo("Write MTD block %" PRIdOFF " from offset %" PRIdOFF "\n",
nextblock, offset);
nxfrd = MTD_BWRITE(dev->mtd, nextblock, blkstowrite, &buffer[offset]);
if (nxfrd != blkstowrite)
{
ferr("ERROR: Write block %" PRIdOFF " failed: %zd.\n",
nextblock, nxfrd);
return -EIO;
}
nextblock += blkstowrite;
remaining -= blkstowrite;
offset += blkstowrite * dev->geo.blocksize;
alignedblock += mtdblkspererase;
}
return nsectors;
}
* Name: smart_geometry
*
* Description: Return device geometry
*
****************************************************************************/
static int smart_geometry(FAR struct inode *inode,
FAR struct geometry *geometry)
{
FAR struct smart_struct_s *dev;
uint32_t erasesize;
finfo("Entry\n");
if (geometry)
{
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
dev = ((FAR struct smart_multiroot_device_s *)inode->i_private)->dev;
#else
dev = inode->i_private;
#endif
geometry->geo_available = true;
geometry->geo_mediachanged = false;
geometry->geo_writeenabled = true;
erasesize = dev->geo.erasesize;
geometry->geo_nsectors = dev->geo.neraseblocks * erasesize /
dev->sectorsize;
geometry->geo_sectorsize = dev->sectorsize;
strlcpy(geometry->geo_model, dev->geo.model,
sizeof(geometry->geo_model));
finfo("available: true mediachanged: false writeenabled: %s\n",
geometry->geo_writeenabled ? "true" : "false");
finfo("nsectors: %" PRIuOFF " sectorsize: %" PRIi16 "\n",
geometry->geo_nsectors, geometry->geo_sectorsize);
return OK;
}
return -EINVAL;
}
* Name: smart_setsectorsize
*
* Description: Sets the device's sector size and recalculates sector size
* dependent variables.
*
****************************************************************************/
static int smart_setsectorsize(FAR struct smart_struct_s *dev, uint16_t size)
{
uint32_t erasesize;
uint32_t totalsectors;
uint32_t allocsize;
if (size == 0)
{
size = CONFIG_MTD_SMART_SECTOR_SIZE;
}
if (size == dev->sectorsize)
{
return OK;
}
erasesize = dev->geo.erasesize;
dev->neraseblocks = dev->geo.neraseblocks;
dev->erasesize = erasesize;
dev->sectorsize = size;
dev->mtdblkspersector = dev->sectorsize / dev->geo.blocksize;
DEBUGASSERT(dev->sectorsize >= dev->geo.blocksize);
DEBUGASSERT(erasesize / dev->sectorsize <= 256);
if (erasesize / dev->sectorsize > 256)
{
* 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
{
* block
*/
dev->sectorsperblk = erasesize / dev->sectorsize;
if (dev->sectorsperblk == 256)
{
dev->availsectperblk = 255;
}
else if (dev->sectorsperblk == 0)
{
return -EINVAL;
}
else
{
dev->availsectperblk = dev->sectorsperblk;
}
}
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
dev->unusedsectors = 0;
dev->blockerases = 0;
#endif
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->smap != NULL)
{
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)
{
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
* the storage space for releasecount and freecounts.
*/
totalsectors = dev->neraseblocks * dev->sectorsperblk;
if (totalsectors > 65536)
{
ferr("ERROR: Invalid SMART sector count %" PRIu32 "\n", totalsectors);
return -EINVAL;
}
else if (totalsectors == 65536)
{
* to allow a smaller sector size with almost maximum flash usage.
*/
totalsectors -= 2;
}
dev->totalsectors = (uint16_t)totalsectors;
#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)
{
ferr("ERROR: Error allocating SMART virtual map buffer\n");
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)
{
ferr("ERROR: Error allocating SMART sector cache\n");
goto errexit;
}
#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
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)
{
ferr("ERROR: 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
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
if (dev->erasecounts == NULL)
{
dev->erasecounts = (FAR uint8_t *)
smart_malloc(dev, dev->neraseblocks, "Erase counts");
}
if (!dev->erasecounts)
{
ferr("ERROR: Error allocating erase count array\n");
goto errexit;
}
memset(dev->erasecounts, 0, dev->neraseblocks);
#endif
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
dev->wearstatus = (FAR uint8_t *)smart_malloc(dev, dev->neraseblocks >>
SMART_WEAR_BIT_DIVIDE, "Wear status");
if (!dev->wearstatus)
{
ferr("ERROR: 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
dev->rwbuffer = (FAR char *)smart_malloc(dev, size, "RW Buffer");
if (!dev->rwbuffer)
{
ferr("ERROR: Error allocating SMART read/write buffer\n");
goto errexit;
}
return OK;
* previously been allocated.
*/
errexit:
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->smap)
{
smart_free(dev, dev->smap);
dev->smap = NULL;
}
#else
if (dev->sbitmap)
{
smart_free(dev, dev->sbitmap);
dev->sbitmap = NULL;
}
if (dev->scache)
{
smart_free(dev, dev->scache);
dev->scache = NULL;
}
#endif
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
if (dev->wearstatus)
{
smart_free(dev, dev->wearstatus);
dev->wearstatus = NULL;
}
#endif
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
if (dev->erasecounts)
{
smart_free(dev, dev->erasecounts);
dev->erasecounts = NULL;
}
#endif
return -ENOMEM;
}
* Name: smart_bytewrite
*
* Description: Writes a non-page size count of bytes to the underlying
* 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 actually changed.
*
****************************************************************************/
static ssize_t smart_bytewrite(FAR struct smart_struct_s *dev, size_t offset,
int nbytes, FAR const uint8_t *buffer)
{
ssize_t ret;
#ifdef CONFIG_MTD_BYTE_WRITE
if (dev->mtd->write != NULL)
{
ret = dev->mtd->write(dev->mtd, offset, nbytes, buffer);
}
else
#endif
{
uint32_t startblock;
uint16_t nblocks;
startblock = offset / dev->geo.blocksize;
nblocks = (offset - startblock * dev->geo.blocksize + nbytes +
dev->geo.blocksize - 1) / dev->geo.blocksize;
DEBUGASSERT(nblocks <= dev->mtdblkspersector);
ret = MTD_BREAD(dev->mtd, startblock, nblocks,
(FAR uint8_t *)dev->rwbuffer);
if (ret < 0)
{
ferr("ERROR: Error %zd reading from device\n", -ret);
goto errout;
}
memcpy(&dev->rwbuffer[offset - startblock * dev->geo.blocksize],
buffer, nbytes);
ret = MTD_BWRITE(dev->mtd, startblock, nblocks,
(FAR uint8_t *)dev->rwbuffer);
if (ret < 0)
{
ferr("ERROR: Error %zd writing to device\n", -ret);
goto errout;
}
}
ret = nbytes;
errout:
return ret;
}
* Name: smart_add_sector_to_cache
*
* Description: Adds a logical to physical sector mapping 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;
uint16_t x;
uint16_t oldest;
index = 1;
if (dev->cache_entries < CONFIG_MTD_SMART_SECTOR_CACHE_SIZE)
{
oldest = 0;
index = dev->cache_entries++;
}
else
{
* it
*/
oldest = 0xffff;
for (x = 0; x < CONFIG_MTD_SMART_SECTOR_CACHE_SIZE; x++)
{
if (dev->scache[x].logical < SMART_FIRST_ALLOC_SECTOR)
{
continue;
}
if (dev->scache[x].birth < oldest)
{
oldest = dev->scache[x].birth;
index = x;
}
}
}
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)
{
_err("Add Cache sector: Log=%d, Phys=%d at index %d from line %d\n",
logical, physical, index, line);
}
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;
uint16_t sector;
uint16_t x;
uint16_t physical;
uint16_t logicalsector;
struct smart_sect_header_s header;
size_t readaddress;
physical = 0xffff;
if (logical == dev->cache_lastlog)
{
return dev->cache_lastphys;
}
for (x = 0; x < dev->cache_entries; x++)
{
if (dev->scache[x].logical == logical)
{
physical = dev->scache[x].physical;
break;
}
}
* for it and add it to the cache.
*/
if (physical == 0xffff)
{
* 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->availsectperblk && physical == 0xffff;
sector++)
{
for (block = 0; block < dev->geo.neraseblocks; block++)
{
readaddress = block * dev->erasesize +
sector * dev->sectorsize;
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;
}
logicalsector = *((FAR uint16_t *)header.logicalsector);
#if CONFIG_SMARTFS_ERASEDSTATE == 0x00
if (logicalsector == 0)
{
continue;
}
#endif
if ((header.status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))
{
continue;
}
if ((header.status & SMART_STATUS_RELEASED) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))
{
continue;
}
if ((header.status & SMART_STATUS_VERBITS) !=
SMART_STATUS_VERSION)
{
continue;
}
if (logicalsector == logical)
{
* cache
*/
physical = block * dev->sectorsperblk + sector;
smart_add_sector_to_cache(dev, logical, physical,
__LINE__);
break;
}
}
}
}
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;
for (x = 0; x < dev->cache_entries; x++)
{
if (dev->scache[x].logical == logical)
{
dev->scache[x].physical = physical;
* 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)
{
_err("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)
{
bits >>= 4;
}
else
{
bits &= 0x0f;
}
return g_wearbit_to_levelmap4[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;
for (x = 0; x < dev->geo.neraseblocks; x++)
{
level = smart_get_wear_level(dev, x);
if (level < dev->minwearlevel)
{
dev->minwearlevel = level;
}
if (level > dev->maxwearlevel)
{
dev->maxwearlevel = level;
}
}
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
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 without the
* need to relocate 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;
uint8_t oldlevel;
oldlevel = smart_get_wear_level(dev, block);
if (level > 15)
{
_err("ERROR: Fatal Design Error! Wear level > 15, block=%d\n", block);
* 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 = g_wearlevel_to_bitmap4[level];
if (block & 0x01)
{
dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] &= 0x0f;
dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] |= bits << 4;
}
else
{
dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] &= 0xf0;
dev->wearstatus[block >> SMART_WEAR_BIT_DIVIDE] |= bits;
}
dev->wearflags |= SMART_WEARFLAGS_WRITE_NEEDED;
if (oldlevel + 1 == level)
{
if (level > dev->maxwearlevel)
{
dev->maxwearlevel = level;
}
* we need to rescan for min.
*/
if (oldlevel == dev->minwearlevel)
{
smart_find_wear_minmax(dev);
if (oldlevel != dev->minwearlevel)
{
finfo("##### 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
* information and fills in logical sector mapping, freesector
* count, etc.
*
****************************************************************************/
static int smart_scan(FAR struct smart_struct_s *dev)
{
int sector;
int ret;
uint16_t totalsectors;
uint16_t sectorsize;
uint16_t prerelease;
uint16_t logicalsector;
uint16_t winner;
uint16_t loser;
uint32_t readaddress;
uint32_t offset;
uint16_t seq1;
uint16_t seq2;
uint16_t seqwrap;
struct smart_sect_header_s header;
#ifdef CONFIG_MTD_SMART_MINIMIZE_RAM
int dupsector;
uint16_t duplogsector;
#endif
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
int x;
char devname[32];
FAR struct smart_multiroot_device_s *rootdirdev;
#endif
static const uint16_t sizetbl[8] =
{
CONFIG_MTD_SMART_SECTOR_SIZE,
512, 1024, 4096, 2048, 8192, 16384, 32768
};
finfo("Entry\n");
* sectors of decreasing size. On a formatted volume, the sector
* size is saved in the header status byte of search sector, so
* by starting with the largest supported sector size and
* decreasing from there, we will be sure to find data that is
* a header and not sector data.
*/
sectorsize = 0xffff;
offset = 16384;
while (sectorsize == 0xffff)
{
readaddress = 0;
while (readaddress < dev->erasesize * dev->geo.neraseblocks)
{
ret = MTD_READ(dev->mtd, readaddress,
sizeof(struct smart_sect_header_s),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
}
if (header.status != CONFIG_SMARTFS_ERASEDSTATE)
{
sectorsize =
sizetbl[(header.status & SMART_STATUS_SIZEBITS) >> 2];
break;
}
readaddress += offset;
}
if (sectorsize == 0xffff)
{
sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
}
offset >>= 1;
if (offset < 256 && sectorsize == 0xffff)
{
* sector size to the CONFIG value
*/
sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
}
}
ret = smart_setsectorsize(dev, sectorsize);
if (ret != OK)
{
goto err_out;
}
totalsectors = dev->totalsectors;
dev->formatstatus = SMART_FMT_STAT_NOFMT;
dev->freesectors = dev->availsectperblk * dev->geo.neraseblocks;
dev->releasesectors = 0;
for (sector = 0; sector < dev->neraseblocks; sector++)
{
if (sector == dev->neraseblocks - 1 && dev->totalsectors == 65534)
{
prerelease = 2;
}
else
{
prerelease = 0;
}
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
smart_set_count(dev, dev->freecount, sector,
dev->availsectperblk - prerelease);
smart_set_count(dev, dev->releasecount, sector, prerelease);
#else
dev->freecount[sector] = dev->availsectperblk - prerelease;
dev->releasecount[sector] = prerelease;
#endif
}
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
for (sector = 0; sector < totalsectors; sector++)
{
dev->smap[sector] = -1;
}
#else
memset(dev->sbitmap, 0, (dev->totalsectors + 7) >> 3);
#endif
loser = totalsectors;
for (sector = 0; sector < totalsectors; sector++)
{
finfo("Scan sector %d\n", sector);
winner = sector;
readaddress = sector * dev->mtdblkspersector * dev->geo.blocksize;
ret = MTD_READ(dev->mtd, readaddress,
sizeof(struct smart_sect_header_s),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
}
logicalsector = *((FAR uint16_t *)header.logicalsector);
#if CONFIG_SMARTFS_ERASEDSTATE == 0x00
if (logicalsector == 0)
{
logicalsector = -1;
}
#endif
if ((header.status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))
{
continue;
}
* 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--;
* update the erase block's releasecount.
*/
if ((header.status & SMART_STATUS_RELEASED) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))
{
* released sectors per erase block.
*/
dev->releasesectors++;
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
smart_add_count(dev, dev->releasecount,
sector / dev->sectorsperblk, 1);
#else
dev->releasecount[sector / dev->sectorsperblk]++;
#endif
continue;
}
if ((header.status & SMART_STATUS_VERBITS) != SMART_STATUS_VERSION)
{
continue;
}
if (logicalsector >= totalsectors)
{
ferr("ERROR: Invalid logical sector %d at physical %d.\n",
logicalsector, sector);
continue;
}
* information to validate the format signature.
*/
if (logicalsector == 0)
{
ret = MTD_READ(dev->mtd, readaddress, 32,
(FAR uint8_t *)dev->rwbuffer);
if (ret != 32)
{
ferr("ERROR: Error reading physical sector %d.\n", sector);
goto err_out;
}
if (dev->rwbuffer[SMART_FMT_POS1] != SMART_FMT_SIG1 ||
dev->rwbuffer[SMART_FMT_POS2] != SMART_FMT_SIG2 ||
dev->rwbuffer[SMART_FMT_POS3] != SMART_FMT_SIG3 ||
dev->rwbuffer[SMART_FMT_POS4] != SMART_FMT_SIG4)
{
* What should we do? Release it?
*/
continue;
}
dev->formatstatus = SMART_FMT_STAT_FORMATTED;
dev->namesize = dev->rwbuffer[SMART_FMT_NAMESIZE_POS];
dev->formatversion = dev->rwbuffer[SMART_FMT_VERSION_POS];
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
dev->rootdirentries = dev->rwbuffer[SMART_FMT_ROOTDIRS_POS];
* additional block devices.
*/
for (x = 1; x < dev->rootdirentries; x++)
{
if (dev->partname[0] != '\0')
{
snprintf(devname, sizeof(devname), "/dev/smart%d%sd%d",
dev->minor, dev->partname, x + 1);
}
else
{
snprintf(devname, sizeof(devname), "/dev/smart%dd%d",
dev->minor, x + 1);
}
* the SMART device structure and the root directory number.
*/
rootdirdev = (FAR struct smart_multiroot_device_s *)
smart_malloc(dev, sizeof(*rootdirdev), "Root Dir");
if (rootdirdev == NULL)
{
ferr("ERROR: Memory alloc failed\n");
ret = -ENOMEM;
goto err_out;
}
rootdirdev->dev = dev;
rootdirdev->rootdirnum = x;
* structure.
*/
ret = register_blockdriver(devname, &g_bops, 0, rootdirdev);
}
#endif
}
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->smap[logicalsector] != 0xffff)
#else
if (dev->sbitmap[logicalsector >> 3] & (1 << (logicalsector & 0x07)))
#endif
{
* the same logical sector. Use the sequence number information
* to resolve who wins.
*/
#if SMART_STATUS_VERSION == 1
if ((header.status & SMART_STATUS_CRC) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC))
{
seq2 = header.seq;
}
else
{
seq2 = *((FAR uint16_t *) &header.seq);
}
#else
seq2 = header.seq;
#endif
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
readaddress = dev->smap[logicalsector] * dev->mtdblkspersector *
dev->geo.blocksize;
#else
* claiming to be this logical sector.
*/
for (dupsector = 0; dupsector < sector; dupsector++)
{
readaddress = dupsector * dev->mtdblkspersector *
dev->geo.blocksize;
ret = MTD_READ(dev->mtd, readaddress,
sizeof(struct smart_sect_header_s),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
}
duplogsector = *((FAR uint16_t *)header.logicalsector);
#if CONFIG_SMARTFS_ERASEDSTATE == 0x00
if (duplogsector == 0)
{
duplogsector = -1;
}
#endif
if ((header.status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))
{
continue;
}
if ((header.status & SMART_STATUS_RELEASED) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))
{
continue;
}
if ((header.status & SMART_STATUS_VERBITS) !=
SMART_STATUS_VERSION)
{
continue;
}
* sector
*/
if (duplogsector == logicalsector)
{
break;
}
}
#endif
ret = MTD_READ(dev->mtd, readaddress,
sizeof(struct smart_sect_header_s),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
}
#if SMART_STATUS_VERSION == 1
if ((header.status & SMART_STATUS_CRC) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC))
{
seq1 = header.seq;
seqwrap = 0xf0;
}
else
{
seq1 = *((FAR uint16_t *)&header.seq);
seqwrap = 0xfff0;
}
#else
seq1 = header.seq;
seqwrap = 0xf0;
#endif
if ((seq1 > seqwrap && seq2 < 10) || seq2 > seq1)
{
winner = sector;
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
loser = dev->smap[logicalsector];
#else
loser = dupsector;
#endif
}
else
{
loser = sector;
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
winner = dev->smap[logicalsector];
#else
winner = smart_cache_lookup(dev, logicalsector);
#endif
}
finfo("Duplicate Sector winner=%d, loser=%d\n", winner, loser);
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
ret = MTD_BREAD(dev->mtd, winner * dev->mtdblkspersector,
dev->mtdblkspersector,
(FAR uint8_t *)dev->rwbuffer);
if (ret == dev->mtdblkspersector)
{
ret = smart_validate_crc(dev);
}
if (ret != OK)
{
* sector. After swapping the winner and the loser sector, we
* will release the loser sector with CRC error.
*/
if (sector == winner)
{
* loser : origin -> sector(CRC error)
*/
winner = loser;
loser = sector;
}
else
{
* loser : sector -> origin(CRC error)
*/
loser = winner;
winner = sector;
}
finfo("Duplicate Sector winner=%d, loser=%d\n", winner, loser);
}
#endif
readaddress = loser * dev->mtdblkspersector * dev->geo.blocksize;
ret = MTD_READ(dev->mtd, readaddress,
sizeof(struct smart_sect_header_s),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto err_out;
}
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
header.status &= ~SMART_STATUS_RELEASED;
#else
header.status |= SMART_STATUS_RELEASED;
#endif
offset = readaddress +
offsetof(struct smart_sect_header_s, status);
ret = smart_bytewrite(dev, offset, 1, &header.status);
if (ret < 0)
{
ferr("ERROR: Error %d releasing duplicate sector\n", -ret);
goto err_out;
}
}
if (sector == loser)
{
continue;
}
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->smap[logicalsector] = winner;
#else
dev->sbitmap[logicalsector >> 3] |= 1 << (logicalsector & 0x07);
if (logicalsector < SMART_FIRST_ALLOC_SECTOR)
{
smart_add_sector_to_cache(dev, logicalsector, winner, __LINE__);
}
#endif
}
#if defined (CONFIG_MTD_SMART_WEAR_LEVEL) && (SMART_STATUS_VERSION == 1)
#ifdef CONFIG_MTD_SMART_CONVERT_WEAR_FORMAT
* 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
if (sector != 0xffff)
{
ret = MTD_BREAD(dev->mtd, sector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error reading physical sector %d.\n", sector);
goto err_out;
}
if (dev->rwbuffer[SMART_WEAR_LEVEL_FORMAT_SIG] == 0)
{
* in with 0xff.
*/
uint16_t newsector = smart_findfreephyssector(dev, false);
if (newsector == 0xffff)
{
ferr("ERROR: Can't find a free sector for relocation\n");
ret = -ENOSPC;
goto err_out;
}
memset(&dev->rwbuffer[SMART_WEAR_LEVEL_FORMAT_SIG], 0xff,
dev->mtdblkspersector * dev->geo.blocksize -
SMART_WEAR_LEVEL_FORMAT_SIG);
smart_relocate_sector(dev, sector, newsector);
dev->freesectors--;
dev->releasesectors++;
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->smap[0] = newsector;
dev->freecount[newsector / dev->sectorsperblk]--;
dev->releasecount[sector / dev->sectorsperblk]++;
#else
smart_update_cache(dev, 0, newsector);
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
smart_add_count(dev, dev->freecount,
newsector / dev->sectorsperblk, -1);
smart_add_count(dev, dev->releasecount,
sector / dev->sectorsperblk, 1);
#endif
#endif
}
}
#endif
#endif
#ifdef CONFIG_MTD_SMART_FSCK
smart_fsck(dev);
#endif
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
smart_read_wearstatus(dev);
#endif
finfo("SMART Scan\n");
finfo(" Erase size: %10d\n", dev->sectorsperblk * dev->sectorsize);
finfo(" Erase count: %10d\n", dev->neraseblocks);
finfo(" Sect/block: %10d\n", dev->sectorsperblk);
finfo(" MTD Blk/Sect: %10d\n", dev->mtdblkspersector);
if (dev->mtdblkspersector == 0 || dev->sectorsperblk == 0 ||
dev->sectorsperblk == 0 || dev->sectorsize == 0)
{
ferr("ERROR: Invalid Geometry!\n");
ret = -EINVAL;
goto err_out;
}
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
finfo(" Allocations:\n");
for (sector = 0; sector < SMART_MAX_ALLOCS; sector++)
{
if (dev->alloc[sector].ptr != NULL)
{
finfo(" %s: %d\n",
dev->alloc[sector].name, dev->alloc[sector].size);
}
}
#endif
ret = OK;
err_out:
return ret;
}
* Name: smart_getformat
*
* Description: Populates the SMART format structure based on the format
* information for the inode.
*
****************************************************************************/
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
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(FAR struct smart_struct_s *dev,
FAR struct smart_format_s *fmt)
#endif
{
int ret;
finfo("Entry\n");
DEBUGASSERT(fmt);
* status, then we must perform a scan of the device to search
* for the format marker
*/
if (dev->formatstatus != SMART_FMT_STAT_FORMATTED)
{
ret = smart_scan(dev);
if (ret != OK)
{
goto err_out;
}
}
if (dev->formatstatus == SMART_FMT_STAT_FORMATTED)
{
fmt->flags = SMART_FMT_ISFORMATTED;
}
else
{
fmt->flags = 0;
}
fmt->sectorsize = dev->sectorsize;
fmt->availbytes = dev->sectorsize -
sizeof(struct smart_sect_header_s);
fmt->nsectors = dev->totalsectors;
fmt->nfreesectors = dev->freesectors;
fmt->namesize = dev->namesize;
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
fmt->nrootdirentries = dev->rootdirentries;
fmt->rootdirnum = rootdirnum;
#endif
fmt->nfreesectors += dev->releasesectors;
fmt->nfreesectors -= dev->sectorsperblk + 4;
ret = OK;
err_out:
return ret;
}
* 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, bool forceerase)
{
uint16_t freecount;
uint16_t releasecount;
uint16_t 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)
{
#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
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
smart_set_wear_level(dev, block, smart_get_wear_level(dev, block) + 1);
#endif
* 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
* 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)
{
fwarn(" ...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;
uint16_t x;
uint16_t sector;
uint16_t minblock;
uint16_t nextsector;
uint16_t newsector;
int ret;
FAR struct smart_sect_header_s *header;
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
FAR struct smart_allocsector_s *allocsector;
#endif
* 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)
{
fwarn(" ...about to relocate static data %d\n", block);
}
#endif
if (smart_get_wear_level(dev, block) >= SMART_WEAR_FULL_RELOCATE_THRESHOLD)
{
* block to it in it's entirety.
*/
* with no free + released blocks.
*/
freecount = dev->sectorsperblk + 1;
minblock = dev->geo.neraseblocks;
for (x = 0; x < dev->geo.neraseblocks; x++)
{
if (smart_get_wear_level(dev, x) == dev->minwearlevel)
{
* be moved into a worn block. First get the format and
* dir sectors.
*/
#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
if (freecount == 0)
{
* sectors. relocate this block to the more highly worn
* block.
*/
break;
}
}
}
x = minblock;
* 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
finfo("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++)
{
ret = MTD_BREAD(dev->mtd, sector * dev->mtdblkspersector,
dev->mtdblkspersector,
(FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error reading sector %d\n", sector);
ret = -EIO;
goto errout;
}
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
allocsector = dev->allocsector;
while (allocsector)
{
if (allocsector->physical == sector)
{
break;
}
allocsector = allocsector->next;
}
* location and move on to the next block ... there is no data to
* move yet.
*/
if (allocsector)
{
newsector = nextsector++;
if (newsector == 0xffff)
{
ferr("ERROR: Can't find a free sector for relocation\n");
ret = -ENOSPC;
goto errout;
}
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)))
{
* just continue to the next sector and don't move it.
*/
continue;
}
* block
*/
newsector = nextsector++;
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
}
#ifdef CONFIG_SMART_LOCAL_CHECKFREE
if (smart_checkfree(dev, __LINE__) != OK)
{
fwarn(" ...about to erase static block %d\n", block);
}
#endif
smart_erase_block_if_empty(dev, x, true);
}
#ifdef CONFIG_SMART_LOCAL_CHECKFREE
if (smart_checkfree(dev, __LINE__) != OK)
{
fwarn(" ...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
crc = crc8((FAR uint8_t *)
&dev->rwbuffer[sizeof(struct smart_sect_header_s)],
dev->mtdblkspersector * dev->geo.blocksize -
sizeof(struct smart_sect_header_s));
crc = crc8part((FAR uint8_t *)dev->rwbuffer, 3, crc);
crc = crc8part((FAR uint8_t *)
&dev->rwbuffer[offsetof(struct smart_sect_header_s, status)], 1, crc);
#elif defined(CONFIG_SMART_CRC_16)
crc = crc16((FAR uint8_t *)
&dev->rwbuffer[sizeof(struct smart_sect_header_s)],
dev->mtdblkspersector * dev->geo.blocksize -
sizeof(struct smart_sect_header_s));
crc = crc16part((FAR uint8_t *) dev->rwbuffer, 2, crc);
crc = crc16part((FAR uint8_t *)
&dev->rwbuffer[offsetof(struct smart_sect_header_s, status)], 2, crc);
#elif defined(CONFIG_SMART_CRC_32)
crc = crc32((FAR uint8_t *)
&dev->rwbuffer[sizeof(struct smart_sect_header_s)],
dev->mtdblkspersector * dev->geo.blocksize -
sizeof(struct smart_sect_header_s));
crc = crc32part((FAR uint8_t *)dev->rwbuffer, 6, crc);
#else
#error "Unknown CRC size!"
#endif
return crc;
}
#endif
* Name: smart_llformat
*
* Description: Performs a low-level format of the flash device. This
* involves erasing the device and writing a valid sector
* zero (logical) with proper format signature.
*
* Input Parameters:
*
* arg: Upper 16 bits contains the sector size
* Lower 16 bits contains the number of root dir entries
*
****************************************************************************/
static inline int smart_llformat(FAR struct smart_struct_s *dev,
unsigned long arg)
{
FAR struct smart_sect_header_s *sectorheader;
size_t wrcount;
int x;
int ret;
uint8_t sectsize;
uint8_t prerelease;
uint16_t sectorsize;
finfo("Entry\n");
sectorsize = arg >> 16;
if (sectorsize == 0)
{
sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
}
ret = smart_setsectorsize(dev, sectorsize);
if (ret != OK)
{
return ret;
}
if (dev->erasesize == 0 || dev->sectorsperblk == 0)
{
dev->erasesize = dev->geo.erasesize;
ferr("ERROR: Invalid geometery ... "
"Sectors per erase block must be 1-256\n");
ferr(" Erase block size = %" PRId32 "\n",
dev->erasesize);
ferr(" Sector size = %d\n",
dev->sectorsize);
ferr(" Sectors/erase block = %" PRId32 "\n",
dev->erasesize / dev->sectorsize);
return -EINVAL;
}
ret = MTD_IOCTL(dev->mtd, MTDIOC_BULKERASE, 0);
if (ret < 0)
{
return ret;
}
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
sectorheader->seq = 0;
#else
*((FAR uint16_t *)§orheader->seq) = 0;
#endif
#else
sectorheader->seq = 0;
#endif
sectsize = dev->sectorsize < 4096 ? (dev->sectorsize >> 9) :
dev->sectorsize == 4096 ? 3 : 5 + (dev->sectorsize >> 14);
sectsize <<= 2;
#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xff )
*((FAR uint16_t *) sectorheader->logicalsector) = 0;
sectorheader->status = (uint8_t)~(SMART_STATUS_COMMITTED |
SMART_STATUS_VERBITS |
SMART_STATUS_SIZEBITS) |
SMART_STATUS_VERSION |
sectsize;
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
sectorheader->status &= ~SMART_STATUS_CRC;
#endif
#else
*((FAR uint16_t *) sectorheader->logicalsector) = 0xffff;
sectorheader->status = (uint8_t)(SMART_STATUS_COMMITTED |
SMART_STATUS_VERSION |
sectsize);
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
sectorheader->status |= SMART_STATUS_CRC;
#endif
#endif
dev->rwbuffer[SMART_FMT_POS1] = SMART_FMT_SIG1;
dev->rwbuffer[SMART_FMT_POS2] = SMART_FMT_SIG2;
dev->rwbuffer[SMART_FMT_POS3] = SMART_FMT_SIG3;
dev->rwbuffer[SMART_FMT_POS4] = SMART_FMT_SIG4;
dev->rwbuffer[SMART_FMT_VERSION_POS] = SMART_FMT_VERSION;
dev->rwbuffer[SMART_FMT_NAMESIZE_POS] = CONFIG_SMARTFS_MAXNAMLEN;
dev->rwbuffer[SMART_FMT_ROOTDIRS_POS] = (uint8_t)(arg & 0xff);
#ifdef CONFIG_SMART_CRC_8
sectorheader->crc8 = smart_calc_sector_crc(dev);
#elif defined(CONFIG_SMART_CRC_16)
*((FAR uint16_t *)sectorheader->crc16) = smart_calc_sector_crc(dev);
#elif defined(CONFIG_SMART_CRC_32)
*((FAR uint32_t *)sectorheader->crc32) = smart_calc_sector_crc(dev);
#endif
wrcount = MTD_BWRITE(dev->mtd, 0, dev->mtdblkspersector,
(FAR uint8_t *)dev->rwbuffer);
if (wrcount != dev->mtdblkspersector)
{
ferr("ERROR: Write block 0 failed: %zu.\n", wrcount);
return -EIO;
}
ret = smart_setsectorsize(dev, sectorsize);
if (ret != OK)
{
return ret;
}
dev->formatstatus = SMART_FMT_STAT_UNKNOWN;
dev->freesectors = dev->availsectperblk * dev->geo.neraseblocks - 1;
dev->releasesectors = 0;
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
dev->uneven_wearcount = 0;
#endif
for (x = 0; x < dev->neraseblocks; x++)
{
* we never use the last two sectors in this mode.
*/
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
}
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
smart_set_count(dev, dev->freecount, 0, dev->availsectperblk - 1);
#else
dev->freecount[0]--;
#endif
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->smap[0] = 0;
for (x = 1; x < dev->totalsectors; x++)
{
dev->smap[x] = -1;
}
#endif
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
for (x = 2; x < 8; x++)
{
char devname[18];
snprintf(devname, sizeof(devname), "/dev/smart%dd%d", dev->minor, x);
unregister_blockdriver(devname);
}
#endif
return OK;
}
* 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)
{
size_t offset;
FAR struct smart_sect_header_s *header;
uint8_t newstatus;
int ret;
header = (FAR struct smart_sect_header_s *)dev->rwbuffer;
#if SMART_STATUS_VERSION == 1
if ((header->status & SMART_STATUS_CRC) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC))
{
#endif
header->seq++;
if (header->seq == 0xff)
{
header->seq = 1;
}
#if SMART_STATUS_VERSION == 1
}
else
{
(*((FAR uint16_t *)&header->seq))++;
if (*((FAR uint16_t *)&header->seq) == 0xffff)
{
*((FAR uint16_t *)&header->seq) = 1;
}
}
#endif
* calculate an updated CRC for the sector prior to writing
* since we changed the sequence number.
*/
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
header->status &= ~(SMART_STATUS_COMMITTED | SMART_STATUS_CRC);
#else
header->status |= SMART_STATUS_COMMITTED | SMART_STATUS_CRC;
#endif
#ifdef CONFIG_SMART_CRC_8
header->crc8 = smart_calc_sector_crc(dev);
#elif defined(CONFIG_SMART_CRC_16)
*((FAR uint16_t *)header->crc16) = smart_calc_sector_crc(dev);
#elif defined(CONFIG_SMART_CRC_32)
*((FAR uint32_t *)header->crc32) = smart_calc_sector_crc(dev);
#endif
ret = MTD_BWRITE(dev->mtd, newsector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("Error writing to new sector %d\n", newsector);
goto errout;
}
#else
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
header->status |= SMART_STATUS_COMMITTED;
#else
header->status &= ~SMART_STATUS_COMMITTED;
#endif
ret = MTD_BWRITE(dev->mtd, newsector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("Error writing to new sector %d\n", newsector);
goto errout;
}
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)
{
ferr("ERROR: Error %d committing new sector %d\n" -ret, newsector);
goto errout;
}
#endif
#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)
{
ferr("ERROR: Error %d releasing old sector %d\n" -ret, oldsector);
}
errout:
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;
uint16_t 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
* 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)
{
fwarn(" ...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
if (freecount >= dev->freesectors)
{
ferr("ERROR: 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
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
for (x = block * dev->sectorsperblk; x <
block * dev->sectorsperblk + dev->availsectperblk; x++)
{
ret = MTD_BREAD(dev->mtd, x * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error reading sector %d\n", x);
ret = -EIO;
goto errout;
}
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
allocsector = dev->allocsector;
while (allocsector)
{
if (allocsector->physical == x)
{
break;
}
allocsector = allocsector->next;
}
* 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)
{
ferr("ERROR: Can't find a free sector for relocation\n");
ret = -ENOSPC;
goto errout;
}
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)))
{
* just continue to the next sector and don't move it.
*/
continue;
}
newsector = smart_findfreephyssector(dev, false);
if (newsector == 0xffff)
{
ferr("ERROR: Can't find a free sector for relocation\n");
ret = -ENOSPC;
goto errout;
}
if ((ret = smart_relocate_sector(dev, x, newsector)) < 0)
{
goto errout;
}
}
#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
}
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
smart_set_wear_level(dev, block, smart_get_wear_level(dev, block) + 1);
#endif
if (x == dev->neraseblocks && dev->totalsectors == 65534)
{
* 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)
{
fwarn(" ...while relocating block %d, "
"free=%d, release=%d, oldrelease=%d\n",
block, freecount, releasecount, oldrelease);
}
#endif
* threshold requiring static data relocation.
*/
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
smart_relocate_static_data(dev, block);
#endif
return OK;
errout:
#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
* count logic, taking into account reserved sectors.
*
****************************************************************************/
static int smart_findfreephyssector(FAR struct smart_struct_s *dev,
bool canrelocate)
{
uint16_t count;
uint16_t allocfreecount;
uint16_t allocblock;
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
uint16_t wornfreecount;
uint16_t wornblock;
uint8_t wearlevel;
uint8_t wornlevel;
uint8_t maxwearlevel;
#endif
uint16_t physicalsector;
uint16_t block;
uint32_t readaddr;
struct smart_sect_header_s header;
int ret;
uint16_t i;
* 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 (i = 0; i < dev->neraseblocks; i++)
{
* currently selected block
*/
#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
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))
{
* with free sectors left, then we will use it.
*/
if (i < dev->neraseblocks - 1 || !wornfreecount)
{
wornfreecount = count;
wornblock = block;
wornlevel = wearlevel;
}
}
}
else
#endif
if (count > allocfreecount)
{
if (i < dev->neraseblocks - 1 || !allocfreecount)
{
allocblock = block;
allocfreecount = count;
}
}
if (++block >= dev->neraseblocks)
{
block = 0;
}
}
if (allocblock == 0xffff)
{
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
if (canrelocate && wornfreecount < (dev->sectorsperblk >> 2) &&
wornlevel == maxwearlevel)
{
block = 0;
for (i = 0; i < 8; )
{
if (smart_get_wear_level(dev, block) <
SMART_WEAR_FORCE_REORG_THRESHOLD)
{
if (smart_relocate_block(dev, block) < 0)
{
ferr("ERROR: Error relocating block while finding "
"free phys sector\n");
return -1;
}
i++;
}
block++;
}
if (i > 0)
{
canrelocate = false;
goto retry;
}
}
else
{
dev->wearflags |= SMART_WEARFLAGS_FORCE_REORG;
}
if (wornblock != 0xffff)
{
allocblock = wornblock;
}
else
#endif
{
char buffer[8 * 12 + 1];
long remaining;
int j;
int k;
ferr("ERROR: Program bug! Expected a free sector, free=%d\n",
dev->freesectors);
for (i = 0, remaining = dev->neraseblocks;
remaining > 0;
i += 8, remaining -= 8)
{
for (j = 0, k = 0; j < 8 && j < remaining ; j++)
{
snprintf(&buffer[k], sizeof(buffer) - k,
"%12d", dev->freecount[i + j]);
k += strlen(&buffer[k]);
}
ferr("%04x:%s\n", i, buffer);
}
return -ENOSPC;
}
}
* allocate.
*/
for (i = allocblock * dev->sectorsperblk;
i < allocblock * dev->sectorsperblk + dev->availsectperblk; i++)
{
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
FAR struct smart_allocsector_s *allocsect;
allocsect = dev->allocsector;
while (allocsect)
{
if (allocsect->physical == i)
{
break;
}
allocsect = allocsect->next;
}
* to the next physical sector in this block ... this one has
* a temporary allocation assigned.
*/
if (allocsect)
{
continue;
}
#endif
readaddr = i * dev->mtdblkspersector * dev->geo.blocksize;
ret = MTD_READ(dev->mtd, readaddr, sizeof(struct smart_sect_header_s),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
ferr("ERROR: Error reading phys sector %d\n", physicalsector);
return -1;
}
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 = i;
dev->lastallocblock = allocblock;
break;
}
else
{
* Just in case for the recovery of this fatal situation,
* after once erasing the sector, return the sector as a free
* sector.
*/
if (1 == dev->availsectperblk)
{
MTD_ERASE(dev->mtd, allocblock, 1);
physicalsector = i;
dev->lastallocblock = allocblock;
break;
}
}
}
if (physicalsector == 0xffff)
{
ferr("ERROR: Program bug! Expected a free sector\n");
}
if (physicalsector >= dev->totalsectors)
{
ferr("ERROR: Program bug! Selected sector too big!!!\n");
}
return physicalsector;
}
* Name: smart_garbagecollect
*
* Description: Performs garbage collection if needed. This is determined
* by the count of released sectors relative to free and
* total sectors.
*
****************************************************************************/
static int smart_garbagecollect(FAR struct smart_struct_s *dev)
{
uint16_t collectblock;
uint16_t releasemax;
bool collect = true;
int x;
int ret;
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
uint8_t count;
#endif
while (collect)
{
collect = false;
* free sectors. If it is, then we will do garbage collection.
*/
if (dev->releasesectors > dev->freesectors &&
dev->freesectors < (dev->totalsectors >> 5))
{
collect = true;
}
if (dev->freesectors <= (dev->sectorsperblk << 0) + 4)
{
collect = true;
}
if (collect)
{
collectblock = 0xffff;
releasemax = 0;
for (x = 0; x < dev->neraseblocks; x++)
{
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
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
}
#if 0
releasemax = smart_get_count(dev, dev->releasecount, collectblock);
#endif
if (collectblock == 0xffff)
{
ret = -ENOSPC;
goto errout;
}
#ifdef CONFIG_SMART_LOCAL_CHECKFREE
if (smart_checkfree(dev, __LINE__) != OK)
{
fwarn(" ...before collecting block %d\n", collectblock);
}
#endif
#ifdef CONFIG_MTD_SMART_PACK_COUNTS
finfo("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
finfo("Collecting block %d, free=%d released=%d\n",
collectblock, dev->freecount[collectblock],
dev->releasecount[collectblock]);
#endif
ret = smart_relocate_block(dev, collectblock);
#ifdef CONFIG_SMART_LOCAL_CHECKFREE
if (smart_checkfree(dev, __LINE__) != OK)
{
fwarn(" ...while collecting block %d\n", collectblock);
}
#endif
if (ret != OK)
{
goto errout;
}
}
}
return OK;
errout:
return ret;
}
* 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.
*
****************************************************************************/
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
static int smart_write_wearstatus(struct smart_struct_s *dev)
{
uint16_t sector;
uint16_t remaining;
uint16_t towrite;
struct smart_read_write_s req;
int ret;
uint8_t buffer[8];
uint8_t write_buffer = 0;
sector = 0;
remaining = dev->geo.neraseblocks >> 1;
memset(buffer, 0xff, sizeof(buffer));
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
if (dev->blockerases > 0)
{
*((FAR uint32_t *)buffer) = dev->blockerases;
write_buffer = 1;
}
#endif
if (dev->uneven_wearcount != 0)
{
*((FAR uint32_t *)&buffer[4]) = dev->uneven_wearcount;
write_buffer = 1;
}
* uneven wearcount (or both)
*/
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;
}
}
while (remaining)
{
towrite = remaining;
if (towrite >
dev->sectorsize - (SMARTFS_FMT_WEAR_POS +
sizeof(struct smart_sect_header_s)))
{
towrite = dev->sectorsize -
(SMARTFS_FMT_WEAR_POS +
sizeof(struct smart_sect_header_s));
}
req.logsector = sector;
req.offset = SMARTFS_FMT_WEAR_POS;
req.count = towrite;
req.buffer =
&dev->wearstatus[(dev->geo.neraseblocks >> SMART_WEAR_BIT_DIVIDE) -
remaining];
ret = smart_writesector(dev, (unsigned long)&req);
if (ret != OK)
{
goto errout;
}
remaining -= towrite;
if (remaining)
{
sector++;
if (sector >= SMART_FIRST_DIR_SECTOR)
{
ferr("ERROR: Invalid geometry "
"- wear level status too large\n");
ret = -EINVAL;
goto errout;
}
}
}
dev->wearflags &= ~SMART_WEARFLAGS_WRITE_NEEDED;
ret = OK;
errout:
return ret;
}
#endif
* 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.
*
****************************************************************************/
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
static inline int smart_read_wearstatus(FAR struct smart_struct_s *dev)
{
struct smart_read_write_s req;
uint16_t sector;
uint16_t physsector;
uint16_t remaining;
uint16_t toread;
uint8_t buffer[8];
int ret;
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;
}
dev->uneven_wearcount = *((FAR uint32_t *)&buffer[4]);
#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xff )
if (dev->uneven_wearcount == 0xffffffff)
{
dev->uneven_wearcount = 0;
}
#endif
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
dev->blockerases = *((FAR uint32_t *)buffer);
#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xff )
if (dev->blockerases == 0xffffffff)
{
dev->blockerases = 0;
}
#endif
#endif
remaining = dev->geo.neraseblocks >> 1;
while (remaining)
{
toread = remaining;
if (toread > dev->sectorsize -
(SMARTFS_FMT_WEAR_POS + sizeof(struct smart_sect_header_s)))
{
toread = dev->sectorsize -
(SMARTFS_FMT_WEAR_POS +
sizeof(struct smart_sect_header_s));
}
req.logsector = sector;
req.offset = SMARTFS_FMT_WEAR_POS;
req.count = toread;
req.buffer =
&dev->wearstatus[(dev->geo.neraseblocks >> SMART_WEAR_BIT_DIVIDE) -
remaining];
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
physsector = dev->smap[req.logsector];
#else
physsector = smart_cache_lookup(dev, req.logsector);
#endif
if ((sector != 0) && (physsector == 0xffff))
{
ret = smart_allocsector(dev, sector);
if (ret != sector)
{
ferr("ERROR: Unable to allocate wear level status sector %d\n",
sector);
ret = -EINVAL;
goto errout;
}
}
ret = smart_readsector(dev, (unsigned long)&req);
if (ret != toread)
{
goto errout;
}
remaining -= toread;
if (remaining)
{
sector++;
if (sector >= SMART_FIRST_DIR_SECTOR)
{
ferr("ERROR: Invalid geometry "
"- wear level status too large\n");
ret = -EINVAL;
goto errout;
}
}
}
smart_find_wear_minmax(dev);
#ifdef CONFIG_MTD_SMART_SECTOR_ERASE_DEBUG
for (sector = 0; sector < dev->geo.neraseblocks; sector++)
{
dev->erasecounts[sector] = smart_get_wear_level(dev, sector);
}
#endif
ret = OK;
errout:
return ret;
}
#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.
*
****************************************************************************/
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->seq) = 0;
#endif
#else
header->seq = 0;
#endif
* 000b - 256 bytes
* 001b - 512 bytes
* 010b - 1024 bytes
* 100b - 2048 bytes
* 011b - 4096 bytes
* 101b - 8192 bytes
* 110b - 16384 bytes
* 110b - 32768 bytes
*/
sectsize = dev->sectorsize < 4096 ? (dev->sectorsize >> 9) :
dev->sectorsize == 4096 ? 3 : 5 + (dev->sectorsize >> 14);
sectsize <<= 2;
#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
#else
header->status = SMART_STATUS_COMMITTED | SMART_STATUS_VERSION | sectsize;
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
header->status |= SMART_STATUS_CRC;
#endif
#endif
#ifndef CONFIG_MTD_SMART_ENABLE_CRC
finfo("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)
{
ferr("ERROR: Write block %d failed: %d.\n", physical *
dev->mtdblkspersector, ret);
return -EIO;
}
#endif
return ret;
}
* 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;
crc = smart_calc_sector_crc(dev);
header = (FAR struct smart_sect_header_s *)dev->rwbuffer;
#ifdef CONFIG_SMART_CRC_8
if (crc != header->crc8)
{
return -EIO;
}
#elif defined(CONFIG_SMART_CRC_16)
if (crc != *((uint16_t *)header->crc16))
{
return -EIO;
}
#elif defined(CONFIG_SMART_CRC_32)
if (crc != *((FAR uint32_t *)header->crc32))
{
return -EIO;
}
#endif
return OK;
}
#endif
* Name: smart_writesector
*
* Description: Writes data to the specified logical sector. The sector
* should have already been allocated prior to the write. If
* the logical sector already has data on the device, it will
* be released and a new physical sector will be created and
* mapped to the logical sector.
*
****************************************************************************/
static int smart_writesector(FAR struct smart_struct_s *dev,
unsigned long arg)
{
int ret;
bool needsrelocate = false;
uint32_t mtdblock;
uint16_t physsector;
uint16_t oldphyssector;
uint16_t 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
finfo("Entry\n");
req = (FAR struct smart_read_write_s *)arg;
DEBUGASSERT(req->offset <= dev->sectorsize);
DEBUGASSERT(req->offset + req->count <= dev->sectorsize);
if (req->logsector >= dev->totalsectors)
{
ferr("ERROR: Logical sector %d too large\n", req->logsector);
ret = -EINVAL;
goto errout;
}
header = (FAR struct smart_sect_header_s *)dev->rwbuffer;
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
if (dev->minwearlevel >= SMART_WEAR_MIN_LEVEL ||
(dev->minwearlevel > 0 &&
dev->maxwearlevel >= SMART_WEAR_REORG_THRESHOLD))
{
offset = dev->minwearlevel;
finfo("Reducing wear level bits by %zu\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;
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)
{
ferr("ERROR: Logical sector %d not allocated\n", req->logsector);
ret = -EINVAL;
goto errout;
}
mtdblock = physsector * dev->mtdblkspersector;
ret = MTD_BREAD(dev->mtd, mtdblock, dev->mtdblkspersector,
(FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error reading phys sector %d\n", physsector);
ret = -EIO;
goto errout;
}
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
allocsector = dev->allocsector;
while (allocsector)
{
if (allocsector->logical == req->logsector)
{
break;
}
allocsector = allocsector->next;
}
* it is not a temporary alloc (i.e. initial alloc before the very first
* write operation).
*/
if (!allocsector)
{
needsrelocate = true;
}
#else
* 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++)
{
byte = dev->rwbuffer[sizeof(struct smart_sect_header_s) +
req->offset + x];
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
if (((byte ^ req->buffer[x]) | byte) != byte)
{
needsrelocate = true;
break;
}
#else
if (((byte ^ req->buffer[x]) | req->buffer[x]) != req->buffer[x])
{
needsrelocate = true;
break;
}
#endif
}
#endif
* bits from 1 to 0 without needing 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)
{
oldphyssector = physsector;
physsector = smart_findfreephyssector(dev, false);
if (physsector == 0xffff)
{
ferr("ERROR: Error relocating sector %d\n", req->logsector);
ret = -EIO;
goto errout;
}
#if SMART_STATUS_VERSION == 1
if ((header->status & SMART_STATUS_CRC) !=
(CONFIG_SMARTFS_ERASEDSTATE & 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
header->status &= SMART_STATUS_COMMITTED;
#endif
}
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
* with the header information prior to copying the write data to the buf.
*/
if (allocsector)
{
smart_write_alloc_sector(dev, allocsector->logical,
allocsector->physical);
if (dev->allocsector == allocsector)
{
dev->allocsector = allocsector->next;
}
else
{
FAR struct smart_allocsector_s *prev;
prev = dev->allocsector;
while (prev && prev->next != allocsector)
{
prev = prev->next;
}
if (prev)
{
prev->next = allocsector->next;
}
}
kmm_free(allocsector);
}
memcpy(&dev->rwbuffer[sizeof(struct smart_sect_header_s) + req->offset],
req->buffer, req->count);
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
header->status &= ~(SMART_STATUS_COMMITTED | SMART_STATUS_CRC);
#else
header->status |= SMART_STATUS_COMMITTED | SMART_STATUS_CRC;
#endif
#ifdef CONFIG_SMART_CRC_8
header->crc8 = smart_calc_sector_crc(dev);
#elif defined(CONFIG_SMART_CRC_16)
*((FAR uint16_t *)header->crc16) = smart_calc_sector_crc(dev);
#elif defined(CONFIG_SMART_CRC_32)
*((FAR uint32_t *)header->crc32) = smart_calc_sector_crc(dev);
#endif
#else
memcpy(&dev->rwbuffer[sizeof(struct smart_sect_header_s) + req->offset],
req->buffer, req->count);
#endif
if (needsrelocate)
{
ret = MTD_BWRITE(dev->mtd, physsector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error writing to physical sector %d\n", physsector);
ret = -EIO;
goto errout;
}
#ifndef CONFIG_MTD_SMART_ENABLE_CRC
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
byte = header->status & ~SMART_STATUS_COMMITTED;
#else
byte = header->status | SMART_STATUS_COMMITTED;
#endif
offset = physsector * dev->mtdblkspersector * dev->geo.blocksize +
offsetof(struct smart_sect_header_s, status);
ret = smart_bytewrite(dev, offset, 1, &byte);
if (ret != 1)
{
finfo("Error committing physical sector %d\n", physsector);
ret = -EIO;
goto errout;
}
#endif
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
byte = header->status & ~(SMART_STATUS_RELEASED |
SMART_STATUS_COMMITTED);
#else
byte = header->status | SMART_STATUS_RELEASED |
SMART_STATUS_COMMITTED;
#endif
offset = mtdblock * dev->geo.blocksize +
offsetof(struct smart_sect_header_s, status);
ret = smart_bytewrite(dev, offset, 1, &byte);
* newly allocated physical sector.
*/
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
smart_checkfree(dev, __LINE__);
#endif
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
dev->smap[req->logsector] = physsector;
#else
smart_update_cache(dev, req->logsector, physsector);
#endif
smart_erase_block_if_empty(dev, block, false);
* ensure we don't fill up our flash with released blocks.
*/
smart_garbagecollect(dev);
}
else
{
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
ret = MTD_BWRITE(dev->mtd, physsector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error writing to physical sector %d\n", physsector);
ret = -EIO;
goto errout;
}
ret = MTD_BREAD(dev->mtd, physsector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret == dev->mtdblkspersector)
{
ret = smart_validate_crc(dev);
}
if (ret != OK)
{
ferr("ERROR: Error validating physical sector %d\n", physsector);
ret = -EIO;
goto errout;
}
#else
* 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;
errout:
return ret;
}
* Name: smart_readsector
*
* Description: Reads data from the specified logical sector. The sector
* should have already been allocated prior to the read.
*
****************************************************************************/
static int smart_readsector(FAR struct smart_struct_s *dev,
unsigned long arg)
{
int ret;
uint16_t physsector;
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
finfo("Entry\n");
req = (FAR struct smart_read_write_s *)arg;
DEBUGASSERT(req->offset < dev->sectorsize);
DEBUGASSERT(req->offset + req->count + sizeof(struct smart_sect_header_s)
<= dev->sectorsize);
if (req->logsector >= dev->totalsectors)
{
ferr("ERROR: Logical sector %d too large\n", req->logsector);
return -EINVAL;
}
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
physsector = dev->smap[req->logsector];
#else
physsector = smart_cache_lookup(dev, req->logsector);
#endif
if (physsector == 0xffff)
{
ferr("ERROR: Logical sector %d not allocated\n", req->logsector);
return -EINVAL;
}
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
* validate the CRC.
*/
ret = MTD_BREAD(dev->mtd, physsector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error reading phys sector %d\n", physsector);
return -EIO;
}
#if SMART_STATUS_VERSION == 1
header = (FAR struct smart_sect_header_s *)dev->rwbuffer;
if ((header->status & SMART_STATUS_CRC) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_CRC))
{
* CRC not enabled for this sector, so skip the CRC test.
*/
}
else
#endif
{
ret = smart_validate_crc(dev);
if (ret != OK)
{
ferr("ERROR: Error validating sector %d CRC during read\n",
physsector);
return -EIO;
}
}
memmove((FAR char *)req->buffer, &dev->rwbuffer[req->offset +
sizeof(struct smart_sect_header_s)], req->count);
ret = req->count;
#else
ret = MTD_READ(dev->mtd, physsector * dev->mtdblkspersector *
dev->geo.blocksize, sizeof(struct smart_sect_header_s),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
ferr("ERROR: Error reading sector %d header\n", physsector);
return -EIO;
}
if (((*(FAR uint16_t *)header.logicalsector) != req->logsector) ||
((header.status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)))
{
ferr("ERROR: Error in logical sector %d header, phys=%d\n",
req->logsector, physsector);
return -EIO;
}
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,
(FAR uint8_t *)req->buffer);
if (ret != req->count)
{
ferr("ERROR: Error reading phys sector %d\n", physsector);
return -EIO;
}
#endif
return ret;
}
* Name: smart_allocsector
*
* Description: Allocates a new logical sector. If an argument is given,
* then it tries to allocate the specified sector number.
*
****************************************************************************/
static inline int smart_allocsector(FAR struct smart_struct_s *dev,
unsigned long requested)
{
uint16_t logsector = 0xffff;
uint16_t physicalsector;
#ifndef CONFIG_MTD_SMART_ENABLE_CRC
int ret;
#endif
int x;
* allocation. We have to ensure we keep enough reserved sectors
* on hand to do released sector garbage collection.
*/
if (dev->freesectors <= (dev->sectorsperblk << 0) + 4)
{
if (dev->releasesectors + dev->freesectors > dev->sectorsperblk + 4)
{
for (x = 0; x < dev->availsectperblk; x++)
{
smart_garbagecollect(dev);
if (dev->freesectors > dev->availsectperblk + 4)
{
break;
}
}
if (dev->freesectors <= (dev->availsectperblk << 0) + 4)
{
return -ENOSPC;
}
}
else
{
return -ENOSPC;
}
}
* sector if it isn't already in use.
*/
if ((requested > 0) && (requested < dev->totalsectors))
{
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->smap[requested] == (uint16_t) -1)
#else
if (!(dev->sbitmap[requested >> 3] & (1 << (requested & 0x07))))
#endif
{
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
FAR struct smart_allocsector_s *allocsect;
allocsect = dev->allocsector;
while (allocsect)
{
if (allocsect->logical == requested)
{
break;
}
allocsect = allocsect->next;
}
if (allocsect != NULL)
{
}
else
#endif
logsector = requested;
}
}
if (logsector == 0xffff)
{
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;
* 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)
{
continue;
}
#endif
logsector = x;
break;
}
}
}
if (logsector == 0xffff)
{
* something happened and we didn't find any free
* logical sectors. What do do? Report an error?
* rescan and try again to "self heal" in case of a
* bug in our code?
*/
ferr("ERROR: No free logical sector numbers! Free sectors = %d\n",
dev->freesectors);
return -EIO;
}
* 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.
*/
smart_garbagecollect(dev);
physicalsector = smart_findfreephyssector(dev, false);
finfo("Alloc: log=%d, phys=%d, erase block=%d, free=%d, released=%d\n",
logsector, physicalsector, physicalsector /
dev->sectorsperblk, dev->freesectors, dev->releasesectors);
if (physicalsector == 0xffff)
{
return -ENOSPC;
}
#ifdef CONFIG_MTD_SMART_ENABLE_CRC
* the data is written via writesector. Just add the allocation to
* our temporary allocsector list and we'll pick it up later.
*/
{
FAR struct smart_allocsector_s *allocsect =
(FAR struct smart_allocsector_s *)
kmm_malloc(sizeof(struct smart_allocsector_s));
if (allocsect == NULL)
{
ferr("ERROR: Out of memory allocting sector\n");
return -ENOMEM;
}
* smartfs layer's mutex, so no locking is required.
*/
allocsect->logical = logsector;
allocsect->physical = physicalsector;
allocsect->next = dev->allocsector;
dev->allocsector = allocsect;
}
#else
* later.
*/
ret = smart_write_alloc_sector(dev, logsector, physicalsector);
if (ret != 1)
{
return ret;
}
#endif
#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 logsector;
}
* Name: smart_freesector
*
* Description: Frees a logical sector from the device. Freeing (also
* called releasing) is performed by programming the released
* bit in the sector header's status byte.
*
****************************************************************************/
static inline int smart_freesector(FAR struct smart_struct_s *dev,
unsigned long logicalsector)
{
int ret;
int readaddr;
uint16_t physsector;
uint16_t block;
struct smart_sect_header_s header;
size_t offset;
if ((logicalsector > 2) && (logicalsector < dev->totalsectors))
{
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
if (dev->smap[logicalsector] == (uint16_t) -1)
#else
if (!(dev->sbitmap[logicalsector >> 3] &
(1 << (logicalsector & 0x07))))
#endif
{
ferr("ERROR: Invalid release - sector %ld not allocated\n",
logicalsector);
ret = -EINVAL;
goto errout;
}
}
#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),
(FAR uint8_t *)&header);
if (ret != sizeof(struct smart_sect_header_s))
{
goto errout;
}
if (*((FAR uint16_t *)header.logicalsector) != (uint16_t)logicalsector)
{
* code?
*/
ferr("ERROR: Sector %ld logical sector in header doesn't match\n",
logicalsector);
ret = -EINVAL;
goto errout;
}
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
header.status &= ~SMART_STATUS_RELEASED;
#else
header.status |= SMART_STATUS_RELEASED;
#endif
offset = readaddr + offsetof(struct smart_sect_header_s, status);
ret = smart_bytewrite(dev, offset, 1, &header.status);
if (ret != 1)
{
ferr("ERROR: Error updating physical sector %d status\n", physsector);
goto errout;
}
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
#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
smart_erase_block_if_empty(dev, block, false);
ret = OK;
errout:
return ret;
}
* Name: smart_ioctl
*
* Description: Return device geometry
*
****************************************************************************/
static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
{
FAR struct smart_struct_s *dev;
int ret;
#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
finfo("Entry\n");
DEBUGASSERT(inode->i_private);
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
dev = ((FAR struct smart_multiroot_device_s *)inode->i_private)->dev;
#else
dev = inode->i_private;
#endif
* to directly to the underlying MTD device.
*/
switch (cmd)
{
case BIOC_GETFORMAT:
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
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, (FAR struct smart_format_s *)arg);
#endif
goto ok_out;
case BIOC_READSECT:
ret = smart_readsector(dev, arg);
goto ok_out;
case BIOC_LLFORMAT:
ret = smart_llformat(dev, arg);
goto ok_out;
case BIOC_ALLOCSECT:
if (arg < 3)
{
arg = (unsigned long)-1;
}
ret = smart_allocsector(dev, arg);
goto ok_out;
case BIOC_FREESECT:
ret = smart_freesector(dev, arg);
goto ok_out;
case BIOC_WRITESECT:
ret = smart_writesector(dev, arg);
#ifdef CONFIG_MTD_SMART_WEAR_LEVEL
if (dev->wearflags & SMART_WEARFLAGS_WRITE_NEEDED)
{
smart_write_wearstatus(dev);
}
#endif
goto ok_out;
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
case BIOC_GETPROCFSD:
procfs_data = (FAR struct mtd_smart_procfs_data_s *)arg;
procfs_data->totalsectors = dev->totalsectors;
procfs_data->sectorsize = dev->sectorsize;
procfs_data->freesectors = dev->freesectors;
procfs_data->releasesectors = dev->releasesectors;
procfs_data->namelen = dev->namesize;
procfs_data->formatversion = dev->formatversion;
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
procfs_data->neraseblocks = dev->geo.neraseblocks;
procfs_data->erasecounts = dev->erasecounts;
#endif
#ifdef CONFIG_MTD_SMART_ALLOC_DEBUG
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;
finfo("Debug level set to %d\n", dev->debuglevel);
ret = OK;
goto ok_out;
}
#endif
break;
}
* driver. Other possible MTD driver ioctl commands are passed through
* to the MTD driver (unchanged).
*/
ret = MTD_IOCTL(dev->mtd, cmd, arg);
if (ret < 0)
{
ferr("ERROR: MTD ioctl(%04x) failed: %d\n", cmd, ret);
}
ok_out:
return ret;
}
#ifdef CONFIG_MTD_SMART_FSCK
* Name: smart_fsck_crc
*
* Description: Validate CRC to check smartfs filesystem
*
****************************************************************************/
#ifdef CONFIG_MTD_SMART_FSCK_ENABLE_CRC
static int smart_fsck_crc(FAR struct smart_struct_s *dev,
uint16_t physsector)
{
int ret;
ret = MTD_BREAD(dev->mtd, physsector * dev->mtdblkspersector,
dev->mtdblkspersector, (FAR uint8_t *)dev->rwbuffer);
if (ret != dev->mtdblkspersector)
{
ferr("ERROR: Error reading phys sector %d\n", physsector);
return ret;
}
ret = smart_validate_crc(dev);
if (ret != OK)
{
ferr("ERROR: Error validating sector %d CRC\n", physsector);
return ret;
}
return ret;
}
#endif
* Name: smart_fsck_file
*
* Description: fsck for file entry
*
****************************************************************************/
static int smart_fsck_file(FAR struct smart_struct_s *dev,
FAR uint8_t *checkmap, uint16_t logsector)
{
int ret = OK;
ssize_t size;
uint32_t readaddress;
FAR struct smart_sect_header_s *header;
FAR struct smart_chain_header_s *chain;
FAR uint8_t *usedmap;
size_t mapsize;
uint16_t physsector;
int i;
if (logsector >= dev->totalsectors)
{
ret = -EINVAL;
return ret;
}
mapsize = (dev->totalsectors + 7) / 8;
usedmap = kmm_zalloc(mapsize);
if (!usedmap)
{
ferr("ERROR: Out of memory used map\n");
return OK;
}
do
{
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
physsector = dev->smap[logsector];
#else
physsector = smart_cache_lookup(dev, logsector);
#endif
if (physsector >= dev->totalsectors)
{
ret = -ENXIO;
ferr("ERROR: Invalid phys sector %d\n", physsector);
break;
}
#ifdef CONFIG_MTD_SMART_FSCK_ENABLE_CRC
if (smart_fsck_crc(dev, physsector) != OK)
{
ret = -ENOENT;
ferr("ERROR: CRC phys sector %d\n", physsector);
break;
}
#endif
readaddress = physsector * dev->mtdblkspersector * dev->geo.blocksize;
size = MTD_READ(dev->mtd, readaddress,
sizeof(struct smart_sect_header_s) +
sizeof(struct smart_chain_header_s),
(FAR uint8_t *)dev->rwbuffer);
if (size != (sizeof(struct smart_sect_header_s) +
sizeof(struct smart_chain_header_s)))
{
ret = -EIO;
ferr("Error reading phys sector %d\n", physsector);
break;
}
header = (FAR struct smart_sect_header_s *)&dev->rwbuffer[0];
chain = (FAR struct smart_chain_header_s *)
&dev->rwbuffer[sizeof(struct smart_sect_header_s)];
if (((header->status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) ||
((header->status & SMART_STATUS_RELEASED) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)))
{
ret = -ENOENT;
ferr("ERROR: status(%02x) phys sector %d\n",
header->status, physsector);
break;
}
SET_BITMAP(usedmap, logsector);
logsector = SMARTFS_NEXTSECTOR(chain);
}
while (logsector != 0xffff);
if (ret == OK)
{
for (i = 0; i < mapsize; i++)
{
checkmap[i] &= ~usedmap[i];
}
}
else
{
for (i = 0; i < mapsize; i++)
{
checkmap[i] |= usedmap[i];
}
}
kmm_free(usedmap);
return ret;
}
* Name: smart_fsck_directory
*
* Description: fsck for directory entry
*
****************************************************************************/
static int smart_fsck_directory(FAR struct smart_struct_s *dev,
FAR uint8_t *checkmap, uint16_t logsector)
{
int ret = OK;
int relocate = 0;
ssize_t size;
FAR uint8_t *rwbuffer;
FAR struct smart_sect_header_s *header;
FAR struct smart_chain_header_s *chain;
FAR struct smart_entry_header_s *entry;
uint16_t entrysector;
uint16_t physsector;
uint16_t nextsector;
uint16_t newsector;
int entrysize;
FAR uint8_t *bottom;
FAR uint8_t *cur;
#ifdef CONFIG_DEBUG_FS_INFO
char entryname[dev->namesize + 1];
#endif
if ((logsector < SMART_FIRST_DIR_SECTOR) ||
(logsector >= dev->totalsectors))
{
ret = -EINVAL;
ferr("ERROR: Invalid log sector %d\n", logsector);
return ret;
}
rwbuffer = kmm_malloc(dev->sectorsize);
if (!rwbuffer)
{
ferr("ERROR: Out of memory sector buffer\n");
return OK;
}
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
physsector = dev->smap[logsector];
#else
physsector = smart_cache_lookup(dev, logsector);
#endif
if (physsector >= dev->totalsectors)
{
ret = -ENXIO;
ferr("ERROR: Invalid phys sector %d\n", physsector);
goto errout;
}
size = MTD_BREAD(dev->mtd, physsector * dev->mtdblkspersector,
dev->mtdblkspersector, rwbuffer);
if (size != dev->mtdblkspersector)
{
ret = -EIO;
ferr("ERROR: reading phys sector %d\n", physsector);
goto errout;
}
header = (FAR struct smart_sect_header_s *)&rwbuffer[0];
chain = (FAR struct smart_chain_header_s *)
&rwbuffer[sizeof(struct smart_sect_header_s)];
entry = (FAR struct smart_entry_header_s *)
&rwbuffer[sizeof(struct smart_sect_header_s) +
sizeof(struct smart_chain_header_s)];
#ifdef CONFIG_MTD_SMART_FSCK_ENABLE_CRC
if (smart_fsck_crc(dev, physsector) != OK)
{
ret = -ENOENT;
ferr("ERROR: CRC phys sector %d\n", physsector);
goto errout;
}
#endif
if (((header->status & SMART_STATUS_COMMITTED) ==
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) ||
((header->status & SMART_STATUS_RELEASED) !=
(CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)))
{
ret = -ENOENT;
ferr("ERROR: status(%02x) phys sector %d\n",
header->status, physsector);
goto errout;
}
nextsector = SMARTFS_NEXTSECTOR(chain);
if (nextsector != 0xffff)
{
finfo("Check next log sector %d\n", nextsector);
ret = smart_fsck_directory(dev, checkmap, nextsector);
if (ret != OK)
{
ferr("Invalidate next log sector %d\n", nextsector);
SMARTFS_SET_NEXTSECTOR(chain, 0xffff);
relocate = 1;
}
}
#define SMARTFS_DIRENT_EMPTY 0x8000
#define SMARTFS_DIRENT_ACTIVE 0x4000
#define SMARTFS_DIRENT_TYPE 0x2000
#define SMARTFS_DIRENT_DELETING 0x1000
#define SMARTFS_DIRENT_RESERVED 0x0E00
entrysize = sizeof(struct smart_entry_header_s) + dev->namesize;
bottom = rwbuffer + dev->sectorsize;
cur = &rwbuffer[sizeof(struct smart_sect_header_s) +
sizeof(struct smart_chain_header_s)];
while ((cur + entrysize) <= bottom)
{
ret = OK;
entry = (FAR struct smart_entry_header_s *)cur;
if (entry->flags == 0xffff)
{
break;
}
#ifdef CONFIG_DEBUG_FS_INFO
strlcpy(entryname,
(FAR const char *)(cur + sizeof(struct smart_entry_header_s)),
sizeof(entryname));
finfo("Check entry (name=%s flags=%02x logsector=%02x)\n",
entryname, entry->flags, entry->firstsector);
#endif
if (entry->flags & SMARTFS_DIRENT_ACTIVE)
{
entrysector = entry->firstsector;
if (entry->flags & SMARTFS_DIRENT_TYPE)
{
ret = smart_fsck_directory(dev, checkmap, entrysector);
}
else
{
ret = smart_fsck_file(dev, checkmap, entrysector);
}
}
if (ret != OK)
{
#ifdef CONFIG_DEBUG_FS_INFO
finfo("Remove entry (name=%s flags=%02x)\n",
entryname, entry->flags);
#endif
if ((cur + (2 * entrysize)) <= bottom)
{
memmove(cur, cur + entrysize, bottom - (cur + entrysize));
memset(bottom - entrysize, CONFIG_SMARTFS_ERASEDSTATE,
entrysize);
}
else
{
* exist
*/
memset(cur, CONFIG_SMARTFS_ERASEDSTATE, entrysize);
cur += entrysize;
}
relocate = 1;
}
else
{
cur += entrysize;
}
}
if (relocate)
{
newsector = smart_findfreephyssector(dev, false);
if (newsector == 0xffff)
{
ret = -ENOSPC;
ferr("Can't find a free sector for relocation\n");
goto errout;
}
memcpy(dev->rwbuffer, rwbuffer, dev->sectorsize);
ret = smart_relocate_sector(dev, physsector, newsector);
if (ret < 0)
{
ret = -EIO;
ferr("Can't relocate\n");
goto errout;
}
#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
}
kmm_free(rwbuffer);
CLR_BITMAP(checkmap, logsector);
return OK;
errout:
kmm_free(rwbuffer);
SET_BITMAP(checkmap, logsector);
return ret;
}
* Name: smart_fsck
*
* Description: Check and repair the file system
*
****************************************************************************/
static int smart_fsck(FAR struct smart_struct_s *dev)
{
uint16_t logsector;
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
uint16_t physsector;
#endif
FAR uint8_t *checkmap;
size_t mapsize;
uint8_t rootdirentries;
int x;
finfo("Entry\n");
mapsize = (dev->totalsectors + 7) / 8;
checkmap = kmm_zalloc(mapsize);
if (!checkmap)
{
ferr("ERROR: Out of memory fsck map\n");
return -ENOMEM;
}
#ifndef CONFIG_MTD_SMART_MINIMIZE_RAM
for (logsector = 0; logsector < dev->totalsectors; logsector++)
{
physsector = dev->smap[logsector];
if (physsector < dev->totalsectors)
{
SET_BITMAP(checkmap, logsector);
}
}
#else
memcpy(checkmap, dev->sbitmap, mapsize);
#endif
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
rootdirentries = dev->rootdirentries;
#else
rootdirentries = 1;
#endif
for (x = 0; x < rootdirentries; x++)
{
smart_fsck_directory(dev, checkmap, SMART_FIRST_DIR_SECTOR + x);
}
for (logsector = SMART_FIRST_ALLOC_SECTOR;
logsector < dev->totalsectors; logsector++)
{
if (ISSET_BITMAP(checkmap, logsector))
{
smart_freesector(dev, logsector);
}
}
kmm_free(checkmap);
return OK;
}
#endif
* Public Functions
****************************************************************************/
* Name: smart_initialize
*
* Description:
* Initialize to provide a block driver wrapper around an MTD interface
*
* Input Parameters:
* minor - The minor device number. The MTD block device will be
* registered as as /dev/smartN where N is the minor number.
* mtd - The MTD device that supports the FLASH interface.
*
****************************************************************************/
int smart_initialize(int minor, FAR struct mtd_dev_s *mtd,
FAR const char *partname)
{
FAR struct smart_struct_s *dev;
int ret = -ENOMEM;
uint32_t totalsectors;
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
FAR struct smart_multiroot_device_s *rootdirdev = NULL;
#endif
#ifdef CONFIG_DEBUG_FEATURES
if (minor < 0 || minor > 255 || !mtd)
{
return -EINVAL;
}
#endif
dev = (FAR struct smart_struct_s *)
smart_zalloc(NULL, sizeof(struct smart_struct_s), "Dev struct");
if (dev)
{
char devname[18];
dev->mtd = mtd;
* complaints on some architectures where the sizeof long is different
* from the size of a pointer).
*/
ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY,
(unsigned long)((uintptr_t)&dev->geo));
if (ret < 0)
{
ferr("ERROR: MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
goto errout;
}
dev->sectorsize = 0;
ret = smart_setsectorsize(dev, CONFIG_MTD_SMART_SECTOR_SIZE);
if (ret != OK)
{
goto errout;
}
totalsectors = dev->neraseblocks * dev->sectorsperblk;
if (totalsectors > 65536)
{
ferr("ERROR: SMART Sector size too small for device\n");
ret = -EINVAL;
goto errout;
}
else if (totalsectors == 65536)
{
totalsectors -= 2;
}
dev->totalsectors = (uint16_t)totalsectors;
dev->freesectors = (uint16_t)dev->availsectperblk *
dev->geo.neraseblocks;
dev->lastallocblock = 0;
dev->debuglevel = 0;
dev->formatstatus = SMART_FMT_STAT_UNKNOWN;
dev->namesize = CONFIG_SMARTFS_MAXNAMLEN;
if (partname)
{
strlcpy(dev->partname, partname, SMART_PARTNAME_SIZE);
}
else
{
dev->partname[0] = '\0';
}
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
dev->minor = minor;
#endif
ret = smart_scan(dev);
if (ret < 0)
{
ferr("ERROR: smart_scan failed: %d\n", -ret);
goto errout;
}
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
if (partname != NULL)
{
snprintf(devname, sizeof(devname), "/dev/smart%d%sd1", minor,
partname);
}
else
{
snprintf(devname, sizeof(devname), "/dev/smart%dd1", minor);
}
* the SMART device structure and the root directory number.
*/
rootdirdev = (FAR struct smart_multiroot_device_s *)
smart_malloc(dev, sizeof(*rootdirdev), "Root Dir");
if (rootdirdev == NULL)
{
ferr("ERROR: register_blockdriver failed: %d\n", -ret);
ret = -ENOMEM;
goto errout;
}
rootdirdev->dev = dev;
rootdirdev->rootdirnum = 0;
ret = register_blockdriver(devname, &g_bops, 0, rootdirdev);
#else
if (partname != NULL)
{
snprintf(devname, sizeof(devname), "/dev/smart%d%s", minor,
partname);
}
else
{
snprintf(devname, sizeof(devname), "/dev/smart%d", minor);
}
ret = register_blockdriver(devname, &g_bops, 0, dev);
#endif
if (ret < 0)
{
ferr("ERROR: register_blockdriver failed: %d\n", -ret);
goto errout;
}
}
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;
}
* Name: smart_loop_register_driver
*
* Description:
* Registers SmartFS Loop Driver
****************************************************************************/
#ifdef CONFIG_SMART_DEV_LOOP
int smart_loop_register_driver(void)
{
return register_driver("/dev/smart", &g_fops, 0666, NULL);
}
#endif
* Name: smart_losetup
*
* Description: Dynamically setups up a SMART enabled loop device that
* is backed by a file. The resulting loop device is a
* MTD type block device vs. a generic block device.
*
****************************************************************************/
#ifdef CONFIG_SMART_DEV_LOOP
static int smart_losetup(int minor, FAR const char *filename,
int sectsize, int erasesize, off_t offset, bool readonly)
{
FAR struct mtd_dev_s *mtd;
struct stat sb;
int x;
int ret;
char devpath[20];
mtd = filemtd_initialize(filename, offset, sectsize, erasesize);
if (mtd == NULL)
{
return -ENOENT;
}
if (minor == -1)
{
* Searching 0 to 256 should be sufficient.
*/
for (x = 0; x < 256; x++)
{
snprintf(devpath, sizeof(devpath), "/dev/smart%d", x);
ret = nx_stat(devpath, &sb, 1);
if (ret < 0)
{
minor = x;
break;
}
}
}
ret = smart_initialize(minor, mtd, NULL);
if (ret != OK)
{
filemtd_teardown(mtd);
}
return ret;
}
#endif
* Name: loteardown
*
* Description:
* Undo the setup performed by losetup
*
****************************************************************************/
#ifdef CONFIG_SMART_DEV_LOOP
static int smart_loteardown(FAR const char *devname)
{
FAR struct smart_struct_s *dev;
FAR struct inode *inode;
int ret;
#ifdef CONFIG_DEBUG_FEATURES
if (!devname)
{
return -EINVAL;
}
#endif
* inode reference.
*/
ret = open_blockdriver(devname, MS_RDONLY, &inode);
if (ret < 0)
{
ferr("ERROR: Failed to open %s: %d\n", devname, -ret);
return ret;
}
dev = inode->i_private;
if (!filemtd_isfilemtd(dev->mtd))
{
ferr("ERROR: Device is not a SMART loop: %s\n", devname);
return -EINVAL;
}
close_blockdriver(inode);
filemtd_teardown(dev->mtd);
unregister_blockdriver(devname);
kmm_free(dev);
return OK;
}
#endif
* Name: smart_loop_read
****************************************************************************/
#ifdef CONFIG_SMART_DEV_LOOP
static ssize_t smart_loop_read(FAR struct file *filep, FAR char *buffer,
size_t len)
{
return 0;
}
#endif
* Name: smart_loop_write
****************************************************************************/
#ifdef CONFIG_SMART_DEV_LOOP
static ssize_t smart_loop_write(FAR struct file *filep,
FAR const char *buffer, size_t len)
{
return len;
}
#endif
* Name: smart_loop_ioctl
****************************************************************************/
#ifdef CONFIG_SMART_DEV_LOOP
static int smart_loop_ioctl(FAR struct file *filep, int cmd,
unsigned long arg)
{
int ret;
switch (cmd)
{
* Description: Setup the loop device
* Argument: A pointer to a read-only instance of struct losetup_s.
* Dependencies: The loop device must be enabled (CONFIG_DEV_LOOP=y)
*/
case SMART_LOOPIOC_SETUP:
{
FAR struct smart_losetup_s *setup =
(FAR struct smart_losetup_s *)((uintptr_t)arg);
if (setup == NULL)
{
ret = -EINVAL;
}
else
{
ret = smart_losetup(setup->minor, setup->filename,
setup->sectsize, setup->erasesize,
setup->offset, setup->readonly);
}
}
break;
* Description: Teardown a loop device previously setup via
* LOOPIOC_SETUP
* Argument: A read-able pointer to the path of the device to be
* torn down
* Dependencies: The loop device must be enabled (CONFIG_DEV_LOOP=y)
*/
case SMART_LOOPIOC_TEARDOWN:
{
FAR const char *devname = (FAR const char *)((uintptr_t)arg);
if (devname == NULL)
{
ret = -EINVAL;
}
else
{
ret = smart_loteardown(devname);
}
}
break;
default:
ret = -ENOTTY;
}
return ret;
}
#endif