summaryrefslogblamecommitdiff
path: root/nuttx/fs/fs_fat32.c
blob: 6f3e35e74107c6859dcf8e2d8f6581643cb8770f (plain) (tree)












































                                                                              
                  


































                                                                              



                                                           



                                                               



                                                             










                                                                             

                                                                         



                                                                               

                                                                           














































































































                                                                                 

                                                                       

                                                                            

                                                                   

                                                                            

                                                                      






















































































































































                                                                              



                                                                      










































                                                                              

                                                                            











































                                                                              

                       

                             








                                                                       









                                                                         

                                                                       

     








                                                                                                 
                                                                      




                                                   



                                                     
                                                             





                                                      
                                                                         




                                                   



                                                        
                                                                


















                                                                                
                                                                                       














                                                          
















                                                                      







                                                                              
                                                

                                  
     
                                                       
     
      
     
                                                  
     

                                                                                                   

























































                                                                               


                                                                            













                                                                              




                                                                            

































                                                                              
                               

     
                                  
   




                                  



                  
















                                                                       













                                                                              

                                                                   
 


                                                                      

                                                 




                    
 

                           

                
                                 
     



































                                                                    







                                                                              






                                       
 


                                                                      
 








                                                                              

                                       

          




                                   













                                                                              

                                                                   
 

                                       

          




                                   















                                                                              

                                       

          




                                   















                                                                              

                                       

          




                                   






















                                                                              

                                                                  










































                                                                              









































                                                                   






                                                                              
/****************************************************************************
 * fs_fat32.c
 *
 *   Copyright (C) 2007 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name Gregory Nutt nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <sys/types.h>
#include <stdlib.h>
#include <semaphore.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/fs.h>

#include "fs_internal.h"
#include "fs_fat32.h"

#if CONFIG_FS_FAT

/****************************************************************************
 * Definitions
 ****************************************************************************/

/* Access to data in raw sector data */

#define UBYTE_VAL(p,o)            (((ubyte*)(p))[o])
#define UBYTE_PTR(p,o)            &UBYTE_VAL(p,o)
#define UBYTE_PUT(p,o,v)          (UBYTE_VAL(p,o)=(ubyte)(v))

#define UINT16_PTR(p,o)           ((uint16*)UBYTE_PTR(p,o))
#define UINT16_VAL(p,o)           (*UINT16_PTR(p,o))
#define UINT16_PUT(p,o,v)         (UINT16_VAL(p,o)=(uint16)(v))

#define UINT32_PTR(p,o)           ((uint32*)UBYTE_PTR(p,o))
#define UINT32_VAL(p,o)           (*UINT32_PTR(p,o))
#define UINT32_PUT(p,o,v)         (UINT32_VAL(p,o)=(uint32)(v))

/* Regardless of the endian-ness of the target or alignment of the data, no
 * special operations are required for byte, string or byte array accesses.
 * The FAT data stream is little endian so multiple byte values must be
 * accessed byte-by-byte for big-endian targets.
 */

#define MBR_GETSECPERCLUS(p)      UBYTE_VAL(p,BS_SECPERCLUS)
#define MBR_GETNUMFATS(p)         UBYTE_VAL(p,BS_NUMFATS)
#define MBR_GETMEDIA(p)           UBYTE_VAL(p,BS_MEDIA)
#define MBR_GETDRVNUM16(p)        UBYTE_VAL(p,BS16_DRVNUM)
#define MBR_GETDRVNUM32(p)        UBYTE_VAL(p,BS32_DRVNUM)
#define MBR_GETBOOTSIG16(p)       UBYTE_VAL(p,BS16_BOOTSIG)
#define MBR_GETBOOTSIG32(p)       UBYTE_VAL(p,BS32_BOOTSIG)

#define MBR_PUTSECPERCLUS(p,v)    UBYTE_PUT(p,BS_SECPERCLUS),v)
#define MBR_PUTNUMFATS(p,v)       UBYTE_PUT(p,BS_NUMFATS,v)
#define MBR_PUTMEDIA(p,v)         UBYTE_PUT(p,BS_MEDIA,v)
#define MBR_PUTDRVNUM16(p,v)      UBYTE_PUT(p,BS16_DRVNUM,v)
#define MBR_PUTDRVNUM32(p,v)      UBYTE_PUT(p,BS32_DRVNUM,v)
#define MBR_PUTBOOTSIG16(p,v)     UBYTE_PUT(p,BS16_BOOTSIG,v)
#define MBR_PUTBOOTSIG32(p,v)     UBYTE_PUT(p,BS32_BOOTSIG,v)

/* For the all targets, unaligned values need to be accessed byte-by-byte.
 * Some architectures may handle unaligned accesses with special interrupt
 * handlers.  But even in that case, it is more efficient to avoid the traps.
 */

