summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-04-30 19:10:54 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-04-30 19:10:54 -0600
commit68dd7cdd0c0e72d1ea1dcf484feab17c9a74f5c7 (patch)
tree89393bdc5d755eedd833b9ffdc53ced1654a13d1
parentcc5ee148cf3e87f8f7d78bb09612f3876a7ee3a8 (diff)
downloadnuttx-68dd7cdd0c0e72d1ea1dcf484feab17c9a74f5c7.tar.gz
nuttx-68dd7cdd0c0e72d1ea1dcf484feab17c9a74f5c7.tar.bz2
nuttx-68dd7cdd0c0e72d1ea1dcf484feab17c9a74f5c7.zip
SMART block driver plus changes to M25P and RAM drivers needed for SMART support
-rw-r--r--nuttx/configs/mikroe-stm32f4/src/up_nsh.c6
-rw-r--r--nuttx/configs/mikroe-stm32f4/usbnsh/defconfig4
-rw-r--r--nuttx/configs/sim/mtdpart/defconfig1
-rw-r--r--nuttx/configs/sim/nxffs/defconfig1
-rw-r--r--nuttx/drivers/mtd/Kconfig57
-rw-r--r--nuttx/drivers/mtd/Make.defs14
-rw-r--r--nuttx/drivers/mtd/m25px.c253
-rw-r--r--nuttx/drivers/mtd/rammtd.c56
-rw-r--r--nuttx/drivers/mtd/smart.c2177
-rw-r--r--nuttx/include/nuttx/smart.h116
10 files changed, 2657 insertions, 28 deletions
diff --git a/nuttx/configs/mikroe-stm32f4/src/up_nsh.c b/nuttx/configs/mikroe-stm32f4/src/up_nsh.c
index e3f973794..179d0eab0 100644
--- a/nuttx/configs/mikroe-stm32f4/src/up_nsh.c
+++ b/nuttx/configs/mikroe-stm32f4/src/up_nsh.c
@@ -153,7 +153,7 @@
****************************************************************************/
/* Pre-allocated simulated flash */
-#ifdef CONFIG_MTD_RAM
+#ifdef CONFIG_RAMMTD
//static uint8_t g_simflash[CONFIG_EXAMPLES_SMART_BUFSIZE];
#endif
@@ -217,7 +217,7 @@ int nsh_archinitialize(void)
/* Create a RAM MTD device if configured */
-#ifdef CONFIG_MTD_RAM
+#ifdef CONFIG_RAMMTD
{
uint8_t *start = (uint8_t *) kmalloc(CONFIG_EXAMPLES_SMART_BUFSIZE);
mtd = rammtd_initialize(start, CONFIG_EXAMPLES_SMART_BUFSIZE);
@@ -229,7 +229,7 @@ int nsh_archinitialize(void)
#endif
}
-#endif /* CONFIG_MTD_RAM */
+#endif /* CONFIG_RAMMTD */
#endif /* CONFIG_MTD */
#endif /* CONFIG_STM32_SPI3 */
diff --git a/nuttx/configs/mikroe-stm32f4/usbnsh/defconfig b/nuttx/configs/mikroe-stm32f4/usbnsh/defconfig
index fb474a0c9..3567353f5 100644
--- a/nuttx/configs/mikroe-stm32f4/usbnsh/defconfig
+++ b/nuttx/configs/mikroe-stm32f4/usbnsh/defconfig
@@ -413,8 +413,8 @@ CONFIG_MP25P_BYTEWRITE=y
CONFIG_MTD_SMART=y
CONFIG_MTD_SMART_SECTOR_SIZE=512
# CONFIG_MTD_RAMTRON is not set
-CONFIG_MTD_RAM=y
-CONFIG_MTD_RAM_SMART=y
+CONFIG_RAMMTD=y
+CONFIG_RAMMTD_SMART=y
# CONFIG_MTD_SST25 is not set
# CONFIG_MTD_SST39FV is not set
# CONFIG_MTD_W25 is not set
diff --git a/nuttx/configs/sim/mtdpart/defconfig b/nuttx/configs/sim/mtdpart/defconfig
index f3e195919..e35f72438 100644
--- a/nuttx/configs/sim/mtdpart/defconfig
+++ b/nuttx/configs/sim/mtdpart/defconfig
@@ -216,7 +216,6 @@ CONFIG_RAMMTD_FLASHSIM=y
# CONFIG_MTD_AT45DB is not set
# CONFIG_MTD_MP25P is not set
# CONFIG_MTD_RAMTRON is not set
-# CONFIG_MTD_RAM is not set
# CONFIG_MTD_SST25 is not set
# CONFIG_MTD_SST39FV is not set
# CONFIG_MTD_W25 is not set
diff --git a/nuttx/configs/sim/nxffs/defconfig b/nuttx/configs/sim/nxffs/defconfig
index a2e663e8a..d3b0b0f9b 100644
--- a/nuttx/configs/sim/nxffs/defconfig
+++ b/nuttx/configs/sim/nxffs/defconfig
@@ -216,7 +216,6 @@ CONFIG_RAMMTD_FLASHSIM=y
# CONFIG_MTD_AT45DB is not set
# CONFIG_MTD_MP25P is not set
# CONFIG_MTD_RAMTRON is not set
-# CONFIG_MTD_RAM is not set
# CONFIG_MTD_SST25 is not set
# CONFIG_MTD_SST39FV is not set
# CONFIG_MTD_W25 is not set
diff --git a/nuttx/drivers/mtd/Kconfig b/nuttx/drivers/mtd/Kconfig
index f7ded475a..6755ffe7a 100644
--- a/nuttx/drivers/mtd/Kconfig
+++ b/nuttx/drivers/mtd/Kconfig
@@ -51,6 +51,13 @@ config RAMMTD_FLASHSIM
RAMMTD_FLASHSIM will add some extra logic to improve the level of
FLASH simulation.
+config RAMMTD_SMART
+ bool "SMART block driver support in the RAM MTD driver"
+ default n
+ ---help---
+ Builds in additional ioctl and interface code to support the
+ SMART block driver / filesystem.
+
endif
config MTD_AT24XX
@@ -113,8 +120,54 @@ config MP25P_MANUFACTURER
for the STMicro MP25x serial FLASH. If, for example, you are using the a Macronix
International MX25 serial FLASH, the correct manufacturer ID would be 0xc2.
+config MP25P_MEMORY_TYPE
+ hex "MP25P memory type ID"
+ default 0x20
+ ---help---
+ The memory type for M25 "P" series is 0x20, but the driver also supports "F" series
+ devices, such as the EON EN25F80 part which adds a 4K sector erase capability. The
+ ID for "F" series parts from EON is 0x31.
+
+config MP25P_SUBSECTOR_ERASE
+ bool "Sub-Sector Erase"
+ default n
+ ---help---
+ Some devices (the EON EN25F80) support a smaller erase block size (4K vs 64K).
+ This option enables support for sub-sector erase. The SMART file system can
+ take advantage of this option if it is enabled.
+
+config MP25P_BYTEWRITE
+ bool "Enable ByteWrite ioctl support"
+ default n
+ ---help---
+ The MP25P series of devices allow writing to a page with less than a full-page
+ size of data. In this case, only the written bytes are updated without affecting
+ the other bytes in the page. The SMART FS requires this option for proper operation.
+
endif
+config MTD_SMART
+ bool "Sector Mapped Allocation for Really Tiny (SMART) Flash support"
+ default y
+ select MP25P_BYTEWRITE
+ ---help---
+ The MP25x series of Flash devices are typically very small and have a very large
+ erase block size. This causes issues with the standard Flash Translation Layer
+ block driver since it tries to allocate a RAM block the size of a flash erase
+ block, which is typically 64K. This block driver uses a different approach
+ to sacrifice performance for RAM memory footprint by saving data in sectors
+ (typically 2K - 4K based on memory size) and relocating sectors as needed when
+ an erase block needs to be erased.
+
+config MTD_SMART_SECTOR_SIZE
+ int "SMART Device sector size"
+ depends on MTD_SMART
+ default 1024
+ ---help---
+ Sets the size of a single alloction on the SMART device. Larger sector sizes
+ reduce overhead per sector, but cause more wasted space with a lot of smaller
+ files.
+
config MTD_RAMTRON
bool "SPI-based RAMTRON NVRAM Devices FM25V10"
default n
@@ -122,10 +175,6 @@ config MTD_RAMTRON
---help---
SPI-based RAMTRON NVRAM Devices FM25V10
-config MTD_RAM
- bool "Memory bus ram"
- default n
-
config MTD_SST25
bool "SPI-based SST25 FLASH"
default n
diff --git a/nuttx/drivers/mtd/Make.defs b/nuttx/drivers/mtd/Make.defs
index fd16fd405..3f6c8b6fb 100644
--- a/nuttx/drivers/mtd/Make.defs
+++ b/nuttx/drivers/mtd/Make.defs
@@ -3,7 +3,7 @@
# These driver supports various Memory Technology Devices (MTD) using the
# NuttX MTD interface.
#
-# Copyright (C) 2009-2012 Gregory Nutt. All rights reserved.
+# Copyright (C) 2009-2013 Gregory Nutt. All rights reserved.
# Author: Gregory Nutt <gnutt@nuttx.org>
#
# Redistribution and use in source and binary forms, with or without
@@ -39,12 +39,16 @@
ifeq ($(CONFIG_MTD),y)
-CSRCS += at45db.c flash_eraseall.c ftl.c m25px.c rammtd.c ramtron.c
+CSRCS += at45db.c flash_eraseall.c ftl.c m25px.c ramtron.c
ifeq ($(CONFIG_MTD_PARTITION),y)
CSRCS += mtd_partition.c
endif
+ifeq ($(CONFIG_RAMMTD),y)
+CSRCS += rammtd.c
+endif
+
ifeq ($(CONFIG_MTD_AT24XX),y)
CSRCS += at24xx.c
endif
@@ -65,6 +69,12 @@ ifeq ($(CONFIG_MTD_AT25),y)
CSRCS += at25.c
endif
+ifeq ($(CONFIG_MTD_SMART),y)
+ifeq ($(CONFIG_FS_SMARTFS),y)
+CSRCS += smart.c
+endif
+endif
+
# Include MTD driver support
DEPPATH += --dep-path mtd
diff --git a/nuttx/drivers/mtd/m25px.c b/nuttx/drivers/mtd/m25px.c
index 4f96c5a3b..ad0f536c2 100644
--- a/nuttx/drivers/mtd/m25px.c
+++ b/nuttx/drivers/mtd/m25px.c
@@ -78,12 +78,18 @@
# define CONFIG_MP25P_MANUFACTURER 0x20
#endif
+#ifndef CONFIG_MP25P_MEMORY_TYPE
+# define CONFIG_MP25P_MEMORY_TYPE 0x20
+#endif
+
/* M25P Registers *******************************************************************/
/* Indentification register values */
#define M25P_MANUFACTURER CONFIG_MP25P_MANUFACTURER
-#define M25P_MEMORY_TYPE 0x20
+#define M25P_MEMORY_TYPE CONFIG_MP25P_MEMORY_TYPE
+#define M25P_RES_ID 0x13
#define M25P_M25P1_CAPACITY 0x11 /* 1 M-bit */
+#define M25P_EN25F80_CAPACITY 0x14 /* 8 M-bit */
#define M25P_M25P32_CAPACITY 0x16 /* 32 M-bit */
#define M25P_M25P64_CAPACITY 0x17 /* 64 M-bit */
#define M25P_M25P128_CAPACITY 0x18 /* 128 M-bit */
@@ -98,6 +104,17 @@
#define M25P_M25P1_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
#define M25P_M25P1_NPAGES 512
+/* EN25F80 capacity is 1,048,576 bytes:
+ * (16 sectors) * (65,536 bytes per sector)
+ * (512 pages) * (256 bytes per page)
+ */
+
+#define M25P_EN25F80_SECTOR_SHIFT 16 /* Sector size 1 << 15 = 65,536 */
+#define M25P_EN25F80_NSECTORS 16
+#define M25P_EN25F80_PAGE_SHIFT 8 /* Page size 1 << 8 = 256 */
+#define M25P_EN25F80_NPAGES 4096
+#define M25P_EN25F80_SUBSECT_SHIFT 12 /* Sub-Sector size 1 << 12 = 4,096 */
+
/* M25P32 capacity is 4,194,304 bytes:
* (64 sectors) * (65,536 bytes per sector)
* (16384 pages) * (256 bytes per page)
@@ -142,6 +159,7 @@
#define M25P_BE 0xc7 /* 1 Bulk Erase 0 0 0 */
#define M25P_DP 0xb9 /* 2 Deep power down 0 0 0 */
#define M25P_RES 0xab /* 2 Read Electronic Signature 0 3 >=1 */
+#define M25P_SSE 0x20 /* 1 Sub-Sector Erase 0 0 0 */
/* NOTE 1: All parts, NOTE 2: M25P632/M25P64 */
@@ -181,6 +199,9 @@ struct m25p_dev_s
uint8_t pageshift; /* 8 */
uint16_t nsectors; /* 128 or 64 */
uint32_t npages; /* 32,768 or 65,536 */
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+ uint8_t subsectorshift; /* 0, 12 or 13 (4K or 8K) */
+#endif
};
/************************************************************************************
@@ -198,6 +219,9 @@ static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t offset);
static inline int m25p_bulkerase(struct m25p_dev_s *priv);
static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
off_t offset);
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+static inline void m25p_subsectorerase(struct m25p_dev_s *priv, off_t offset);
+#endif
/* MTD driver methods */
@@ -292,6 +316,10 @@ static inline int m25p_readid(struct m25p_dev_s *priv)
{
/* Okay.. is it a FLASH capacity that we understand? */
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+ priv->subsectorshift = 0;
+#endif
+
if (capacity == M25P_M25P1_CAPACITY)
{
/* Save the FLASH geometry */
@@ -302,6 +330,19 @@ static inline int m25p_readid(struct m25p_dev_s *priv)
priv->npages = M25P_M25P1_NPAGES;
return OK;
}
+ else if (capacity == M25P_EN25F80_CAPACITY)
+ {
+ /* Save the FLASH geometry */
+
+ priv->sectorshift = M25P_EN25F80_SECTOR_SHIFT;
+ priv->nsectors = M25P_EN25F80_NSECTORS;
+ priv->pageshift = M25P_EN25F80_PAGE_SHIFT;
+ priv->npages = M25P_EN25F80_NPAGES;
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+ priv->subsectorshift = M25P_EN25F80_SUBSECT_SHIFT;
+#endif
+ return OK;
+ }
else if (capacity == M25P_M25P32_CAPACITY)
{
/* Save the FLASH geometry */
@@ -356,7 +397,7 @@ static void m25p_waitwritecomplete(struct m25p_dev_s *priv)
/* Send "Read Status Register (RDSR)" command */
(void)SPI_SEND(priv->dev, M25P_RDSR);
-
+
/* Loop as long as the memory is busy with a write cycle */
do
@@ -424,7 +465,7 @@ static void m25p_writeenable(struct m25p_dev_s *priv)
/* Send "Write Enable (WREN)" command */
(void)SPI_SEND(priv->dev, M25P_WREN);
-
+
/* Deselect the FLASH */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
@@ -477,6 +518,53 @@ static inline void m25p_sectorerase(struct m25p_dev_s *priv, off_t sector)
}
/************************************************************************************
+ * Name: m25p_subsectorerase
+ ************************************************************************************/
+
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+static inline void m25p_subsectorerase(struct m25p_dev_s *priv, off_t subsector)
+{
+ off_t offset = subsector << priv->subsectorshift;
+
+ fvdbg("subsector: %9lx\n", (long)subsector);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ m25p_waitwritecomplete(priv);
+
+ /* Send write enable instruction */
+
+ m25p_writeenable(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send the "Sub-Sector Erase (SSE)" instruction */
+
+ (void)SPI_SEND(priv->dev, M25P_SSE);
+
+ /* Send the sector offset high byte first. For all of the supported
+ * parts, the sector number is completely contained in the first byte
+ * and the values used in the following two bytes don't really matter.
+ */
+
+ (void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, offset & 0xff);
+
+ /* Deselect the FLASH */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Erased\n");
+}
+#endif
+
+/************************************************************************************
* Name: m25p_bulkerase
************************************************************************************/
@@ -533,7 +621,7 @@ static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *bu
/* Enable the write access to the FLASH */
m25p_writeenable(priv);
-
+
/* Select this FLASH part */
SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
@@ -551,7 +639,7 @@ static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *bu
/* Then write the specified number of bytes */
SPI_SNDBLOCK(priv->dev, buffer, 1 << priv->pageshift);
-
+
/* Deselect the FLASH: Chip Select high */
SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
@@ -559,6 +647,53 @@ static inline void m25p_pagewrite(struct m25p_dev_s *priv, FAR const uint8_t *bu
}
/************************************************************************************
+ * Name: m25p_bytewrite
+ ************************************************************************************/
+
+#ifdef CONFIG_MP25P_BYTEWRITE
+static inline void m25p_bytewrite(struct m25p_dev_s *priv, FAR const uint8_t *buffer,
+ off_t offset, uint16_t count)
+{
+ fvdbg("offset: %08lx count:%d\n", (long)offset, count);
+
+ /* Wait for any preceding write to complete. We could simplify things by
+ * perform this wait at the end of each write operation (rather than at
+ * the beginning of ALL operations), but have the wait first will slightly
+ * improve performance.
+ */
+
+ m25p_waitwritecomplete(priv);
+
+ /* Enable the write access to the FLASH */
+
+ m25p_writeenable(priv);
+
+ /* Select this FLASH part */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, true);
+
+ /* Send "Page Program (PP)" command */
+
+ (void)SPI_SEND(priv->dev, M25P_PP);
+
+ /* Send the page offset high byte first. */
+
+ (void)SPI_SEND(priv->dev, (offset >> 16) & 0xff);
+ (void)SPI_SEND(priv->dev, (offset >> 8) & 0xff);
+ (void)SPI_SEND(priv->dev, offset & 0xff);
+
+ /* Then write the specified number of bytes */
+
+ SPI_SNDBLOCK(priv->dev, buffer, count);
+
+ /* Deselect the FLASH: Chip Select high */
+
+ SPI_SELECT(priv->dev, SPIDEV_FLASH, false);
+ fvdbg("Written\n");
+}
+#endif
+
+/************************************************************************************
* Name: m25p_erase
************************************************************************************/
@@ -614,6 +749,7 @@ static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t n
{
FAR struct m25p_dev_s *priv = (FAR struct m25p_dev_s *)dev;
size_t blocksleft = nblocks;
+ size_t pagesize = 1 << priv->pageshift;
fvdbg("startblock: %08lx nblocks: %d\n", (long)startblock, (int)nblocks);
@@ -623,6 +759,7 @@ static ssize_t m25p_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, size_t n
while (blocksleft-- > 0)
{
m25p_pagewrite(priv, buffer, startblock);
+ buffer += pagesize;
startblock++;
}
m25p_unlock(priv->dev);
@@ -703,10 +840,15 @@ static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
* appear so.
*/
- geo->blocksize = (1 << priv->pageshift);
- geo->erasesize = (1 << priv->sectorshift);
- geo->neraseblocks = priv->nsectors;
- ret = OK;
+ geo->blocksize = (1 << priv->pageshift);
+ geo->erasesize = (1 << priv->sectorshift);
+ geo->neraseblocks = priv->nsectors;
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+ geo->subsectorsize = (1 << priv->subsectorshift);
+ geo->nsubsectors = priv->nsectors * (1 << (priv->sectorshift -
+ priv->subsectorshift));
+#endif
+ ret = OK;
fvdbg("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
@@ -723,7 +865,98 @@ static int m25p_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
m25p_unlock(priv->dev);
}
break;
-
+
+#ifdef CONFIG_FS_SMARTFS
+ case MTDIOC_GETCAPS:
+ {
+ ret = 0;
+#ifdef CONFIG_MP25P_BYTEWRITE
+ ret |= MTDIOC_CAPS_BYTEWRITE;
+#endif
+
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+ ret |= MTDIOC_CAPS_SECTERASE;
+#endif
+ break;
+ }
+#endif /* CONFIG_FS_SMARTFS */
+
+#ifdef CONFIG_MP25P_SUBSECTOR_ERASE
+ case MTDIOC_SECTERASE:
+ {
+ m25p_subsectorerase(priv, (off_t) arg);
+ ret = OK;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_MP25P_BYTEWRITE
+ case MTDIOC_BYTEWRITE:
+ {
+ struct mtd_byte_write_s *bytewrite = (struct mtd_byte_write_s *) arg;
+ int startpage;
+ int endpage;
+ int count;
+ int index;
+ int pagesize;
+ int bytestowrite;
+ size_t offset;
+
+ /* We must test if the offset + count crosses one or more pages
+ * and perform individual writes. The devices can only write in
+ * page increments.
+ */
+
+ startpage = bytewrite->offset / (1 << priv->pageshift);
+ endpage = (bytewrite->offset + bytewrite->count) / (1 << priv->pageshift);
+
+ if (startpage == endpage)
+ {
+ m25p_bytewrite(priv, bytewrite->buffer, bytewrite->offset,
+ bytewrite->count);
+ }
+ else
+ {
+ /* Write the 1st partial-page */
+
+ count = bytewrite->count;
+ pagesize = (1 << priv->pageshift);
+ offset = bytewrite->offset;
+ bytestowrite = pagesize - (bytewrite->offset & (pagesize-1));
+ m25p_bytewrite(priv, bytewrite->buffer, offset, bytestowrite);
+
+ /* Update offset and count */
+
+ offset += bytestowrite;
+ count -= bytestowrite;
+ index = bytestowrite;
+
+ /* Write full pages */
+
+ while (count >= pagesize)
+ {
+ m25p_bytewrite(priv, &bytewrite->buffer[index], offset, pagesize);
+
+ /* Update offset and count */
+
+ offset += pagesize;
+ count -= pagesize;
+ index += pagesize;
+ }
+
+ /* Now write any partial page at the end */
+
+ if (count > 0)
+ {
+ m25p_bytewrite(priv, &bytewrite->buffer[index], offset, count);
+ }
+ }
+
+ ret = OK;
+ break;
+ }
+#endif
+
case MTDIOC_XIPBASE:
default:
ret = -ENOTTY; /* Bad command */
diff --git a/nuttx/drivers/mtd/rammtd.c b/nuttx/drivers/mtd/rammtd.c
index 1d7a570e1..cbfe83e45 100644
--- a/nuttx/drivers/mtd/rammtd.c
+++ b/nuttx/drivers/mtd/rammtd.c
@@ -1,7 +1,7 @@
/****************************************************************************
* drivers/mtd/rammtd.c
*
- * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2011-2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -225,6 +225,30 @@ static int ram_erase(FAR struct mtd_dev_s *dev, off_t startblock, size_t nblocks
}
/****************************************************************************
+ * Name: ram_readbytes
+ ****************************************************************************/
+
+#ifdef CONFIG_RAMMTD_SMART
+static ssize_t ram_read_bytes(FAR struct mtd_dev_s *dev, off_t offset,
+ size_t nbytes, FAR uint8_t *buf)
+{
+ FAR struct ram_dev_s *priv = (FAR struct ram_dev_s *)dev;
+
+ DEBUGASSERT(dev && buf);
+
+ /* Don't let read read past end of buffer */
+
+ if (offset + nbytes > priv->nblocks * CONFIG_RAMMTD_ERASESIZE)
+ {
+ return 0;
+ }
+
+ ram_read(buf, &priv->start[offset], nbytes);
+ return nbytes;
+}
+#endif
+
+/****************************************************************************
* Name: ram_bread
****************************************************************************/
@@ -349,13 +373,31 @@ static int ram_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg)
{
size_t size = priv->nblocks * CONFIG_RAMMTD_ERASESIZE;
- /* Erase the entire device */
+ /* Erase the entire device */
memset(priv->start, CONFIG_RAMMTD_ERASESTATE, size);
- ret = OK;
+ ret = OK;
}
break;
-
+
+#ifdef CONFIG_RAMMTD_SMART
+ case MTDIOC_GETCAPS:
+ {
+ ret = MTDIOC_CAPS_BYTEWRITE;
+ break;
+ }
+
+ case MTDIOC_BYTEWRITE:
+ {
+ struct mtd_byte_write_s *bytewrite = (struct mtd_byte_write_s *) arg;
+
+ ram_write(&priv->start[bytewrite->offset], bytewrite->buffer,
+ bytewrite->count);
+ ret = OK;
+ break;
+ }
+#endif
+
default:
ret = -ENOTTY; /* Bad command */
break;
@@ -402,7 +444,7 @@ FAR struct mtd_dev_s *rammtd_initialize(FAR uint8_t *start, size_t size)
fdbg("Need to provide at least one full erase block\n");
return NULL;
}
-
+
/* Perform initialization as necessary */
priv->mtd.erase = ram_erase;
@@ -411,6 +453,10 @@ FAR struct mtd_dev_s *rammtd_initialize(FAR uint8_t *start, size_t size)
priv->mtd.ioctl = ram_ioctl;
priv->mtd.erase = ram_erase;
+#ifdef CONFIG_RAMMTD_SMART
+ priv->mtd.read = ram_read_bytes;
+#endif
+
priv->start = start;
priv->nblocks = nblocks;
return &priv->mtd;
diff --git a/nuttx/drivers/mtd/smart.c b/nuttx/drivers/mtd/smart.c
new file mode 100644
index 000000000..c20656565
--- /dev/null
+++ b/nuttx/drivers/mtd/smart.c
@@ -0,0 +1,2177 @@
+/****************************************************************************
+ * drivers/mtd/smart.c
+ *
+ * Sector Mapped Allocation for Really Tiny (SMART) Flash block driver.
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+#include <nuttx/smart.h>
+
+/****************************************************************************
+ * Private Definitions
+ ****************************************************************************/
+
+#define SMART_STATUS_COMMITTED 0x80
+#define SMART_STATUS_RELEASED 0x40
+#define SMART_STATUS_SIZEBITS 0x1C
+#define SMART_STATUS_VERBITS 0x03
+#define SMART_STATUS_VERSION 0x01
+
+#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_AGING_POS 32
+
+#define SMART_FMT_VERSION 1
+
+#define SMART_FIRST_ALLOC_SECTOR 16 /* First logical sector number we will
+ * use for assignment of requested Alloc
+ * sectors. All enries below this are
+ * reserved (some for root dir entries,
+ * other for our use, such as format
+ * sector, etc. */
+
+#if defined(CONFIG_FS_READAHEAD) || (defined(CONFIG_FS_WRITABLE) && defined(CONFIG_FS_WRITEBUFFER))
+# define CONFIG_SMART_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
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct smart_struct_s
+{
+ FAR struct mtd_dev_s *mtd; /* Contained MTD interface */
+ struct mtd_geometry_s geo; /* Device geometry */
+ uint16_t neraseblocks; /* Number of erase blocks or sub-sectors */
+ uint16_t freesectors; /* Total number of free sectors */
+ uint16_t mtdBlksPerSector; /* Number of MTD blocks per SMART Sector */
+ uint16_t sectorsPerBlk; /* Number of sectors per erase block */
+ uint16_t sectorsize; /* Sector size on device */
+ uint16_t totalsectors; /* Total number of sectors on device */
+ FAR uint16_t *sMap; /* Virtual to physical sector map */
+ FAR uint8_t *releasecount; /* Count of released sectors per erase block */
+ FAR uint8_t *freecount; /* Count of free sectors per erase block */
+ FAR char *rwbuffer; /* Our sector read/write buffer */
+ uint8_t formatversion; /* Format version on the device */
+ uint8_t formatstatus; /* Indicates the status of the device format */
+ uint8_t namesize; /* Length of filenames on this device */
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ uint8_t rootdirentries; /* Number of root directory entries */
+ uint8_t minor; /* Minor number of the block entry */
+#endif
+};
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+struct smart_multiroot_device_s
+{
+ FAR struct smart_struct_s* dev;
+ uint8_t rootdirnum;
+};
+#endif
+
+struct smart_sect_header_s
+{
+ uint8_t logicalsector[2]; /* The logical sector number */
+ uint8_t seq[2]; /* Incrementing sequence number */
+ uint8_t status; /* Status of this sector:
+ * Bit 7: 1 = Not commited
+ * 0 = commited
+ * Bit 6: 1 = Not released
+ * 0 = released
+ * Bit 5: Reserved - 1
+ * Bit 4: Reserved - 1
+ * Bit 3: Reserved - 1
+ * Bit 2: Reserved - 1
+ * Bit 1-0: Format version */
+};
+
+/****************************************************************************
+ * 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, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors);
+#endif
+static int smart_geometry(FAR struct inode *inode, struct geometry *geometry);
+static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct block_operations g_bops =
+{
+ smart_open, /* open */
+ smart_close, /* close */
+ smart_read, /* read */
+#ifdef CONFIG_FS_WRITABLE
+ smart_write, /* write */
+#else
+ NULL, /* write */
+#endif
+ smart_geometry, /* geometry */
+ smart_ioctl /* ioctl */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: smart_open
+ *
+ * Description: Open the block device
+ *
+ ****************************************************************************/
+
+static int smart_open(FAR struct inode *inode)
+{
+ fvdbg("Entry\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smart_close
+ *
+ * Description: close the block device
+ *
+ ****************************************************************************/
+
+static int smart_close(FAR struct inode *inode)
+{
+ fvdbg("Entry\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smart_reload
+ *
+ * Description: Read the specified numer 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, mtdStartBlock;
+
+ /* Calculate the number of MTD blocks to read */
+
+ mtdBlocks = nblocks * dev->mtdBlksPerSector;
+
+ /* Calculate the first MTD block number */
+
+ mtdStartBlock = startblock * dev->mtdBlksPerSector;
+
+ /* Read the full erase block into the buffer */
+
+ fdbg("Read %d blocks starting at block %d\n", mtdBlocks, mtdStartBlock);
+ nread = MTD_BREAD(dev->mtd, mtdStartBlock, mtdBlocks, buffer);
+ if (nread != mtdBlocks)
+ {
+ fdbg("Read %d blocks starting at block %d failed: %d\n",
+ nblocks, startblock, nread);
+ }
+
+ return nread;
+}
+
+/****************************************************************************
+ * Name: smart_read
+ *
+ * Description: Read the specified numer of sectors
+ *
+ ****************************************************************************/
+
+static ssize_t smart_read(FAR struct inode *inode, unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ struct smart_struct_s *dev;
+
+ fvdbg("SMART: sector: %d nsectors: %d\n", start_sector, nsectors);
+
+ DEBUGASSERT(inode && inode->i_private);
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev;
+#else
+ dev = (struct smart_struct_s *)inode->i_private;
+#endif
+ return smart_reload(dev, buffer, start_sector, nsectors);
+}
+
+/****************************************************************************
+ * Name: smart_write
+ *
+ * Description: Write (or buffer) the specified number of sectors
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static ssize_t smart_write(FAR struct inode *inode, const unsigned char *buffer,
+ size_t start_sector, unsigned int nsectors)
+{
+ 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, mtdblockcount;
+
+ fvdbg("sector: %d nsectors: %d\n", start_sector, nsectors);
+
+ DEBUGASSERT(inode && inode->i_private);
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev;
+#else
+ dev = (struct smart_struct_s *)inode->i_private;
+#endif
+
+ /* I think maybe we need to lock on a mutex here */
+
+ /* Get the aligned block. Here is is assumed: (1) The number of R/W blocks
+ * 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;
+
+ /* Convert SMART blocks into MTD blocks */
+
+ mtdstartblock = start_sector * dev->mtdBlksPerSector;
+ mtdblockcount = nsectors * dev->mtdBlksPerSector;
+ mtdBlksPerErase = dev->mtdBlksPerSector * dev->sectorsPerBlk;
+
+ fvdbg("mtdsector: %d mtdnsectors: %d\n", mtdstartblock, mtdblockcount);
+
+ /* Start at first block to be written */
+
+ remaining = mtdblockcount;
+ nextblock = mtdstartblock;
+ offset = 0;
+
+ /* Loop for all blocks to be written */
+
+ while (remaining > 0)
+ {
+ /* If this is an aligned block, then erase the block */
+
+ if (alignedblock == nextblock)
+ {
+ /* Erase the erase block */
+
+ eraseblock = alignedblock / mtdBlksPerErase;
+ ret = MTD_ERASE(dev->mtd, eraseblock, 1);
+ if (ret < 0)
+ {
+ fdbg("Erase block=%d failed: %d\n", eraseblock, ret);
+
+ /* Unlock the mutex if we add one */
+
+ return ret;
+ }
+ }
+
+ /* Calculate the number of blocks to write. */
+
+ blkstowrite = mtdBlksPerErase;
+ if (nextblock != alignedblock)
+ {
+ blkstowrite = alignedblock - nextblock;
+ }
+
+ if (blkstowrite > remaining)
+ {
+ blkstowrite = remaining;
+ }
+
+ /* Try to write to the sector. */
+
+ fdbg("Write MTD block %d from offset %d\n", nextblock, offset);
+ nxfrd = MTD_BWRITE(dev->mtd, nextblock, blkstowrite, &buffer[offset]);
+ if (nxfrd != blkstowrite)
+ {
+ /* The block is not empty!! What to do? */
+
+ fdbg("Write block %d failed: %d.\n", nextblock, nxfrd);
+
+ /* Unlock the mutex if we add one */
+
+ return -EIO;
+ }
+
+ /* Then update for amount written */
+
+ nextblock += blkstowrite;
+ remaining -= blkstowrite;
+ offset += blkstowrite * dev->geo.blocksize;
+ alignedblock += mtdBlksPerErase;
+ }
+
+ return nsectors;
+}
+#endif /* CONFIG_FS_WRITABLE */
+
+/****************************************************************************
+ * Name: smart_geometry
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int smart_geometry(FAR struct inode *inode, struct geometry *geometry)
+{
+ struct smart_struct_s *dev;
+ uint32_t erasesize;
+
+ fvdbg("Entry\n");
+
+ DEBUGASSERT(inode);
+ if (geometry)
+ {
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev;
+#else
+ dev = (struct smart_struct_s *)inode->i_private;
+#endif
+ geometry->geo_available = true;
+ geometry->geo_mediachanged = false;
+#ifdef CONFIG_FS_WRITABLE
+ geometry->geo_writeenabled = true;
+#else
+ geometry->geo_writeenabled = false;
+#endif
+
+ erasesize = dev->geo.erasesize;
+ if (erasesize == 0)
+ {
+ erasesize = 65536;
+ }
+
+ geometry->geo_nsectors = dev->geo.neraseblocks * erasesize /
+ dev->sectorsize;
+ geometry->geo_sectorsize = dev->sectorsize;
+
+ fvdbg("available: true mediachanged: false writeenabled: %s\n",
+ geometry->geo_writeenabled ? "true" : "false");
+ fvdbg("nsectors: %d sectorsize: %d\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
+ * dependant variables.
+ *
+ ****************************************************************************/
+
+static int smart_setsectorsize(struct smart_struct_s *dev, uint16_t size)
+{
+ uint32_t erasesize;
+ uint32_t totalsectors;
+ int ret;
+
+ /* Check if the device supports sub-sector erase regions. If it does, then
+ * use the sub-sector erase size instead of the larger erase size to
+ * provide finer granularity.
+ */
+
+#ifdef CONFIG_MTD_SUBSECTOR_ERASE
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_GETCAPS, 0);
+ if (ret >= 0)
+ {
+ /* The block device supports the GETCAPS ioctl. Now check if sub-sector
+ * erase is supported.
+ */
+
+ if (!(ret & MTDIOC_CAPS_SECTERASE))
+ {
+ /* Ensure the subsector size is zero */
+ dev->geo.subsectorsize = 0;
+ dev->geo.nsubsectors = 0;
+ }
+ }
+
+ /* Now calculate the variables */
+
+ if (dev->geo.subsectorsize != 0)
+ {
+ /* Use the sub-sector erase size */
+
+ erasesize = dev->geo.subsectorsize;
+ dev->neraseblocks = dev->geo.nsubsectors;
+ }
+ else
+#endif /* CONFIG_MTD_SUBSECTOR_ERASE */
+ {
+ erasesize = dev->geo.erasesize;
+ dev->neraseblocks = dev->geo.neraseblocks;
+ }
+
+ /* Most FLASH devices have erase size of 64K, but geo.erasesize is only
+ * 16 bits, so it will be zero
+ */
+
+ if (erasesize == 0)
+ {
+ erasesize = 65536;
+ }
+
+ dev->sectorsize = size;
+ dev->mtdBlksPerSector = dev->sectorsize / dev->geo.blocksize;
+ dev->sectorsPerBlk = erasesize / dev->sectorsize;
+
+ /* Release any existing rwbuffer and sMap */
+
+ if (dev->sMap != NULL)
+ {
+ kfree(dev->sMap);
+ }
+
+ if (dev->rwbuffer != NULL)
+ {
+ kfree(dev->rwbuffer);
+ }
+
+ /* Allocate a virtual to physical sector map buffer. Also allocate
+ * the storage space for releasecount and freecounts.
+ */
+
+ totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
+ dev->totalsectors = (uint16_t) totalsectors;
+
+ dev->sMap = (uint16_t *) kmalloc(totalsectors * sizeof(uint16_t) +
+ (dev->neraseblocks << 1));
+ if (!dev->sMap)
+ {
+ fdbg("Error allocating SMART virtual map buffer\n");
+ kfree(dev);
+ return -EINVAL;
+ }
+
+ dev->releasecount = (uint8_t *) dev->sMap + (totalsectors * sizeof(uint16_t));
+ dev->freecount = dev->releasecount + dev->neraseblocks;
+
+ /* Allocate a read/write buffer */
+
+ dev->rwbuffer = (char *) kmalloc(size);
+ if (!dev->rwbuffer)
+ {
+ fdbg("Error allocating SMART read/write buffer\n");
+ kfree(dev->sMap);
+ kfree(dev);
+ return -EINVAL;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * 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(struct smart_struct_s *dev)
+{
+ int sector;
+ int ret;
+ int x;
+ uint16_t totalsectors;
+ uint16_t sectorsize;
+ uint16_t logicalsector;
+ uint16_t seq1;
+ uint16_t seq2;
+ char devname[18];
+ size_t readaddress;
+ struct smart_sect_header_s header;
+ struct mtd_byte_write_s bytewrite;
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ struct smart_multiroot_device_s *rootdirdev;
+#endif
+
+ fvdbg("Entry\n");
+
+ /* Read the 1st header from the device. We always keep the
+ * 1st sector's header's sectorsize field accurate, even
+ * after we erase an MTD block/sector */
+
+ ret = MTD_READ(dev->mtd, 0, sizeof(struct smart_sect_header_s),
+ (uint8_t *) &header);
+ if (ret != sizeof(struct smart_sect_header_s))
+ {
+ goto err_out;
+ }
+
+ /* Now set the sectorsize and other sectorsize derived variables */
+
+ if (header.status == CONFIG_SMARTFS_ERASEDSTATE)
+ {
+ sectorsize = CONFIG_MTD_SMART_SECTOR_SIZE;
+ }
+ else
+ {
+ sectorsize = (header.status & SMART_STATUS_SIZEBITS) << 7;
+ }
+
+ ret = smart_setsectorsize(dev, sectorsize);
+ if (ret != OK)
+ {
+ goto err_out;
+ }
+
+ /* Initialize the device variables */
+
+ totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
+ dev->formatstatus = SMART_FMT_STAT_NOFMT;
+ dev->freesectors = totalsectors;
+
+ /* Initialize the freecount and releasecount arrays */
+
+ for (sector = 0; sector < dev->neraseblocks; sector++)
+ {
+ dev->freecount[sector] = dev->sectorsPerBlk;
+ dev->releasecount[sector] = 0;
+ }
+
+ /* Initialize the sector map */
+
+ for (sector = 0; sector < totalsectors; sector++)
+ {
+ dev->sMap[sector] = -1;
+ }
+
+ /* Now scan the MTD device */
+
+ for (sector = 0; sector < totalsectors; sector++)
+ {
+ fvdbg("Scan sector %d\n", sector);
+
+ /* Calculate the read address for this sector */
+
+ readaddress = sector * dev->mtdBlksPerSector * dev->geo.blocksize;
+
+ /* Read the header for this sector */
+
+ ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s),
+ (uint8_t *) &header);
+ if (ret != sizeof(struct smart_sect_header_s))
+ {
+ goto err_out;
+ }
+
+ /* Get the logical sector number for this physical sector */
+
+ logicalsector = *((uint16_t *) header.logicalsector);
+#if CONFIG_SMARTFS_ERASEDSTATE == 0x00
+ if (logicalsector == 0)
+ {
+ logicalsector = -1;
+ }
+#endif
+
+ /* Test if this sector has been commited */
+
+ if ((header.status & SMART_STATUS_COMMITTED) ==
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED))
+ {
+ continue;
+ }
+
+ /* This block is commited, therefore not free. Update the
+ * erase block's freecount.
+ */
+
+ dev->freecount[sector / dev->sectorsPerBlk]--;
+ dev->freesectors--;
+
+ /* Test if this sector has been release and if it has,
+ * update the erase block's releasecount.
+ */
+
+ if ((header.status & SMART_STATUS_RELEASED) !=
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED))
+ {
+ dev->releasecount[sector / dev->sectorsPerBlk]++;
+ continue;
+ }
+
+ if ((header.status & SMART_STATUS_VERBITS) != SMART_STATUS_VERSION)
+ {
+ continue;
+ }
+
+ /* Validate the logical sector number is in bounds */
+
+ if (logicalsector >= totalsectors)
+ {
+ /* Error in logical sector read from the MTD device */
+
+ fdbg("Invalid logical sector %d at physical %d.\n",
+ logicalsector, sector);
+ continue;
+ }
+
+ /* If this is logical sector zero, then read in the signature
+ * information to validate the format signature.
+ */
+
+ if (logicalsector == 0)
+ {
+ /* Read the sector data */
+
+ ret = MTD_READ(dev->mtd, readaddress, 32,
+ (uint8_t*) dev->rwbuffer);
+ if (ret != 32)
+ {
+ fdbg("Error reading physical sector %d.\n", sector);
+ goto err_out;
+ }
+
+ /* Validate the format signature */
+
+ if (dev->rwbuffer[SMART_FMT_POS1] != SMART_FMT_SIG1 ||
+ dev->rwbuffer[SMART_FMT_POS2] != SMART_FMT_SIG2 ||
+ dev->rwbuffer[SMART_FMT_POS3] != SMART_FMT_SIG3 ||
+ dev->rwbuffer[SMART_FMT_POS4] != SMART_FMT_SIG4)
+ {
+ /* Invalid signature on a sector claiming to be sector 0!
+ * What should we do? Release it?*/
+
+ continue;
+ }
+
+ /* TODO: May want to validate / save the erase block aging info */
+
+ /* Mark the volume as formatted and set the sector size */
+
+ dev->formatstatus = SMART_FMT_STAT_FORMATTED;
+ dev->namesize = dev->rwbuffer[SMART_FMT_NAMESIZE_POS];
+ dev->formatversion = dev->rwbuffer[SMART_FMT_VERSION_POS];
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ dev->rootdirentries = dev->rwbuffer[SMART_FMT_ROOTDIRS_POS];
+
+ /* If rootdirentries is greater than 1, then we need to register
+ * additional block devices.
+ */
+
+ for (x = 1; x < dev->rootdirentries; x++)
+ {
+ snprintf(devname, 18, "/dev/smart%dd%d", dev->minor, x + 1);
+
+ /* Inode private data is a reference to a struct containing
+ * the SMART device structure and the root directory number.
+ */
+
+ rootdirdev = (struct smart_multiroot_device_s*) kmalloc(sizeof(*rootdirdev));
+ if (rootdirdev == NULL)
+ {
+ fdbg("Memory alloc failed\n");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ /* Populate the rootdirdev */
+
+ rootdirdev->dev = dev;
+ rootdirdev->rootdirnum = x;
+ ret = register_blockdriver(dev->rwbuffer, &g_bops, 0, rootdirdev);
+
+ /* Inode private data is a reference to the SMART device structure */
+
+ ret = register_blockdriver(devname, &g_bops, 0, rootdirdev);
+ }
+#endif
+ }
+
+ /* Test for duplicate logical sectors on the device */
+
+ if (dev->sMap[logicalsector] != -1)
+ {
+ /* TODO: Uh-oh, we found more than 1 physical sector claiming to be
+ * the * same logical sector. Use the sequence number information
+ * to resolve who wins.
+ */
+
+ uint16_t loser;
+
+ seq2 = *((uint16_t *) header.seq);
+
+ /* We must re-read the 1st physical sector to get it's seq number */
+
+ readaddress = dev->sMap[logicalsector] * dev->mtdBlksPerSector * dev->geo.blocksize;
+ ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s),
+ (uint8_t *) &header);
+ if (ret != sizeof(struct smart_sect_header_s))
+ {
+ goto err_out;
+ }
+
+ seq1 = *((uint16_t *) header.seq);
+
+ /* Now determine who wins */
+
+ if (seq1 > 0xFFF0 && seq2 < 10)
+ {
+ /* Seq 2 is the winner ... we assume it wrapped */
+
+ loser = dev->sMap[logicalsector];
+ dev->sMap[logicalsector] = sector;
+ }
+ else if (seq2 > seq1)
+ {
+ /* Seq 2 is bigger, so it's the winner */
+
+ loser = dev->sMap[logicalsector];
+ dev->sMap[logicalsector] = sector;
+ }
+ else
+ {
+ /* We keep the original mapping and seq2 is the loser */
+
+ loser = sector;
+ }
+
+ /* Now release the loser sector */
+
+ readaddress = loser * dev->mtdBlksPerSector * dev->geo.blocksize;
+ ret = MTD_READ(dev->mtd, readaddress, sizeof(struct smart_sect_header_s),
+ (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
+ bytewrite.offset = readaddress + offsetof(struct smart_sect_header_s, status);
+ bytewrite.count = 1;
+ bytewrite.buffer = &header.status;
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+ }
+
+ /* Update the logical to physical sector map */
+
+ dev->sMap[logicalsector] = sector;
+ }
+
+ fdbg("SMART Scan\n");
+ fdbg(" Erase size: %10d\n", dev->sectorsPerBlk * dev->sectorsize);
+ fdbg(" Erase count: %10d\n", dev->neraseblocks);
+ fdbg(" Sect/block: %10d\n", dev->sectorsPerBlk);
+ fdbg(" MTD Blk/Sect: %10d\n", dev->mtdBlksPerSector);
+
+ 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(struct smart_struct_s *dev,
+ struct smart_format_s *fmt, uint8_t rootdirnum)
+#else
+static inline int smart_getformat(struct smart_struct_s *dev, struct smart_format_s *fmt)
+#endif
+{
+ int ret;
+ int x;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(fmt);
+
+ /* Test if we know the format status or not. If we don't know the
+ * status, then we must perform a scan of the device to search
+ * for the format marker
+ */
+
+ if (dev->formatstatus != SMART_FMT_STAT_FORMATTED)
+ {
+ /* Perform the scan */
+
+ ret = smart_scan(dev);
+
+ if (ret != OK)
+ {
+ goto err_out;
+ }
+ }
+
+ /* TODO: Determine if the underlying MTD device supports bytewrite */
+
+ /* Now fill in the structure */
+
+ 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->neraseblocks * dev->sectorsPerBlk;
+ fmt->nfreesectors = dev->freesectors;
+ fmt->namesize = dev->namesize;
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ fmt->nrootdirentries = dev->rootdirentries;
+ fmt->rootdirnum = rootdirnum;
+#endif
+
+ /* Add the released sectors to the reported free sector count */
+
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+ fmt->nfreesectors += dev->releasecount[x];
+ }
+
+ /* Subtract the reserved sector count */
+
+ fmt->nfreesectors -= dev->sectorsPerBlk + 4;
+
+ ret = OK;
+
+err_out:
+ return ret;
+}
+
+/****************************************************************************
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static inline int smart_llformat(struct smart_struct_s *dev, unsigned long arg)
+{
+ struct smart_sect_header_s *sectorheader;
+ size_t wrcount;
+ size_t totalsectors;
+ int x;
+ int ret;
+ uint8_t sectsize;
+
+ fvdbg("Entry\n");
+
+ /* Erase the MTD device */
+
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BULKERASE, 0);
+ if (ret != OK)
+ {
+ return ret;
+ }
+
+ /* Now construct a logical sector zero header to write to the device.
+ * We fill it with zero so when we add sector aging, all the sector
+ * ages will already be initialized to zero without needing special
+ * logic to deal with a 0xFF erased-state value.
+ */
+
+ sectorheader = (struct smart_sect_header_s *) dev->rwbuffer;
+ memset(dev->rwbuffer, 0, dev->sectorsize);
+ memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, SMARTFS_FMT_AGING_POS);
+ *((uint16_t *) sectorheader->seq) = 0;
+
+ sectsize = (CONFIG_MTD_SMART_SECTOR_SIZE >> 9) << 2;
+#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF )
+ *((uint16_t *) sectorheader->logicalsector) = 0;
+ sectorheader->status = (uint8_t) ~(SMART_STATUS_COMMITTED | SMART_STATUS_VERBITS |
+ SMART_STATUS_SIZEBITS) | SMART_STATUS_VERSION |
+ sectsize;
+#else
+ *((uint16_t *) sectorheader->logicalsector) = 0xFFFF;
+ sectorheader->status = (uint8_t) (SMART_STATUS_COMMITTED | SMART_STATUS_VERSION |
+ sectsize);
+#endif
+
+ /* Now add the format signature to the sector */
+
+ 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;
+
+ /* Record the number of root directory entries we have */
+
+ dev->rwbuffer[SMART_FMT_ROOTDIRS_POS] = (uint8_t) arg;
+
+ /* Write the sector to the flash */
+
+ wrcount = MTD_BWRITE(dev->mtd, 0, dev->mtdBlksPerSector,
+ (uint8_t *) dev->rwbuffer);
+ if (wrcount != dev->mtdBlksPerSector)
+ {
+ /* The block is not empty!! What to do? */
+
+ fdbg("Write block 0 failed: %d.\n", wrcount);
+
+ /* Unlock the mutex if we add one */
+
+ return -EIO;
+ }
+
+ /* Now initialize our internal control variables */
+
+ ret = smart_setsectorsize(dev, CONFIG_MTD_SMART_SECTOR_SIZE);
+ if (ret != OK)
+ {
+ return ret;
+ }
+
+ dev->formatstatus = SMART_FMT_STAT_UNKNOWN;
+ dev->freesectors = dev->neraseblocks * dev->sectorsPerBlk - 1;
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+ /* Initialize the released and free counts */
+
+ dev->releasecount[x] = 0;
+ dev->freecount[x] = dev->sectorsPerBlk;
+ }
+
+ /* Account for the format sector */
+
+ dev->freecount[0]--;
+
+ /* Now initialize the logical to physical sector map */
+
+ dev->sMap[0] = 0; /* Logical sector zero = physical sector 0 */
+
+ totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
+ for (x = 1; x < totalsectors; x++)
+ {
+ /* Mark all other logical sectors as non-existant */
+
+ dev->sMap[x] = -1;
+ }
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+
+ /* Un-register any extra directory device entries */
+
+ for (x = 2; x < 8; x++)
+ {
+ snprintf(dev->rwbuffer, 18, "/dev/smart%dd%d", dev->minor, x);
+ unregister_blockdriver(dev->rwbuffer);
+ }
+#endif
+
+ return OK;
+}
+#endif /* CONFIG_FS_WRITABLE */
+
+/****************************************************************************
+ * 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(struct smart_struct_s *dev)
+{
+ uint16_t allocfreecount;
+ uint16_t allocblock;
+ uint16_t physicalsector;
+ uint16_t x;
+ uint32_t readaddr;
+ struct smart_sect_header_s header;
+ int ret;
+
+ /* Determine which erase block we should allocate the new
+ * sector from. This is based on the number of free sectors
+ * available in each erase block. */
+
+ allocfreecount = 0;
+ allocblock = 0xFFFF;
+ physicalsector = 0xFFFF;
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+ /* Test if this block has more free blocks than the
+ * currently selected block */
+
+ if (dev->freecount[x] > allocfreecount)
+ {
+ /* Assign this block to alloc from */
+
+ allocblock = x;
+ allocfreecount = dev->freecount[x];
+ }
+ }
+
+ /* Check if we found an allocblock. */
+
+ if (allocblock == 0xFFFF)
+ {
+ /* No free sectors found! Bug? */
+ return -EIO;
+ }
+
+ /* Now find a free physical sector within this selected
+ * erase block to allocate. */
+
+ for (x = allocblock * dev->sectorsPerBlk;
+ x < (allocblock+1) * dev->sectorsPerBlk; x++)
+ {
+ /* Check if this physical sector is available */
+
+ readaddr = x * dev->mtdBlksPerSector * dev->geo.blocksize;
+ ret = MTD_READ(dev->mtd, readaddr, sizeof(struct smart_sect_header_s),
+ (uint8_t *) &header);
+ if (ret != sizeof(struct smart_sect_header_s))
+ {
+ fvdbg("Error reading phys sector %d\n", physicalsector);
+ return -EIO;
+ }
+
+ if ((*((uint16_t *) header.logicalsector) == 0xFFFF) &&
+ (*((uint16_t *) header.seq) == 0xFFFF) &&
+ ((header.status & SMART_STATUS_COMMITTED) ==
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)))
+ {
+ physicalsector = x;
+ break;
+ }
+ }
+
+ 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.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static int smart_garbagecollect(struct smart_struct_s *dev)
+{
+ uint16_t releasedsectors;
+ uint16_t collectblock;
+ uint16_t releasemax;
+ uint16_t newsector;
+ bool collect = TRUE;
+ int x;
+ int ret;
+ struct smart_sect_header_s *header;
+ struct mtd_byte_write_s bytewrite;
+
+ while (collect)
+ {
+ collect = FALSE;
+
+ /* Calculate the number of released sectors on the device */
+
+ releasedsectors = 0;
+ collectblock = 0xFFFF;
+ releasemax = 0;
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+ releasedsectors += dev->releasecount[x];
+ if (dev->releasecount[x] > releasemax)
+// if (dev->releasecount[x] > releasemax &&
+// dev->freecount[x] < (dev->sectorsPerBlk >> 1))
+ {
+ releasemax = dev->releasecount[x];
+ collectblock = x;
+ }
+ }
+
+ /* Test if the released sectors count is greater than the
+ * free sectors. If it is, then we will do garbage collection */
+
+ if (releasedsectors > dev->freesectors)
+ collect = TRUE;
+
+ /* Test if we have more reached our reserved free sector limit */
+
+ if (dev->freesectors <= (dev->sectorsPerBlk << 0) + 4)
+ collect = TRUE;
+
+ /* Test if we need to garbage collect */
+
+ if (collect)
+ {
+ if (collectblock == 0xFFFF)
+ {
+ /* Need to collect, but no sectors with released blocks! */
+
+ ret = -ENOSPC;
+ goto errout;
+ }
+
+ fdbg("Collecting block %d, free=%d released=%d\n",
+ collectblock, dev->freecount[collectblock],
+ dev->releasecount[collectblock]);
+
+ if (dev->freecount[collectblock] == 6)
+ {
+ fdbg("here!\n");
+ }
+
+ /* Perform collection on block with the most released sectors.
+ * First mark the block as having no free sectors so we don't
+ * try to move sectors into the block we are trying to erase. */
+
+ dev->freecount[collectblock] = 0;
+
+ /* Next move all live data in the block to a new home. */
+
+ for (x = collectblock * dev->sectorsPerBlk; x <
+ (collectblock + 1) * dev->sectorsPerBlk; x++)
+ {
+ /* Read the next sector from this erase block */
+
+ ret = MTD_BREAD(dev->mtd, x * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ fdbg("Error reading sector %d\n", x);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Test if if the block is in use */
+
+ header = (struct smart_sect_header_s *) dev->rwbuffer;
+ if (((header->status & SMART_STATUS_COMMITTED) ==
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)) ||
+ ((header->status & SMART_STATUS_RELEASED) !=
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_RELEASED)))
+ {
+ /* This sector doesn't have live data (free or released).
+ * just continue to the next sector and don't move it. */
+
+ continue;
+ }
+
+ /* Find a new sector where it can live, NOT in this erase block */
+
+ newsector = smart_findfreephyssector(dev);
+ if (newsector == 0xFFFF)
+ {
+ /* Unable to find a free sector!!! */
+
+ fdbg("Can't find a free sector for relocation\n");
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Increment the sequence number and clear the "commit" flag */
+
+ (*((uint16_t *) header->seq))++;
+ if (*((uint16_t *) header->seq) == 0xFFFF)
+ {
+ *((uint16_t *) header->seq) = 1;
+ }
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status |= SMART_STATUS_COMMITTED;
+#else
+ header->status &= ~SMART_STATUS_COMMITTED;
+#endif
+
+ /* Write the data to the new physical sector location */
+
+ ret = MTD_BWRITE(dev->mtd, newsector * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer);
+
+ /* Commit the sector */
+
+ bytewrite.offset = newsector * dev->mtdBlksPerSector * dev->geo.blocksize +
+ offsetof(struct smart_sect_header_s, status);
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status &= ~SMART_STATUS_COMMITTED;
+#else
+ header->status |= SMART_STATUS_COMMITTED;
+#endif
+ bytewrite.count = 1;
+ bytewrite.buffer = &header->status;
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+
+ /* Release the old physical sector */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status &= ~SMART_STATUS_RELEASED;
+#else
+ header->status |= SMART_STATUS_RELEASED;
+#endif
+ bytewrite.offset = x * dev->mtdBlksPerSector * dev->geo.blocksize +
+ offsetof(struct smart_sect_header_s, status);
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+
+ /* Update the variables */
+
+ dev->sMap[*((uint16_t *) header->logicalsector)] = newsector;
+ dev->freecount[newsector / dev->sectorsPerBlk]--;
+ }
+
+ /* Now erase the erase block */
+
+#ifdef CONFIG_MTD_SUBSECTOR_ERASE
+ if (dev->geo.subsectorsize != 0)
+ {
+ /* Perform a sub-sector erase */
+ MTD_IOCTL(dev->mtd, MTDIOC_SECTERASE, collectblock);
+ }
+ else
+#endif
+ {
+ MTD_ERASE(dev->mtd, collectblock, 1);
+ }
+
+ dev->freesectors += dev->releasecount[collectblock];
+ dev->freecount[collectblock] = dev->sectorsPerBlk;
+ dev->releasecount[collectblock] = 0;
+
+ /* If this is block zero, then be sure to write the sector size */
+
+ if (collectblock == 0)
+ {
+ /* Set the sector size in the 1st header */
+
+ uint8_t sectsize = dev->sectorsize >> 7;
+ header = (struct smart_sect_header_s *) dev->rwbuffer;
+#if ( CONFIG_SMARTFS_ERASEDSTATE == 0xFF )
+ header->status = (uint8_t) ~SMART_STATUS_SIZEBITS | sectsize;
+#else
+ header->status = (uint8_t) sectsize;
+#endif
+ /* Write the sector size to the device */
+
+ bytewrite.offset = offsetof(struct smart_sect_header_s, status);
+ bytewrite.count = 1;
+ bytewrite.buffer = &header->status;
+ MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+ }
+
+ /* Update the block aging information in the format signature sector */
+ }
+ else
+ {
+ /* Test for aging sectors and push them to a new location
+ * so we wear evenly.
+ */
+ }
+ }
+
+ return OK;
+
+errout:
+ return ret;
+}
+#endif /* CONFIG_FS_WRITABLE */
+
+/****************************************************************************
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static inline int smart_writesector(struct smart_struct_s *dev, unsigned long arg)
+{
+ int ret;
+ uint16_t x;
+ bool needsrelocate = FALSE;
+ uint16_t mtdblock;
+ uint16_t physsector;
+ struct smart_read_write_s *req;
+ struct smart_sect_header_s *header;
+ struct mtd_byte_write_s bytewrite;
+ uint8_t byte;
+
+ fvdbg("Entry\n");
+ req = (struct smart_read_write_s *) arg;
+ DEBUGASSERT(req->offset <= dev->sectorsize);
+ DEBUGASSERT(req->offset+req->count <= dev->sectorsize);
+
+ /* Ensure the logical sector has been allocated */
+
+ if (req->logsector >= dev->totalsectors)
+ {
+ fdbg("Logical sector %d too large\n", req->logsector);
+
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ physsector = dev->sMap[req->logsector];
+ if (physsector == 0xFFFF)
+ {
+ fdbg("Logical sector %d not allocated\n", req->logsector);
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ /* Read the sector data into our buffer */
+
+ mtdblock = physsector * dev->mtdBlksPerSector;
+ ret = MTD_BREAD(dev->mtd, mtdblock, dev->mtdBlksPerSector, (uint8_t *)
+ dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ fdbg("Error reading phys sector %d\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Test if we need to relocate the sector to perform the write */
+
+ for (x = 0; x < req->count; x++)
+ {
+ /* Test if the next byte can be written to the flash */
+
+ 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
+ }
+
+ if (needsrelocate)
+ {
+ /* Find a new physical sector to save data to */
+
+ physsector = smart_findfreephyssector(dev);
+ if (physsector == 0xFFFF)
+ {
+ fdbg("Error relocating sector %d\n", req->logsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Update the sequence number to indicate the sector was moved */
+
+ header = (struct smart_sect_header_s *) dev->rwbuffer;
+ (*((uint16_t *) header->seq))++;
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status |= SMART_STATUS_COMMITTED;
+#else
+ header->status &= SMART_STATUS_COMMITTED;
+#endif
+ }
+
+ /* Now copy the data to the sector buffer. */
+
+ memcpy(&dev->rwbuffer[sizeof(struct smart_sect_header_s) + req->offset],
+ req->buffer, req->count);
+
+ /* Now write the sector buffer to the device. */
+
+ if (needsrelocate)
+ {
+ /* Write the entire sector to the new physical location, uncommitted. */
+
+ ret = MTD_BWRITE(dev->mtd, physsector * dev->mtdBlksPerSector,
+ dev->mtdBlksPerSector, (uint8_t *) dev->rwbuffer);
+ if (ret != dev->mtdBlksPerSector)
+ {
+ fdbg("Error writing to physical sector %d\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Commit the new physical sector */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status &= ~SMART_STATUS_COMMITTED;
+#else
+ header->status |= SMART_STATUS_COMMITTED;
+#endif
+ bytewrite.offset = physsector * dev->mtdBlksPerSector *
+ dev->geo.blocksize + offsetof(struct smart_sect_header_s, status);
+ bytewrite.count = 1;
+ bytewrite.buffer = (uint8_t *) &header->status;
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+ if (ret != OK)
+ {
+ fvdbg("Error committing physical sector %d\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Release the old physical sector */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status &= ~SMART_STATUS_RELEASED;
+#else
+ header->status |= SMART_STATUS_RELEASED;
+#endif
+ bytewrite.offset = mtdblock * dev->geo.blocksize +
+ offsetof(struct smart_sect_header_s, status);
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+
+ /* Update releasecount for released sector and freecount for the
+ * newly allocated physical sector. */
+
+ dev->releasecount[dev->sMap[req->logsector] / dev->sectorsPerBlk]++;
+ dev->freecount[physsector / dev->sectorsPerBlk]--;
+ dev->freesectors--;
+
+ /* Update the sector map */
+
+ dev->sMap[req->logsector] = physsector;
+
+ /* Since we performed a relocation, do garbage collection to
+ * ensure we don't fill up our flash with released blocks.
+ */
+
+ smart_garbagecollect(dev);
+ }
+ else
+ {
+ /* Not relocated. Just write the portion of the sector that needs
+ * to be written. */
+
+ bytewrite.offset = mtdblock * dev->geo.blocksize +
+ sizeof(struct smart_sect_header_s) + req->offset;
+ bytewrite.count = req->count;
+ bytewrite.buffer = req->buffer;
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+ }
+
+ ret = OK;
+
+errout:
+ return ret;
+}
+#endif /* CONFIG_FS_WRITABLE */
+
+/****************************************************************************
+ * Name: smart_readsector
+ *
+ * Description: Reads data from the specified logical sector. The sector
+ * should have already been allocated prior to the read.
+ *
+ ****************************************************************************/
+
+static inline int smart_readsector(struct smart_struct_s *dev, unsigned long arg)
+{
+ int ret;
+ uint32_t readaddr;
+ uint16_t physsector;
+ struct smart_read_write_s *req;
+ struct smart_sect_header_s header;
+
+ fvdbg("Entry\n");
+ req = (struct smart_read_write_s *) arg;
+ DEBUGASSERT(req->offset < dev->sectorsize);
+ DEBUGASSERT(req->offset+req->count < dev->sectorsize);
+
+ /* Ensure the logical sector has been allocated */
+
+ if (req->logsector >= dev->totalsectors)
+ {
+ fdbg("Logical sector %d too large\n", req->logsector);
+
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ physsector = dev->sMap[req->logsector];
+ if (physsector == 0xFFFF)
+ {
+ fdbg("Logical sector %d not allocated\n", req->logsector);
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ /* Read the sector header data to validate as a sanity check */
+
+ ret = MTD_READ(dev->mtd, physsector * dev->mtdBlksPerSector * dev->geo.blocksize,
+ sizeof(struct smart_sect_header_s), (uint8_t *) &header);
+ if (ret != sizeof(struct smart_sect_header_s))
+ {
+ fvdbg("Error reading sector %d header\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Do a sanity check on the header data */
+
+ if (((*(uint16_t *) header.logicalsector) != req->logsector) ||
+ ((header.status & SMART_STATUS_COMMITTED) ==
+ (CONFIG_SMARTFS_ERASEDSTATE & SMART_STATUS_COMMITTED)))
+ {
+ /* Error in sector header! How do we handle this? */
+
+ fdbg("Error in logical sector %d header, phys=%d\n",
+ req->logsector, physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+ /* Read the sector data into the buffer */
+
+ readaddr = (uint32_t) physsector * dev->mtdBlksPerSector * dev->geo.blocksize +
+ req->offset + sizeof(struct smart_sect_header_s);;
+
+ ret = MTD_READ(dev->mtd, readaddr, req->count, (uint8_t *)
+ req->buffer);
+ if (ret != req->count)
+ {
+ fdbg("Error reading phys sector %d\n", physsector);
+ ret = -EIO;
+ goto errout;
+ }
+
+errout:
+ 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.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static inline int smart_allocsector(struct smart_struct_s *dev, unsigned long requested)
+{
+ int x;
+ int ret;
+ uint16_t logsector = 0xFFFF; /* Logical sector number selected */
+ uint16_t physicalsector; /* The selected physical sector */
+ uint16_t releasecount;
+ struct smart_sect_header_s *header;
+ uint8_t sectsize;
+
+ /* Validate that we have enough sectors available to perform an
+ * allocation. We have to ensure we keep enough reserved sectors
+ * on hand to do released sector garbage collection. */
+
+ releasecount = 0;
+ for (x = 0; x < dev->neraseblocks; x++)
+ {
+ releasecount += dev->releasecount[x];
+ }
+
+ if (dev->freesectors <= (dev->sectorsPerBlk << 0) + 4)
+ {
+ /* We are at our free sector limit. Test if we have
+ * sectors we can release */
+
+ if (releasecount == 0)
+ {
+ /* No space left!! */
+
+ return -ENOSPC;
+ }
+ }
+
+ /* Check if a specific sector is being requested and allocate that
+ * sector if it isn't already in use */
+
+ if ((requested > 2) && (requested < dev->totalsectors))
+ {
+ /* Validate the sector is not already allocated */
+
+ if (dev->sMap[requested] == (uint16_t) -1)
+ {
+ logsector = requested;
+ }
+ }
+
+ /* Check if we need to scan for an available logical sector */
+
+ if (logsector == 0xFFFF)
+ {
+ /* Loop through all sectors and find one to allocate */
+
+ for (x = SMART_FIRST_ALLOC_SECTOR; x < dev->totalsectors; x++)
+ {
+ if (dev->sMap[x] == (uint16_t) -1)
+ {
+ /* Unused logical sector found. Use this one */
+
+ logsector = x;
+ break;
+ }
+ }
+ }
+
+ /* Test for an error allocating a sector */
+
+ if (logsector == 0xFFFF)
+ {
+ /* Hmmm. We think we had enough logical sectors, but
+ * 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? */
+
+ fdbg("Couldn't find free sector when I expected too\n");
+
+ /* Unlock the mutex if we add one */
+
+ return -EIO;
+ }
+
+ /* Check if we need to do garbage collection. We have to
+ * ensure we keep enough reserved free sectors to per 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);
+
+ /* Find a free physical sector */
+
+ physicalsector = smart_findfreephyssector(dev);
+ fvdbg("Alloc: log=%d, phys=%d, erase block=%d, free=%d, released=%d\n",
+ logsector, physicalsector, physicalsector /
+ dev->sectorsPerBlk, dev->freesectors, releasecount);
+
+ /* Create a header to assign the logical sector */
+
+ memset(dev->rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, dev->sectorsize);
+ header = (struct smart_sect_header_s *) dev->rwbuffer;
+ *((uint16_t *) header->logicalsector) = logsector;
+ *((uint16_t *) header->seq) = 0;
+ sectsize = dev->sectorsize >> 7;
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header->status = ~(SMART_STATUS_COMMITTED | SMART_STATUS_SIZEBITS |
+ SMART_STATUS_VERBITS) | SMART_STATUS_VERSION | sectsize;
+#else
+ header->status = SMART_STATUS_COMMITTED | SMART_STATUS_VERSION | sectsize;
+#endif
+
+ /* Write the header to the physical sector location */
+
+ x = physicalsector * dev->mtdBlksPerSector;
+
+ fvdbg("Write MTD block %d\n", x);
+ ret = MTD_BWRITE(dev->mtd, x, 1, (uint8_t *) dev->rwbuffer);
+ if (ret != 1)
+ {
+ /* The block is not empty!! What to do? */
+
+ fdbg("Write block %d failed: %d.\n", x, ret);
+
+ /* Unlock the mutex if we add one */
+
+ return -EIO;
+ }
+
+ /* Map the sector and update the free sector counts */
+
+ dev->sMap[logsector] = physicalsector;
+ dev->freecount[physicalsector / dev->sectorsPerBlk]--;
+ dev->freesectors--;
+
+ /* Return the logical sector number */
+
+ return logsector;
+}
+#endif /* CONFIG_FS_WRITABLE */
+
+/****************************************************************************
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FS_WRITABLE
+static inline int smart_freesector(struct smart_struct_s *dev, unsigned long
+ logicalsector)
+{
+ int ret;
+ int readaddr;
+ uint16_t physsector;
+ uint16_t block;
+ struct smart_sect_header_s header;
+ struct mtd_byte_write_s bytewrite;
+
+ /* Check if the logical sector is within bounds */
+
+ if ((logicalsector > 2) && (logicalsector < dev->totalsectors))
+ {
+ /* Validate the sector is actually allocated */
+
+ if (dev->sMap[logicalsector] == (uint16_t) -1)
+ {
+ fdbg("Invalid release - sector %d not allocated\n", logicalsector);
+ ret = -EINVAL;
+ goto errout;
+ }
+ }
+
+ /* Okay to release the sector. Read the sector header info */
+
+ physsector = dev->sMap[logicalsector];
+ readaddr = physsector * dev->mtdBlksPerSector * dev->geo.blocksize;
+ ret = MTD_READ(dev->mtd, readaddr, sizeof(struct smart_sect_header_s),
+ (uint8_t *) &header);
+ if (ret != sizeof(struct smart_sect_header_s))
+ {
+ goto errout;
+ }
+
+ /* Do a sanity check on the logical sector number */
+
+ if (*((uint16_t *) header.logicalsector) != (uint16_t) logicalsector)
+ {
+ /* Hmmm... something is wrong. This should always match! Bug in our code? */
+
+ fdbg("Sector %d logical sector in header doesn't match\n", logicalsector);
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ /* Mark the sector as released */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ header.status &= ~SMART_STATUS_RELEASED;
+#else
+ header.status |= SMART_STATUS_RELEASED;
+#endif
+
+ /* Write the status back to the device */
+
+ bytewrite.offset = readaddr + offsetof(struct smart_sect_header_s, status);
+ bytewrite.count = 1;
+ bytewrite.buffer = &header.status;
+ ret = MTD_IOCTL(dev->mtd, MTDIOC_BYTEWRITE, (unsigned long) &bytewrite);
+ if (ret != OK)
+ {
+ fdbg("Error updating physicl sector %d status\n", physsector);
+ goto errout;
+ }
+
+ /* Update the erase block's release count */
+
+ block = physsector / dev->sectorsPerBlk;
+ dev->releasecount[block]++;
+
+ /* Unmap this logical sector */
+
+ dev->sMap[logicalsector] = (uint16_t) -1;
+
+ /* If this block has only released blocks, then erase it */
+
+ if (dev->releasecount[block] + dev->freecount[block] == dev->sectorsPerBlk)
+ {
+ /* Erase the block */
+
+#ifdef CONFIG_MTD_SUBSECTOR_ERASE
+ if (dev->geo.subsectorsize != 0)
+ {
+ /* Perform a sub-sector erase */
+ MTD_IOCTL(dev->mtd, MTDIOC_SECTERASE, block);
+ }
+ else
+#endif
+ MTD_ERASE(dev->mtd, block, 1);
+
+ dev->freesectors += dev->releasecount[block];
+ dev->releasecount[block] = 0;
+ dev->freecount[block] = dev->sectorsPerBlk;
+ }
+
+ ret = OK;
+
+errout:
+ return ret;
+}
+#endif /* CONFIG_FS_WRITABLE */
+
+/****************************************************************************
+ * Name: smart_ioctl
+ *
+ * Description: Return device geometry
+ *
+ ****************************************************************************/
+
+static int smart_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
+{
+ struct smart_struct_s *dev ;
+ int ret;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(inode && inode->i_private);
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ dev = ((struct smart_multiroot_device_s*) inode->i_private)->dev;
+#else
+ dev = (struct smart_struct_s *)inode->i_private;
+#endif
+
+ /* Process the ioctl's we care about first, pass any we don't respond
+ * to directly to the underlying MTD device.
+ */
+
+ switch (cmd)
+ {
+ case BIOC_XIPBASE:
+ /* The argument accompanying the BIOC_XIPBASE should be non-NULL. If
+ * DEBUG is enabled, we will catch it here instead of in the MTD
+ * driver.
+ */
+
+#ifdef CONFIG_DEBUG
+ if (arg == 0)
+ {
+ fdbg("ERROR: BIOC_XIPBASE argument is NULL\n");
+ return -EINVAL;
+ }
+#endif
+
+ /* Just change the BIOC_XIPBASE command to the MTDIOC_XIPBASE command. */
+
+ cmd = MTDIOC_XIPBASE;
+ break;
+
+ case BIOC_GETFORMAT:
+
+ /* Return the format information for the device */
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ ret = smart_getformat(dev, (struct smart_format_s *) arg,
+ ((struct smart_multiroot_device_s*) inode->i_private)->rootdirnum);
+#else
+ ret = smart_getformat(dev, (struct smart_format_s *) arg);
+#endif
+ goto ok_out;
+
+ case BIOC_READSECT:
+
+ /* Do a logical sector read and return the data */
+ ret = smart_readsector(dev, arg);
+ goto ok_out;
+
+#ifdef CONFIG_FS_WRITABLE
+ case BIOC_LLFORMAT:
+
+ /* Perform a low-level format on the flash */
+
+ ret = smart_llformat(dev, arg);
+ goto ok_out;
+
+ case BIOC_ALLOCSECT:
+
+ /* Allocate a logical sector for the upper layer file system */
+
+ ret = smart_allocsector(dev, arg);
+ goto ok_out;
+
+ case BIOC_FREESECT:
+
+ /* Free the specified logical sector */
+
+ ret = smart_freesector(dev, arg);
+ goto ok_out;
+
+ case BIOC_WRITESECT:
+
+ /* Write to the sector */
+
+ ret = smart_writesector(dev, arg);
+ goto ok_out;
+#endif /* CONFIG_FS_WRITABLE */
+
+ }
+
+ /* No other block driver ioctl commmands are not recognized by this
+ * 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)
+ {
+ fdbg("ERROR: MTD ioctl(%04x) failed: %d\n", cmd, ret);
+ }
+
+ok_out:
+ return ret;
+}
+
+/****************************************************************************
+ * 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)
+{
+ struct smart_struct_s *dev;
+ int ret = -ENOMEM;
+ uint32_t totalsectors;
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ struct smart_multiroot_device_s *rootdirdev;
+#endif
+
+ /* Sanity check */
+
+#ifdef CONFIG_DEBUG
+ if (minor < 0 || minor > 255 || !mtd)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Allocate a SMART device structure */
+
+ dev = (struct smart_struct_s *)kmalloc(sizeof(struct smart_struct_s));
+ if (dev)
+ {
+ /* Initialize the SMART device structure */
+
+ dev->mtd = mtd;
+
+ /* Get the device geometry. (casting to uintptr_t first eliminates
+ * complaints on some architectures where the sizeof long is different
+ * from the size of a pointer).
+ */
+
+ /* Set these to zero in case the device doesn't support them */
+
+ dev->geo.subsectorsize= 0;
+ dev->geo.nsubsectors = 0;
+ ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&dev->geo));
+ if (ret < 0)
+ {
+ fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", ret);
+ kfree(dev);
+ goto errout;
+ }
+
+ /* Set the sector size to the default for now */
+
+ dev->sMap = NULL;
+ dev->rwbuffer = NULL;
+ ret = smart_setsectorsize(dev, CONFIG_MTD_SMART_SECTOR_SIZE);
+ if (ret != OK)
+ {
+ kfree(dev);
+ goto errout;
+ }
+
+ /* Calculate the totalsectors on this device and validate */
+
+ totalsectors = dev->neraseblocks * dev->sectorsPerBlk;
+ if (totalsectors > 65534)
+ {
+ fdbg("SMART Sector size too small for device\n");
+ kfree(dev);
+ ret = -EINVAL;
+ goto errout;
+ }
+ dev->freesectors = (uint16_t) totalsectors;
+
+ /* Mark the device format status an unknown */
+
+ dev->formatstatus = SMART_FMT_STAT_UNKNOWN;
+ dev->namesize = CONFIG_SMARTFS_MAXNAMLEN;
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ dev->minor = minor;
+#endif
+
+ /* Create a MTD block device name */
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ snprintf(dev->rwbuffer, 18, "/dev/smart%dd1", minor);
+
+ /* Inode private data is a reference to a struct containing
+ * the SMART device structure and the root directory number.
+ */
+
+ rootdirdev = (struct smart_multiroot_device_s*) kmalloc(sizeof(*rootdirdev));
+ if (rootdirdev == NULL)
+ {
+ fdbg("register_blockdriver failed: %d\n", -ret);
+ kfree(dev->sMap);
+ kfree(dev->rwbuffer);
+ kfree(dev);
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ /* Populate the rootdirdev */
+
+ rootdirdev->dev = dev;
+ rootdirdev->rootdirnum = 0;
+ ret = register_blockdriver(dev->rwbuffer, &g_bops, 0, rootdirdev);
+
+#else
+ snprintf(dev->rwbuffer, 18, "/dev/smart%d", minor);
+
+ /* Inode private data is a reference to the SMART device structure */
+
+ ret = register_blockdriver(dev->rwbuffer, &g_bops, 0, dev);
+#endif
+
+ if (ret < 0)
+ {
+ fdbg("register_blockdriver failed: %d\n", -ret);
+ kfree(dev->sMap);
+ kfree(dev->rwbuffer);
+ kfree(dev);
+ }
+
+ /* Do a scan of the device */
+
+ smart_scan(dev);
+ }
+
+errout:
+ return ret;
+}
diff --git a/nuttx/include/nuttx/smart.h b/nuttx/include/nuttx/smart.h
new file mode 100644
index 000000000..7e9fafd77
--- /dev/null
+++ b/nuttx/include/nuttx/smart.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+ * include/nuttx/smart.h
+ * Sector Mapped Allocation for Really Tiny (SMART) FLASH interface
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_SMART_H
+#define __INCLUDE_NUTTX_SMART_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/* Macros to hide implementation */
+
+#define SMART_FMT_ISFORMATTED 0x01
+#define SMART_FMT_HASBYTEWRITE 0x02
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* The following defines the format information for the device. This
+ * information is retrieved via the BIOC_GETFORMAT ioctl.
+ */
+
+struct smart_format_s
+{
+ uint16_t sectorsize; /* Size of one read/write sector */
+ uint16_t availbytes; /* Number of bytes available in each sector */
+ uint16_t nsectors; /* Total number of sectors on device */
+ uint16_t nfreesectors; /* Number of free sectors on device */
+ uint8_t flags; /* Format flags (see above) */
+ uint8_t namesize; /* Size of filenames on this volume */
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ uint8_t nrootdirentries; /* Number of root directories on this device */
+ uint8_t rootdirnum; /* Root directory number for this dev entry */
+#endif
+};
+
+/* The following defines the information for writing a logical sector
+ * to the device.
+ */
+
+struct smart_read_write_s
+{
+ uint16_t logsector; /* The logical sector number */
+ uint16_t offset; /* Offset within the sector to write to */
+ uint16_t count; /* Number of bytes to write */
+ const uint8_t *buffer; /* Pointer to the data to write */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* __INCLUDE_NUTTX_SMART_H */