diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2007-05-20 16:58:37 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2007-05-20 16:58:37 +0000 |
commit | e5a7df43992dcc514b4bb1bf9ab93ae25d0b8ed2 (patch) | |
tree | 5597f0667121d711e1b7370a2b238213bde00be0 | |
parent | 80c8fab4acd3dcdd301d39db7d16d2e5993c76af (diff) | |
download | nuttx-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.c | 209 | ||||
-rw-r--r-- | nuttx/fs/fs_fat32.h | 58 | ||||
-rw-r--r-- | nuttx/fs/fs_fat32util.c | 819 |
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 */ |