/* Unaligned multi-byte access macros */

#define MBR_GETBYTESPERSEC(p)      fat_getuint16(UBYTE_PTR(p,BS_BYTESPERSEC))
#define MBR_GETROOTENTCNT(p)       fat_getuint16(UBYTE_PTR(p,BS_ROOTENTCNT))
#define MBR_GETTOTSEC16(p)         fat_getuint16(UBYTE_PTR(p,BS_TOTSEC16))
#define MBR_GETVOLID16(p)          fat_getuint32(UBYTE_PTR(p,BS16_VOLID))
#define MBR_GETVOLID32(p)          fat_getuint32(UBYTE_PTR(p,BS32_VOLID))

#define MBR_PUTBYTESPERSEC(p,v)    fat_putuint16(UBYTE_PTR(p,BS_BYTESPERSEC),v)
#define MBR_PUTROOTENTCNT(p,v)     fat_putuint16(UBYTE_PTR(p,BS_ROOTENTCNT),v)
#define MBR_PUTTOTSEC16(p,v)       fat_putuint16(UBYTE_PTR(p,BS_TOTSEC16),v)
#define MBR_PUTVOLID16(p,v)        fat_putuint32(UBYTE_PTR(p,BS16_VOLID),v)
#define MBR_PUTVOLID32(p,v)        fat_putuint32(UBYTE_PTR(p,BS32_VOLID),v)

/* But for multi-byte values, the endian-ness of the target vs. the little
 * endian order of the byte stream or alignment of the data within the byte
 * stream can force special, byte-by-byte accesses.
 */

#ifdef CONFIG_ARCH_BIGENDIAN
/* If the target is big-endian, then even aligned multi-byte values must be
 * accessed byte-by-byte.
 */

# define MBR_GETRESVDSECCOUNT(p)   fat_getuint16(UBYTE_PTR(p,BS_RESVDSECCOUNT))
# define MBR_GETFATSZ16(p)         fat_getuint16(UBYTE_PTR(p,BS_FATSZ16))
# define MBR_GETSECPERTRK(p)       fat_getuint16(UBYTE_PTR(p,BS_SECPERTRK))
# define MBR_GETNUMHEADS(p)        fat_getuint16(UBYTE_PTR(p,BS_NUMHEADS))
# define MBR_GETHIDSEC(p)          fat_getuint32(UBYTE_PTR(p,BS_HIDSEC))
# define MBR_GETTOTSEC32(p)        fat_getuint32(UBYTE_PTR(p,BS_TOTSEC32))
# define MBR_GETFATSZ32(p)         fat_getuint32(UBYTE_PTR(p,BS32_FATSZ32))
# define MBR_GETEXTFLAGS(p)        fat_getuint16(UBYTE_PTR(p,BS32_EXTFLAGS))
# define MBR_GETFSVER(p)           fat_getuint16(UBYTE_PTR(p,BS32_FSVER))
# define MBR_GETROOTCLUS(p)        fat_getuint32(UBYTE_PTR(p,BS32_ROOTCLUS))
# define MBR_GETFSINFO(p)          fat_getuint16(UBYTE_PTR(p,BS32_FSINFO))
# define MBR_GETBKBOOTSEC(p)       fat_getuint16(UBYTE_PTR(p,BS32_BKBOOTSEC))
# define MBR_GETSIGNATURE(p)       fat_getuint16(UBYTE_PTR(p,BS_SIGNATURE))

# define MBR_GETPARTSECTOR(s)      fat_getuint32(s);

# define FSI_GETLEADSIG(p)         fat_getuint32(UBYTE_PTR(p,FSI_LEADSIG))
# define FSI_GETSTRUCTSIG(p)       fat_getuint32(UBYTE_PTR(p,FSI_STRUCTSIG))
# define FSI_GETFREECOUNT(p)       fat_getuint32(UBYTE_PTR(p,FSI_FREECOUNT))
# define FSI_GETNXTFREE(p)         fat_getuint32(UBYTE_PTR(p,FSI_NXTFREE))
# define FSI_GETTRAILSIG(p)        fat_getuint32(UBYTE_PTR(p,FSI_TRAILSIG))

