From 2fd7dfe98ac64e201c22e8cb07bab75433dd6b60 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 30 Apr 2013 20:13:30 -0600 Subject: Add Ken Pettit's SMART FS --- nuttx/ChangeLog | 3 +- nuttx/fs/Kconfig | 1 + nuttx/fs/Makefile | 1 + nuttx/fs/fs_mount.c | 6 + nuttx/fs/smartfs/Kconfig | 53 + nuttx/fs/smartfs/Make.defs | 54 + nuttx/fs/smartfs/smartfs.h | 353 ++++++ nuttx/fs/smartfs/smartfs_mksmartfs.c | 191 ++++ nuttx/fs/smartfs/smartfs_smart.c | 1981 ++++++++++++++++++++++++++++++++++ nuttx/fs/smartfs/smartfs_utils.c | 1293 ++++++++++++++++++++++ nuttx/include/nuttx/fs/dirent.h | 27 +- nuttx/include/nuttx/fs/ioctl.h | 40 +- nuttx/include/nuttx/fs/mksmartfs.h | 109 ++ nuttx/include/nuttx/mtd.h | 47 +- nuttx/include/nuttx/spi.h | 3 +- nuttx/include/sys/statfs.h | 1 + 16 files changed, 4151 insertions(+), 12 deletions(-) create mode 100644 nuttx/fs/smartfs/Kconfig create mode 100644 nuttx/fs/smartfs/Make.defs create mode 100644 nuttx/fs/smartfs/smartfs.h create mode 100644 nuttx/fs/smartfs/smartfs_mksmartfs.c create mode 100644 nuttx/fs/smartfs/smartfs_smart.c create mode 100644 nuttx/fs/smartfs/smartfs_utils.c create mode 100644 nuttx/include/nuttx/fs/mksmartfs.h diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index abb9eb601..0504c1ca2 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -4651,4 +4651,5 @@ * configs/sim/mtdpart: A new configuration to test MTD partitions (2013-4-30). * configs/sim/mkroe-stm32f4: Support for the MikroElektronika - Mikromedia for STM32F4 development board (from Ken Petit, 2013-4-30). \ No newline at end of file + Mikromedia for STM32F4 development board (from Ken Petit, 2013-4-30). + * fs/smartfs: Add Ken Pettits SMART FS (2013-4-30). diff --git a/nuttx/fs/Kconfig b/nuttx/fs/Kconfig index 6aac7a3f7..798a610f4 100644 --- a/nuttx/fs/Kconfig +++ b/nuttx/fs/Kconfig @@ -14,6 +14,7 @@ source fs/fat/Kconfig source fs/nfs/Kconfig source fs/nxffs/Kconfig source fs/romfs/Kconfig +source fs/smartfs/Kconfig source fs/binfs/Kconfig comment "System Logging" diff --git a/nuttx/fs/Makefile b/nuttx/fs/Makefile index 2a1fd75a8..d9e0900fa 100644 --- a/nuttx/fs/Makefile +++ b/nuttx/fs/Makefile @@ -103,6 +103,7 @@ include fat/Make.defs include romfs/Make.defs include nxffs/Make.defs include nfs/Make.defs +include smartfs/Make.defs include binfs/Make.defs endif diff --git a/nuttx/fs/fs_mount.c b/nuttx/fs/fs_mount.c index a91e7d366..d65d5da56 100644 --- a/nuttx/fs/fs_mount.c +++ b/nuttx/fs/fs_mount.c @@ -99,6 +99,9 @@ extern const struct mountpt_operations fat_operations; #ifdef CONFIG_FS_ROMFS extern const struct mountpt_operations romfs_operations; #endif +#ifdef CONFIG_FS_SMARTFS +extern const struct mountpt_operations smartfs_operations; +#endif static const struct fsmap_t g_bdfsmap[] = { @@ -107,6 +110,9 @@ static const struct fsmap_t g_bdfsmap[] = #endif #ifdef CONFIG_FS_ROMFS { "romfs", &romfs_operations }, +#endif +#ifdef CONFIG_FS_SMARTFS + { "smartfs", &smartfs_operations }, #endif { NULL, NULL }, }; diff --git a/nuttx/fs/smartfs/Kconfig b/nuttx/fs/smartfs/Kconfig new file mode 100644 index 000000000..b082092e3 --- /dev/null +++ b/nuttx/fs/smartfs/Kconfig @@ -0,0 +1,53 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config FS_SMARTFS + bool "SMART file system" + default n + depends on !DISABLE_MOUNTPOINT + ---help--- + Enable NuttX SMART Flash file system (SMARTFS) support. + +if FS_SMARTFS +config SMARTFS_ERASEDSTATE + hex "FLASH erased state" + default 0xff + ---help--- + The erased state of FLASH. + This must have one of the values of 0xff or 0x00. + Default: 0xff. + +config SMARTFS_MAXNAMLEN + int "Maximum file name length" + default 16 + ---help--- + The maximum size of a SMARTFS file name at a given + directory level. Overall path name is not restricted + by this value, only individual segments in a path, + such as (assuming /usr is the mount point): + + /usr/games/chess/player_stats/game1.xml + + Default: 16. + +config SMARTFS_MULTI_ROOT_DIRS + bool "Support multiple Root Directories / Mount Points" + default n + ---help--- + Enables support for multiple root directory entries + on the SMART FLASH. Multiple root directories means + the device can have multiple mount point in the VFS, + each with it's own unique directory structure under it. + + When this option is enabled, instead of reporting the + SMART block devices as "/dev/mtdsmart0", they will be + reported as "/dev/mtdsmart0d1", "/dev/mtdsmart0d2", etc. + The number of root entries actually created is set + during the "mksmartfs" command when the device is + initialized. + + Default: y. + +endif diff --git a/nuttx/fs/smartfs/Make.defs b/nuttx/fs/smartfs/Make.defs new file mode 100644 index 000000000..9604d546f --- /dev/null +++ b/nuttx/fs/smartfs/Make.defs @@ -0,0 +1,54 @@ +############################################################################ +# Make.defs +# +# Copyright (C) 2008, 2011, 2013 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. +# +############################################################################ + +ifeq ($(CONFIG_FS_SMARTFS),y) + +# Files required for SmartFS file system support + +ASRCS += +CSRCS += smartfs_smart.c smartfs_utils.c + +# Files required for mksmartfs utility function + +ASRCS += +CSRCS += smartfs_mksmartfs.c + +# Include SMART build support + +DEPPATH += --dep-path smartfs +VPATH += :smartfs +CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)fs$(DELIM)smartfs} + +endif diff --git a/nuttx/fs/smartfs/smartfs.h b/nuttx/fs/smartfs/smartfs.h new file mode 100644 index 000000000..d1d3928c4 --- /dev/null +++ b/nuttx/fs/smartfs/smartfs.h @@ -0,0 +1,353 @@ +/**************************************************************************** + * fs/smartfs/smartfs.h + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit + * + * References: Linux/Documentation/filesystems/romfs.txt + * + * 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 __FS_SMARTFS_SMARTFS_H +#define __FS_SMARTFS_SMARTFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* SMART Definitions ********************************************************/ +/* General SMART organization. The following example assumes 4 logical + * sectors per FLASH erase block. The actual relationship is determined by + * the FLASH geometry reported by the MTD driver. + * + * ERASE LOGICAL Sectors begin with a sector header. Sectors may + * BLOCK SECTOR CONTENTS be marked as "released," pending garbage collection + * n 4*n --+---------------+ + * Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes) + * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes) + * |SSSSSSSSSSSSSSS| Status bits (1 byte) + * +---------------+ + * |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte) + * |NNNNNNNNNNNNNNN| Number of next logical sector in chain + * |UUUUUUUUUUUUUUU| Number of bytes used in this sector + * | | + * | (Sector Data) | + * | | + * 4*n+1 --+---------------+ + * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes) + * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes) + * |SSSSSSSSSSSSSSS| Status bits (1 byte) + * +---------------+ + * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte) + * |NNNNNNNNNNNNNNN| Number of next logical sector in chain + * |UUUUUUUUUUUUUUU| Number of bytes used in this sector + * | | + * | (Sector Data) | + * | | + * 4*n+2 --+---------------+ + * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes) + * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes) + * |SSSSSSSSSSSSSSS| Status bits (1 byte) + * +---------------+ + * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte) + * |NNNNNNNNNNNNNNN| Number of next logical sector in chain + * |UUUUUUUUUUUUUUU| Number of bytes used in this sector + * | | + * | (Sector Data) | + * | | + * 4*n+3 --+---------------+ + * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes) + * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes) + * |SSSSSSSSSSSSSSS| Status bits (1 byte) + * +---------------+ + * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte) + * |NNNNNNNNNNNNNNN| Number of next logical sector in chain + * |UUUUUUUUUUUUUUU| Number of bytes used in this sector + * | | + * | (Sector Data) | + * | | + * n+1 4*(n+1) --+---------------+ + * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes) + * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes) + * |SSSSSSSSSSSSSSS| Status bits (1 byte) + * +---------------+ + * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte) + * |NNNNNNNNNNNNNNN| Number of next logical sector in chain + * |UUUUUUUUUUUUUUU| Number of bytes used in this sector + * | | + * | | + * | | + * --+---------------+ + * + * General operation: + * Physical sectors are allocated and assigned a logical sector number + * and a starting sequence number of zero. + * + * SECTOR HEADER: + * The sector header (first 5 bytes) tracks the state of each sector and + * is used by the SMART MTD block driver. At the block level, there is + * no notion of sector chaining, only allocated sectors within erase + * blocks. + * + * FILE SYSTEM (FS) HEADER: + * The file system header (next 5 bytes) tracks file and directory entries + * and chains. + * + * SMART Limitations: + * 1. SMART currently depends on the underlying MTD block driver supporting + * single-byte programming operation. This is due to the method it + * uses for marking a sector as "released", committed, etc. + * 2. Garbage collection can occur when a new sector is allocated or when + * existing sector data is overwritten with new data. Thus, occasionally, + * file writing may take longer than other times. + * 3. The implementation curently does not track bad blocks on the device. + * 4. There is no true wear-leveling implemented yet, though provesion have + * been made to reserve logical sectors to allow it to be added using + * a "sector aging" tracking mechanism. + */ + +/* Values for SMART inode state. + * + * SMART_STATE_FILE - The inode is a valid usuable, file + * INODE_STATE_DELETED - The inode has been deleted. + * Other values - The inode is bad and has an invalid state. + * + * Care is taken so that the VALID to DELETED transition only involves burning + * bits from the erased to non-erased state. + */ + +#define INODE_STATE_FILE (CONFIG_NXFFS_ERASEDSTATE ^ 0x22) +#define INODE_STATE_DELETED (CONFIG_NXFFS_ERASEDSTATE ^ 0xaa) + +/* Directory entry flag definitions */ + +#define SMARTFS_DIRENT_EMPTY 0x8000 /* Set to non-erase state when entry used */ +#define SMARTFS_DIRENT_ACTIVE 0x4000 /* Set to erase state when entry is active */ +#define SMARTFS_DIRENT_TYPE 0x2000 /* Indicates the type of entry (file/dir) */ +#define SMARTFS_DIRENT_DELETING 0x1000 /* Directory entry is being deleted */ +#define SMARTFS_DIRENT_RESERVED 0x0E00 /* Reserved bits */ +#define SMARTFS_DIRENT_MODE 0x01FF /* Mode the file was created with */ + +#define SMARTFS_DIRENT_TYPE_DIR 0x2000 +#define SMARTFS_DIRENT_TYPE_FILE 0x0000 + +/* Number of bytes in the SMART magic sequences */ + +#define SMART_MAGICSIZE 4 + +/* Quasi-standard definitions */ + +#ifndef MIN +# define MIN(a,b) (a < b ? a : b) +#endif + +#ifndef MAX +# define MAX(a,b) (a > b ? a : b) +#endif + +/* Underlying MTD Block driver access functions */ + +#define FS_BOPS(f) (f)->fs_blkdriver->u.i_bops +#define FS_IOCTL(f,c,a) (FS_BOPS(f)->ioctl ? FS_BOPS(f)->ioctl((f)->fs_blkdriver,c,a) : (-ENOSYS)) + +/* The logical sector number of the root directory. */ + +#define SMARTFS_ROOT_DIR_SECTOR 3 + +/* Defines the sector types */ + +#define SMARTFS_SECTOR_TYPE_DIR 1 +#define SMARTFS_SECTOR_TYPE_FILE 2 + +#ifndef CONFIG_SMARTFS_DIRDEPTH +#define CONFIG_SMARTFS_DIRDEPTH 8 +#endif + +#define SMARTFS_ERASEDSTATE_16BIT (uint16_t) ((CONFIG_SMARTFS_ERASEDSTATE << 8) | \ + CONFIG_SMARTFS_ERASEDSTATE) + +#ifndef offsetof +#define offsetof(type, member) ( (size_t) &( ( (type *) 0)->member)) +#endif + +#define SMARTFS_NEXTSECTOR(h) ( *((uint16_t *) h->nextsector)) +#define SMARTFS_USED(h) ( *((uint16_t *) h->used)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This structure defines each packed block on the FLASH media */ + +/* This is an in-memory representation of the SMART inode as extracted from + * FLASH and with additional state information. + */ + +struct smartfs_entry_s +{ + uint16_t firstsector; /* Sector number of the name */ + uint16_t dsector; /* Sector number of the directory entry */ + uint16_t doffset; /* Offset of the directory entry */ + uint16_t dfirst; /* 1st sector number of the directory entry */ + uint16_t flags; /* Flags, including mode */ + FAR char *name; /* inode name */ + uint32_t utc; /* Time stamp */ + uint32_t datlen; /* Length of inode data */ +}; + +/* This is an on-device representation of the SMART inode it esists on + * the FLASH. + */ + +struct smartfs_entry_header_s +{ + uint16_t flags; /* Flags, including permissions: + 15: Empty entry + 14: Active entry + 12-0: Permissions bits */ + int16_t firstsector; /* Sector number of the name */ + uint32_t utc; /* Time stamp */ + char name[0]; /* inode name */ +}; + +/* This structure describes the smartfs header at the start of each + * sector. It manages the sector chain and used bytes in the sector. + */ + +struct smartfs_chain_header_s +{ + uint8_t type; /* Type of sector entry (file or dir) */ + uint8_t nextsector[2];/* Next logical sector in the chain */ + uint8_t used[2]; /* Number of bytes used in this sector */ +}; + +/* This structure describes the state of one open file. This structure + * is protected by the volume semaphore. + */ + +struct smartfs_ofile_s +{ + struct smartfs_ofile_s *fnext; /* Supports a singly linked list */ + int16_t crefs; /* Reference count */ + mode_t oflags; /* Open mode */ + struct smartfs_entry_s entry; /* Describes the SMARTFS inode entry */ + size_t filepos; /* Current file position */ + uint16_t currsector; /* Current sector of filepos */ + uint16_t curroffset; /* Current offset in sector */ + uint16_t byteswritten;/* Count of bytes written to currsector + * that have not been recorded in the + * sector yet. We delay updating the + * used field until the file is closed, + * a seek, or more data is written that + * causes the sector to change. */ +}; + +/* This structure represents the overall mountpoint state. An instance of this + * structure is retained as inode private data on each mountpoint that is + * mounted with a smartfs filesystem. + */ + +struct smartfs_mountpt_s +{ +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + struct smartfs_mountpt_s *fs_next; /* Pointer to next SMART filesystem */ +#endif + FAR struct inode *fs_blkdriver; /* Our underlying block device */ + sem_t *fs_sem; /* Used to assure thread-safe access */ + FAR struct smartfs_ofile_s *fs_head; /* A singly-linked list of open files */ + bool fs_mounted; /* true: The file system is ready */ + struct smart_format_s fs_llformat; /* Low level device format info */ + char *fs_rwbuffer; /* Read/Write working buffer */ + char *fs_workbuffer;/* Working buffer */ + uint8_t fs_rootsector;/* Root directory sector num */ +}; + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/**************************************************************************** + * Internal function prototypes + ****************************************************************************/ + +/* Semaphore access for internal use */ + +void smartfs_semtake(struct smartfs_mountpt_s *fs); +void smartfs_semgive(struct smartfs_mountpt_s *fs); + +/* Forward references for utility functions */ + +struct smartfs_mountpt_s; + +/* Utility functions */ + +int smartfs_mount(struct smartfs_mountpt_s *fs, bool writeable); + +int smartfs_unmount(struct smartfs_mountpt_s *fs); + +int smartfs_finddirentry(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *direntry, const char *relpath, + uint16_t *parentdirsector, const char **filename); + +int smartfs_createentry(struct smartfs_mountpt_s *fs, + uint16_t parentdirsector, const char* filename, + uint16_t type, + mode_t mode, struct smartfs_entry_s *direntry, + uint16_t sectorno); + +int smartfs_deleteentry(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *entry); + +int smartfs_countdirentries(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *entry); + +int smartfs_truncatefile(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *entry); + +struct file; /* Forward references */ +struct inode; +struct fs_dirent_s; +struct statfs; +struct stat; + +#endif /* __FS_SMARTFS_SMARTFS_H */ diff --git a/nuttx/fs/smartfs/smartfs_mksmartfs.c b/nuttx/fs/smartfs/smartfs_mksmartfs.c new file mode 100644 index 000000000..9c2dacc7e --- /dev/null +++ b/nuttx/fs/smartfs/smartfs_mksmartfs.c @@ -0,0 +1,191 @@ +/**************************************************************************** + * fs/smartfs/smartfs_mksmartfs.c + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "smartfs.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mksmartfs + * + * Description: + * Make a SMART Flash file system image on the specified block device + * + * Inputs: + * pathname - the full path to a registered block driver + * nrootdirs - Number of root directory entries to create. + * + * Return: + * Zero (OK) on success; -1 (ERROR) on failure with errno set appropriately: + * + * EINVAL - NULL block driver string, bad number of FATS in 'fmt', bad FAT + * size in 'fmt', bad cluster size in 'fmt' + * ENOENT - 'pathname' does not refer to anything in the filesystem. + * ENOTBLK - 'pathname' does not refer to a block driver + * EACCES - block driver does not support wrie or geometry methods + * + * Assumptions: + * - The caller must assure that the block driver is not mounted and not in + * use when this function is called. The result of formatting a mounted + * device is indeterminate (but likely not good). + * + ****************************************************************************/ + +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS +int mksmartfs(FAR const char *pathname, uint8_t nrootdirs) +#else +int mksmartfs(FAR const char *pathname) +#endif +{ + struct inode* inode; + struct smart_format_s fmt; + int ret; + int x; + uint8_t type; + struct smart_read_write_s request; + + /* Find the inode of the block driver indentified by 'source' */ + + ret = open_blockdriver(pathname, 0, &inode); + if (ret < 0) + { + fdbg("Failed to open %s\n", pathname); + goto errout; + } + + /* Make sure that the inode supports the write and geometry methods at a minimum */ + + if (!inode->u.i_bops->write || !inode->u.i_bops->geometry) + { + fdbg("%s does not support write or geometry methods\n", pathname); + ret = -EACCES; + goto errout_with_driver; + } + + /* Validate the block device is a SMART device */ + + /* Perform a low-level SMART format */ + +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + ret = inode->u.i_bops->ioctl(inode, BIOC_LLFORMAT, nrootdirs); +#else + ret = inode->u.i_bops->ioctl(inode, BIOC_LLFORMAT, 0); +#endif + if (ret != OK) + { + fdbg("Error creating low-level format: %d\n", ret); + goto errout_with_driver; + } + + /* Get the format information so we know how big the sectors are */ + + ret = inode->u.i_bops->ioctl(inode, BIOC_GETFORMAT, (unsigned long) &fmt); + + /* Now Write the filesystem to media. Loop for each root dir entry and + * allocate the reserved Root Dir Enty, then write a blank root dir for it. + */ + + type = SMARTFS_SECTOR_TYPE_DIR; + request.offset = 0; + request.count = 1; + request.buffer = &type; + x = 0; +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + for (; x < nrootdirs; x++) +#endif + { + ret = inode->u.i_bops->ioctl(inode, BIOC_ALLOCSECT, SMARTFS_ROOT_DIR_SECTOR + x); + if (ret != SMARTFS_ROOT_DIR_SECTOR + x) + { + ret = -EIO; + goto errout_with_driver; + } + + /* Mark this block as a directory entry */ + + request.logsector = SMARTFS_ROOT_DIR_SECTOR + x; + + /* Issue a write to the sector, single byte */ + + ret = inode->u.i_bops->ioctl(inode, BIOC_WRITESECT, (unsigned long) &request); + if (ret != 0) + { + ret = -EIO; + goto errout_with_driver; + } + } + +errout_with_driver: + /* Close the driver */ + + (void)close_blockdriver(inode); + +errout: + /* Release all allocated memory */ + + /* Return any reported errors */ + + if (ret < 0) + { + errno = -ret; + return ERROR; + } + + return OK; +} diff --git a/nuttx/fs/smartfs/smartfs_smart.c b/nuttx/fs/smartfs/smartfs_smart.c new file mode 100644 index 000000000..552c0e96e --- /dev/null +++ b/nuttx/fs/smartfs/smartfs_smart.c @@ -0,0 +1,1981 @@ +/**************************************************************************** + * fs/smartfs/smartfs_smart.c + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit + * + * 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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "smartfs.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int smartfs_open(FAR struct file *filep, const char *relpath, + int oflags, mode_t mode); +static int smartfs_close(FAR struct file *filep); +static ssize_t smartfs_read(FAR struct file *filep, char *buffer, size_t buflen); +static ssize_t smartfs_write(FAR struct file *filep, const char *buffer, + size_t buflen); +static off_t smartfs_seek(FAR struct file *filep, off_t offset, int whence); +static int smartfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg); + +static int smartfs_sync(FAR struct file *filep); +static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp); + +static int smartfs_opendir(struct inode *mountpt, const char *relpath, + struct fs_dirent_s *dir); +static int smartfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir); +static int smartfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir); + +static int smartfs_bind(FAR struct inode *blkdriver, const void *data, + void **handle); +static int smartfs_unbind(void *handle, FAR struct inode **blkdriver); +static int smartfs_statfs(struct inode *mountpt, struct statfs *buf); + +static int smartfs_unlink(struct inode *mountpt, const char *relpath); +static int smartfs_mkdir(struct inode *mountpt, const char *relpath, + mode_t mode); +static int smartfs_rmdir(struct inode *mountpt, const char *relpath); +static int smartfs_rename(struct inode *mountpt, const char *oldrelpath, + const char *newrelpath); +static int smartfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf); + +static off_t smartfs_seek_internal(struct smartfs_mountpt_s *fs, + struct smartfs_ofile_s *sf, + off_t offset, int whence); + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +static uint8_t g_seminitialized = FALSE; +static sem_t g_sem; + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/* See fs_mount.c -- this structure is explicitly externed there. + * We use the old-fashioned kind of initializers so that this will compile + * with any compiler. + */ + +const struct mountpt_operations smartfs_operations = +{ + smartfs_open, /* open */ + smartfs_close, /* close */ + smartfs_read, /* read */ + smartfs_write, /* write */ + smartfs_seek, /* seek */ + smartfs_ioctl, /* ioctl */ + + smartfs_sync, /* sync */ + smartfs_dup, /* dup */ + + smartfs_opendir, /* opendir */ + NULL, /* closedir */ + smartfs_readdir, /* readdir */ + smartfs_rewinddir, /* rewinddir */ + + smartfs_bind, /* bind */ + smartfs_unbind, /* unbind */ + smartfs_statfs, /* statfs */ + + smartfs_unlink, /* unlinke */ + smartfs_mkdir, /* mkdir */ + smartfs_rmdir, /* rmdir */ + smartfs_rename, /* rename */ + smartfs_stat /* stat */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: smartfs_open + ****************************************************************************/ + +static int smartfs_open(FAR struct file *filep, const char *relpath, + int oflags, mode_t mode) +{ + struct inode *inode; + struct smartfs_mountpt_s *fs; + int ret; + uint16_t parentdirsector; + const char *filename; + struct smartfs_ofile_s *sf; + + /* Sanity checks */ + + DEBUGASSERT((filep->f_priv == NULL) && (filep->f_inode != NULL)); + + /* Get the mountpoint inode reference from the file structure and the + * mountpoint private data from the inode structure + */ + + inode = filep->f_inode; + fs = inode->i_private; + + DEBUGASSERT(fs != NULL); + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Locate the directory entry for this path */ + + sf = (struct smartfs_ofile_s *) kmalloc(sizeof *sf); + if (sf == NULL) + { + ret = ENOMEM; + goto errout_with_semaphore; + } + + sf->entry.name = NULL; + ret = smartfs_finddirentry(fs, &sf->entry, relpath, &parentdirsector, + &filename); + + /* Three possibililities: (1) a node exists for the relpath and + * dirinfo describes the directory entry of the entity, (2) the + * node does not exist, or (3) some error occurred. + */ + + if (ret == OK) + { + /* The name exists -- but is is a file or a directory ? */ + + if (sf->entry.flags & SMARTFS_DIRENT_TYPE_DIR) + { + /* Can't open a dir as a file! */ + + ret = EISDIR; + goto errout_with_buffer; + } + + /* It would be an error if we are asked to create it exclusively */ + + if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) + { + /* Already exists -- can't create it exclusively */ + + ret = -EEXIST; + goto errout_with_buffer; + } + + /* TODO: Test open mode based on the file mode */ + + /* The file exists. Check if we are opening it for O_CREAT or + * O_TRUNC mode and delete the sector chain if we are. */ + + if ((oflags & (O_CREAT | O_TRUNC)) != 0) + { + /* Don't truncate if open for APPEND */ + + if (!(oflags & O_APPEND)) + { + /* Truncate the file as part of the open */ + + ret = smartfs_truncatefile(fs, &sf->entry); + if (ret < 0) + { + goto errout_with_buffer; + } + } + } + } + else if (ret == -ENOENT) + { + /* The file does not exist. Were we asked to create it? */ + + if ((oflags & O_CREAT) == 0) + { + /* No.. then we fail with -ENOENT */ + + ret = -ENOENT; + goto errout_with_buffer; + } + + /* Yes... test if the parent directory is valid */ + + if (parentdirsector != 0xFFFF) + { + /* We can create in the given parent directory */ + + ret = smartfs_createentry(fs, parentdirsector, filename, + SMARTFS_DIRENT_TYPE_FILE, mode, + &sf->entry, 0xFFFF); + if (ret != OK) + { + goto errout_with_buffer; + } + } + else + { + /* Trying to create in a directory that doesn't exist */ + + ret = -ENOENT; + goto errout_with_buffer; + } + } + else + { + goto errout_with_buffer; + } + + /* Now perform the "open" on the file in direntry */ + + sf->oflags = oflags; + sf->crefs = 1; + sf->filepos = 0; + sf->curroffset = sizeof(struct smartfs_chain_header_s); + sf->currsector = sf->entry.firstsector; + sf->byteswritten = 0; + + /* Test if we opened for APPEND mode. If we did, then seek to the + * end of the file. + */ + + if (oflags & O_APPEND) + { + /* Perform the seek */ + + smartfs_seek_internal(fs, sf, 0, SEEK_END); + } + + /* Attach the private date to the struct file instance */ + + filep->f_priv = sf; + + /* Then insert the new instance into the mountpoint structure. + * It needs to be there (1) to handle error conditions that effect + * all files, and (2) to inform the umount logic that we are busy + * (but a simple reference count could have done that). + */ + + sf->fnext = fs->fs_head; + fs->fs_head = sf; + + ret = OK; + goto errout_with_semaphore; + +errout_with_buffer: + if (sf->entry.name != NULL) + { + /* Free the space for the name too */ + + kfree(sf->entry.name); + sf->entry.name = NULL; + } + + kfree(sf); + +errout_with_semaphore: + smartfs_semgive(fs); + if (ret == -EINVAL) + { + ret = -EIO; + } + + return ret; +} + +/**************************************************************************** + * Name: smartfs_close + ****************************************************************************/ + +static int smartfs_close(FAR struct file *filep) +{ + struct inode *inode; + struct smartfs_mountpt_s *fs; + struct smartfs_ofile_s *sf; + struct smartfs_ofile_s *nextfile; + struct smartfs_ofile_s *prevfile; + + /* Sanity checks */ + + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + inode = filep->f_inode; + fs = inode->i_private; + sf = filep->f_priv; + + /* Sync the file */ + + smartfs_sync(filep); + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Check if we are the last one with a reference to the file and + * only close if we are. */ + + if (sf->crefs > 1) + { + /* The file is opened more than once. Just decrement the + * reference count and return. */ + + sf->crefs--; + goto okout; + } + + /* Remove ourselves from the linked list */ + + nextfile = fs->fs_head; + while ((nextfile != sf) && (nextfile != NULL)) + { + /* Save the previous file pointer too */ + + prevfile = nextfile; + nextfile = nextfile->fnext; + } + + if (nextfile != NULL) + { + /* Test if we were the first entry */ + + if (nextfile == fs->fs_head) + { + /* Assign a new head */ + + fs->fs_head = nextfile->fnext; + } + else + { + /* Take ourselves out of the list */ + + prevfile->fnext = nextfile->fnext; + } + } + + /* Now free the pointer */ + + filep->f_priv = NULL;; + if (sf->entry.name != NULL) + { + /* Free the space for the name too */ + + kfree(sf->entry.name); + sf->entry.name = NULL; + } + kfree(sf); + +okout: + smartfs_semgive(fs); + return OK; +} + +/**************************************************************************** + * Name: smartfs_read + ****************************************************************************/ + +static ssize_t smartfs_read(FAR struct file *filep, char *buffer, size_t buflen) +{ + struct inode *inode; + struct smartfs_mountpt_s *fs; + struct smartfs_ofile_s *sf; + struct smart_read_write_s readwrite; + struct smartfs_chain_header_s *header; + int ret = OK; + uint32_t bytesread; + uint16_t bytestoread; + uint16_t bytesinsector; + + /* Sanity checks */ + + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + sf = filep->f_priv; + inode = filep->f_inode; + fs = inode->i_private; + + DEBUGASSERT(fs != NULL); + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Loop until all byte read or error */ + + bytesread = 0; + while (bytesread != buflen) + { + /* Test if we are at the end of data */ + + if (sf->currsector == SMARTFS_ERASEDSTATE_16BIT) + { + /* Break and return the number of bytes we read (may be zero) */ + + break; + } + + /* Read the curent sector into our buffer */ + + readwrite.logsector = sf->currsector; + readwrite.offset = 0; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + readwrite.count = fs->fs_llformat.availbytes; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d reading sector %d data\n", ret, sf->currsector); + goto errout_with_semaphore; + } + + /* Point header to the read data to get used byte count */ + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + + /* Get number of used bytes in this sector */ + + bytesinsector = *((uint16_t *) header->used); + if (bytesinsector == SMARTFS_ERASEDSTATE_16BIT) + { + /* No bytes to read from this sector */ + + bytesinsector = 0; + } + + /* Calculate the number of bytes to read into the buffer */ + + bytestoread = bytesinsector - (sf->curroffset - + sizeof(struct smartfs_chain_header_s)); + if (bytestoread + bytesread > buflen) + { + /* Truncate bytesto read based on buffer len */ + + bytestoread = buflen - bytesread; + } + + /* Copy data to the read buffer */ + + if (bytestoread > 0) + { + /* Do incremental copy from this sector */ + + memcpy(&buffer[bytesread], &fs->fs_rwbuffer[sf->curroffset], bytestoread); + bytesread += bytestoread; + sf->filepos += bytestoread; + sf->curroffset += bytestoread; + } + + /* Test if we are at the end of the data in this sector */ + + if ((bytestoread == 0) || (sf->curroffset == fs->fs_llformat.availbytes)) + { + /* Set the next sector as the current sector */ + + sf->currsector = SMARTFS_NEXTSECTOR(header); + sf->curroffset = sizeof(struct smartfs_chain_header_s); + + /* Test if at end of data */ + + if (sf->currsector == SMARTFS_ERASEDSTATE_16BIT) + { + /* No more data! Return what we have */ + + break; + } + } + } + + /* Return the number of bytes we read */ + + ret = bytesread; + +errout_with_semaphore: + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_sync_internal + * + * Description: Synchronize the file state on disk to match internal, in- + * memory state. + * + ****************************************************************************/ + +static int smartfs_sync_internal(struct smartfs_mountpt_s *fs, + struct smartfs_ofile_s *sf) +{ + struct smart_read_write_s readwrite; + struct smartfs_chain_header_s *header; + int ret; + + /* Test if we have written bytes to the current sector that + * need to be recorded in the chain header's used bytes field. */ + + if (sf->byteswritten > 0) + { + fdbg("Syncing sector %d\n", sf->currsector); + + /* Read the existing sector used bytes value */ + + readwrite.logsector = sf->currsector; + readwrite.offset = 0; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + readwrite.count = sizeof(struct smartfs_chain_header_s); + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d reading sector %d data\n", ret, sf->currsector); + goto errout; + } + + /* Add new byteswritten to existing value */ + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + if (*((uint16_t *) header->used) == SMARTFS_ERASEDSTATE_16BIT) + { + *((uint16_t *) header->used) = sf->byteswritten; + } + else + { + *((uint16_t *) header->used) += sf->byteswritten; + } + + readwrite.offset = offsetof(struct smartfs_chain_header_s, used); + readwrite.count = sizeof(uint16_t); + readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[readwrite.offset]; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d writing used bytes for sector %d\n", ret, sf->currsector); + goto errout; + } + + sf->byteswritten = 0; + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: smartfs_write + ****************************************************************************/ + +static ssize_t smartfs_write(FAR struct file *filep, const char *buffer, + size_t buflen) +{ + struct inode *inode; + struct smartfs_mountpt_s *fs; + struct smartfs_ofile_s *sf; + struct smart_read_write_s readwrite; + struct smartfs_chain_header_s *header; + size_t byteswritten; + int ret; + + /* Sanity checks. I have seen the following assertion misfire if + * CONFIG_DEBUG_MM is enabled while re-directing output to a + * file. In this case, the debug output can get generated while + * the file is being opened, FAT data structures are being allocated, + * and things are generally in a perverse state. + */ + +#ifdef CONFIG_DEBUG_MM + if (filep->f_priv == NULL || filep->f_inode == NULL) + { + return -ENXIO; + } +#else + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); +#endif + + /* Recover our private data from the struct file instance */ + + sf = filep->f_priv; + inode = filep->f_inode; + fs = inode->i_private; + + DEBUGASSERT(fs != NULL); + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Test the permissions. Only allow write if the file was opened with + * write flags. + */ + + if ((sf->oflags & O_WROK) == 0) + { + ret = -EACCES; + goto errout_with_semaphore; + } + + /* First test if we are overwriting an existing location or writing to + * a new one. */ + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + byteswritten = 0; + while ((sf->filepos < sf->entry.datlen) && (buflen > 0)) + { + /* Overwriting data caused by a seek, etc. In this case, we need + * to check if the write causes the file length to be extended + * or not and update it accordingly. We will write data up to + * the current end-of-file and then break, allowing the next while + * loop below to write the additional data to the end of the file. + */ + + readwrite.offset = sf->curroffset; + readwrite.logsector = sf->currsector; + readwrite.buffer = (uint8_t *) &buffer[byteswritten]; + readwrite.count = fs->fs_llformat.availbytes - sf->curroffset; + + /* Limit the write based on available data to write */ + + if (readwrite.count > buflen) + readwrite.count = buflen; + + /* Limit the write based on current file length */ + + if (readwrite.count > sf->entry.datlen - sf->filepos) + { + /* Limit the write length so we write to the current EOF. */ + + readwrite.count = sf->entry.datlen - sf->filepos; + } + + /* Now perform the write. */ + + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d writing sector %d data\n", ret, sf->currsector); + goto errout_with_semaphore; + } + + /* Update our control variables */ + + sf->filepos += readwrite.count; + sf->curroffset += readwrite.count; + buflen -= readwrite.count; + byteswritten += readwrite.count; + + /* Test if we wrote to the end of the current sector */ + + if (sf->curroffset == fs->fs_llformat.availbytes) + { + /* Wrote to the end of the sector. Update to point to the + * next sector for additional writes. First read the sector + * header to get the sector chain info. + */ + + readwrite.offset = 0; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + readwrite.count = sizeof(struct smartfs_chain_header_s); + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d reading sector %d header\n", ret, sf->currsector); + goto errout_with_semaphore; + } + + /* Now get the chained sector info and reset the offset */ + + sf->curroffset = sizeof(struct smartfs_chain_header_s); + sf->currsector = SMARTFS_NEXTSECTOR(header); + } + } + + /* Now append data to end of the file. */ + + while (buflen > 0) + { + /* We will fill up the current sector. Write data to + * the current sector first. + */ + + readwrite.offset = sf->curroffset; + readwrite.logsector = sf->currsector; + readwrite.buffer = (uint8_t *) &buffer[byteswritten]; + readwrite.count = fs->fs_llformat.availbytes - sf->curroffset; + if (readwrite.count > buflen) + { + /* Limit the write base on remaining bytes to write */ + + readwrite.count = buflen; + } + + /* Perform the write */ + + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d writing sector %d data\n", ret, sf->currsector); + goto errout_with_semaphore; + } + + /* Update our control variables */ + + sf->entry.datlen += readwrite.count; + sf->byteswritten += readwrite.count; + sf->filepos += readwrite.count; + sf->curroffset += readwrite.count; + buflen -= readwrite.count; + byteswritten += readwrite.count; + + /* Test if we wrote a full sector of data */ + + if (sf->curroffset == fs->fs_llformat.availbytes) + { + /* Sync the file to write this sector out */ + + smartfs_sync_internal(fs, sf); + + /* Allocate a new sector if needed */ + + if (buflen > 0) + { + /* Allocate a new sector */ + + ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF); + if (ret < 0) + { + fdbg("Error %d allocating new sector\n", ret); + goto errout_with_semaphore; + } + + /* Copy the new sector to the old one and chain it */ + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + *((uint16_t *) header->nextsector) = (uint16_t) ret; + readwrite.offset = offsetof(struct smartfs_chain_header_s, + nextsector); + readwrite.buffer = (uint8_t *) header->nextsector; + readwrite.count = sizeof(uint16_t); + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d writing next sector\n", ret); + goto errout_with_semaphore; + } + + /* Record the new sector in our tracking variables and + * reset the offset to "zero". + */ + + sf->currsector = SMARTFS_NEXTSECTOR(header); + sf->curroffset = sizeof(struct smartfs_chain_header_s); + } + } + } + + ret = byteswritten; + +errout_with_semaphore: + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_seek_internal + * + * Description: Performs the logic of the seek function. This is an internal + * function because it does not provide semaphore protection and + * therefore must be called from one of the other public + * interface routines (open, seek, etc.). + * + ****************************************************************************/ + +static off_t smartfs_seek_internal(struct smartfs_mountpt_s *fs, + struct smartfs_ofile_s *sf, + off_t offset, int whence) +{ + struct smart_read_write_s readwrite; + struct smartfs_chain_header_s *header; + int ret; + off_t newpos; + off_t sectorstartpos; + + /* Test if this is a seek to get the current file pos */ + + if ((whence == SEEK_CUR) && (offset == 0)) + { + return sf->filepos; + } + + /* Test if we need to sync the file */ + + if (sf->byteswritten > 0) + { + /* Perform a sync */ + + smartfs_sync_internal(fs, sf); + } + + /* Calculate the file position to seek to based on current position */ + + switch (whence) + { + case SEEK_SET: + newpos = offset; + break; + + case SEEK_CUR: + newpos = sf->filepos + offset; + break; + + case SEEK_END: + newpos = sf->entry.datlen - offset; + break; + } + + /* Ensure newpos is in range */ + + if (newpos < 0) + { + newpos = 0; + } + + if (newpos > sf->entry.datlen) + { + newpos = sf->entry.datlen; + } + + /* Now perform the seek. Test if we are seeking within the current + * sector and can skip the search to save time. + */ + + sectorstartpos = sf->filepos - (sf->curroffset - sizeof(struct + smartfs_chain_header_s)); + if (newpos >= sectorstartpos && newpos < sectorstartpos + + fs->fs_llformat.availbytes - sizeof(struct smartfs_chain_header_s)) + { + /* Seeking within the current sector. Just update the offset */ + + sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos-sectorstartpos; + sf->filepos = newpos; + + return newpos; + } + + /* Nope, we have to search for the sector and offset. If the new pos is greater + * than the current pos, then we can start from the beginning of the current + * sector, otherwise we have to start from the beginning of the file. + */ + + if (newpos > sf->filepos) + { + sf->filepos = sectorstartpos; + } + else + { + sf->currsector = sf->entry.firstsector; + sf->filepos = 0; + } + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + while ((sf->currsector != SMARTFS_ERASEDSTATE_16BIT) && + (sf->filepos + fs->fs_llformat.availbytes - + sizeof(struct smartfs_chain_header_s) < newpos)) + { + /* Read the sector's header */ + + readwrite.logsector = sf->currsector; + readwrite.offset = 0; + readwrite.count = sizeof(struct smartfs_chain_header_s); + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d reading sector %d header\n", ret, sf->currsector); + goto errout; + } + + /* Point to next sector and update filepos */ + + sf->currsector = SMARTFS_NEXTSECTOR(header); + sf->filepos += SMARTFS_USED(header); + } + + /* Now calculate the offset */ + + sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos - sf->filepos; + sf->filepos = newpos; + return newpos; + +errout: + return ret; +} + +/**************************************************************************** + * Name: smartfs_seek + ****************************************************************************/ + +static off_t smartfs_seek(FAR struct file *filep, off_t offset, int whence) +{ + struct inode *inode; + struct smartfs_mountpt_s *fs; + struct smartfs_ofile_s *sf; + int ret; + + /* Sanity checks */ + + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + sf = filep->f_priv; + inode = filep->f_inode; + fs = inode->i_private; + + DEBUGASSERT(fs != NULL); + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Call our internal routine to perform the seek */ + + ret = smartfs_seek_internal(fs, sf, offset, whence); + + if (ret >= 0) + { + filep->f_pos = ret; + } + + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_ioctl + ****************************************************************************/ + +static int smartfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + /* We don't use any ioctls */ + + return -ENOSYS; +} + +/**************************************************************************** + * Name: smartfs_sync + * + * Description: Synchronize the file state on disk to match internal, in- + * memory state. + * + ****************************************************************************/ + +static int smartfs_sync(FAR struct file *filep) +{ + struct inode *inode; + struct smartfs_mountpt_s *fs; + struct smartfs_ofile_s *sf; + int ret = OK; + + /* Sanity checks */ + + DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + sf = filep->f_priv; + inode = filep->f_inode; + fs = inode->i_private; + + DEBUGASSERT(fs != NULL); + + /* Take the semaphore */ + + smartfs_semtake(fs); + + ret = smartfs_sync_internal(fs, sf); + + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smart_dup + * + * Description: Duplicate open file data in the new file structure. + * + ****************************************************************************/ + +static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp) +{ + FAR struct smart_mountpt_s *fs; + struct smartfs_ofile_s *sf; + + fvdbg("Dup %p->%p\n", oldp, newp); + + /* Sanity checks */ + + DEBUGASSERT(oldp->f_priv != NULL && + newp->f_priv == NULL && + newp->f_inode != NULL); + + /* Recover our private data from the struct file instance */ + + fs = (struct smart_mountpt_s *)oldp->f_inode->i_private; + sf = oldp->f_priv; + + DEBUGASSERT(fs != NULL); + DEBUGASSERT(sf != NULL); + + /* Just increment the reference count on the ofile */ + + sf->crefs++; + newp->f_priv = (FAR void *)sf; + + return OK; +} + +/**************************************************************************** + * Name: smartfs_opendir + * + * Description: Open a directory for read access + * + ****************************************************************************/ + +static int smartfs_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir) +{ + struct smartfs_mountpt_s *fs; + int ret; + struct smartfs_entry_s entry; + uint16_t parentdirsector; + const char *filename; + + /* Sanity checks */ + + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); + + /* Recover our private data from the inode instance */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Search for the path on the volume */ + + entry.name = NULL; + ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, + &filename); + if (ret < 0) + { + goto errout_with_semaphore; + } + + /* Populate our private data in the fs_dirent_s struct */ + + dir->u.smartfs.fs_firstsector = entry.firstsector; + dir->u.smartfs.fs_currsector = entry.firstsector; + dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); + + ret = OK; + +errout_with_semaphore: + /* If space for the entry name was allocated, then free it */ + + if (entry.name != NULL) + { + kfree(entry.name); + entry.name = NULL; + } + + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_readdir + * + * Description: Read the next directory entry + * + ****************************************************************************/ + +static int smartfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) +{ + struct smartfs_mountpt_s *fs; + int ret; + uint16_t entrysize; + uint16_t namelen; + struct smartfs_chain_header_s *header; + struct smart_read_write_s readwrite; + struct smartfs_entry_header_s *entry; + + /* Sanity checks */ + + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); + + /* Recover our private data from the inode instance */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Read sectors and search entries until one found or no more */ + + entrysize = sizeof(struct smartfs_entry_header_s) + + fs->fs_llformat.namesize; + while (dir->u.smartfs.fs_currsector != SMARTFS_ERASEDSTATE_16BIT) + { + /* Read the logical sector */ + + readwrite.logsector = dir->u.smartfs.fs_currsector; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.buffer = (uint8_t *)fs->fs_rwbuffer; + readwrite.offset = 0; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + goto errout_with_semaphore; + } + + /* Now search for entries, starting at curroffset */ + + while (dir->u.smartfs.fs_curroffset < ret) + { + /* Point to next entry */ + + entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[ + dir->u.smartfs.fs_curroffset]; + + /* Test if this entry is valid and active */ + + if (((entry->flags & SMARTFS_DIRENT_EMPTY) == + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) || + ((entry->flags & SMARTFS_DIRENT_ACTIVE) != + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE))) + { + /* This entry isn't valid, skip it */ + + dir->u.smartfs.fs_curroffset += entrysize; + entry = (struct smartfs_entry_header_s *) + &fs->fs_rwbuffer[dir->u.smartfs.fs_curroffset]; + + continue; + } + + /* Entry found! Report it */ + + if ((entry->flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR) + { + dir->fd_dir.d_type = DTYPE_DIRECTORY; + } + else + { + dir->fd_dir.d_type = DTYPE_FILE; + } + + /* Copy the entry name to dirent */ + + namelen = fs->fs_llformat.namesize; + if (namelen > NAME_MAX) + { + namelen = NAME_MAX; + } + + memset(dir->fd_dir.d_name, 0, namelen); + strncpy(dir->fd_dir.d_name, entry->name, namelen); + + /* Now advance to the next entry */ + + dir->u.smartfs.fs_curroffset += entrysize; + if (dir->u.smartfs.fs_curroffset >= fs->fs_llformat.availbytes) + { + /* We advanced past the end of the sector. Go to next sector */ + + dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + dir->u.smartfs.fs_currsector = SMARTFS_NEXTSECTOR(header); + } + + /* Now exit */ + + ret = OK; + goto errout_with_semaphore; + } + + /* No more entries in this sector. Move on to next sector and + * continue the search. If no more sectors, then we are all + * done and will report ENOENT. + */ + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); + dir->u.smartfs.fs_currsector = SMARTFS_NEXTSECTOR(header); + } + + /* If we arrive here, then there are no more entries */ + + ret = -ENOENT; + +errout_with_semaphore: + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_rewindir + * + * Description: Reset directory read to the first entry + * + ****************************************************************************/ + +static int smartfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) +{ + int ret = OK; + + /* Sanity checks */ + + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL); + + /* Reset the directory to the first entry */ + + dir->u.smartfs.fs_currsector = dir->u.smartfs.fs_firstsector; + dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s); + + return ret; +} + +/**************************************************************************** + * Name: smartfs_bind + * + * Description: This implements a portion of the mount operation. This + * function allocates and initializes the mountpoint private data and + * binds the blockdriver inode to the filesystem private data. The final + * binding of the private data (containing the blockdriver) to the + * mountpoint is performed by mount(). + * + ****************************************************************************/ + +static int smartfs_bind(FAR struct inode *blkdriver, const void *data, + void **handle) +{ + struct smartfs_mountpt_s *fs; + int ret; + + /* Open the block driver */ + + if (!blkdriver || !blkdriver->u.i_bops) + { + return -ENODEV; + } + + if (blkdriver->u.i_bops->open && + blkdriver->u.i_bops->open(blkdriver) != OK) + { + return -ENODEV; + } + + /* Create an instance of the mountpt state structure */ + + fs = (struct smartfs_mountpt_s *)kzalloc(sizeof(struct smartfs_mountpt_s)); + if (!fs) + { + return -ENOMEM; + } + + /* If the global semaphore hasn't been initialized, then + * initialized it now. */ + + fs->fs_sem = &g_sem; + if (!g_seminitialized) + { + sem_init(&g_sem, 0, 0); /* Initialize the semaphore that controls access */ + g_seminitialized = TRUE; + } + else + { + /* Take the semaphore for the mount */ + + smartfs_semtake(fs); + } + + /* Initialize the allocated mountpt state structure. The filesystem is + * responsible for one reference ont the blkdriver inode and does not + * have to addref() here (but does have to release in ubind(). + */ + + fs->fs_blkdriver = blkdriver; /* Save the block driver reference */ + fs->fs_head = NULL; + + /* Now perform the mount. */ + + ret = smartfs_mount(fs, true); + if (ret != 0) + { + kfree(fs); + smartfs_semgive(fs); + return ret; + } + + *handle = (void*)fs; + smartfs_semgive(fs); + return OK; +} + +/**************************************************************************** + * Name: smartfs_unbind + * + * Description: This implements the filesystem portion of the umount + * operation. + * + ****************************************************************************/ + +static int smartfs_unbind(void *handle, FAR struct inode **blkdriver) +{ + struct smartfs_mountpt_s *fs = (struct smartfs_mountpt_s*)handle; + int ret; + + if (!fs) + { + return -EINVAL; + } + + /* Check if there are sill any files opened on the filesystem. */ + + ret = OK; /* Assume success */ + smartfs_semtake(fs); + if (fs->fs_head != NULL) + { + /* We cannot unmount now.. there are open files */ + + smartfs_semgive(fs); + + ret = -EBUSY; + } + else + { + /* Unmount ... close the block driver */ + + ret = smartfs_unmount(fs); + } + + smartfs_semgive(fs); + kfree(fs); + + return ret; +} + +/**************************************************************************** + * Name: smartfs_statfs + * + * Description: Return filesystem statistics + * + ****************************************************************************/ + +static int smartfs_statfs(struct inode *mountpt, struct statfs *buf) +{ + struct smartfs_mountpt_s *fs; + int ret = OK; + + /* Sanity checks */ + + DEBUGASSERT(mountpt && mountpt->i_private); + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + smartfs_semtake(fs); + + /* Implement the logic!! */ + + memset(buf, 0, sizeof(struct statfs)); + buf->f_type = SMARTFS_MAGIC; + + /* Re-request the low-level format info to update free blocks */ + + ret = FS_IOCTL(fs, BIOC_GETFORMAT, (unsigned long) &fs->fs_llformat); + + buf->f_namelen = fs->fs_llformat.namesize; + buf->f_bsize = fs->fs_llformat.sectorsize; + buf->f_blocks = fs->fs_llformat.nsectors; + buf->f_bfree = fs->fs_llformat.nfreesectors; + buf->f_bavail = fs->fs_llformat.nfreesectors; + buf->f_files = 0; + buf->f_ffree = fs->fs_llformat.nfreesectors; + + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_unlink + * + * Description: Remove a file + * + ****************************************************************************/ + +static int smartfs_unlink(struct inode *mountpt, const char *relpath) +{ + struct smartfs_mountpt_s *fs; + int ret = OK; + struct smartfs_entry_s entry; + const char *filename; + uint16_t parentdirsector; + + /* Sanity checks */ + + DEBUGASSERT(mountpt && mountpt->i_private); + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + smartfs_semtake(fs); + + /* Locate the directory entry for this path */ + + entry.name = NULL; + ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, + &filename); + + if (ret == OK) + { + /* The name exists -- validate it is a file, not a dir */ + + if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR) + { + ret = -EISDIR; + goto errout_with_semaphore; + } + + /* TODO: Need to check permissions? */ + + /* Okay, we are clear to delete the file. Use the deleteentry routine. */ + + smartfs_deleteentry(fs, &entry); + + } + else + { + /* Just report the error */ + + goto errout_with_semaphore; + } + + ret = OK; + +errout_with_semaphore: + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_mkdir + * + * Description: Create a directory + * + ****************************************************************************/ + +static int smartfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode) +{ + struct smartfs_mountpt_s *fs; + int ret; + struct smartfs_entry_s entry; + uint16_t parentdirsector; + const char *filename; + + /* Sanity checks */ + + DEBUGASSERT(mountpt && mountpt->i_private); + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + smartfs_semtake(fs); + + /* Locate the directory entry for this path */ + + entry.name = NULL; + ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, + &filename); + + /* Three possibililities: (1) a node exists for the relpath and + * dirinfo describes the directory entry of the entity, (2) the + * node does not exist, or (3) some error occurred. + */ + + if (ret == OK) + { + /* The name exists -- can't create */ + + ret = -EEXIST; + goto errout_with_semaphore; + } + else if (ret == -ENOENT) + { + /* It doesn't exist ... we can create it, but only if we have + * the right permissions and if the parentdirsector is valid. */ + + if (parentdirsector == 0xFFFF) + { + /* Invalid entry in the path (non-existant dir segment) */ + + goto errout_with_semaphore; + } + + /* Check mode */ + + /* Create the directory */ + + ret = smartfs_createentry(fs, parentdirsector, filename, + SMARTFS_DIRENT_TYPE_DIR, mode, &entry, 0xFFFF); + if (ret != OK) + { + goto errout_with_semaphore; + } + + ret = OK; + } + +errout_with_semaphore: + if (entry.name != NULL) + { + /* Free the filename space allocation */ + + kfree(entry.name); + entry.name = NULL; + } + + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_rmdir + * + * Description: Remove a directory + * + ****************************************************************************/ + +int smartfs_rmdir(struct inode *mountpt, const char *relpath) +{ + struct smartfs_mountpt_s *fs; + int ret = OK; + struct smartfs_entry_s entry; + const char *filename; + uint16_t parentdirsector; + + /* Sanity checks */ + + DEBUGASSERT(mountpt && mountpt->i_private); + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + /* Take the semaphore */ + + smartfs_semtake(fs); + + /* Locate the directory entry for this path */ + + entry.name = NULL; + ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, + &filename); + + if (ret == OK) + { + /* The name exists -- validate it is a dir, not a file */ + + if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE) + { + ret = -ENOTDIR; + goto errout_with_semaphore; + } + + /* TODO: Need to check permissions? */ + + /* Check if the directory is emtpy */ + + ret = smartfs_countdirentries(fs, &entry); + if (ret < 0) + { + goto errout_with_semaphore; + } + + /* Only continue if there are zero entries in the directory */ + + if (ret != 0) + { + ret = -ENOTEMPTY; + goto errout_with_semaphore; + } + + /* Okay, we are clear to delete the directory. Use the deleteentry routine. */ + + ret = smartfs_deleteentry(fs, &entry); + if (ret < 0) + { + goto errout_with_semaphore; + } + } + else + { + /* Just report the error */ + + goto errout_with_semaphore; + } + + ret = OK; + +errout_with_semaphore: + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_rename + * + * Description: Rename a file or directory + * + ****************************************************************************/ + +int smartfs_rename(struct inode *mountpt, const char *oldrelpath, + const char *newrelpath) +{ + struct smartfs_mountpt_s *fs; + int ret; + struct smartfs_entry_s oldentry; + uint16_t oldparentdirsector; + const char *oldfilename; + struct smartfs_entry_s newentry; + uint16_t newparentdirsector; + const char *newfilename; + mode_t mode; + uint16_t type; + uint16_t sector; + uint16_t offset; + uint16_t entrysize; + struct smartfs_entry_header_s *direntry; + struct smart_read_write_s readwrite; + + /* Sanity checks */ + + DEBUGASSERT(mountpt && mountpt->i_private); + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + smartfs_semtake(fs); + + /* Search for old entry to validate it exists */ + + oldentry.name = NULL; + newentry.name = NULL; + ret = smartfs_finddirentry(fs, &oldentry, oldrelpath, &oldparentdirsector, + &oldfilename); + if (ret < 0) + { + goto errout_with_semaphore; + } + + /* Search for the new entry and validate it DOESN'T exist, unless we + * are copying to a directory and keeping the same filename, such as: + * + * mv /mntpoint/somefile.txt /mntpoint/somedir + * + * in which case, we need to validate the /mntpoint/somedir/somefile.txt + * doens't exsit. + */ + + ret = smartfs_finddirentry(fs, &newentry, newrelpath, &newparentdirsector, + &newfilename); + if (ret == OK) + { + /* Test if it's a file. If it is, then it's an error */ + + if ((newentry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE) + { + ret = -EEXIST; + goto errout_with_semaphore; + } + + /* Nope, it's a directory. Now search the directory for oldfilename */ + + sector = newentry.firstsector; + while (sector != SMARTFS_ERASEDSTATE_16BIT) + { + /* Read the next sector of diretory entries */ + + readwrite.logsector = sector; + readwrite.offset = 0; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d reading sector %d data\n", ret, oldentry.dsector); + goto errout_with_semaphore; + } + + /* Search for newfilename in this sector */ + + offset = sizeof(struct smartfs_chain_header_s); + entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize; + while (offset + entrysize < fs->fs_llformat.availbytes) + { + /* Test the next entry */ + + direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; + if (((direntry->flags & SMARTFS_DIRENT_EMPTY) == + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) || + ((direntry->flags & SMARTFS_DIRENT_ACTIVE) != + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE))) + { + /* This entry isn't valid, skip it */ + + offset += entrysize; + continue; + } + + /* Test if this entry matches newfilename */ + + if (strcmp(newfilename, direntry->name) == 0) + { + /* Uh-oh, looks like the entry already exists */ + + ret = -EEXIST; + goto errout_with_semaphore; + } + + offset += entrysize; + } + + /* Chain to next sector */ + + sector = SMARTFS_NEXTSECTOR(((struct smartfs_chain_header_s *)fs->fs_rwbuffer)); + } + + /* Okay, we will create newfilename in the newentry directory */ + + newparentdirsector = newentry.firstsector; + newfilename = oldfilename; + } + + /* Test if the new parent directory is valid */ + + if (newparentdirsector != 0xFFFF) + { + /* We can move to the given parent directory */ + + mode = oldentry.flags & SMARTFS_DIRENT_MODE; + type = oldentry.flags & SMARTFS_DIRENT_TYPE; + ret = smartfs_createentry(fs, newparentdirsector, newfilename, + type, mode, &newentry, oldentry.firstsector); + if (ret != OK) + { + goto errout_with_semaphore; + } + + /* Now mark the old entry as inactive */ + + readwrite.logsector = oldentry.dsector; + readwrite.offset = 0; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d reading sector %d data\n", ret, oldentry.dsector); + goto errout_with_semaphore; + } + + direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[oldentry.doffset]; +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + direntry->flags &= ~SMARTFS_DIRENT_ACTIVE; +#else + direntry->flags |= SMARTFS_DIRENT_ACTIVE; +#endif + + /* Now write the updated flags back to the device */ + + readwrite.offset = oldentry.doffset; + readwrite.count = sizeof(direntry->flags); + readwrite.buffer = (uint8_t *) &direntry->flags; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d writing flag bytes for sector %d\n", ret, readwrite.logsector); + goto errout_with_semaphore; + } + } + else + { + /* Trying to create in a directory that doesn't exist */ + + ret = -ENOENT; + goto errout_with_semaphore; + } + + ret = OK; + +errout_with_semaphore: + if (oldentry.name != NULL) + { + kfree(oldentry.name); + oldentry.name = NULL; + } + + if (newentry.name != NULL) + { + kfree(newentry.name); + newentry.name = NULL; + } + + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Name: smartfs_stat + * + * Description: Return information about a file or directory + * + ****************************************************************************/ + +static int smartfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf) +{ + struct smartfs_mountpt_s *fs; + struct smartfs_entry_s entry; + int ret = -ENOENT; + uint16_t parentdirsector; + const char *filename; + + /* Sanity checks */ + + DEBUGASSERT(mountpt && mountpt->i_private); + + /* Get the mountpoint private data from the inode structure */ + + fs = mountpt->i_private; + + smartfs_semtake(fs); + + /* Find the directory entry corresponding to relpath */ + + entry.name = NULL; + ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector, + &filename); + if (ret < 0) + { + goto errout_with_semaphore; + } + + /* Initialize the stat structure */ + + memset(buf, 0, sizeof(struct stat)); + if (entry.firstsector == fs->fs_rootsector) + { + /* It's directory name of the mount point */ + + buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR|S_IWOTH|S_IWGRP|S_IWUSR; + ret = OK; + goto errout_with_semaphore; + } + + buf->st_mode = entry.flags & 0xFFF; + if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR) + { + buf->st_mode |= S_IFDIR; + } + else + { + buf->st_mode |= S_IFREG; + } + + buf->st_size = entry.datlen; + buf->st_blksize = fs->fs_llformat.availbytes; + buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize; + buf->st_atime = 0; + buf->st_ctime = 0; + + ret = OK; + +errout_with_semaphore: + if (entry.name != NULL) + { + kfree(entry.name); + entry.name = NULL; + } + + smartfs_semgive(fs); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/nuttx/fs/smartfs/smartfs_utils.c b/nuttx/fs/smartfs/smartfs_utils.c new file mode 100644 index 000000000..a72fef691 --- /dev/null +++ b/nuttx/fs/smartfs/smartfs_utils.c @@ -0,0 +1,1293 @@ +/**************************************************************************** + * fs/smartfs/smartfs_utils.c + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "smartfs.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS +static struct smartfs_mountpt_s* g_mounthead = NULL; +#endif + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: smartfs_semtake + ****************************************************************************/ + +void smartfs_semtake(struct smartfs_mountpt_s *fs) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(fs->fs_sem) != 0) + { + /* The only case that an error should occur here is if + * the wait was awakened by a signal. + */ + + ASSERT(*get_errno_ptr() == EINTR); + } +} + +/**************************************************************************** + * Name: smartfs_semgive + ****************************************************************************/ + +void smartfs_semgive(struct smartfs_mountpt_s *fs) +{ + sem_post(fs->fs_sem); +} + +/**************************************************************************** + * Name: smartfs_mount + * + * Desciption: This function is called only when the mountpoint is first + * established. It initializes the mountpoint structure and verifies + * that a valid SMART filesystem is provided by the block driver. + * + * The caller should hold the mountpoint semaphore + * + ****************************************************************************/ + +int smartfs_mount(struct smartfs_mountpt_s *fs, bool writeable) +{ + FAR struct inode *inode; + struct geometry geo; + int ret = OK; +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + struct smartfs_mountpt_s *nextfs; +#endif + + /* Assume that the mount is not successful */ + + fs->fs_mounted = false; + + /* Check if there is media available */ + + inode = fs->fs_blkdriver; + if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry || + inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available) + { + ret = -ENODEV; + goto errout; + } + + /* Make sure that that the media is write-able (if write access is needed) */ + + if (writeable && !geo.geo_writeenabled) + { + ret = -EACCES; + goto errout; + } + + /* Get the SMART low-level format information to validate the device has been + * formatted and scan properly for logical to physical sector mapping. + */ + + ret = FS_IOCTL(fs, BIOC_GETFORMAT, (unsigned long) &fs->fs_llformat); + if (ret != OK) + { + fdbg("Error getting device low level format: %d\n", ret); + goto errout; + } + + /* Validate the low-level format is valid */ + + if (!(fs->fs_llformat.flags & SMART_FMT_ISFORMATTED)) + { + fdbg("No low-level format found\n"); + ret = -ENODEV; + goto errout; + } + + /* Allocate a read/write buffer */ + +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + /* Scan linked list of mounted file systems to find another FS with + * the same blockdriver. We will reuse the buffers. + */ + + nextfs = g_mounthead; + while (nextfs != NULL) + { + /* Test if this FS uses the same block driver */ + + if (nextfs->fs_blkdriver == fs->fs_blkdriver) + { + /* Yep, it's the same block driver. Reuse the buffers. + * we can do this because we are protected by the same + * semaphore. + */ + + fs->fs_rwbuffer = nextfs->fs_rwbuffer; + fs->fs_workbuffer = nextfs->fs_workbuffer; + break; + } + + /* Advance to next FS */ + + nextfs = nextfs->fs_next; + } + + /* If we didn't find a FS above, then allocate some buffers */ + + if (nextfs == NULL) + { + fs->fs_rwbuffer = (char *) kmalloc(fs->fs_llformat.availbytes); + fs->fs_workbuffer = (char *) kmalloc(256); + } + + /* Now add ourselves to the linked list of SMART mounts */ + + fs->fs_next = g_mounthead; + g_mounthead = fs; + + /* Set our root directory sector based on the directory entry + * reported by the block driver (based on which device is + * associated with this mount. + */ + + fs->fs_rootsector = SMARTFS_ROOT_DIR_SECTOR + fs->fs_llformat.rootdirnum; + +#else + fs->fs_rwbuffer = (char *) kmalloc(fs->fs_llformat.availbytes); + fs->fs_workbuffer = (char *) kmalloc(256); + fs->fs_rootsector = SMARTFS_ROOT_DIR_SECTOR; +#endif + + /* We did it! */ + + fs->fs_mounted = TRUE; + + fdbg("SMARTFS:\n"); + fdbg("\t Sector size: %d\n", fs->fs_llformat.sectorsize); + fdbg("\t Bytes/sector %d\n", fs->fs_llformat.availbytes); + fdbg("\t Num sectors: %d\n", fs->fs_llformat.nsectors); + fdbg("\t Free sectors: %d\n", fs->fs_llformat.nfreesectors); + fdbg("\t Max filename: %d\n", CONFIG_SMARTFS_MAXNAMLEN); +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + fdbg("\t RootDirEntries: %d\n", fs->fs_llformat.nrootdirentries); +#endif + fdbg("\t RootDirSector: %d\n", fs->fs_rootsector); + +errout: + return ret; +} + +/**************************************************************************** + * Name: smartfs_unmount + * + * Desciption: This function is called only when the mountpoint is being + * unbound. If we are serving multiple directories, then we have to + * remove ourselves from the mount linked list, and potentially free + * the shared buffers. + * + * The caller should hold the mountpoint semaphore + * + ****************************************************************************/ + +int smartfs_unmount(struct smartfs_mountpt_s *fs) +{ + int ret = OK; + struct inode *inode; +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + struct smartfs_mountpt_s *nextfs; + struct smartfs_mountpt_s *prevfs; + int count = 0; + int found = FALSE; +#endif + +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS + /* Start at the head of the mounts and search for our entry. Also + * count the number of entries that match our blkdriver. + */ + + nextfs = g_mounthead; + prevfs = NULL; + while (nextfs != NULL) + { + /* Test if this FS's blkdriver matches ours (it could be us) */ + + if (nextfs->fs_blkdriver == fs->fs_blkdriver) + count++; + + /* Test if this entry is our's */ + + if (nextfs == fs) + { + found = TRUE; + } + + /* Keep track of the previous entry until our's is found */ + + if (!found) + { + /* Save this entry as the previous entry */ + + prevfs = nextfs; + } + + /* Advance to the next entry */ + + nextfs = nextfs->fs_next; + } + + /* Ensure we found our FS */ + + if (!found) + { + /* Our entry not found! Invalid unmount or bug somewhere */ + + return -EINVAL; + } + + /* If the count is only one, then we need to delete the shared + * buffers because we are the last ones. + */ + + if (count == 1) + { + /* Close the block driver */ + + if (fs->fs_blkdriver) + { + inode = fs->fs_blkdriver; + if (inode) + { + if (inode->u.i_bops && inode->u.i_bops->close) + { + (void)inode->u.i_bops->close(inode); + } + } + } + + /* Free the buffers */ + + kfree(fs->fs_rwbuffer); + kfree(fs->fs_workbuffer); + + /* Set the buffer's to invalid value to catch program bugs */ + + fs->fs_rwbuffer = (char *) 0xDEADBEEF; + fs->fs_workbuffer = (char *) 0xDEADBEEF; + } + + /* Now removed ourselves from the linked list */ + + if (fs == g_mounthead) + { + /* We were the first ones. Set a new head */ + + g_mounthead = fs->fs_next; + } + else + { + /* Remove from the middle of the list somewhere */ + + prevfs->fs_next = fs->fs_next; + } +#else + if (fs->fs_blkdriver) + { + inode = fs->fs_blkdriver; + if (inode) + { + if (inode->u.i_bops && inode->u.i_bops->close) + { + (void)inode->u.i_bops->close(inode); + } + } + } + + /* Release the mountpoint private data */ + + kfree(fs->fs_rwbuffer); + kfree(fs->fs_workbuffer); +#endif + + return ret; +} + +/**************************************************************************** + * Name: smartfs_finddirentry + * + * Description: Finds an entry in the filesystem as specified by relpath. + * If found, the direntry will be populated with information + * for accessing the entry. + * + * If the final directory segement of relpath just before the + * last segment (the target file/dir) is valid, then the + * parentdirsector will indicate the logical sector number of + * the parent directory where a new entry should be created, + * and the filename pointer will point to the final segment + * (i.e. the "filename"). + * + ****************************************************************************/ + +int smartfs_finddirentry(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *direntry, const char *relpath, + uint16_t *parentdirsector, const char **filename) +{ + int ret = -ENOENT; + const char *segment; + const char *ptr; + uint16_t seglen; + uint16_t depth = 0; + uint16_t dirstack[CONFIG_SMARTFS_DIRDEPTH]; + uint16_t dirsector; + uint16_t entrysize; + uint16_t offset; + struct smartfs_chain_header_s *header; + struct smart_read_write_s readwrite; + struct smartfs_entry_header_s *entry; + + /* Initialize directory level zero as the root sector */ + + dirstack[0] = fs->fs_rootsector; + entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize; + + /* Test if this is a request for the root directory */ + + if (*relpath == '\0') + { + direntry->firstsector = fs->fs_rootsector; + direntry->flags = SMARTFS_DIRENT_TYPE_DIR | 0777; + direntry->utc = 0; + direntry->dsector = 0; + direntry->doffset = 0; + direntry->dfirst = fs->fs_rootsector; + direntry->name = NULL; + direntry->datlen = 0; + + *parentdirsector = 0; /* Our parent is the format sector I guess */ + return OK; + } + + /* Parse through each segment of relpath */ + + segment = relpath; + while (segment != NULL && *segment != '\0') + { + /* Find the end of this segment. It will be '/' or NULL. */ + + ptr = segment; + seglen = 0; + while (*ptr != '/' && *ptr != '\0') + { + seglen++; + ptr++; + } + + strncpy(fs->fs_workbuffer, segment, seglen); + fs->fs_workbuffer[seglen] = '\0'; + + /* Search for "." and ".." as segment names */ + + if (strcmp(fs->fs_workbuffer, ".") == 0) + { + /* Just ignore this segment. Advance ptr if not on NULL */ + + if (*ptr == '/') + { + ptr++; + } + + segment = ptr; + continue; + } + else if (strcmp(fs->fs_workbuffer, "..") == 0) + { + /* Up one level */ + + if (depth == 0) + { + /* We went up one level past our mount point! */ + + goto errout; + } + + /* "Pop" to the previous directory level */ + + depth--; + if (*ptr == '/') + { + ptr++; + } + + segment = ptr; + continue; + } + else + { + /* Search for the entry in the current directory */ + + dirsector = dirstack[depth]; + + /* Read the directory */ + +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + while (dirsector != 0xFFFF) +#else + while (dirsector != 0) +#endif + { + /* Read the next directory in the chain */ + + readwrite.logsector = dirsector; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.buffer = (uint8_t *)fs->fs_rwbuffer; + readwrite.offset = 0; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + goto errout; + } + + /* Point to next sector in chain */ + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + dirsector = SMARTFS_NEXTSECTOR(header); + + /* Search for the entry */ + + offset = sizeof(struct smartfs_chain_header_s); + entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; + while (offset < readwrite.count) + { + /* Test if this entry is valid and active */ + + if (((entry->flags & SMARTFS_DIRENT_EMPTY) == + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) || + ((entry->flags & SMARTFS_DIRENT_ACTIVE) != + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE))) + { + /* This entry isn't valid, skip it */ + + offset += entrysize; + entry = (struct smartfs_entry_header_s *) + &fs->fs_rwbuffer[offset]; + + continue; + } + + /* Test if the name matches */ + + if (strcmp(entry->name, fs->fs_workbuffer) == 0) + { + /* We found it! If this is the last segment entry, + * then report the entry. If it isn't the last + * entry, then validate it is a directory entry and + * open it and continue searching. + */ + + if (*ptr == '\0') + { + /* We are at the last segment. Report the entry */ + + /* Fill in the entry */ + + direntry->firstsector = entry->firstsector; + direntry->flags = entry->flags; + direntry->utc = entry->utc; + direntry->dsector = readwrite.logsector; + direntry->doffset = offset; + direntry->dfirst = dirstack[depth]; + if (direntry->name == NULL) + { + direntry->name = (char *) kmalloc(fs->fs_llformat.namesize); + } + + strcpy(direntry->name, entry->name); + direntry->datlen = 0; + + /* Scan the file's sectors to calculate the length and perform + * a rudamentary check. + */ + + if ((entry->flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE) + { + dirsector = entry->firstsector; + readwrite.count = sizeof(struct smartfs_chain_header_s); + readwrite.buffer = (uint8_t *)fs->fs_rwbuffer; + readwrite.offset = 0; + + while (dirsector != SMARTFS_ERASEDSTATE_16BIT) + { + /* Read the next sector of the file */ + + readwrite.logsector = dirsector; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error in sector chain at %d!\n", dirsector); + break; + } + + /* Add used bytes to the total and point to next sector */ + + if (*((uint16_t *) header->used) != SMARTFS_ERASEDSTATE_16BIT) + { + direntry->datlen += *((uint16_t *) header->used); + } + + dirsector = SMARTFS_NEXTSECTOR(header); + } + } + + *parentdirsector = dirstack[depth]; + *filename = segment; + ret = OK; + goto errout; + } + else + { + /* Validate it's a directory */ + + if ((entry->flags & SMARTFS_DIRENT_TYPE) != + SMARTFS_DIRENT_TYPE_DIR) + { + /* Not a directory! Report the error */ + + ret = -ENOTDIR; + goto errout; + } + + /* "Push" the directory and continue searching */ + + if (depth >= CONFIG_SMARTFS_DIRDEPTH - 1) + { + /* Directory depth too big */ + + ret = -ENAMETOOLONG; + goto errout; + } + + dirstack[++depth] = entry->firstsector; + segment = ptr + 1; + break; + } + } + + /* Not this entry. Skip to the next one */ + + offset += entrysize; + entry = (struct smartfs_entry_header_s *) + &fs->fs_rwbuffer[offset]; + } + + /* Test if a directory entry was found and break if it was */ + + if (offset < readwrite.count) + { + break; + } + } + + /* If we found a dir entry, then continue searching */ + + if (offset < readwrite.count) + { + /* Update the segment pointer */ + + if (*ptr != '\0') + { + ptr++; + } + + segment = ptr; + continue; + } + + /* Entry not found! Report the error. Also, if this is the last + * segment, then report the parent directory sector. + */ + + if (*ptr == '\0') + { + *parentdirsector = dirstack[depth]; + *filename = segment; + } + else + { + *parentdirsector = 0xFFFF; + *filename = NULL; + } + + ret = -ENOENT; + goto errout; + } + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: smartfs_createentry + * + * Description: Creates a new entry in the specified parent directory, using + * the specified type and name. If the given sectorno is + * 0xFFFF, then a new sector is allocated for the new entry, + * otherwise the supplied sectorno is used. + * + ****************************************************************************/ + +int smartfs_createentry(struct smartfs_mountpt_s *fs, + uint16_t parentdirsector, const char* filename, + uint16_t type, + mode_t mode, struct smartfs_entry_s *direntry, + uint16_t sectorno) +{ + struct smart_read_write_s readwrite; + int ret; + uint16_t psector; + uint16_t nextsector; + uint16_t offset; + uint16_t found; + uint16_t entrysize; + struct smartfs_entry_header_s *entry; + struct smartfs_chain_header_s *chainheader; + + /* Start at the 1st sector in the parent directory */ + + psector = parentdirsector; + found = FALSE; + entrysize = sizeof(struct smartfs_entry_header_s) + + fs->fs_llformat.namesize; + + /* Validate the name isn't too long */ + + if (strlen(filename) + 1 > fs->fs_llformat.namesize) + { + return -ENAMETOOLONG; + } + + /* Read the parent directory sector and find a place to insert + * the new entry. + */ + + while (1) + { + /* Read the next sector */ + + readwrite.logsector = psector; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.offset = 0; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + goto errout; + } + + /* Get the next chained sector */ + + chainheader = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + nextsector = SMARTFS_NEXTSECTOR(chainheader); + + /* Search for an empty entry in this sector */ + + offset = sizeof(struct smartfs_chain_header_s); + entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; + while (offset + entrysize < readwrite.count) + { + /* Check if this entry is available */ + + if ((entry->flags == SMARTFS_ERASEDSTATE_16BIT) || + ((entry->flags & + (SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE) ) == + (~SMARTFS_ERASEDSTATE_16BIT & + (SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE) ))) + { + /* We found an empty entry. Use it. */ + + found = TRUE; + break; + } + + /* Not available. Skip to next entry */ + + offset += entrysize; + entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; + } + + /* If we found an entry, stop the search */ + + if (found) + { + break; + } + + /* If there are no more sectors, then we need to add one to make + * room for the new entry. + */ + + if (nextsector == SMARTFS_ERASEDSTATE_16BIT) + { + /* Allocate a new sector and chain it to the last one */ + + ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF); + if (ret < 0) + { + goto errout; + } + + nextsector = (uint16_t) ret; + + /* Chain the next sector into this sector sector */ + + *((uint16_t *) chainheader->nextsector) = nextsector; + readwrite.offset = offsetof(struct smartfs_chain_header_s, + nextsector); + readwrite.count = sizeof(uint16_t); + readwrite.buffer = chainheader->nextsector; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error chaining sector %d\n", nextsector); + goto errout; + } + } + + /* Now update to the next sector */ + + psector = nextsector; + } + + /* We found an insertion point. Create the entry at sector,offset */ + +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + entry->flags = (uint16_t) (SMARTFS_DIRENT_ACTIVE | SMARTFS_DIRENT_DELETING | + SMARTFS_DIRENT_RESERVED | type | (mode & SMARTFS_DIRENT_MODE)); +#else + entry->flags = (uint16_t) (SMARTFS_DIRENT_EMPTY | type | (mode & SMARTFS_DIRENT_MODE)); +#endif + + if (sectorno == 0xFFFF) + { + /* Allocate a new sector for the file / dir */ + + ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF); + if (ret < 0) + { + goto errout; + } + + nextsector = ret; + + /* Set the newly allocated sector's type (file or dir) */ + + nextsector = (uint16_t) ret; + if ((type & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR) + { + chainheader->type = SMARTFS_SECTOR_TYPE_DIR; + } + else + { + chainheader->type = SMARTFS_SECTOR_TYPE_FILE; + } + + readwrite.count = 1; + readwrite.offset = offsetof(struct smartfs_chain_header_s, type); + readwrite.buffer = (uint8_t *) &chainheader->type; + readwrite.logsector = nextsector; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error %d setting new sector type for sector %d\n",ret, nextsector); + goto errout; + } + } + else + { + /* Use the provided sector number */ + + nextsector = sectorno; + } + + /* Create the directory entry to be written in the parent's sector */ + + entry->firstsector = nextsector; + entry->utc = 0; + memset(entry->name, 0, fs->fs_llformat.namesize); + strncpy(entry->name, filename, fs->fs_llformat.namesize-1); + + /* Now write the new entry to the parent directory sector */ + + readwrite.logsector = psector; + readwrite.offset = offset; + readwrite.count = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize; + readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[offset]; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + goto errout; + } + + /* Now fill in the entry */ + + direntry->firstsector = nextsector; + direntry->dsector = psector; + direntry->doffset = offset; + direntry->flags = entry->flags; + direntry->utc = 0; + direntry->datlen = 0; + if (direntry->name == NULL) + { + direntry->name = (FAR char *) kmalloc(fs->fs_llformat.namesize); + } + + memset(direntry->name, 0, fs->fs_llformat.namesize); + strncpy(direntry->name, filename, fs->fs_llformat.namesize-1); + + ret = OK; + +errout: + return ret; +} + +/**************************************************************************** + * Name: smartfs_deleteentry + * + * Description: Deletes an entry from the filesystem (file or dir) by + * freeing all the entry's sectors and then marking it inactive + * in it's parent's directory sector. For a directory, it + * does not validate the directory is empty, nor does it do + * a recursive delete. + * + ****************************************************************************/ + +int smartfs_deleteentry(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *entry) +{ + int ret; + uint16_t nextsector; + uint16_t sector; + uint16_t count; + uint16_t entrysize; + uint16_t offset; + struct smartfs_entry_header_s *direntry; + struct smartfs_chain_header_s *header; + struct smart_read_write_s readwrite; + + /* Okay, delete the file. Loop through each sector and release them + + * TODO: We really should walk the list backward to avoid lost + * sectors in the event we lose power. However this requires + * allocting a buffer to build the sector list since we don't + * store a doubly-linked list of sectors on the deivce. We + * could test if the sector data buffer is big enough and + * just use that, and only allocate a new buffer if the + * sector buffer isn't big enough. Do do this, however, we + * need to change the code below as it is using the a few + * bytes of the buffer to read in header info. + */ + + nextsector = entry->firstsector; + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + readwrite.offset = 0; + readwrite.count = sizeof(struct smartfs_chain_header_s); + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + while (nextsector != SMARTFS_ERASEDSTATE_16BIT) + { + /* Read the next sector into our buffer */ + + sector = nextsector; + readwrite.logsector = sector; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error reading sector %d\n", nextsector); + break; + } + + /* Release this sector */ + + nextsector = SMARTFS_NEXTSECTOR(header); + ret = FS_IOCTL(fs, BIOC_FREESECT, sector); + } + + /* Remove the entry from the directory tree */ + + readwrite.logsector = entry->dsector; + readwrite.offset = 0; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error reading directory info at sector %s\n", entry->dsector); + goto errout; + } + + /* Mark this entry as inactive */ + + direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[entry->doffset]; +#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF + direntry->flags &= ~SMARTFS_DIRENT_ACTIVE; +#else + direntry->flags |= SMARTFS_DIRENT_ACTIVE; +#endif + + /* Write the updated flags back to the sector */ + + readwrite.offset = entry->doffset; + readwrite.count = sizeof(uint16_t); + readwrite.buffer = (uint8_t *) &direntry->flags; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error marking entry inactive at sector %s\n", entry->dsector); + goto errout; + } + + /* Test if any entries in this sector are being used */ + + if ((entry->dsector != fs->fs_rootsector) && + (entry->dsector != entry->dfirst)) + { + /* Scan the sector and count used entries */ + + count = 0; + offset = sizeof(struct smartfs_chain_header_s); + entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize; + while (offset + entrysize < fs->fs_llformat.availbytes) + { + /* Test the next entry */ + + direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; + if (((direntry->flags & SMARTFS_DIRENT_EMPTY) != + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) && + ((direntry->flags & SMARTFS_DIRENT_ACTIVE) == + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE))) + { + /* Count this entry */ + count++; + } + + /* Advance to next entry */ + + offset += entrysize; + } + + /* Test if the count it zero. If it is, then we will release the sector */ + + if (count == 0) + { + /* Okay, to release the sector, we must find the sector that we + * are chained to and remove ourselves from the chain. First + * save our nextsector value so we can "unchain" ourselves. + */ + + nextsector = SMARTFS_NEXTSECTOR(header); + + /* Now loop through the dir sectors to find ourselves in the chain */ + + sector = entry->dfirst; + readwrite.offset = 0; + readwrite.count = sizeof(struct smartfs_chain_header_s); + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + while (sector != SMARTFS_ERASEDSTATE_16BIT) + { + /* Read the header for the next sector */ + + readwrite.logsector = sector; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error reading sector %d\n", nextsector); + break; + } + + /* Test if this sector "points" to us */ + + if (SMARTFS_NEXTSECTOR(header) == entry->dsector) + { + /* We found ourselves in the chain. Update the chain. */ + + SMARTFS_NEXTSECTOR(header) = nextsector; + readwrite.offset = offsetof(struct smartfs_chain_header_s, nextsector); + readwrite.count = sizeof(uint16_t); + readwrite.buffer = header->nextsector; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error unchaining sector (%d)\n", nextsector); + goto errout; + } + + /* Now release our sector */ + + ret = FS_IOCTL(fs, BIOC_FREESECT, (unsigned long) entry->dsector); + if (ret < 0) + { + fdbg("Error freeing sector %d\n", entry->dsector); + goto errout; + } + + /* Break out of the loop, we are done! */ + + break; + } + + /* Chain to the next sector */ + + sector = SMARTFS_NEXTSECTOR(header); + } + } + } + + ret = OK; + +errout: + return ret; +} + +/**************************************************************************** + * Name: smartfs_countdirentries + * + * Description: Counts the number of items in the specified directory entry. + * This routine assumes you have validated the entry you are + * passing is in fact a directory sector, though it checks + * just in case you were stupid :-) + * + ****************************************************************************/ + +int smartfs_countdirentries(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *entry) +{ + int ret; + uint16_t nextsector; + uint16_t offset; + uint16_t entrysize; + int count; + struct smartfs_entry_header_s *direntry; + struct smartfs_chain_header_s *header; + struct smart_read_write_s readwrite; + + /* Walk through the directory's sectors and count entries */ + + count = 0; + nextsector = entry->firstsector; + while (nextsector != SMARTFS_ERASEDSTATE_16BIT) + { + /* Read the next sector into our buffer */ + + readwrite.logsector = nextsector; + readwrite.offset = 0; + readwrite.count = fs->fs_llformat.availbytes; + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error reading sector %d\n", nextsector); + break; + } + + /* Validate this is a directory type sector */ + + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + if (header->type != SMARTFS_SECTOR_TYPE_DIR) + { + fdbg("Sector %d is not a DIR sector!\n", nextsector); + goto errout; + } + + /* Loop for all entries in this sector and count them */ + + offset = sizeof(struct smartfs_chain_header_s); + entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize; + direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; + while (offset + entrysize < readwrite.count) + { + if (((direntry->flags & SMARTFS_DIRENT_EMPTY) != + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) && + ((direntry->flags & SMARTFS_DIRENT_ACTIVE) == + (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE))) + { + /* Count this entry */ + count++; + } + + offset += entrysize; + direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset]; + } + + /* Get the next sector from the header */ + + nextsector = SMARTFS_NEXTSECTOR(header); + } + + ret = count; + +errout: + return ret; +} + +/**************************************************************************** + * Name: smartfs_truncatefile + * + * Description: Truncates an existing file on the device so that it occupies + * zero bytes and can be completely re-written. + * + ****************************************************************************/ + +int smartfs_truncatefile(struct smartfs_mountpt_s *fs, + struct smartfs_entry_s *entry) +{ + int ret; + uint16_t nextsector; + uint16_t sector; + struct smartfs_chain_header_s *header; + struct smart_read_write_s readwrite; + + /* Walk through the directory's sectors and count entries */ + + nextsector = entry->firstsector; + header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer; + + while (nextsector != SMARTFS_ERASEDSTATE_16BIT) + { + /* Read the next sector's header into our buffer */ + + readwrite.logsector = nextsector; + readwrite.offset = 0; + readwrite.count = sizeof(struct smartfs_chain_header_s); + readwrite.buffer = (uint8_t *) fs->fs_rwbuffer; + ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error reading sector %d header\n", nextsector); + goto errout; + } + + /* Get the next chained sector */ + + sector = SMARTFS_NEXTSECTOR(header); + + /* If this is the 1st sector of the file, then just overwrite + * the sector data with the erased state value. The underlying + * SMART block driver will detect this and release the old + * sector and create a new one with the new (blank) data. + */ + + if (nextsector == entry->firstsector) + { + /* Fill our buffer with erased data */ + + memset(fs->fs_rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes); + header->type = SMARTFS_SECTOR_TYPE_FILE; + + /* Now write the new sector data */ + + readwrite.count = fs->fs_llformat.availbytes; + ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite); + if (ret < 0) + { + fdbg("Error blanking 1st sector (%d) of file\n", nextsector); + goto errout; + } + + /* Set the entry's data length to zero ... we just truncated */ + + entry->datlen = 0; + } + else + { + /* Not the 1st sector -- release it */ + + ret = FS_IOCTL(fs, BIOC_FREESECT, (unsigned long) nextsector); + if (ret < 0) + { + fdbg("Error freeing sector %d\n", nextsector); + goto errout; + } + } + + /* Now move on to the next sector */ + + nextsector = sector; + } + + ret = OK; + +errout: + return ret; +} diff --git a/nuttx/include/nuttx/fs/dirent.h b/nuttx/include/nuttx/fs/dirent.h index f8d356850..1235790cc 100644 --- a/nuttx/include/nuttx/fs/dirent.h +++ b/nuttx/include/nuttx/fs/dirent.h @@ -134,6 +134,20 @@ struct nfsdir_s }; #endif +#ifdef CONFIG_FS_SMARTFS +/* SMARTFS is the Sector Mapped Allocation for Really Tiny FLASH filesystem. + * it is designed to use small sectors on small serial FLASH devices, using + * minimal RAM footprint. + */ + +struct fs_smartfsdir_s +{ + uint16_t fs_firstsector; /* First sector of directory list */ + uint16_t fs_currsector; /* Current sector of directory list */ + uint16_t fs_curroffset; /* Current offset withing current sector */ +}; +#endif + #endif /* CONFIG_DISABLE_MOUNTPOINT */ struct fs_dirent_s @@ -174,19 +188,22 @@ struct fs_dirent_s #ifndef CONFIG_DISABLE_MOUNTPOINT #ifdef CONFIG_FS_FAT - struct fs_fatdir_s fat; + struct fs_fatdir_s fat; #endif #ifdef CONFIG_FS_ROMFS - struct fs_romfsdir_s romfs; + struct fs_romfsdir_s romfs; #endif #ifdef CONFIG_FS_BINFS - struct fs_binfsdir_s binfs; + struct fs_binfsdir_s binfs; #endif #ifdef CONFIG_FS_NXFFS - struct fs_nxffsdir_s nxffs; + struct fs_nxffsdir_s nxffs; #endif #ifdef CONFIG_NFS - struct nfsdir_s nfs; + struct nfsdir_s nfs; +#endif +#ifdef CONFIG_FS_SMARTFS + struct fs_smartfsdir_s smartfs; #endif #endif /* !CONFIG_DISABLE_MOUNTPOINT */ } u; diff --git a/nuttx/include/nuttx/fs/ioctl.h b/nuttx/include/nuttx/fs/ioctl.h index 9cde497a8..463b96e18 100644 --- a/nuttx/include/nuttx/fs/ioctl.h +++ b/nuttx/include/nuttx/fs/ioctl.h @@ -105,7 +105,7 @@ #define FIOC_OPTIMIZE _FIOC(0x0003) /* IN: None * OUT: None */ -#define FIOC_FILENAME _FIOC(0x0004) /* IN: FAR const char ** pointer +#define FIOC_FILENAME _FIOC(0x0004) /* IN: FAR const char ** pointer * OUT: Pointer to a persistent file name * (Guaranteed to persist while the file * is open). @@ -146,6 +146,35 @@ * IN: None * OUT: None (ioctl return value provides * success/failure indication). */ +#define BIOC_LLFORMAT _BIOC(0x0004) /* Low-Level Format on SMART flash devices + * IN: None + * OUT: None (ioctl return value provides + * success/failure indication). */ +#define BIOC_GETFORMAT _BIOC(0x0005) /* Returns SMART flash format information + * such as format status, logical sector + * size, total sectors, free sectors, etc. + * IN: None + * OUT: Pointer to the format information. */ +#define BIOC_ALLOCSECT _BIOC(0x0006) /* Allocate a logical sector from the block + * device. + * IN: None + * OUT: Logical sector number allocated. */ +#define BIOC_FREESECT _BIOC(0x0007) /* Allocate a logical sector from the block + * device. + * IN: None + * OUT: Logical sector number allocated. */ +#define BIOC_READSECT _BIOC(0x0008) /* Read a logical sector from the block + * device. + * IN: Pointer to sector read data (the + * logical sector number, count and + * read buffer address + * OUT: Number of bytes read or error */ +#define BIOC_WRITESECT _BIOC(0x0009) /* Write to data to a logical sector + * IN: Pointer to sector write data (the + * logical secor number and write + * buffer address + * OUT: None (ioctl return value provides + * success/failure indication). */ /* NuttX MTD driver ioctl definitions ***************************************/ @@ -164,6 +193,15 @@ * of device memory */ #define MTDIOC_BULKERASE _MTDIOC(0x0003) /* IN: None * OUT: None */ +#define MTDIOC_GETCAPS _MTDIOC(0x0004) /* IN: None + * OUT: Capabilities flags */ +#define MTDIOC_SECTERASE _MTDIOC(0x0005) /* IN: Sector number to erase + * OUT: None */ +#define MTDIOC_BYTEWRITE _MTDIOC(0x0006) /* IN: Pointer to bytewrite structure + * OUT: None */ + +#define MTDIOC_CAPS_SECTERASE 0x01 +#define MTDIOC_CAPS_BYTEWRITE 0x02 /* NuttX ARP driver ioctl definitions (see netinet/arp.h) *******************/ diff --git a/nuttx/include/nuttx/fs/mksmartfs.h b/nuttx/include/nuttx/fs/mksmartfs.h new file mode 100644 index 000000000..b22b7fcc3 --- /dev/null +++ b/nuttx/include/nuttx/fs/mksmartfs.h @@ -0,0 +1,109 @@ +/**************************************************************************** + * include/nuttx/fs/mksmartfs.h + * + * Copyright (C) 2013 Ken Pettit. All rights reserved. + * Author: Ken Pettit + * + * 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_MKSMARTFS_H +#define __INCLUDE_NUTTX_SMART_MKSMARTFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Global Variables + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" { +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: mksmartfs + * + * Description: + * Make a SMART (Sector Mapped Allocation for Really Tiny) Flash file + * system image on the specified block device (must be a SMART device). + * + * Inputs: + * pathname - the full path to a registered block driver + * nrootdirs - the number of Root Directory entries to support + * on this device (supports multiple mount points). + * + * Return: + * Zero (OK) on success; -1 (ERROR) on failure with errno set appropriately: + * + * EINVAL - NULL block driver string + * ENOENT - 'pathname' does not refer to anything in the filesystem. + * ENOTBLK - 'pathname' does not refer to a block driver + * EACCESS - block driver does not support write or geometry methods or + * is not a SMART device + * + * Assumptions: + * - The caller must assure that the block driver is not mounted and not in + * use when this function is called. The result of formatting a mounted + * device is indeterminate (but likely not good). + * + ****************************************************************************/ + +#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS +EXTERN int mksmartfs(FAR const char *pathname, uint8_t nrootdirs); +#else +EXTERN int mksmartfs(FAR const char *pathname); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __INCLUDE_NUTTX_SMART_MKSMARTFS_H */ diff --git a/nuttx/include/nuttx/mtd.h b/nuttx/include/nuttx/mtd.h index a196639c4..d90ea5f6b 100644 --- a/nuttx/include/nuttx/mtd.h +++ b/nuttx/include/nuttx/mtd.h @@ -58,6 +58,14 @@ #define MTD_BWRITE(d,s,n,b)((d)->bwrite ? (d)->bwrite(d,s,n,b) : (-ENOSYS)) #define MTD_IOCTL(d,c,a) ((d)->ioctl ? (d)->ioctl(d,c,a) : (-ENOSYS)) +/* If any of the low-level device drivers declare they want sub-sector erase + * support, then define MTD_SUBSECTOR_ERASE. + */ + +#if defined(CONFIG_MP25P_SUBSECTOR_ERASE) +# define CONFIG_MTD_SUBSECTOR_ERASE 1 +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -70,10 +78,25 @@ struct mtd_geometry_s { - uint16_t blocksize; /* Size of one read/write block */ - uint16_t erasesize; /* Size of one erase blocks -- must be a multiple - * of blocksize. */ - size_t neraseblocks; /* Number of erase blocks */ + uint16_t blocksize; /* Size of one read/write block */ + uint16_t erasesize; /* Size of one erase blocks -- must be a multiple + * of blocksize. */ + size_t neraseblocks; /* Number of erase blocks */ +#ifdef CONFIG_MTD_SUBSECTOR_ERASE + uint16_t subsectorsize; /* Size of the sub-sector erase block */ + uint16_t nsubsectors; /* Number of sub-sector erase blocks */ +#endif +}; + +/* The following defines the information for writing bytes to a sector + * that are not a full page write (bytewrite). + */ + +struct mtd_byte_write_s +{ + uint32_t offset; /* Offset within the device to write to */ + uint16_t count; /* Number of bytes to write */ + const uint8_t *buffer; /* Pointer to the data to write */ }; /* This structure defines the interface to a simple memory technology device. @@ -169,6 +192,22 @@ FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, int ftl_initialize(int minor, FAR struct mtd_dev_s *mtd); +/**************************************************************************** + * Name: smart_initialize + * + * Description: + * Initialize to provide a Sector Mapped Allocation for Really Tiny (SMART) + * Flash block driver wrapper around an MTD interface + * + * Input Parameters: + * minor - The minor device number. The MTD block device will be + * registered as as /dev/mtdsmartN 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); + /**************************************************************************** * Name: flash_eraseall * diff --git a/nuttx/include/nuttx/spi.h b/nuttx/include/nuttx/spi.h index 520874f7b..7eb7fb908 100644 --- a/nuttx/include/nuttx/spi.h +++ b/nuttx/include/nuttx/spi.h @@ -357,7 +357,8 @@ enum spi_dev_e SPIDEV_WIRELESS, /* Select SPI Wireless device */ SPIDEV_TOUCHSCREEN, /* Select SPI touchscreen device */ SPIDEV_EXPANDER, /* Select SPI I/O expander device */ - SPIDEV_MUX /* Select SPI multiplexer device */ + SPIDEV_MUX, /* Select SPI multiplexer device */ + SPIDEV_AUDIO /* Select SPI audio codec device */ }; /* Certain SPI devices may required differnt clocking modes */ diff --git a/nuttx/include/sys/statfs.h b/nuttx/include/sys/statfs.h index 70e963dd9..c498a864f 100644 --- a/nuttx/include/sys/statfs.h +++ b/nuttx/include/sys/statfs.h @@ -100,6 +100,7 @@ #define BINFS_MAGIC 0x4242 #define NXFFS_MAGIC 0x4747 +#define SMARTFS_MAGIC 0x54524D53 /**************************************************************************** * Type Definitions -- cgit v1.2.3