summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2007-05-20 16:58:37 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2007-05-20 16:58:37 +0000
commite5a7df43992dcc514b4bb1bf9ab93ae25d0b8ed2 (patch)
tree5597f0667121d711e1b7370a2b238213bde00be0
parent80c8fab4acd3dcdd301d39db7d16d2e5993c76af (diff)
downloadnuttx-e5a7df43992dcc514b4bb1bf9ab93ae25d0b8ed2.tar.gz
nuttx-e5a7df43992dcc514b4bb1bf9ab93ae25d0b8ed2.tar.bz2
nuttx-e5a7df43992dcc514b4bb1bf9ab93ae25d0b8ed2.zip
FAT read logic integrated
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@237 42af7a65-404d-4744-a932-0658087f49c3
-rw-r--r--nuttx/fs/fs_fat32.c209
-rw-r--r--nuttx/fs/fs_fat32.h58
-rw-r--r--nuttx/fs/fs_fat32util.c819
3 files changed, 719 insertions, 367 deletions
diff --git a/nuttx/fs/fs_fat32.c b/nuttx/fs/fs_fat32.c
index 007bfdcec..9d75e9a49 100644
--- a/nuttx/fs/fs_fat32.c
+++ b/nuttx/fs/fs_fat32.c
@@ -254,6 +254,15 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
goto errout_with_semaphore;
}
+ /* Create a file buffer to support partial sector accesses */
+
+ ff->ff_buffer = (ubyte*)malloc(fs->fs_hwsectorsize);
+ if (!ff->ff_buffer)
+ {
+ ret = -ENOMEM;
+ goto errout_with_struct;
+ }
+
/* Initialize the file private data (only need to initialize non-zero elements) */
ff->ff_open = TRUE;
@@ -262,7 +271,7 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
/* Save information that can be used later to recover the directory entry */
- ff->ff_dirsector = fs->fs_sector;
+ ff->ff_dirsector = fs->fs_currentsector;
ff->ff_dirindex = dirinfo.fd_index;
/* File cluster/size info */
@@ -279,7 +288,6 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
{
ff->ff_position = ff->ff_size;
}
- return OK;
/* Attach the private date to the struct file instance */
@@ -297,7 +305,12 @@ static int fat_open(FAR struct file *filp, const char *rel_path,
fat_semgive(fs);
return OK;
- /* Error exits */
+ /* Error exits -- goto's are nasty things, but they sure can make error
+ * handling a lot simpler.
+ */
+
+ errout_with_struct:
+ free(ff);
errout_with_semaphore:
fat_semgive(fs);
@@ -342,6 +355,13 @@ static ssize_t fat_read(FAR struct file *filp, char *buffer, size_t buflen)
struct inode *inode;
struct fat_mountpt_s *fs;
struct fat_file_s *ff;
+ unsigned int cluster;
+ unsigned int bytesread;
+ unsigned int readsize;
+ unsigned int nsectors;
+ size_t readsector;
+ size_t bytesleft;
+ char *userbuffer = buffer;
int ret;
/* Sanity checks */
@@ -358,12 +378,174 @@ static ssize_t fat_read(FAR struct file *filp, char *buffer, size_t buflen)
/* Make sure that the mount is still healthy */
+ fat_semtake(fs);
ret = fat_checkmount(fs);
if (ret != OK)
{
- return ret;
+ goto errout_with_semaphore;
}
- return -ENOSYS;
+
+ /* Check if the file was opened with read access */
+
+ if ((ff->ff_oflags & O_RDOK) == 0)
+ {
+ ret= -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* Get the number of bytes left in the file */
+
+ bytesleft = ff->ff_size - ff->ff_position;
+
+ /* Truncate read count so that it does not exceed the number
+ * of bytes left in the file.
+ */
+
+ if (buflen > bytesleft)
+ {
+ buflen = bytesleft;
+ }
+
+ /* Loop until either (1) all data has been transferred, or (2) an
+ * error occurs.
+ */
+
+ readsize = 0;
+ while (buflen > 0)
+ {
+ /* Get offset into the sector where we begin the read */
+
+ int sectorindex = ff->ff_position & SEC_NDXMASK(fs);
+
+ /* Check if the current read stream happens to lie on a
+ * sector boundary.
+ */
+
+ if (sectorindex == 0)
+ {
+ /* Try to read another contiguous sector from the cluster */
+
+ ff->ff_sectorsincluster--;
+
+ /* Are there unread sectors remaining in the cluster? */
+
+ if (ff->ff_sectorsincluster > 0)
+ {
+ /* Yes.. There are more sectors in this cluster to be read
+ * just increment the current sector number and read.
+ */
+
+ readsector = ff->ff_currentsector + 1;
+ }
+ else
+ {
+ /* No.. Handle a special case of the first sector */
+
+ if (ff->ff_position == 0)
+ {
+ cluster = ff->ff_startcluster;
+ }
+
+ /* But in the general case, we have to find the next cluster
+ * in the FAT.
+ */
+
+ else
+ {
+ cluster = fat_getcluster(fs, ff->ff_currentcluster);
+ }
+
+ /* Verify the cluster number */
+
+ if (cluster < 2 || cluster >= fs->fs_nclusters)
+ {
+ ret = -EINVAL; /* Not the right error */
+ goto errout_with_semaphore;
+ }
+
+ /* Setup to read the first sector from the new cluster */
+
+ ff->ff_currentcluster = cluster;
+ readsector = fat_cluster2sector(fs, cluster);
+ ff->ff_sectorsincluster = fs->fs_fatsecperclus;
+ }
+
+ /* Check if the user has provided a buffer large enough to
+ * hold one or more complete sectors.
+ */
+
+ nsectors = buflen / fs->fs_hwsectorsize;
+ if (nsectors > 0)
+ {
+ /* Read maximum contiguous sectors directly without using
+ * our tiny read buffer.
+ *
+ * Limit the number of sectors that we read on this time
+ * through the loop to the remaining contiguous sectors
+ * in this cluster
+ */
+
+ if (nsectors > ff->ff_sectorsincluster)
+ {
+ nsectors = ff->ff_sectorsincluster;
+ }
+
+ /* We are not sure of the state of the file buffer so
+ * the safest thing to do is just invalidate it
+ */
+
+ (void)fat_ffcacheinvalidate(fs, ff);
+
+ /* Read all of the sectors directory into user memory */
+
+ ret = fat_hwread(fs, userbuffer, readsector, nsectors);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ ff->ff_sectorsincluster -= nsectors - 1;
+ bytesread = nsectors * fs->fs_hwsectorsize;
+ }
+ else
+ {
+ /* We are reading a partial sector. 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.
+ */
+
+ ret = fat_ffcacheread(fs, ff, readsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Copy the partial sector into the user buffer */
+
+ bytesread = fs->fs_hwsectorsize - sectorindex;
+ if (bytesread > buflen)
+ {
+ bytesread = buflen;
+ }
+
+ memcpy(userbuffer, &ff->ff_buffer[sectorindex], bytesread);
+ }
+
+ /* Set up for the next sector read */
+
+ userbuffer += bytesread;
+ ff->ff_position += bytesread;
+ readsize += bytesread;
+ buflen -= bytesread;
+ }
+ }
+
+ fat_semgive(fs);
+ return readsize;
+
+ errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
}
/****************************************************************************
@@ -392,6 +574,7 @@ static ssize_t fat_write(FAR struct file *filp, const char *buffer,
/* Make sure that the mount is still healthy */
+ fat_semtake(fs);
ret = fat_checkmount(fs);
if (ret != OK)
{
@@ -425,6 +608,7 @@ static off_t fat_seek(FAR struct file *filp, off_t offset, int whence)
/* Make sure that the mount is still healthy */
+ fat_semtake(fs);
ret = fat_checkmount(fs);
if (ret != OK)
{
@@ -458,6 +642,7 @@ static int fat_ioctl(FAR struct file *filp, int cmd, unsigned long arg)
/* Make sure that the mount is still healthy */
+ fat_semtake(fs);
ret = fat_checkmount(fs);
if (ret != OK)
{
@@ -485,7 +670,21 @@ static int fat_bind(FAR struct inode *blkdriver, const void *data,
struct fat_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 fat_mountpt_s *)zalloc(sizeof(struct fat_mountpt_s));
if ( !fs )
{
diff --git a/nuttx/fs/fs_fat32.h b/nuttx/fs/fs_fat32.h
index ecb5ff66c..e6e5a5b85 100644
--- a/nuttx/fs/fs_fat32.h
+++ b/nuttx/fs/fs_fat32.h
@@ -146,12 +146,6 @@
#define FATATTR_LONGNAME \
(FATATTR_READONLY|FATATTR_HIDDEN|FATATTR_SYSTEM|FATATTR_VOLUMEID)
-/* File system types */
-
-#define FSTYPE_FAT12 0
-#define FSTYPE_FAT16 1
-#define FSTYPE_FAT32 2
-
/* Directory indexing helper. Each directory entry is 32-bytes in length.
* The number of directory entries in a sector then varies with the size
* of the sector supported in hardware.
@@ -165,6 +159,19 @@
#define SEC_NSECTORS(f,n) ((n) / (f)->fs_hwsectorsize)
/****************************************************************************
+ * File system types */
+
+#define FSTYPE_FAT12 0
+#define FSTYPE_FAT16 1
+#define FSTYPE_FAT32 2
+
+/* File buffer flags */
+
+#define FFBUFF_VALID 1
+#define FFBUFF_DIRTY 2
+#define FFBUFF_MODIFIED 4
+
+/****************************************************************************
* These offset describe the FSINFO sector
*/
@@ -413,7 +420,7 @@ struct fat_mountpt_s
size_t fs_rootbase; /* MBR: Cluster no. of 1st cluster of root dir */
size_t fs_database; /* Logical block of start data sectors */
size_t fs_fsinfo; /* MBR: Sector number of FSINFO sector */
- size_t fs_sector; /* The sector number buffered in fs_buffer */
+ size_t fs_currentsector; /* The sector number buffered in fs_buffer */
uint32 fs_nclusters; /* Maximum number of data clusters */
uint32 fs_fatsize; /* MBR: Count of sectors occupied by one fat */
uint32 fs_fattotsec; /* MBR: Total count of sectors on the volume */
@@ -439,13 +446,17 @@ struct fat_file_s
{
struct fat_file_s *ff_next; /* Retained in a singly linked list */
boolean ff_open; /* TRUE: The file is (still) open */
+ boolean ff_bflags; /* The file buffer flags */
ubyte ff_oflags; /* Flags provided when file was opened */
ubyte ff_sectorsincluster; /* Sectors remaining in cluster */
uint16 ff_dirindex; /* Index into ff_dirsector to directory entry */
+ uint32 ff_currentcluster; /* Current cluster being accessed */
size_t ff_dirsector; /* Sector containing the directory entry */
size_t ff_position; /* File position for read/write/seek in bytes */
size_t ff_size; /* Size of the file in bytes */
size_t ff_startcluster; /* Start cluster of file on media */
+ size_t ff_currentsector; /* Current sector in the file buffer */
+ ubyte *ff_buffer; /* File buffer (for partial sector accesses) */
};
/* This structure is used internally for describing directory entries */
@@ -480,19 +491,52 @@ extern "C" {
#define EXTERN extern
#endif
+/* Utitilies to handle unaligned or byte swapped accesses */
+
EXTERN uint16 fat_getuint16(ubyte *ptr);
EXTERN uint32 fat_getuint32(ubyte *ptr);
EXTERN void fat_putuint16(ubyte *ptr, uint16 value16);
EXTERN void fat_putuint32(ubyte *ptr, uint32 value32);
+
+/* Manage the per-mount semaphore that protects access to shared resources */
+
EXTERN void fat_semtake(struct fat_mountpt_s *fs);
EXTERN void fat_semgive(struct fat_mountpt_s *fs);
+
+/* Handle hardware interactions for mounting */
+
EXTERN int fat_mount(struct fat_mountpt_s *fs, boolean writeable);
EXTERN int fat_checkmount(struct fat_mountpt_s *fs);
+
+/* low-level hardware access */
+
+EXTERN int fat_hwread(struct fat_mountpt_s *fs, ubyte *buffer, size_t sector,
+ unsigned int nsectors);
+EXTERN int fat_hwwrite(struct fat_mountpt_s *fs, ubyte *buffer, size_t sector,
+ unsigned int nsectors);
+
+/* Cluster access helpers */
+
+EXTERN ssize_t fat_cluster2sector(struct fat_mountpt_s *fs, uint32 cluster );
+EXTERN ssize_t fat_getcluster(struct fat_mountpt_s *fs, unsigned int clusterno);
+EXTERN int fat_putcluster(struct fat_mountpt_s *fs, unsigned int clusterno, size_t startsector);
+
+/* Help for traverseing directory trees */
+
EXTERN int fat_nextdirentry(struct fat_dirinfo_s *dirinfo);
EXTERN int fat_finddirentry(struct fat_dirinfo_s *dirinfo, const char *path);
+
+/* File creation helpers */
+
EXTERN int fat_dirtruncate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
EXTERN int fat_dircreate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
+/* File buffer cache (for partial sector accesses) */
+
+EXTERN int fat_ffcacheflush(struct fat_mountpt_s *fs, struct fat_file_s *ff);
+EXTERN int fat_ffcacheread(struct fat_mountpt_s *fs, struct fat_file_s *ff, size_t sector);
+EXTERN int fat_ffcacheinvalidate(struct fat_mountpt_s *fs, struct fat_file_s *ff);
+
#undef EXTERN
#if defined(__cplusplus)
}
diff --git a/nuttx/fs/fs_fat32util.c b/nuttx/fs/fs_fat32util.c
index 6ca885496..9cfce4dc9 100644
--- a/nuttx/fs/fs_fat32util.c
+++ b/nuttx/fs/fs_fat32util.c
@@ -79,88 +79,25 @@
****************************************************************************/
/****************************************************************************
- * Name: fat_hwread
- *
- * Desciption: Read the specified sector into the sector buffer
- *
- ****************************************************************************/
-
-static int fat_hwread(struct fat_mountpt_s *fs, ubyte *buffer,
- size_t sector, unsigned int nsectors)
-{
- int ret = -ENODEV;
- if (fs && fs->fs_blkdriver )
- {
- struct inode *inode = fs->fs_blkdriver;
- if (inode && inode->u.i_bops && inode->u.i_bops->read)
- {
- ssize_t nSectorsRead = inode->u.i_bops->read(inode, buffer,
- sector, nsectors);
- if (nSectorsRead == nsectors)
- {
- ret = OK;
- }
- else if (nSectorsRead < 0)
- {
- ret = nSectorsRead;
- }
- }
- }
- return ret;
-}
-
-/****************************************************************************
- * Name: fat_hwwrite
- *
- * Desciption: Write the sector buffer to the specified sector
- *
- ****************************************************************************/
-
-static int fat_hwwrite(struct fat_mountpt_s *fs, ubyte *buffer,
- size_t sector, unsigned int nsectors)
-{
- int ret = -ENODEV;
- if (fs && fs->fs_blkdriver )
- {
- struct inode *inode = fs->fs_blkdriver;
- if (inode && inode->u.i_bops && inode->u.i_bops->write)
- {
- ssize_t nSectorsWritten =
- inode->u.i_bops->write(inode, buffer, sector, nsectors);
-
- if (nSectorsWritten == nsectors)
- {
- ret = OK;
- }
- else if (nSectorsWritten < 0)
- {
- ret = nSectorsWritten;
- }
- }
- }
- return ret;
-}
-
-/****************************************************************************
- * Name: fat_cacheflush
+ * Name: fat_fscacheflush
*
* Desciption: Flush any dirty sectors as necessary
*
****************************************************************************/
-static int fat_cacheflush(struct fat_mountpt_s *fs)
+static int fat_fscacheflush(struct fat_mountpt_s *fs)
{
int ret;
/* Check if the fs_buffer is dirty. In this case, we will write back the
- * contents of fs_buffer.
+ * contents of fs_buffer.
*/
if (fs->fs_dirty)
{
/* Write the dirty sector */
- ret = fat_hwwrite(fs, fs->fs_buffer, fs->fs_sector, 1);
+ ret = fat_hwwrite(fs, fs->fs_buffer, fs->fs_currentsector, 1);
if (ret < 0)
{
return ret;
@@ -168,15 +105,15 @@ static int fat_cacheflush(struct fat_mountpt_s *fs)
/* Does the sector lie in the FAT region? */
- if (fs->fs_sector < fs->fs_fatbase + fs->fs_fatsize)
+ if (fs->fs_currentsector < fs->fs_fatbase + fs->fs_fatsize)
{
/* Yes, then make the change in the FAT copy as well */
int i;
for (i = fs->fs_fatnumfats; i >= 2; i--)
{
- fs->fs_sector += fs->fs_fatsize;
- ret = fat_hwwrite(fs, fs->fs_buffer, fs->fs_sector, 1);
+ fs->fs_currentsector += fs->fs_fatsize;
+ ret = fat_hwwrite(fs, fs->fs_buffer, fs->fs_currentsector, 1);
if (ret < 0)
{
return ret;
@@ -192,29 +129,29 @@ static int fat_cacheflush(struct fat_mountpt_s *fs)
}
/****************************************************************************
- * Name: fat_cacheread
+ * Name: fat_fscacheread
*
* Desciption: Read the specified sector into the sector cache, flushing any
* existing dirty sectors as necessary.
*
****************************************************************************/
-static int fat_cacheread(struct fat_mountpt_s *fs, uint32 sector)
+static int fat_fscacheread(struct fat_mountpt_s *fs, size_t sector)
{
int ret;
- /* fs->sector holds the current sector that is buffered in fs->fs_buffer.
- * If the requested sector is the same as this sector, then we do nothing.
- * Otherwise, we will have to read the new sector.
+ /* fs->fs_currentsector holds the current sector that is buffered in
+ * fs->fs_buffer. If the requested sector is the same as this sector, then
+ * we do nothing. Otherwise, we will have to read the new sector.
*/
- if (fs->fs_sector != sector)
+ if (fs->fs_currentsector != sector)
{
/* We will need to read the new sector. First, flush the cached
* sector if it is dirty.
*/
- ret = fat_cacheflush(fs);
+ ret = fat_fscacheflush(fs);
if (ret < 0)
{
return ret;
@@ -230,288 +167,13 @@ static int fat_cacheread(struct fat_mountpt_s *fs, uint32 sector)
/* Update the cached sector number */
- fs->fs_sector = sector;
+ fs->fs_currentsector = sector;
}
- return OK;
-}
-
-/****************************************************************************
- * Name: fat_clustger2sector
- *
- * Desciption: Convert a cluster number to a start sector number
- *
- ****************************************************************************/
-
-static ssize_t fat_cluster2sector(struct fat_mountpt_s *fs, uint32 cluster )
-{
- cluster -= 2;
- if (cluster >= fs->fs_nclusters - 2)
- {
- return -EINVAL;
- }
- return cluster * fs->fs_fatsecperclus + fs->fs_database;
-}
-
-/****************************************************************************
- * Name: fat_getcluster
- *
- * Desciption: Get the cluster start sector into the FAT
- *
- ****************************************************************************/
-
-static ssize_t fat_getcluster(struct fat_mountpt_s *fs, unsigned int clusterno)
-{
- /* Verify that the cluster number is within range */
-
- if (clusterno >= 2 && clusterno < fs->fs_nclusters)
- {
- /* Okay.. Read the next cluster from the FAT. The way we will do
- * this depends on the type of FAT filesystm we are dealing with.
- */
- switch (fs->fs_type)
- {
- case FSTYPE_FAT12 :
- {
- size_t fatsector;
- unsigned int fatoffset;
- unsigned int startsector;
- unsigned int fatindex;
-
- /* FAT12 is more complex because it has 12-bits (1.5 bytes)
- * per FAT entry. Get the offset to the first byte:
- */
-
- fatoffset = (clusterno * 3) / 2;
- fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
-
- /* Read the sector at this offset */
-
- if (fat_cacheread(fs, fatsector) < 0)
- {
- /* Read error */
- break;
- }
-
- /* Get the first, LS byte of the cluster from the FAT */
-
- fatindex = fatoffset & SEC_NDXMASK(fs);
- startsector = fs->fs_buffer[fatindex];
-
- /* With FAT12, the second byte of the cluster number may lie in
- * a different sector than the first byte.
- */
-
- fatindex++;
- if (fatindex >= fs->fs_hwsectorsize)
- {
- fatsector++;
- fatindex = 0;
-
- if (fat_cacheread(fs, fatsector) < 0)
- {
- /* Read error */
- break;
- }
- }
-
- /* Get the second, MS byte of the cluster for 16-bits. The
- * does not depend on the endian-ness of the target, but only
- * on the fact that the byte stream is little-endian.
- */
-
- startsector |= (unsigned int)fs->fs_buffer[fatindex] << 8;
-
- /* Now, pick out the correct 12 bit cluster start sector value */
-
- if ((clusterno & 1) != 0)
- {
- /* Odd.. take the MS 12-bits */
- startsector >>= 4;
- }
- else
- {
- /* Even.. take the LS 12-bits */
- startsector &= 0x0fff;
- }
- return startsector;
- }
-
- case FSTYPE_FAT16 :
- {
- unsigned int fatoffset = 2 * clusterno;
- size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
- unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
-
- if (fat_cacheread(fs, fatsector) < 0)
- {
- /* Read error */
- break;
- }
- return FAT_GETFAT16(fs->fs_buffer, fatindex);
- }
-
- case FSTYPE_FAT32 :
- {
- unsigned int fatoffset = 4 * clusterno;
- size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
- unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
-
- if (fat_cacheread(fs, fatsector) < 0)
- {
- /* Read error */
- break;
- }
- return FAT_GETFAT16(fs->fs_buffer, fatindex) & 0x0fffffff;
- }
- default:
- break;
- }
- }
-
- /* There is no cluster information, or an error occured */
- return (ssize_t)ERROR;
+ return OK;
}
/****************************************************************************
- * Name: fat_putcluster
- *
- * Desciption: Write a new cluster start sector into the FAT
- *
- ****************************************************************************/
-
-static int fat_putcluster(struct fat_mountpt_s *fs, unsigned int clusterno, size_t startsector)
-{
- /* Verify that the cluster number is within range. Zero erases the cluster. */
-
- if (clusterno == 0 || (clusterno >= 2 && clusterno < fs->fs_nclusters))
- {
- /* Okay.. Write the next cluster into the FAT. The way we will do
- * this depends on the type of FAT filesystm we are dealing with.
- */
-
- switch (fs->fs_type)
- {
- case FSTYPE_FAT12 :
- {
- size_t fatsector;
- unsigned int fatoffset;
- unsigned int fatindex;
- ubyte value;
-
- /* FAT12 is more complex because it has 12-bits (1.5 bytes)
- * per FAT entry. Get the offset to the first byte:
- */
-
- fatoffset = (clusterno * 3) / 2;
- fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
-
- /* Make sure that the sector at this offset is in the cache */
-
- if (fat_cacheread(fs, fatsector)< 0)
- {
- /* Read error */
- break;
- }
-
- /* Output the LS byte first handling the 12-bit alignment within
- * the 16-bits
- */
-
- fatindex = fatoffset & SEC_NDXMASK(fs);
- if ((clusterno & 1) != 0)
- {
- value = (fs->fs_buffer[fatindex] & 0x0f) | startsector << 4;
- }
- else
- {
- value = (ubyte)startsector;
- }
- fs->fs_buffer[fatindex] = value;
-
- /* With FAT12, the second byte of the cluster number may lie in
- * a different sector than the first byte.
- */
-
- fatindex++;
- if (fatindex >= fs->fs_hwsectorsize)
- {
- /* Read the next sector */
-
- fatsector++;
- fatindex = 0;
-
- /* Set the dirty flag to make sure the sector that we
- * just modified is written out.
- */
-
- fs->fs_dirty = TRUE;
- if (fat_cacheread(fs, fatsector) < 0)
- {
- /* Read error */
- break;
- }
- }
-
- /* Output the MS byte first handling the 12-bit alignment within
- * the 16-bits
- */
-
- if ((clusterno & 1) != 0)
- {
- value = (ubyte)(startsector >> 4);
- }
- else
- {
- value = (fs->fs_buffer[fatindex] & 0xf0) | (startsector & 0x0f);
- }
- fs->fs_buffer[fatindex] = value;
- }
- break;
-
- case FSTYPE_FAT16 :
- {
- unsigned int fatoffset = 2 * clusterno;
- size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
- unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
-
- if (fat_cacheread(fs, fatsector) < 0)
- {
- /* Read error */
- break;
- }
- FAT_PUTFAT16(fs->fs_buffer, fatindex, startsector & 0xffff);
- }
- break;
-
- case FSTYPE_FAT32 :
- {
- unsigned int fatoffset = 4 * clusterno;
- size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
- unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
-
- if (fat_cacheread(fs, fatsector) < 0)
- {
- /* Read error */
- break;
- }
- FAT_PUTFAT32(fs->fs_buffer, fatindex, startsector & 0x0fffffff);
- }
- break;
-
- default:
- return ERROR;
- }
-
- /* Mark the modified sector as "dirty" and return success */
-
- fs->fs_dirty = 1;
- return OK;
- }
- return ERROR;
- }
-
-/****************************************************************************
* Name: fat_path2dirname
*
* Desciption: Convert a user filename into a properly formatted FAT
@@ -1266,6 +928,345 @@ int fat_checkmount(struct fat_mountpt_s *fs)
}
/****************************************************************************
+ * Name: fat_hwread
+ *
+ * Desciption: Read the specified sector into the sector buffer
+ *
+ ****************************************************************************/
+
+int fat_hwread(struct fat_mountpt_s *fs, ubyte *buffer, size_t sector,
+ unsigned int nsectors)
+{
+ int ret = -ENODEV;
+ if (fs && fs->fs_blkdriver )
+ {
+ struct inode *inode = fs->fs_blkdriver;
+ if (inode && inode->u.i_bops && inode->u.i_bops->read)
+ {
+ ssize_t nSectorsRead = inode->u.i_bops->read(inode, buffer,
+ sector, nsectors);
+ if (nSectorsRead == nsectors)
+ {
+ ret = OK;
+ }
+ else if (nSectorsRead < 0)
+ {
+ ret = nSectorsRead;
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_hwwrite
+ *
+ * Desciption: Write the sector buffer to the specified sector
+ *
+ ****************************************************************************/
+
+int fat_hwwrite(struct fat_mountpt_s *fs, ubyte *buffer, size_t sector,
+ unsigned int nsectors)
+{
+ int ret = -ENODEV;
+ if (fs && fs->fs_blkdriver )
+ {
+ struct inode *inode = fs->fs_blkdriver;
+ if (inode && inode->u.i_bops && inode->u.i_bops->write)
+ {
+ ssize_t nSectorsWritten =
+ inode->u.i_bops->write(inode, buffer, sector, nsectors);
+
+ if (nSectorsWritten == nsectors)
+ {
+ ret = OK;
+ }
+ else if (nSectorsWritten < 0)
+ {
+ ret = nSectorsWritten;
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_cluster2sector
+ *
+ * Desciption: Convert a cluster number to a start sector number
+ *
+ ****************************************************************************/
+
+ssize_t fat_cluster2sector(struct fat_mountpt_s *fs, uint32 cluster )
+{
+ cluster -= 2;
+ if (cluster >= fs->fs_nclusters - 2)
+ {
+ return -EINVAL;
+ }
+ return cluster * fs->fs_fatsecperclus + fs->fs_database;
+}
+
+/****************************************************************************
+ * Name: fat_getcluster
+ *
+ * Desciption: Get the cluster start sector into the FAT
+ *
+ ****************************************************************************/
+
+ssize_t fat_getcluster(struct fat_mountpt_s *fs, unsigned int clusterno)
+{
+ /* Verify that the cluster number is within range */
+
+ if (clusterno >= 2 && clusterno < fs->fs_nclusters)
+ {
+ /* Okay.. Read the next cluster from the FAT. The way we will do
+ * this depends on the type of FAT filesystm we are dealing with.
+ */
+
+ switch (fs->fs_type)
+ {
+ case FSTYPE_FAT12 :
+ {
+ size_t fatsector;
+ unsigned int fatoffset;
+ unsigned int startsector;
+ unsigned int fatindex;
+
+ /* FAT12 is more complex because it has 12-bits (1.5 bytes)
+ * per FAT entry. Get the offset to the first byte:
+ */
+
+ fatoffset = (clusterno * 3) / 2;
+ fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+
+ /* Read the sector at this offset */
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+
+ /* Get the first, LS byte of the cluster from the FAT */
+
+ fatindex = fatoffset & SEC_NDXMASK(fs);
+ startsector = fs->fs_buffer[fatindex];
+
+ /* With FAT12, the second byte of the cluster number may lie in
+ * a different sector than the first byte.
+ */
+
+ fatindex++;
+ if (fatindex >= fs->fs_hwsectorsize)
+ {
+ fatsector++;
+ fatindex = 0;
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ }
+
+ /* Get the second, MS byte of the cluster for 16-bits. The
+ * does not depend on the endian-ness of the target, but only
+ * on the fact that the byte stream is little-endian.
+ */
+
+ startsector |= (unsigned int)fs->fs_buffer[fatindex] << 8;
+
+ /* Now, pick out the correct 12 bit cluster start sector value */
+
+ if ((clusterno & 1) != 0)
+ {
+ /* Odd.. take the MS 12-bits */
+ startsector >>= 4;
+ }
+ else
+ {
+ /* Even.. take the LS 12-bits */
+ startsector &= 0x0fff;
+ }
+ return startsector;
+ }
+
+ case FSTYPE_FAT16 :
+ {
+ unsigned int fatoffset = 2 * clusterno;
+ size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ return FAT_GETFAT16(fs->fs_buffer, fatindex);
+ }
+
+ case FSTYPE_FAT32 :
+ {
+ unsigned int fatoffset = 4 * clusterno;
+ size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ return FAT_GETFAT16(fs->fs_buffer, fatindex) & 0x0fffffff;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* There is no cluster information, or an error occured */
+ return (ssize_t)ERROR;
+}
+
+/****************************************************************************
+ * Name: fat_putcluster
+ *
+ * Desciption: Write a new cluster start sector into the FAT
+ *
+ ****************************************************************************/
+
+int fat_putcluster(struct fat_mountpt_s *fs, unsigned int clusterno, size_t startsector)
+{
+ /* Verify that the cluster number is within range. Zero erases the cluster. */
+
+ if (clusterno == 0 || (clusterno >= 2 && clusterno < fs->fs_nclusters))
+ {
+ /* Okay.. Write the next cluster into the FAT. The way we will do
+ * this depends on the type of FAT filesystm we are dealing with.
+ */
+
+ switch (fs->fs_type)
+ {
+ case FSTYPE_FAT12 :
+ {
+ size_t fatsector;
+ unsigned int fatoffset;
+ unsigned int fatindex;
+ ubyte value;
+
+ /* FAT12 is more complex because it has 12-bits (1.5 bytes)
+ * per FAT entry. Get the offset to the first byte:
+ */
+
+ fatoffset = (clusterno * 3) / 2;
+ fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+
+ /* Make sure that the sector at this offset is in the cache */
+
+ if (fat_fscacheread(fs, fatsector)< 0)
+ {
+ /* Read error */
+ break;
+ }
+
+ /* Output the LS byte first handling the 12-bit alignment within
+ * the 16-bits
+ */
+
+ fatindex = fatoffset & SEC_NDXMASK(fs);
+ if ((clusterno & 1) != 0)
+ {
+ value = (fs->fs_buffer[fatindex] & 0x0f) | startsector << 4;
+ }
+ else
+ {
+ value = (ubyte)startsector;
+ }
+ fs->fs_buffer[fatindex] = value;
+
+ /* With FAT12, the second byte of the cluster number may lie in
+ * a different sector than the first byte.
+ */
+
+ fatindex++;
+ if (fatindex >= fs->fs_hwsectorsize)
+ {
+ /* Read the next sector */
+
+ fatsector++;
+ fatindex = 0;
+
+ /* Set the dirty flag to make sure the sector that we
+ * just modified is written out.
+ */
+
+ fs->fs_dirty = TRUE;
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ }
+
+ /* Output the MS byte first handling the 12-bit alignment within
+ * the 16-bits
+ */
+
+ if ((clusterno & 1) != 0)
+ {
+ value = (ubyte)(startsector >> 4);
+ }
+ else
+ {
+ value = (fs->fs_buffer[fatindex] & 0xf0) | (startsector & 0x0f);
+ }
+ fs->fs_buffer[fatindex] = value;
+ }
+ break;
+
+ case FSTYPE_FAT16 :
+ {
+ unsigned int fatoffset = 2 * clusterno;
+ size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ FAT_PUTFAT16(fs->fs_buffer, fatindex, startsector & 0xffff);
+ }
+ break;
+
+ case FSTYPE_FAT32 :
+ {
+ unsigned int fatoffset = 4 * clusterno;
+ size_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ FAT_PUTFAT32(fs->fs_buffer, fatindex, startsector & 0x0fffffff);
+ }
+ break;
+
+ default:
+ return ERROR;
+ }
+
+ /* Mark the modified sector as "dirty" and return success */
+
+ fs->fs_dirty = 1;
+ return OK;
+ }
+ return ERROR;
+ }
+
+/****************************************************************************
* Name: fat_nextdirentry
*
* Desciption: Read the next directory entry from the sector in cache,
@@ -1439,7 +1440,7 @@ int fat_finddirentry(struct fat_dirinfo_s *dirinfo, const char *path)
{
/* Read the next sector into memory */
- ret = fat_cacheread(fs, dirinfo->fd_currsector);
+ ret = fat_fscacheread(fs, dirinfo->fd_currsector);
if (ret < 0)
{
return ret;
@@ -1540,4 +1541,112 @@ int fat_dircreate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
return -ENOSYS;
}
+/****************************************************************************
+ * Name: fat_ffcacheflush
+ *
+ * Desciption: Flush any dirty sectors as necessary
+ *
+ ****************************************************************************/
+
+int fat_ffcacheflush(struct fat_mountpt_s *fs, struct fat_file_s *ff)
+{
+ int ret;
+
+ /* Check if the ff_buffer is dirty. In this case, we will write back the
+ * contents of ff_buffer.
+ */
+
+ if (ff->ff_bflags && (FFBUFF_DIRTY|FFBUFF_VALID) == (FFBUFF_DIRTY|FFBUFF_VALID))
+ {
+ /* Write the dirty sector */
+
+ ret = fat_hwwrite(fs, ff->ff_buffer, ff->ff_currentsector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* No longer dirty */
+
+ ff->ff_bflags &= ~FFBUFF_DIRTY;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_ffcacheread
+ *
+ * Desciption: Read the specified sector into the sector cache, flushing any
+ * existing dirty sectors as necessary.
+ *
+ ****************************************************************************/
+
+int fat_ffcacheread(struct fat_mountpt_s *fs, struct fat_file_s *ff, size_t sector)
+{
+ int ret;
+
+ /* ff->ff_currentsector holds the current sector that is buffered in
+ * ff->ff_buffer. If the requested sector is the same as this sector, then
+ * we do nothing. Otherwise, we will have to read the new sector.
+ */
+
+ if (ff->ff_currentsector != sector || (ff->ff_bflags & FFBUFF_VALID) == 0)
+ {
+ /* We will need to read the new sector. First, flush the cached
+ * sector if it is dirty.
+ */
+
+ ret = fat_ffcacheflush(fs, ff);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Then read the specified sector into the cache */
+
+ ret = fat_hwread(fs, ff->ff_buffer, sector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Update the cached sector number */
+
+ ff->ff_currentsector = sector;
+ ff->ff_bflags |= FFBUFF_VALID;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_ffcacheread
+ *
+ * Desciption: Invalidate the current file buffer contents
+ *
+ ****************************************************************************/
+
+int fat_ffcacheinvalidate(struct fat_mountpt_s *fs, struct fat_file_s *ff)
+{
+ int ret;
+
+ /* Is there anything valid in the buffer now? */
+
+ if ((ff->ff_bflags & FFBUFF_VALID) != 0)
+ {
+ /* We will invalidate the buffered sector */
+
+ ret = fat_ffcacheflush(fs, ff);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Then discard the current cache contents */
+
+ ff->ff_bflags &= ~FFBUFF_VALID;
+ }
+ return OK;
+}
+
#endif /* CONFIG_FS_FAT */