# define MBR_PUTRESVDSECCOUNT(p,v) fat_putuint16(UBYTE_PTR(p,BS_RESVDSECCOUNT,v))
# define MBR_PUTFATSZ16(p,v)       fat_putuint16(UBYTE_PTR(p,BS_FATSZ16,v))
# define MBR_PUTSECPERTRK(p,v)     fat_putuint16(UBYTE_PTR(p,BS_SECPERTRK,v))
# define MBR_PUTNUMHEADS(p,v)      fat_putuint16(UBYTE_PTR(p,BS_NUMHEADS,v))
# define MBR_PUTHIDSEC(p,v)        fat_putuint32(UBYTE_PTR(p,BS_HIDSEC,v))
# define MBR_PUTTOTSEC32(p,v)      fat_putuint32(UBYTE_PTR(p,BS_TOTSEC32,v))
# define MBR_PUTFATSZ32(p,v)       fat_putuint32(UBYTE_PTR(p,BS32_FATSZ32,v))
# define MBR_PUTEXTFLAGS(p,v)      fat_putuint16(UBYTE_PTR(p,BS32_EXTFLAGS,v))
# define MBR_PUTFSVER(p,v)         fat_putuint16(UBYTE_PTR(p,BS32_FSVER,v))
# define MBR_PUTROOTCLUS(p,v)      fat_putuint32(UBYTE_PTR(p,BS32_ROOTCLUS,v))
# define MBR_PUTFSINFO(p,v)        fat_putuint16(UBYTE_PTR(p,BS32_FSINFO,v))
# define MBR_PUTBKBOOTSEC(p,v)     fat_putuint16(UBYTE_PTR(p,BS32_BKBOOTSEC,v))
# define MBR_PUTSIGNATURE(p,v)     fat_getuint16(UBYTE_PTR(p,BS_SIGNATURE),v)

# define FSI_PUTLEADSIG(p,v)       fat_putuint32(UBYTE_PTR(p,FSI_LEADSIG),v)
# define FSI_PUTSTRUCTSIG(p,v)     fat_putuint32(UBYTE_PTR(p,FSI_STRUCTSIG),v)
# define FSI_PUTFREECOUNT(p,v)     fat_putuint32(UBYTE_PTR(p,FSI_FREECOUNT),v)
# define FSI_PUTNXTFREE(p,v)       fat_putuint32(UBYTE_PTR(p,FSI_NXTFREE),v)
# define FSI_PUTTRAILSIG(p,v)      fat_putuint32(UBYTE_PTR(p,FSI_TRAILSIG),v)

#else

/* But nothing special has to be done for the little endian-case for access
 * to aligned mulitbyte values.
 */

# define MBR_GETRESVDSECCOUNT(p)   UINT16_VAL(p,BS_RESVDSECCOUNT)
# define MBR_GETFATSZ16(p)         UINT16_VAL(p,BS_FATSZ16)
# define MBR_GETSECPERTRK(p)       UINT16_VAL(p,BS_SECPERTRK)
# define MBR_GETNUMHEADS(p)        UINT16_VAL(p,BS_NUMHEADS)
# define MBR_GETHIDSEC(p)          UINT32_VAL(p,BS_HIDSEC)
# define MBR_GETTOTSEC32(p)        UINT32_VAL(p,BS_TOTSEC32)
# define MBR_GETFATSZ32(p)         UINT32_VAL(p,BS32_FATSZ32)
# define MBR_GETEXTFLAGS(p)        UINT16_VAL(p,BS32_EXTFLAGS)
# define MBR_GETFSVER(p)           UINT16_VAL(p,BS32_FSVER)
# define MBR_GETROOTCLUS(p)        UINT32_VAL(p,BS32_ROOTCLUS)
# define MBR_GETFSINFO(p)          UINT16_VAL(p,BS32_FSINFO)
# define MBR_GETBKBOOTSEC(p)       UINT16_VAL(p,BS32_BKBOOTSEC)
# define MBR_GETSIGNATURE(p)       UINT16_VAL(p,BS_SIGNATURE)

# define MBR_GETPARTSECTOR(s)      (*((uint32*)(s)))

# define FSI_GETLEADSIG(p)         UINT32_VAL(p,FSI_LEADSIG)
# define FSI_GETSTRUCTSIG(p)       UINT32_VAL(p,FSI_STRUCTSIG)
# define FSI_GETFREECOUNT(p)       UINT32_VAL(p,FSI_FREECOUNT)
# define FSI_GETNXTFREE(p)         UINT32_VAL(p,FSI_NXTFREE)
# define FSI_GETTRAILSIG(p)        UINT32_VAL(p,FSI_TRAILSIG)

