drivers/mtd: Add support for /dev/smart loop device. From Ken Petit
This commit is contained in:
parent
1f8f097783
commit
5821af1bbe
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
****************************************************************************/
|
||||
|
@ -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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user