drivers/mtd: Add support for /dev/smart loop device. From Ken Petit

This commit is contained in:
Ken Pettit 2015-11-28 09:00:26 -06:00 committed by Gregory Nutt
parent 1f8f097783
commit 5821af1bbe
7 changed files with 393 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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