# define MBR_PUTRESVDSECCOUNT(p,v) UINT16_PUT(p,BS_RESVDSECCOUNT,v)
# define MBR_PUTFATSZ16(p,v)       UINT16_PUT(p,BS_FATSZ16,v)
# define MBR_PUTSECPERTRK(p,v)     UINT16_PUT(p,BS_SECPERTRK,v)
# define MBR_PUTNUMHEADS(p,v)      UINT16_PUT(p,BS_NUMHEADS,v)
# define MBR_PUTHIDSEC(p,v)        UINT32_PUT(p,BS_HIDSEC,v)
# define MBR_PUTTOTSEC32(p,v)      UINT32_PUT(p,BS_TOTSEC32,v)
# define MBR_PUTFATSZ32(p,v)       UINT32_PUT(p,BS32_FATSZ32,v)
# define MBR_PUTEXTFLAGS(p,v)      UINT16_PUT(p,BS32_EXTFLAGS,v)
# define MBR_PUTFSVER(p,v)         UINT16_PUT(p,BS32_FSVER,v)
# define MBR_PUTROOTCLUS(p,v)      UINT32_PUT(p,BS32_ROOTCLUS,v)
# define MBR_PUTFSINFO(p,v)        UINT16_PUT(p,BS32_FSINFO,v)
# define MBR_PUTBKBOOTSEC(p,v)     UINT16_PUT(p,BS32_BKBOOTSEC,v)
# define MBR_PUTSIGNATURE(p,v)     UINT16_PUT(p,BS_SIGNATURE,v)

# define FSI_PUTLEADSIG(p)         UINT32_PUT(p,FSI_LEADSIG)
# define FSI_PUTSTRUCTSIG(p)       UINT32_PUT(p,FSI_STRUCTSIG)
# define FSI_PUTFREECOUNT(p)       UINT32_PUT(p,FSI_FREECOUNT)
# define FSI_PUTNXTFREE(p)         UINT32_PUT(p,FSI_NXTFREE)
# define FSI_PUTTRAILSIG(p)        UINT32_PUT(p,FSI_TRAILSIG)

#endif

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static int     fat_open(FAR struct file *filp, FAR struct inode *inode,
                        const char *rel_path, int oflags, mode_t mode);
static int     fat_close(FAR struct file *filp);
static ssize_t fat_read(FAR struct file *filp, char *buffer, size_t buflen);
static ssize_t fat_write(FAR struct file *filp, const char *buffer,
                         size_t buflen);
static off_t   fat_seek(FAR struct file *filp, off_t offset, int whence);
static int     fat_ioctl(FAR struct file *filp, int cmd, unsigned long arg);
static int     fat_bind(FAR struct inode *blkdriver, const void *data,
                        void **handle);
static int     fat_unbind(void *handle);

/****************************************************************************
 * Private Variables
 ****************************************************************************/

/****************************************************************************
 * Public Variables
 ****************************************************************************/

/* See fs_mount.c -- this structure is explicitly externed there.
 * We use the old-fashioned kind of initializers so that this will compile
 * with any compiler.
 */

const struct mountpt_operations fat_operations =
{
  fat_open,
  fat_close,
  fat_read,
  fat_write,
  fat_seek,
  fat_ioctl,
  fat_bind,
  fat_unbind
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: fat_getuint16
 ****************************************************************************/

static uint16 fat_getuint16(ubyte *ptr)
{
#ifdef CONFIG_ARCH_BIGENDIAN
  /* The bytes always have to be swapped if the target is big-endian */

  return ((uint16)ptr[0] << 8) | ptr[1];
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

  return ((uint16)ptr[1] << 8) | ptr[0];
#endif
}

/****************************************************************************
 * Name: fat_getuint32
 ****************************************************************************/

static uint32 fat_getuint32(ubyte *ptr)
{
#ifdef CONFIG_ARCH_BIGENDIAN
  /* The bytes always have to be swapped if the target is big-endian */

  return ((uint32)fat_getuint16(&ptr[0]) << 16) | fat_getuint16(&ptr[2]);
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

  return ((uint32)fat_getuint16(&ptr[2]) << 16) | fat_getuint16(&ptr[0]);
#endif
}

/****************************************************************************
 * Name: fat_putuint16
 ****************************************************************************/

static void fat_putuint16(ubyte *ptr, uint16 value16)
{
  ubyte *val = (ubyte*)&value16;
#ifdef CONFIG_ARCH_BIGENDIAN
  /* The bytes always have to be swapped if the target is big-endian */

  ptr[0] = val[1];
  ptr[1] = val[0];
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

  ptr[0] = val[0];
  ptr[1] = val[1];
#endif
}

/****************************************************************************
 * Name: fat_putuint32
 ****************************************************************************/

static void fat_putuint32(ubyte *ptr, uint32 value32)
{
  uint16 *val = (uint16*)&value32;
#ifdef CONFIG_ARCH_BIGENDIAN
  /* The bytes always have to be swapped if the target is big-endian */

  fat_putuint16(&ptr[0], val[2]);
  fat_putuint16(&ptr[2], val[0]);
#else
  /* Byte-by-byte transfer is still necessary if the address is un-aligned */

  fat_putuint16(&ptr[0], val[0]);
  fat_putuint16(&ptr[2], val[2]);
#endif
}

/****************************************************************************
 * Name: fat_semtake
 ****************************************************************************/

static void fat_semtake(struct fat_mountpt_s *fs)
{
  /* Take the semaphore (perhaps waiting) */

  while (sem_wait(&fs->fs_sem) != 0)
    {
      /* The only case that an error should occur here is if
       * the wait was awakened by a signal.
       */

      ASSERT(*get_errno_ptr() == EINTR);
    }
}

/****************************************************************************
 * Name: fat_semgive
 ****************************************************************************/

static inline void fat_semgive(struct fat_mountpt_s *fs)
{
   sem_post(&fs->fs_sem);
}

/****************************************************************************
 * Name: fat_checkmount
 *
 * Desciption: Check if the mountpoint is still valid.
 *
 *   The caller should hold the mountpoint semaphore
 *
 ****************************************************************************/

static int fat_checkmount(struct fat_mountpt_s *fs)
{
  /* If the fs_mounted flag is FALSE, then we have already handled the loss
   * of the mount.
   */

  if (fs->fs_mounted)
    {
      struct fat_file_s *file;

      /* We still think the mount is healthy.  Check an see if this is
       * still the case
       */

      if (fs->fs_blkdriver)
        {
          struct inode *inode = fs->fs_blkdriver;
          if (inode && inode->u.i_bops && inode->u.i_bops->geometry)
            {
              struct geometry geo;
              int errcode = inode->u.i_bops->geometry(inode, &geo);
              if (errcode == OK && geo.geo_available && !geo.geo_mediachanged)
                {
                  return OK;
                }
            }
        }

      /* If we get here, the mount is NOT healthy */

      fs->fs_mounted = FALSE;

      /* Make sure that this is flagged in every opened file */

      for (file = fs->fs_head; file; file = file->ff_next)
      {
          file->ff_open = FALSE;
      }
    }
  return -ENODEV;
}

/****************************************************************************
 * Name: fat_bread
 *
 * Desciption: Read the specified sector
 *
 ****************************************************************************/

static int fat_bread(struct fat_mountpt_s *fs, size_t sector)
{
  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, fs->fs_buffer,
                                                       sector, 1);
          if (nSectorsRead == 1)
            {
              ret = OK;
            }
          else if (nSectorsRead < 0)
            {
              ret = nSectorsRead;
            }
        }
    }
  return ret;
}

