summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-10-18 08:38:44 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-10-18 08:38:44 -0600
commitd5d32f0f503e378f4e73d645b92a792f7435fdc4 (patch)
treea73afffadc1212fa2c15dad84b235bc283850383
parentd7a76d63d3964ccbaf2d8d8898fc1fd0394e8a88 (diff)
downloadpx4-nuttx-d5d32f0f503e378f4e73d645b92a792f7435fdc4.tar.gz
px4-nuttx-d5d32f0f503e378f4e73d645b92a792f7435fdc4.tar.bz2
px4-nuttx-d5d32f0f503e378f4e73d645b92a792f7435fdc4.zip
Enhanced the FAT32 filesystem code to understand DMA preflight failures, and to use the file sector buffer as a bounce buffer when a user-supplied buffer is not suitable for DMA. From Mike Smith
-rw-r--r--nuttx/ChangeLog4
-rw-r--r--nuttx/fs/fat/fs_fat32.c115
2 files changed, 83 insertions, 36 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 5781d36fd..f2981386c 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -5806,3 +5806,7 @@
* drivers/mmcsd/mmscd_sdio.c: Enhanced the mmcdd_sdio driver to perform
DMA preflight operations and fail DMA read/write requests that fail
preflighting. From Mike Smith (2013-10-18).
+ * fs/fat/fs_fat32.c: Enhanced the FAT32 filesystem code to understand DMA
+ preflight failures, and to use the file sector buffer as a bounce buffer
+ when a user-supplied buffer is not suitable for DMA. From Mike Smith
+ (2013-10-18).
diff --git a/nuttx/fs/fat/fs_fat32.c b/nuttx/fs/fat/fs_fat32.c
index 7cfa7a8bc..f8c9a987e 100644
--- a/nuttx/fs/fat/fs_fat32.c
+++ b/nuttx/fs/fat/fs_fat32.c
@@ -265,7 +265,7 @@ static int fat_open(FAR struct file *filep, const char *relpath,
* directory path was found, but the file was not found in the
* final directory.
*/
-
+
else if (ret == -ENOENT)
{
/* The file does not exist. Were we asked to create it? */
@@ -287,7 +287,7 @@ static int fat_open(FAR struct file *filep, const char *relpath,
}
/* Fall through to finish the file open operation */
-
+
direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
}
@@ -355,7 +355,7 @@ static int fat_open(FAR struct file *filep, const char *relpath,
fs->fs_head = ff->ff_next;
fat_semgive(fs);
-
+
/* In write/append mode, we need to set the file pointer to the end of the file */
if ((oflags & (O_APPEND|O_WRONLY)) == (O_APPEND|O_WRONLY))
@@ -447,6 +447,7 @@ static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen)
uint8_t *userbuffer = (uint8_t*)buffer;
int sectorindex;
int ret;
+ bool force_indirect = false;
/* Sanity checks */
@@ -517,13 +518,15 @@ static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen)
{
bytesread = 0;
+fat_read_restart:
+
/* Check if the user has provided a buffer large enough to
* hold one or more complete sectors -AND- the read is
* aligned to a sector boundary.
*/
nsectors = buflen / fs->fs_hwsectorsize;
- if (nsectors > 0 && sectorindex == 0)
+ if (nsectors > 0 && sectorindex == 0 && !force_indirect)
{
/* Read maximum contiguous sectors directly to the user's
* buffer without using our tiny read buffer.
@@ -549,6 +552,22 @@ static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen)
ret = fat_hwread(fs, userbuffer, ff->ff_currentsector, nsectors);
if (ret < 0)
{
+#ifdef CONFIG_FAT_DMAMEMORY
+ /* The low-level driver may return -EFAULT in the case where
+ * the transfer cannot be performed due to DMA constraints.
+ * It is probable that the buffer is completely un-DMA-able,
+ * so force indirect transfers via the sector buffer and
+ * restart the operation.
+ */
+
+ if (ret == -EFAULT)
+ {
+ fdbg("DMA: read alignment error, restarting indirect\n");
+ force_indirect = true;
+ goto fat_read_restart;
+ }
+#endif
+
goto errout_with_semaphore;
}
@@ -558,7 +577,8 @@ static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen)
}
else
{
- /* We are reading a partial sector. First, read the whole sector
+ /* We are reading a partial sector, or handling a non-DMA-able
+ * whole-sector transfer. First, read the whole sector
* into the file data buffer. This is a caching buffer so if
* it is already there then all is well.
*/
@@ -569,7 +589,7 @@ static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen)
goto errout_with_semaphore;
}
- /* Copy the partial sector into the user buffer */
+ /* Copy the requested part of the sector into the user buffer */
bytesread = fs->fs_hwsectorsize - sectorindex;
if (bytesread > buflen)
@@ -645,6 +665,7 @@ static ssize_t fat_write(FAR struct file *filep, const char *buffer,
uint8_t *userbuffer = (uint8_t*)buffer;
int sectorindex;
int ret;
+ bool force_indirect = false;
/* Sanity checks. I have seen the following assertion misfire if
* CONFIG_DEBUG_MM is enabled while re-directing output to a
@@ -735,8 +756,10 @@ static ssize_t fat_write(FAR struct file *filep, const char *buffer,
* hold one or more complete sectors.
*/
+fat_write_restart:
+
nsectors = buflen / fs->fs_hwsectorsize;
- if (nsectors > 0 && sectorindex == 0)
+ if (nsectors > 0 && sectorindex == 0 && !force_indirect)
{
/* Write maximum contiguous sectors directly from the user's
* buffer without using our tiny read buffer.
@@ -763,6 +786,22 @@ static ssize_t fat_write(FAR struct file *filep, const char *buffer,
ret = fat_hwwrite(fs, userbuffer, ff->ff_currentsector, nsectors);
if (ret < 0)
{
+#ifdef CONFIG_FAT_DMAMEMORY
+ /* The low-level driver may return -EFAULT in the case where
+ * the transfer cannot be performed due to DMA constraints.
+ * It is probable that the buffer is completely un-DMA-able,
+ * so force indirect transfers via the sector buffer and
+ * restart the operation.
+ */
+
+ if (ret == -EFAULT)
+ {
+ fdbg("DMA: write alignment error, restarting indirect\n");
+ force_indirect = true;
+ goto fat_write_restart;
+ }
+#endif
+
goto errout_with_semaphore;
}
@@ -773,30 +812,22 @@ static ssize_t fat_write(FAR struct file *filep, const char *buffer,
}
else
{
- /* We are writing a partial sector -OR- the current sector
- * has not yet been filled.
+ /* Decide whether we are performing a read-modify-write
+ * operation, in which case we have to read the existing sector
+ * into the buffer first.
+ *
+ * There are two cases where we can avoid this read:
*
- * We will first have to read the full sector in memory as
- * part of a read-modify-write operation. NOTE we don't
- * have to read the data on a rare case: When we are extending
- * the file (filep->f_pos == ff->ff_size) -AND- the new data
- * happens to be aligned at the beginning of the sector
- * (sectorindex == 0).
+ * - If we are performing a whole-sector write that was rejected
+ * by fat_hwwrite(), i.e. sectorindex == 0 and buflen >= sector size.
+ *
+ * - If the write is aligned to the beginning of the sector and
+ * extends beyond the end of the file, i.e. sectorindex == 0 and
+ * file pos + buflen >= file size.
*/
- if (filep->f_pos < ff->ff_size || sectorindex != 0)
- {
- /* Read the current sector into memory (perhaps first flushing
- * the old, dirty sector to disk).
- */
-
- ret = fat_ffcacheread(fs, ff, ff->ff_currentsector);
- if (ret < 0)
- {
- goto errout_with_semaphore;
- }
- }
- else
+ if ((sectorindex == 0) && ((buflen >= fs->fs_hwsectorsize) ||
+ ((filep->f_pos + buflen) >= ff->ff_size)))
{
/* Flush unwritten data in the sector cache. */
@@ -810,15 +841,27 @@ static ssize_t fat_write(FAR struct file *filep, const char *buffer,
ff->ff_cachesector = ff->ff_currentsector;
}
+ else
+ {
+ /* Read the current sector into memory (perhaps first flushing
+ * the old, dirty sector to disk).
+ */
- /* Copy the partial sector from the user buffer */
+ ret = fat_ffcacheread(fs, ff, ff->ff_currentsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+ }
+
+ /* Copy the requested part of the sector from the user buffer */
writesize = fs->fs_hwsectorsize - sectorindex;
if (writesize > buflen)
{
- /* We will not write to the end of the buffer. Set
- * write size to the size of the user buffer.
- */
+ /* We will not write to the end of the buffer. Set
+ * write size to the size of the user buffer.
+ */
writesize = buflen;
}
@@ -1445,7 +1488,7 @@ static int fat_opendir(struct inode *mountpt, const char *relpath, struct fs_dir
{
/* The entry is a directory (but not the root directory) */
- dir->u.fat.fd_startcluster =
+ dir->u.fat.fd_startcluster =
((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
DIR_GETFSTCLUSTLO(direntry);
dir->u.fat.fd_currcluster = dir->u.fat.fd_startcluster;
@@ -1499,7 +1542,7 @@ static int fat_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
dir->fd_dir.d_name[0] = '\0';
found = false;
-
+
while (dir->u.fat.fd_currsector && !found)
{
ret = fat_fscacheread(fs, dir->u.fat.fd_currsector);
@@ -2012,7 +2055,7 @@ static int fat_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
goto errout_with_semaphore;
}
- /* Flush any existing, dirty data in fs_buffer (because we need
+ /* Flush any existing, dirty data in fs_buffer (because we need
* it to create the directory entries.
*/
@@ -2294,7 +2337,7 @@ int fat_rename(struct inode *mountpt, const char *oldrelpath,
{
goto errout_with_semaphore;
}
-
+
/* Write the old entry to disk and update FSINFO if necessary */
ret = fat_updatefsinfo(fs);