diff --git a/ChangeLog b/ChangeLog index 1fc07dc19bdf08a8165ca0a84e27a5ab15e5102d..da0c769ba620ab22b80e9b0e4f7461f76982cd9c 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11175,4 +11175,5 @@ statistics (2015-11-26). * net/net_procfs.c: Add basic support for networking entries in the procfs (2015-11-27). - + * mtd/filemtd.c and smart.c: Add support for a /dev/smart loop device. + From Ken Petit (2015-11-28). diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 03f08fd0e7b059746553b01f51328df0cb950fdc..b3bd84d70304be03ed6ccff1bc0ce8a7fc19ece9 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -268,6 +268,28 @@ config RAMMTD_FLASHSIM endif +config FILEMTD + bool "File-based MTD driver" + default n + ---help--- + Build support for a File-based MTD driver. + +if FILEMTD + +config FILEMTD_BLOCKSIZE + int "File MTD block size" + default 512 + +config FILEMTD_ERASESIZE + int "File MTD erase block size" + default 4096 + +config FILEMTD_ERASESTATE + hex "Simulated erase state" + default 0xff + +endif + config MTD_AT24XX bool "I2C-based AT24xx eeprom" default n @@ -479,6 +501,14 @@ config MTD_SMART if MTD_SMART +config SMART_DEV_LOOP + bool "Enable SMART loop device" + select FILEMTD + default n + ---help--- + Supports a smart loop device that can be used to export a + file (or character device) as a SMART block device. + config MTD_SMART_SECTOR_SIZE int "SMART Device sector size" default 1024 diff --git a/drivers/mtd/Make.defs b/drivers/mtd/Make.defs index bd284d6822d1d774ed61bb5cc6eca909845e679a..fdd77d3610b3ca19d7c9302ee1fc6f75090024f0 100644 --- a/drivers/mtd/Make.defs +++ b/drivers/mtd/Make.defs @@ -72,6 +72,10 @@ ifeq ($(CONFIG_RAMMTD),y) CSRCS += rammtd.c endif +ifeq ($(CONFIG_FILEMTD),y) +CSRCS += filemtd.c +endif + ifeq ($(CONFIG_MTD_AT24XX),y) CSRCS += at24xx.c endif diff --git a/drivers/mtd/filemtd.c b/drivers/mtd/filemtd.c index c4dee5db756f879f02e580fd79051fff7aef28d4..18b2e19e1a9d0a2d85c2b1e3f2cb55695c56cbb8 100644 --- a/drivers/mtd/filemtd.c +++ b/drivers/mtd/filemtd.c @@ -95,10 +95,12 @@ struct file_dev_s { - struct mtd_dev_s mtd; /* MTD device */ - int fd; /* File descriptor of underlying file */ - size_t nblocks; /* Number of erase blocks */ - size_t offset; /* Offset from start of file */ + struct mtd_dev_s mtd; /* MTD device */ + int fd; /* File descriptor of underlying file */ + size_t nblocks; /* Number of erase blocks */ + size_t offset; /* Offset from start of file */ + size_t erasesize; /* Offset from start of file */ + size_t blocksize; /* Offset from start of file */ }; /**************************************************************************** @@ -270,8 +272,8 @@ static int file_erase(FAR struct mtd_dev_s *dev, off_t startblock, * corresponding to the number of blocks. */ - offset = startblock * CONFIG_FILEMTD_BLOCKSIZE; - nbytes = nblocks * CONFIG_FILEMTD_BLOCKSIZE; + offset = startblock * priv->blocksize; + nbytes = nblocks * priv->blocksize; /* Then erase the data in the file */ @@ -317,8 +319,8 @@ static ssize_t file_bread(FAR struct mtd_dev_s *dev, off_t startblock, * corresponding to the number of blocks. */ - offset = startblock * CONFIG_FILEMTD_BLOCKSIZE; - nbytes = nblocks * CONFIG_FILEMTD_BLOCKSIZE; + offset = startblock * priv->blocksize; + nbytes = nblocks * priv->blocksize; /* Then read the data from the file */ @@ -357,8 +359,8 @@ static ssize_t file_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, * corresponding to the number of blocks. */ - offset = startblock * CONFIG_FILEMTD_BLOCKSIZE; - nbytes = nblocks * CONFIG_FILEMTD_BLOCKSIZE; + offset = startblock * priv->blocksize; + nbytes = nblocks * priv->blocksize; /* Then write the data to the file */ @@ -379,7 +381,7 @@ static ssize_t file_byteread(FAR struct mtd_dev_s *dev, off_t offset, /* Don't let read read past end of buffer */ - if (offset + nbytes > priv->nblocks * CONFIG_FILEMTD_ERASESIZE) + if (offset + nbytes > priv->nblocks * priv->erasesize) { return 0; } @@ -403,7 +405,7 @@ static ssize_t file_bytewrite(FAR struct mtd_dev_s *dev, off_t offset, /* Don't let the write exceed the original size of the file */ - maxoffset = priv->nblocks * CONFIG_FILEMTD_ERASESIZE; + maxoffset = priv->nblocks * priv->erasesize; if (offset + nbytes > maxoffset) { return 0; @@ -438,8 +440,8 @@ static int file_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg) * the capacity and how to access the device. */ - geo->blocksize = CONFIG_FILEMTD_BLOCKSIZE; - geo->erasesize = CONFIG_FILEMTD_ERASESIZE; + geo->blocksize = priv->blocksize; + geo->erasesize = priv->erasesize; geo->neraseblocks = priv->nblocks; ret = OK; } @@ -482,7 +484,8 @@ static int file_ioctl(FAR struct mtd_dev_s *dev, int cmd, unsigned long arg) * ****************************************************************************/ -FAR struct mtd_dev_s *filemtd_initialize(FAR const char *path, size_t offset) +FAR struct mtd_dev_s *filemtd_initialize(FAR const char *path, size_t offset, + int16_t sectsize, int32_t erasesize) { FAR struct file_dev_s *priv; struct stat sb; @@ -527,9 +530,31 @@ FAR struct mtd_dev_s *filemtd_initialize(FAR const char *path, size_t offset) return NULL; } + /* Set the block size based on the provided sectsize parameter */ + + if (sectsize <= 0) + { + priv->blocksize = CONFIG_FILEMTD_BLOCKSIZE; + } + else + { + priv->blocksize = sectsize; + } + + /* Set the erase size based on the provided erasesize parameter */ + + if (erasesize <= 0) + { + priv->erasesize = CONFIG_FILEMTD_ERASESIZE; + } + else + { + priv->erasesize = erasesize; + } + /* Force the size to be an even number of the erase block size */ - nblocks = (filelen - offset) / CONFIG_FILEMTD_ERASESIZE; + nblocks = (filelen - offset) / priv->erasesize; if (nblocks < 3) { fdbg("Need to provide at least three full erase block\n"); @@ -591,3 +616,24 @@ void filemtd_teardown(FAR struct mtd_dev_s *dev) kmm_free(priv); } + +/**************************************************************************** + * Name: filemtd_isfilemtd + * + * Description: + * Tests if the provided mtd is a filemtd device. + * + * Input Parameters: + * mtd - Pointer to the mtd. + * + ****************************************************************************/ + +bool filemtd_isfilemtd(FAR struct mtd_dev_s *dev) +{ + FAR struct file_dev_s *priv = (FAR struct file_dev_s *) dev; + + if (priv->mtd.erase == file_erase) + return 1; + + return 0; +} diff --git a/drivers/mtd/smart.c b/drivers/mtd/smart.c index c82a3af1127d7825e2f76379fe851bcc34db9710..cb188c1854656932a3064bbad758f0978a413e9c 100644 --- a/drivers/mtd/smart.c +++ b/drivers/mtd/smart.c @@ -43,6 +43,8 @@ #include <sys/types.h> #include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> #include <stdint.h> #include <stdbool.h> #include <stdio.h> @@ -385,6 +387,14 @@ static int smart_relocate_static_data(FAR struct smart_struct_s *dev, uint16_t b static int smart_relocate_sector(FAR struct smart_struct_s *dev, uint16_t oldsector, uint16_t newsector); +#ifdef CONFIG_SMART_DEV_LOOP +static ssize_t smart_loop_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t smart_loop_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int smart_loop_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +#endif /* CONFIG_SMART_DEV_LOOP */ + /**************************************************************************** * Private Data ****************************************************************************/ @@ -403,6 +413,21 @@ static const struct block_operations g_bops = smart_ioctl /* ioctl */ }; +#ifdef CONFIG_SMART_DEV_LOOP +static const struct file_operations g_fops = +{ + 0, /* open */ + 0, /* close */ + smart_loop_read, /* read */ + smart_loop_write, /* write */ + 0, /* seek */ + smart_loop_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; +#endif /* CONFIG_SMART_DEV_LOOP */ + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -5411,6 +5436,10 @@ int smart_initialize(int minor, FAR struct mtd_dev_s *mtd, FAR const char *partn smart_scan(dev); } +#ifdef CONFIG_SMART_DEV_LOOP + (void)register_driver("/dev/smart", &g_fops, 0666, NULL); +#endif + return OK; errout: @@ -5437,3 +5466,210 @@ errout: kmm_free(dev); return ret; } + + /**************************************************************************** + * Name: smart_losetup + * + * Description: Dynamically setups up a SMART enabled loop device that + * is backed by a file. The resulting loop device is a + * MTD type block device vs. a generic block device. + * + ****************************************************************************/ + +#ifdef CONFIG_SMART_DEV_LOOP +static int smart_losetup(int minor, FAR const char *filename, + int sectsize, int erasesize, off_t offset, bool readonly) +{ + FAR struct mtd_dev_s *mtd; + struct stat sb; + int x, ret; + char devpath[20]; + + /* Try to create a filemtd device using the filename provided */ + + mtd = filemtd_initialize(filename, offset, sectsize, erasesize); + if (mtd == NULL) + { + return -ENOENT; + } + + /* Check if we need to dynamically assign a minor number */ + + if (minor == -1) + { + /* Start at zero and stat /dev/smartX until no entry found. + * Searching 0 to 256 should be sufficient. + */ + + for (x = 0; x < 256; x++) + { + snprintf(devpath, sizeof(devpath), "/dev/smart%d", x); + ret = stat(devpath, &sb); + if (ret != 0) + { + /* We can use this minor number */ + + minor = x; + break; + } + } + } + + /* Now create a smart MTD using the filemtd backing it */ + + ret = smart_initialize(minor, mtd, NULL); + + if (ret != OK) + { + filemtd_teardown(mtd); + } + + return ret; +} +#endif /* CONFIG_SMART_DEV_LOOP */ + +/**************************************************************************** + * Name: loteardown + * + * Description: + * Undo the setup performed by losetup + * + ****************************************************************************/ + +#ifdef CONFIG_SMART_DEV_LOOP +static int smart_loteardown(FAR const char *devname) +{ + FAR struct smart_struct_s *dev; + FAR struct inode *inode; + int ret; + + /* Sanity check */ + +#ifdef CONFIG_DEBUG + if (!devname) + { + return -EINVAL; + } +#endif + + /* Open the block driver associated with devname so that we can get the inode + * reference. + */ + + ret = open_blockdriver(devname, MS_RDONLY, &inode); + if (ret < 0) + { + dbg("Failed to open %s: %d\n", devname, -ret); + return ret; + } + + /* Inode private data is a reference to the loop device structure */ + + dev = (FAR struct smart_struct_s *)inode->i_private; + + /* Validate this is a filemtd backended device */ + + if (!filemtd_isfilemtd(dev->mtd)) + { + fdbg("Device is not a SMART loop: %s\n", devname); + return -EINVAL; + } + + close_blockdriver(inode); + + /* Now teardown the filemtd */ + + filemtd_teardown(dev->mtd); + unregister_blockdriver(devname); + + kmm_free(dev); + + return OK; +} +#endif /* CONFIG_SMART_DEV_LOOP */ + +/**************************************************************************** + * Name: smart_loop_read + ****************************************************************************/ + +#ifdef CONFIG_SMART_DEV_LOOP +static ssize_t smart_loop_read(FAR struct file *filep, FAR char *buffer, size_t len) +{ + return 0; /* Return EOF */ +} +#endif /* CONFIG_SMART_DEV_LOOP */ + +/**************************************************************************** + * Name: smart_loop_write + ****************************************************************************/ + +#ifdef CONFIG_SMART_DEV_LOOP +static ssize_t smart_loop_write(FAR struct file *filep, FAR const char *buffer, size_t len) +{ + return len; /* Say that everything was written */ +} +#endif /* CONFIG_SMART_DEV_LOOP */ + +/**************************************************************************** + * Name: smart_loop_ioctl + ****************************************************************************/ + +#ifdef CONFIG_SMART_DEV_LOOP +static int smart_loop_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + int ret; + + switch (cmd) + { + /* Command: LOOPIOC_SETUP + * Description: Setup the loop device + * Argument: A pointer to a read-only instance of struct losetup_s. + * Dependencies: The loop device must be enabled (CONFIG_DEV_LOOP=y) + */ + + case SMART_LOOPIOC_SETUP: + { + FAR struct smart_losetup_s *setup = (FAR struct smart_losetup_s *)((uintptr_t)arg); + + if (setup == NULL) + { + ret = -EINVAL; + } + else + { + ret = smart_losetup(setup->minor, setup->filename, setup->sectsize, + setup->erasesize, setup->offset, setup->readonly); + } + } + break; + + /* Command: LOOPIOC_TEARDOWN + * Description: Teardown a loop device previously setup vis LOOPIOC_SETUP + * Argument: A read-able pointer to the path of the device to be + * torn down + * Dependencies: The loop device must be enabled (CONFIG_DEV_LOOP=y) + */ + + case SMART_LOOPIOC_TEARDOWN: + { + FAR const char *devname = (FAR const char *)((uintptr_t)arg); + + if (devname == NULL) + { + ret = -EINVAL; + } + else + { + ret = smart_loteardown(devname); + } + } + break; + + default: + ret = -ENOTTY; + } + + return ret; +} +#endif /* CONFIG_SMART_DEV_LOOP */ + diff --git a/include/nuttx/fs/smart.h b/include/nuttx/fs/smart.h index f3bff10cb679663cba39ef49e62ce46498bf888e..ec217466aad6d81f31c10c78276a538c1d2d8ea5 100644 --- a/include/nuttx/fs/smart.h +++ b/include/nuttx/fs/smart.h @@ -45,6 +45,7 @@ #include <sys/types.h> #include <stdint.h> +#include <stdbool.h> /**************************************************************************** * Pre-processor Definitions @@ -64,6 +65,27 @@ #define SMARTFS_SECTOR_TYPE_DIR 1 #define SMARTFS_SECTOR_TYPE_FILE 2 +#ifdef CONFIG_SMART_DEV_LOOP +/* Loop device IOCTL commands */ + +/* Command: SMART_LOOPIOC_SETUP + * Description: Setup the loop device + * Argument: A pointer to a read-only instance of struct losetup_s. + * Dependencies: The loop device must be enabled (CONFIG_DEV_LOOP=y) + */ + +/* Command: SMART_LOOPIOC_TEARDOWN + * Description: Teardown a loop device previously setup vis LOOPIOC_SETUP + * Argument: A read-able pointer to the path of the device to be + * torn down + * Dependencies: The loop device must be enabled (CONFIG_DEV_LOOP=y) + */ + +#define SMART_LOOPIOC_SETUP _LOOPIOC(0x0001) +#define SMART_LOOPIOC_TEARDOWN _LOOPIOC(0x0002) + +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -112,6 +134,22 @@ struct smart_procfs_data_s }; #endif +#ifdef CONFIG_SMART_DEV_LOOP +/* This is the structure referred to in the argument to the LOOPIOC_SETUP + * IOCTL command. + */ + +struct smart_losetup_s +{ + FAR const char *filename; /* The file or character device to use */ + int minor; /* The minor number of thedevice */ + int erasesize; /* The erase size to use on the file */ + int sectsize; /* The sector / page size of the file */ + off_t offset; /* An offset that may be applied to the device */ + bool readonly; /* True: Read access will be supported only */ +}; +#endif + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/include/nuttx/mtd/mtd.h b/include/nuttx/mtd/mtd.h index 367fbad1b8a7dfdc7e5091b3ece1e3d1a0109577..39803509f81dbe932f0f883845100f6e04cbab18 100644 --- a/include/nuttx/mtd/mtd.h +++ b/include/nuttx/mtd/mtd.h @@ -525,17 +525,36 @@ FAR struct mtd_dev_s *s25fl1_initialize(FAR struct qspi_dev_s *qspi, FAR struct mtd_dev_s *up_flashinitialize(void); /**************************************************************************** - * Name: up_flashinitialize + * Name: filemtd_initialize * * Description: * Create a file backed MTD device. * ****************************************************************************/ -FAR struct mtd_dev_s *filemtd_initialize(FAR const char *path, size_t offset); +FAR struct mtd_dev_s *filemtd_initialize(FAR const char *path, size_t offset, + int16_t sectsize, int32_t erasesize); + +/**************************************************************************** + * Name: filemtd_teardown + * + * Description: + * Tear down a filemtd device. + * + ****************************************************************************/ void filemtd_teardown(FAR struct mtd_dev_s* mtd); +/**************************************************************************** + * Name: filemtd_isfilemtd + * + * Description: + * Test if MTD is a filemtd device. + * + ****************************************************************************/ + +bool filemtd_isfilemtd(FAR struct mtd_dev_s* mtd); + /**************************************************************************** * Name: mtd_register *