/****************************************************************************
 * Name: fat_readfsinfo
 *
 * Desciption: Read the FAT32 FSINFO sector
 *
 ****************************************************************************/

static int fat_readfsinfo(struct fat_mountpt_s *fs)
{
  /* Verify that this is, indeed, an FSINFO sector */

  if (FSI_GETLEADSIG(fs->fs_buffer) == 0x41615252  &&
      FSI_GETSTRUCTSIG(fs->fs_buffer) == 0x61417272 &&
      FSI_GETTRAILSIG(fs->fs_buffer) == 0xaa550000)
    {
      fs->fs_fsinextfree  = FSI_GETFREECOUNT(fs->fs_buffer);
      fs->fs_fsifreecount = FSI_GETNXTFREE(fs->fs_buffer);
      return OK;
    }
  return -ENODEV;
}

/****************************************************************************
 * Name: fat_checkbootrecord
 *
 * Desciption: Read a sector and verify that it is a a FAT boot record.
 *
 ****************************************************************************/

static int fat_checkbootrecord(struct fat_mountpt_s *fs)
{
  uint32  ndatasectors;
  uint32  fatsize;
  uint16  rootdirsectors = 0;
  boolean notfat32 = FALSE;

  /* Verify the MBR signature at offset 510 in the sector (true even
   * if the sector size is greater than 512.  All FAT file systems have
   * this signature. On a FAT32 volume, the RootEntCount , FatSz16, and
   * FatSz32 values should always be zero.  The FAT sector size should
   * match the reported hardware sector size.
   */

  if (MBR_GETSIGNATURE(fs->fs_buffer) != 0xaa55 ||
      MBR_GETBYTESPERSEC(fs->fs_buffer) != fs->fs_hwsectorsize)
    {
      return -ENODEV;
    }

  /* Verify the FAT32 file system type. The determination of the file
   * system type is based on the number of clusters on the volume:  FAT12
   * volume has < 4085 cluseter, a FAT16 volume has fewer than 65,525
   * clusters, and any larger is FAT32.
   *
   * Get the number of 32-bit directory entries in root directory (zero
   * for FAT32.
   */

  fs->fs_rootentcnt = MBR_GETROOTENTCNT(fs->fs_buffer);
  if (fs->fs_rootentcnt != 0)
  {
      notfat32       = TRUE; /* Must be zero for FAT32 */
      rootdirsectors = (32 * fs->fs_rootentcnt  + fs->fs_hwsectorsize - 1) / fs->fs_hwsectorsize;
  }

  /* Determine the number of sectors in a FAT. */

  fs->fs_fatsize = MBR_GETFATSZ16(fs->fs_buffer); /* Should be zero */
  if (fs->fs_fatsize)
    {
      notfat32 = TRUE; /* Must be zero for FAT32 */
    }
  else
    {
      fs->fs_fatsize = MBR_GETFATSZ32(fs->fs_buffer);
    }

  if (!fs->fs_fatsize || fs->fs_fatsize >= fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the total number of sectors on the volume. */

  fs->fs_fattotsec = MBR_GETTOTSEC16(fs->fs_buffer); /* Should be zero */
  if (fs->fs_fattotsec)
    {
      notfat32 = TRUE; /* Must be zero for FAT32 */
    }
  else
    {
      fs->fs_fattotsec = MBR_GETTOTSEC32(fs->fs_buffer);
    }

  if (!fs->fs_fattotsec || fs->fs_fattotsec > fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the total number of reserved sectors */

  fs->fs_fatresvdseccount = MBR_GETRESVDSECCOUNT(fs->fs_buffer);
  if (fs->fs_fatresvdseccount > fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the number of FATs. This is probably two but could have other values */

  fs->fs_fatnumfats = MBR_GETNUMFATS(fs->fs_buffer);
  fatsize = fs->fs_fatnumfats * fs->fs_fatsize;

  /* Get the total number of data sectors */

  ndatasectors = fs->fs_fattotsec - fs->fs_fatresvdseccount - fatsize - rootdirsectors;
  if (ndatasectors > fs->fs_hwnsectors)
    {
      return -ENODEV;
    }

  /* Get the sectors per cluster */

  fs->fs_fatsecperclus = MBR_GETSECPERCLUS(fs->fs_buffer);

  /* Calculate the number of clusters */

  fs->fs_nclusters = ndatasectors / fs->fs_fatsecperclus;

  /* Finally, the test: */

  if (fs->fs_nclusters < 4085)
    {
      fs->fs_fsinfo = 0;
      fs->fs_type   = FSTYPE_FAT12;
    }
  else if (fs->fs_nclusters < 65525)
    {
      fs->fs_fsinfo = 0;
      fs->fs_type   = FSTYPE_FAT16;
    }

  else if (!notfat32)
    {
      fs->fs_fsinfo   = fs->fs_fatbase + MBR_GETFSINFO(fs->fs_buffer);
      fs->fs_type     = FSTYPE_FAT32;
    }
  else
    {
      return -ENODEV;
    }

  /* We have what appears to be a valid FAT filesystem! Save a few more things
   * from the boot record that we will need later.
   */

  fs->fs_fatbase     += fs->fs_fatresvdseccount;

  if (fs->fs_type == FSTYPE_FAT32)
    {
      fs->fs_rootbase = MBR_GETROOTCLUS(fs->fs_buffer);
    }
  else
    {
      fs->fs_rootbase = fs->fs_fatbase + fatsize; 
    }

  fs->fs_database     = fs->fs_fatbase + fatsize + fs->fs_rootentcnt / (fs->fs_hwsectorsize / 32); 
  fs->fs_fsifreecount = 0xffffffff;

  return OK;
}

/****************************************************************************
 * Name: fat_mount
 *
 * Desciption: This function is called only when the mountpoint is first
 *   established.  It initializes the mountpoint structure and verifies
 *   that a valid FAT32 filesystem is provided by the block driver.
 *
 *   The caller should hold the mountpoint semaphore
 *
 ****************************************************************************/

static int fat_mount(struct fat_mountpt_s *fs, boolean writeable)
{
  FAR struct inode *inode;
  struct geometry geo;
  int ret;

  /* Assume that the mount is successful */

  fs->fs_mounted = TRUE;

  /* Check if there is media available */

  inode = fs->fs_blkdriver;
  if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry ||
      inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available)
    {
      ret = -ENODEV;
      goto errout;
    }

  /* Make sure that that the media is write-able (if write access is needed) */

  if (writeable && !geo.geo_writeenabled)
    {
      ret = -EACCES;
      goto errout;
    }

  /* Save the hardware geometry */

  fs->fs_hwsectorsize = geo.geo_sectorsize;
  fs->fs_hwnsectors   = geo.geo_nsectors;

  /* Allocate a buffer to hold one hardware sector */

  fs->fs_buffer = (ubyte*)malloc(fs->fs_hwsectorsize);
  if (!fs->fs_buffer)
    {
      ret = -ENOMEM;
      goto errout;
    }

  /* Search FAT boot record on the drive.  First check at sector zero.  This
   * could be either the boot record or a partition that refers to the boot
   * record.
   *
   * First read sector zero.  This will be the first access to the drive and a
   * likely failure point.
   */

  fs->fs_fatbase = 0;
  ret = fat_bread(fs, 0);
  if (ret < 0)
    {
      goto errout_with_buffer;
    }

  if (fat_checkbootrecord(fs) != OK)
    {
      /* The contents of sector 0 is not a boot record.  It could be a
       * partition, however.  Assume it is a partition and get the offset
       * into the partition table.  This table is at offset MBR_TABLE and is
       * indexed by 16x the partition number.  Here we support only
       * parition 0.
       */

      ubyte *partition = &fs->fs_buffer[MBR_TABLE + 0];

      /* Check if the partition exists and, if so, get the bootsector for that
       * partition and see if we can find the boot record there.
       */
 
      if (partition[4])
        {
          /* There appears to be a partition, get the sector number of the
           * partition (LBA)
           */

          fs->fs_fatbase = MBR_GETPARTSECTOR(&partition[8]);

          /* Read the new candidate boot sector */

          ret = fat_bread(fs, 0);
          if (ret < 0)
          {
              goto errout_with_buffer;
          }

          /* Check if this is a boot record */

          if (fat_checkbootrecord(fs) != OK)
            {
              goto errout_with_buffer;
            }
        }
    }

  /* We have what appears to be a valid FAT filesystem! Now read the
   * FSINFO sector (FAT32 only)
   */

  if (fs->fs_type == FSTYPE_FAT32)
  {
      ret = fat_readfsinfo(fs);
      if (ret != OK)
      {
          goto errout_with_buffer;
      }
  }

  /* We did it! */

  dbg("FAT%d:\n", fs->fs_type == 0 ? 12 : fs->fs_type == 1  ? 16 : 32);
  dbg("\tHW  sector size:     %d\n", fs->fs_hwsectorsize);
  dbg("\t    sectors:         %d\n", fs->fs_hwnsectors);
  dbg("\tFAT reserved:        %d\n", fs->fs_fatresvdseccount);
  dbg("\t    sectors:         %d\n", fs->fs_fattotsec);
  dbg("\t    start sector:    %d\n", fs->fs_fatbase);
  dbg("\t    root sector:     %d\n", fs->fs_rootbase);
  dbg("\t    root entries:    %d\n", fs->fs_rootentcnt);
  dbg("\t    data sector:     %d\n", fs->fs_database);
  dbg("\t    FSINFO sector:   %d\n", fs->fs_fsinfo);
  dbg("\t    Num FATs:        %d\n", fs->fs_fatnumfats);
  dbg("\t    FAT size:        %d\n", fs->fs_fatsize);
  dbg("\t    sectors/cluster: %d\n", fs->fs_fatsecperclus);
  dbg("\t    max clusters:    %d\n", fs->fs_nclusters);
  dbg("\tFSI free count       %d\n", fs->fs_fsifreecount);
  dbg("\t    next free        %d\n", fs->fs_fsinextfree);

  return OK;

 errout_with_buffer:
  free(fs->fs_buffer);
  fs->fs_buffer = 0;
 errout:
  fs->fs_mounted = FALSE;
  return ret;
}

/****************************************************************************
 * Name: fat_open
 ****************************************************************************/

static int fat_open(FAR struct file *filp, FAR struct inode *inode,
                    const char *rel_path, int oflags, mode_t mode)
{
  struct fat_mountpt_s *fs = (struct fat_mountpt_s *)inode->i_private;
  struct fat_file_s *ff;
  int ret = OK;
 
  /* Make sure that the mount is still healthy */
  if (!fs)
    {
      ret = -ENOSYS;
      goto errout;
    }

  fat_semtake(fs);
  ret = fat_checkmount(fs);
  if (ret != OK)
    {
      goto errout_with_semaphore;
    }

  /* Allocate a new private instance for the struct file */

  ff = (struct fat_file_s *)malloc(sizeof(struct fat_file_s));
  if (!ff)
    {
      ret = -ENOMEM;
      goto errout_with_semaphore;
    }

  /* Find the requested path and open or create the file */
#warning "Open logic missing"

  /* Initialize the new private instance */

  ff->ff_parent = fs;
  ff->ff_open   = TRUE;

  /* Then insert the new instance into the mountpoint structure.
   * It needs to be there (1) to handle error conditions that effect
   * all files, and (2) to inform the umount logic that we are busy
   * (but a simple reference count could have done that).
   */

  ff->ff_next = fs->fs_head;
  fs->fs_head = ff->ff_next;

  fat_semgive(fs);
  return OK;

 errout_with_alloc:
  free(ff);
 errout_with_semaphore:
  fat_semgive(fs);
 errout:
  return ret;
}

/****************************************************************************
 * Name: fat_close
 ****************************************************************************/

static int fat_close(FAR struct file *filp)
{
  struct fat_file_s *ff = filp->f_priv;
  struct fat_mountpt_s *fs;

  if (!ff || !(fs = ff->ff_parent))
    {
      return -EINVAL;
    }

  /* Do not check if the mount is healthy.  We must support closing of
   * the file even when there is healthy mount.
   */

  return -ENOSYS;
}

/****************************************************************************
 * Name: fat_read
 ****************************************************************************/

static ssize_t fat_read(FAR struct file *filp, char *buffer, size_t buflen)
{
  struct fat_file_s *ff = filp->f_priv;
  struct fat_mountpt_s *fs;
  int ret;

  if (!ff || !(fs = ff->ff_parent))
    {
      return -EINVAL;
    }

  /* Make sure that the mount is still healthy */

  ret = fat_checkmount(fs);
  if (ret != OK)
    {
      return ret;
    }
  return -ENOSYS;
}

/****************************************************************************
 * Name: fat_write
 ****************************************************************************/

static ssize_t fat_write(FAR struct file *filp, const char *buffer,
                         size_t buflen)
{
  struct fat_file_s *ff = filp->f_priv;
  struct fat_mountpt_s *fs;
  int ret;

  if (!ff || !(fs = ff->ff_parent))
    {
      return -EINVAL;
    }

  /* Make sure that the mount is still healthy */

  ret = fat_checkmount(fs);
  if (ret != OK)
    {
      return ret;
    }
  return -ENOSYS;
}

/****************************************************************************
 * Name: fat_seek
 ****************************************************************************/

static off_t fat_seek(FAR struct file *filp, off_t offset, int whence)
{
  struct fat_file_s *ff = filp->f_priv;
  struct fat_mountpt_s *fs;
  int ret;

  if (!ff || !(fs = ff->ff_parent))
    {
      return -EINVAL;
    }

  /* Make sure that the mount is still healthy */

  ret = fat_checkmount(fs);
  if (ret != OK)
    {
      return ret;
    }
  return -ENOSYS;
}

/****************************************************************************
 * Name: fat_ioctl
 ****************************************************************************/

static int fat_ioctl(FAR struct file *filp, int cmd, unsigned long arg)
{
  struct fat_file_s *ff = filp->f_priv;
  struct fat_mountpt_s *fs;
  int ret;

  if (!ff || !(fs = ff->ff_parent))
    {
      return -EINVAL;
    }

  /* Make sure that the mount is still healthy */

  ret = fat_checkmount(fs);
  if (ret != OK)
    {
      return ret;
    }

  /* ioctl calls are just passed through to the contained block driver */
  return -ENOSYS;
}

/****************************************************************************
 * Name: fat_bind
 *
 * Description: This implements a portion of the mount operation. This
 *  function allocates and initializes the mountpoint private data and
 *  binds the blockdriver inode to the filesystem private data.  The final
 *  binding of the private data (containing the blockdriver) to the
 *  mountpoint is performed by mount().
 *
 ****************************************************************************/

static int fat_bind(FAR struct inode *blkdriver, const void *data,
                    void **handle)
{
  struct fat_mountpt_s *fs;
  int ret;

  /* Create an instance of the mountpt state structure */
  fs = (struct fat_mountpt_s *)zalloc(sizeof(struct fat_mountpt_s));
  if ( !fs )
    {
      return -ENOMEM;
    }

  /* Initialize the allocated mountpt state structure */

  fs->fs_blkdriver = blkdriver;
  sem_init(&fs->fs_sem, 0, 0);

  /* Then get information about the FAT32 filesystem on the devices managed
   * by this block driver.
   */

  ret = fat_mount(fs, TRUE);
  if ( ret != 0 )
    {
      sem_destroy(&fs->fs_sem);
      free(fs);
      return ret;
    }

  *handle = (void*)fs;
  fat_semgive(fs);
  return OK;
}

/****************************************************************************
 * Name: fat_unbind
 *
 * Description: This implements the filesystem portion of the umount
 *   operation.
 *
 ****************************************************************************/

static int fat_unbind(void *handle)
{
  struct fat_mountpt_s *fs = (struct fat_mountpt_s*)handle;
  int ret;

  if ( !fs )
    {
      return -EINVAL;
    }

  /* Check if there are sill any files opened on the filesystem. */

  ret = OK; /* Assume success */
  fat_semtake(fs);
  if (fs->fs_head)
    {
      /* We cannot unmount now.. there are open files */

      ret = -EBUSY;
    }
  else
    {
       /* Unmount ... close the block driver */

      if (fs->fs_blkdriver)
        {
          struct inode *inode = fs->fs_blkdriver;
          if (inode && inode->u.i_bops && inode->u.i_bops->close)
            {
              (void)inode->u.i_bops->close(inode);
            }
        }

      /* Release the mountpoint private data */

      if (fs->fs_buffer)
        {
          free(fs->fs_buffer);
        }
      free(fs);
    }

  fat_semgive(fs);
  return ret;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

#endif /* CONFIG_FS_FAT */