/* ---------------------------------------------------------------------------
 * FAT_intern.c                                                (C)ChaN, 2008
 * ---------------------------------------------------------------------------
 * Micro-key bv
 * Industrieweg 28, 9804 TG Noordhorn
 * Postbus 92, 9800 AB Zuidhorn
 * The Netherlands
 * Tel: +31 594 503020
 * Fax: +31 594 505825
 * Email: support@microkey.nl  
 * Web: www.microkey.nl
 * ---------------------------------------------------------------------------
 * Description: FatFs - FAT file system module  R0.06        
 * 
 * The FatFs module is an experimenal project to implement FAT file system to
 * cheap microcontrollers. This is a free software and is opened for education,
 * research and development under license policy of following trems.
 * 
 *  Copyright (C) 2008, ChaN, all right reserved.
 * 
 * The FatFs module is a free software and there is no warranty.
 * You can use, modify and/or redistribute it for personal, non-profit or
 * commercial use without restriction under your responsibility.
 * Redistributions of source code must retain the above copyright notice.             
 * ---------------------------------------------------------------------------
 * Last Change:     0.2, Aug 11, 2008, MMi
 *                  Edited to fit into LAN_2636 Project
 *
 *----------------------------------------------------------------------------
 * Feb 26,'06 R0.00  Prototype.
 *
 * Apr 29,'06 R0.01  First stable version.
 *
 * Jun 01,'06 R0.02  Added FAT12 support.
 *                   Removed unbuffered mode.
 *                   Fixed a problem on small (<32M) patition.
 * Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
 *
 * Sep 22,'06 R0.03  Added f_rename().
 *                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
 * Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast.
 *                   Fixed f_mkdir() creates incorrect directory on FAT32.
 *
 * Feb 04,'07 R0.04  Supported multiple drive system.
 *                   Changed some interfaces for multiple drive system.
 *                   Changed f_mountdrv() to f_mount().
 *                   Added f_mkfs().
 * Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive.
 *                   Added a capability of extending file size to f_lseek().
 *                   Added minimization level 3.
 *                   Fixed an endian sensitive code in f_mkfs().
 * May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
 *                   Added FSInfo support.
 *                   Fixed DBCS name can result FR_INVALID_NAME.
 *                   Fixed short seek (<= csize) collapses the file object.
 *
 * Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
 *                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
 *                   Fixed f_mkdir() on FAT32 creates incorrect directory.
 * Feb 03,'08 R0.05a Added f_truncate() and f_utime().
 *                   Fixed off by one error at FAT sub-type determination.
 *                   Fixed btr in f_read() can be mistruncated.
 *                   Fixed cached sector is not flushed when create and close
 *                   without write.
 *
 * Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
 *                   Improved performance of f_lseek() on moving to the same
 *                   or following cluster.
 *---------------------------------------------------------------------------
 */


/* ---------------------------------------------------------------------------
 * System include files
 * ---------------------------------------------------------------------------
 */
/* Compiler includes */
#include <string.h>

#include "LPC23xx.h"
#include "types.h"

/* FreeRTOS includes */
#include "FreeRTOS.h"
#include "task.h"
/* ---------------------------------------------------------------------------
 * Application include files
 * ---------------------------------------------------------------------------
 */
#include "fat_intern.h" /* FatFs declarations                               */
#include "fat_public.h"
#include "fat_diskio.h"     /* Include file for user provided disk functions    */
#include "fat_time.h"   /* Get time from RTC                                */

/* ---------------------------------------------------------------------------
 * Local constant and macro definitions
 * ---------------------------------------------------------------------------
 */

/* ---------------------------------------------------------------------------
 * Global variable definitions
 * ---------------------------------------------------------------------------
 */

/* ---------------------------------------------------------------------------
 * Local variable definitions
 * ---------------------------------------------------------------------------
 */
FATFS *FatFs[_DRIVES] = {0,0};          /* Pointer to file system objects   */
                                        /* (logical drives)                 */
UINT16 fsid;                            /* File system mount ID             */
/* ---------------------------------------------------------------------------
 * Local function definitions
 * ---------------------------------------------------------------------------
 */

BOOLEAN move_window (FATFS *fs, UINT32 sector)
{
    UINT32 wsect;

    wsect = fs->winsect;
    if (wsect != sector)
        { 
        /* Changed current window                                           */
#if !_FS_READONLY                       /* If write-access granted          */
        UINT8 n;
        if (fs->winflag)
            { 
            /* Write back dirty window if needed                            */
            if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK)
                return FALSE;
            fs->winflag = 0;
            if (wsect < (fs->fatbase + fs->sects_fat))
                { 
                /* In FAT area                                              */
                for (n = fs->n_fats; n >= 2; n--)
                    { 
                    /* Refrect the change to FAT copy                       */
                    wsect += fs->sects_fat;
                    disk_write(fs->drive, fs->win, wsect, 1);
                    }
                }
            }
#endif
        if (sector)
            {
            if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK)
                {
                return FALSE;
                }
            fs->winsect = sector;
            }
        }
    return TRUE;
}


#if !_FS_READONLY
FRESULT sync (FATFS *fs)
{
    fs->winflag = 1;
    if (!move_window(fs, 0))
        return FR_RW_ERROR;
#if _USE_FSINFO
    /* Update FSInfo sector if needed                                       */
    if (fs->fs_type == FS_FAT32 && fs->fsi_flag)
        {
        fs->winsect = 0;
        memset(fs->win, 0, 512);
        ST_WORD(&fs->win[BS_55AA], 0xAA55);
        ST_DWORD(&fs->win[FSI_LeadSig], 0x41615252);
        ST_DWORD(&fs->win[FSI_StrucSig], 0x61417272);
        ST_DWORD(&fs->win[FSI_Free_Count], fs->free_clust);
        ST_DWORD(&fs->win[FSI_Nxt_Free], fs->last_clust);
        disk_write(fs->drive, fs->win, fs->fsi_sector, 1);
        fs->fsi_flag = 0;
        }
#endif
    /* Make sure that no pending write process in the physical drive */
    if (disk_ioctl(fs->drive, CTRL_SYNC, NULL) != RES_OK)
        return FR_RW_ERROR;
    return FR_OK;
}
#endif


UINT32 get_cluster (FATFS *fs, UINT32 clust)
{
    UINT16 wc, bc;
    UINT32 fatsect;

    if (clust >= 2 && clust < fs->max_clust)
        {
        /* Is it a valid cluster number?                                    */
        fatsect = fs->fatbase;
        switch (fs->fs_type)
            {
            case FS_FAT12:
                bc = (UINT16)clust * 3 / 2;
                if (!move_window(fs, fatsect + (bc / SS(fs))))
                    break;
                wc = fs->win[bc & (SS(fs) - 1)];
                bc++;
                if (!move_window(fs, fatsect + (bc / SS(fs))))
                    break;
                wc |= (UINT16)fs->win[bc & (SS(fs) - 1)] << 8;
                return (clust & 1) ? (wc >> 4) : (wc & 0xFFF);

            case FS_FAT16:
                if (!move_window(fs, fatsect + (clust / (SS(fs) / 2))))
                    break;
                return LD_WORD(&fs->win[((UINT16)clust * 2) & (SS(fs) - 1)]);

            case FS_FAT32:
                if (!move_window(fs, fatsect + (clust / (SS(fs) / 4))))
                    break;
                return LD_DWORD(&fs->win[((UINT16)clust * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF;
            }
        }

    return 1; /* Out of cluster range, or an error occured */
}


#if !_FS_READONLY
BOOLEAN put_cluster(FATFS *fs, UINT32 clust, UINT32 val)
{
    UINT32 fatsect;
    UINT16 bc;
    UINT8 *p;

    fatsect = fs->fatbase;
    
    switch (fs->fs_type)
        {
        case FS_FAT12:
            bc = (UINT16)clust * 3 / 2;
            if (!move_window(fs, fatsect + (bc / SS(fs))))
                {
                return FALSE;
                }
            p = &fs->win[bc & (SS(fs) - 1)];
            *p = (clust & 1) ? ((*p & 0x0F) | ((UINT8)val << 4)) : (UINT8)val;
            bc++;
            fs->winflag = 1;
            if (!move_window(fs, fatsect + (bc / SS(fs))))
                {
                return FALSE;
                }
            p = &fs->win[bc & (SS(fs) - 1)];
            *p = (clust & 1) ? (UINT8)(val >> 4) : ((*p & 0xF0) | ((UINT8)(val
                    >> 8) & 0x0F));
            break;

        case FS_FAT16:
            if (!move_window(fs, fatsect + (clust / (SS(fs) / 2))))
                {
                return FALSE;
                }
            ST_WORD(&fs->win[((UINT16)clust * 2) & (SS(fs) - 1)], (UINT16)val);
            break;

        case FS_FAT32:
            if (!move_window(fs, fatsect + (clust / (SS(fs) / 4))))
                {
                return FALSE;
                }
            ST_DWORD(&fs->win[((UINT16)clust * 4) & (SS(fs) - 1)], val);
            break;

        default:
            return FALSE;
        }
    fs->winflag = 1;
    return TRUE;
}
#endif /* !_FS_READONLY */


#if !_FS_READONLY
BOOLEAN remove_chain (FATFS *fs, UINT32 clust)
{
    UINT32 nxt;

    while (clust >= 2 && clust < fs->max_clust)
        {
        nxt = get_cluster(fs, clust);
        if (nxt == 1)
            {
            return FALSE;
            }
        if (!put_cluster(fs, clust, 0))
            {
            return FALSE;
            }
        if (fs->free_clust != 0xFFFFFFFF)
            {
            fs->free_clust++;
#if _USE_FSINFO
            fs->fsi_flag = 1;
#endif
            }
        clust = nxt;
        }
    return TRUE;
}
#endif


#if !_FS_READONLY
UINT32 create_chain (FATFS *fs, UINT32 clust)
{
    UINT32 cstat;
    UINT32 ncl;
    UINT32 scl;
    UINT32 mcl = fs->max_clust;

    if (clust == 0)
        { 
        /* Create new chain                                                 */
        scl = fs->last_clust; /* Get suggested start point                  */
        if (scl == 0 || scl >= mcl)
            scl = 1;
        } 
    else
        { 
        /* Stretch existing chain                                           */
        cstat = get_cluster(fs, clust); /* Check the cluster status         */
        if (cstat < 2)
            {
            return 1;                   /* It is an invalid cluster         */
            }
        if (cstat < mcl)
            {
            return cstat;               /* Is already followed by cluster   */
            }
        scl = clust;
        }

    ncl = scl;                          /* Start cluster                    */
    for (;;)
        {
        ncl++;                          /* Next cluster                     */
        if (ncl >= mcl)
            { 
            /* Wrap around                                                  */
            ncl = 2;
            if (ncl > scl)
                return 0;               /* No free custer                   */
            }
        cstat = get_cluster(fs, ncl);   /* Get the cluster status           */
        if (cstat == 0)
            {
            break;                      /* Found a free cluster             */
            }
        if (cstat == 1)
            {
            return 1;                   /* Any error occured                */
            }
        if (ncl == scl)
            {
            return 0;                   /* No free custer                   */
            }
        }

    if (!put_cluster(fs, ncl, 0x0FFFFFFF))
        {
        return 1;                       /* Mark the new cluster "in use"    */
        }
    if (clust != 0 && !put_cluster(fs, clust, ncl))
        {
        return 1;                       /* Link it to previous one if needed*/
        }
    fs->last_clust = ncl;               /* Update fsinfo                    */
    if (fs->free_clust != 0xFFFFFFFF)
        {
        fs->free_clust--;
#if _USE_FSINFO
        fs->fsi_flag = 1;
#endif
        }

    return ncl;                         /* Return new cluster number        */
}
#endif /* !_FS_READONLY */


UINT32 clust2sect(FATFS *fs, UINT32 clust)
{
    clust -= 2;
    if (clust >= (fs->max_clust - 2))
        {
        return 0;                       /* Invalid cluster number           */
        }
    
    return (clust * fs->csize + fs->database);
}


BOOLEAN next_dir_entry(DIR *dj)
{
    UINT32 clust;
    UINT16 idx;

    idx = dj->index + 1;
    if ((idx & ((SS(dj->fs) - 1) / 32)) == 0)
        {
        /* Table sector changed?                                            */
        dj->sect++;                     /* Next sector                      */
        if (dj->clust == 0)
            { 
            /* In static table                                              */
            if (idx >= dj->fs->n_rootdir)
                {
                return FALSE;           /* Reached to end of table          */
                }                
            } 
        else
            { 
            /* In dynamic table                                             */
            if (((idx / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0)
                { 
                /* Cluster changed?                                         */
                clust = get_cluster(dj->fs, dj->clust); /* Get next cluster */
                if (clust < 2 || clust >= dj->fs->max_clust)
                    {
                    /* Reached to end of table                              */
                    return FALSE;
                    }
                dj->clust = clust;      /* Initialize for new cluster       */
                dj->sect = clust2sect(dj->fs, clust);
                }
            }
        }
    dj->index = idx;                    /* Lower bits of dj->index indicates*/
                                        /* offset in dj->sect               */
    return TRUE;
}


#if _FS_MINIMIZE <= 1
void get_fileinfo (FILINFO *finfo, const UINT8 *dir)
{
    UINT8 n;
    UINT8 c;
    UINT8 a;
    char *p;

    p = &finfo->fname[0];
    a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag                        */
    for (n = 0; n < 8; n++)
        { 
        /* Convert file name (body)                                         */
        c = dir[n];
        if (c == ' ')
            {
            break;
            }
        if (c == 0x05)
            {
            c = 0xE5;
            }
        if (a & 0x08 && c >= 'A' && c <= 'Z')
            {
            c += 0x20;
            }
        *p++ = c;
        }
    if (dir[8] != ' ')
        { 
        /* Convert file name (extension)                                    */
        *p++ = '.';
        for (n = 8; n < 11; n++)
            {
            c = dir[n];
            if (c == ' ')
                {
                break;
                }
            if (a & 0x10 && c >= 'A' && c <= 'Z')
                {
                c += 0x20;
                }
            *p++ = c;
            }
        }
    *p = '\0';

    finfo->fattrib = dir[DIR_Attr];              /* Attribute               */
    finfo->fsize = LD_DWORD(&dir[DIR_FileSize]); /* Size                    */
    finfo->fdate = LD_WORD(&dir[DIR_WrtDate]);   /* Date                    */
    finfo->ftime = LD_WORD(&dir[DIR_WrtTime]);   /* Time                    */
}
#endif /* _FS_MINIMIZE <= 1                                                 */


char make_dirfile(const char **path, char *dirname)
{
    UINT8 n;
    UINT8 t;
    UINT8 c;
    UINT8 a;
    UINT8 b;

    memset(dirname, ' ', 8+3);          /* Fill buffer with spaces          */
    a = 0;
    b = 0x18;                           /* NT flag                          */
    n = 0;
    t = 8;
    for (;;)
        {
        c = *(*path)++;
        if (c == '\0' || c == '/')
            { 
            /* Reached to end of str or directory separator                 */
            if (n == 0)
                {
                break;
                }
            dirname[11] = _USE_NTFLAG ? (a & b) : 0;
            return c;
            }
        if (c <= ' ' || c == 0x7F)
            {
            break;                      /* Reject invisible chars           */
            }
        if (c == '.')
            {
            if (!(a & 1) && n >= 1 && n <= 8)
                { 
                /* Enter extension part                                     */
                n = 8;
                t = 11;
                continue;
                }
            break;
            }
        if (_USE_SJIS && ((c >= 0x81 && c <= 0x9F) || /* Accept S-JIS code  */
        (c >= 0xE0 && c <= 0xFC)))
            {
            if (n == 0 && c == 0xE5)    /* Change heading \xE5 to \x05      */
                {
                c = 0x05;
                }
            a ^= 0x01;
            goto md_l2;                 /* LABEL SEE END OF FUNCTION        */
            }
        if (c == '"')
            {
            break;                      /* Reject "                         */
            }
        if (c <= ')')
            {
            goto md_l1;                 /* LABEL SEE END OF FUNCTION        */
            }
        /* Accept ! # $ % & ' ( )                                           */
        if (c <= ',')
            {
            break;                      /* Reject * + ,                     */
            }
        if (c <= '9')
            {
            goto md_l1;
            }
        /* Accept - 0-9                                                     */
        if (c <= '?')
            {
            break;                      /* Reject : ; < = > ?               */
            }
        if (!(a & 1))
            { 
            /* These checks are not applied to S-JIS 2nd byte               */
            if (c == '|')
                {
                break;                  /* Reject |                         */
                }
            if (c >= '[' && c <= ']')
                {
                break;                  /* Reject [ \ ]                     */
                }
            if (_USE_NTFLAG && c >= 'A' && c <= 'Z')
                {
                (t == 8) ? (b &= 0xF7) : (b &= 0xEF);
                }
            if (c >= 'a' && c <= 'z')
                { 
                /* Convert to upper case                                    */
                c -= 0x20;
                if (_USE_NTFLAG)
                    {
                    (t == 8) ? (a |= 0x08) : (a |= 0x10);
                    }
                }
            }
        /* GOTO LABELS                                                      */
        md_l1: a &= 0xFE;       
        md_l2: if (n >= t)
            {
            break;
            }
        dirname[n++] = c;
        }
    return 1;
}


FRESULT trace_path (DIR *dj, char *fn, const char *path, UINT8 **dir)
{
    UINT32 clust;
    char ds;
    UINT8 *dptr = NULL;
    FATFS *fs = dj->fs;

    /* Initialize directory object                                          */
    clust = fs->dirbase;
    if (fs->fs_type == FS_FAT32)
        {
        dj->clust = dj->sclust = clust;
        dj->sect = clust2sect(fs, clust);
        } 
    else
        {
        dj->clust = dj->sclust = 0;
        dj->sect = clust;
        }
    dj->index = 0;

    if (*path == '\0')
        { 
        /* Null path means the root directory                               */
        *dir = NULL;
        return FR_OK;
        }

    for (;;)
        {
        ds = make_dirfile(&path, fn);   /* Get a paragraph into fn[]        */
        if (ds == 1)
            {
            return FR_INVALID_NAME;
            }
        for (;;)
            {
            if (!move_window(fs, dj->sect))
                {
                return FR_RW_ERROR;
                }
            /* Pointer to the directory entry                               */
            dptr = &fs->win[(dj->index & ((SS(fs) - 1) / 32)) * 32]; 
            if (dptr[DIR_Name] == 0)    /* Has it reached to end of dir?    */
                {
                return !ds ? FR_NO_FILE : FR_NO_PATH;
                }
            if (dptr[DIR_Name] != 0xE5  /* Matched?                         */
            && !(dptr[DIR_Attr] & AR_VOL) 
            && !memcmp(&dptr[DIR_Name], fn, 8+3))
                {
                break;
                }
            if (!next_dir_entry(dj))    /* Next directory pointer           */
                {
                return !ds ? FR_NO_FILE : FR_NO_PATH;
                }
            }
        if (!ds)
            {
            *dir = dptr;
            return FR_OK;
            }                           /* Matched with end of path         */
        if (!(dptr[DIR_Attr] & AR_DIR))
            {
            return FR_NO_PATH;          /* Cannot trace because it is a file*/
            }
        /* Get cluster# of the directory                                    */
        clust = ((UINT32)LD_WORD(&dptr[DIR_FstClusHI]) << 16)
                | LD_WORD(&dptr[DIR_FstClusLO]); 
        /* Restart scanning at the new directory                            */
        dj->clust = dj->sclust = clust; 
        dj->sect = clust2sect(fs, clust);
        dj->index = 2;
        }
}



#if !_FS_READONLY
FRESULT reserve_direntry (DIR *dj, UINT8 **dir)
{
    UINT32 clust;
    UINT32 sector;
    UINT8 c;
    UINT8 n;
    UINT8 *dptr;
    FATFS *fs = dj->fs;

    /* Re-initialize directory object                                       */
    clust = dj->sclust;
    if (clust != 0)
        { 
        /* Dyanmic directory table                                          */
        dj->clust = clust;
        dj->sect = clust2sect(fs, clust);
        } 
    else
        { 
        /* Static directory table                                           */
        dj->sect = fs->dirbase;
        }
    dj->index = 0;

    do
        {
        if (!move_window(fs, dj->sect))
            {
            return FR_RW_ERROR;
            }
        /* Pointer to the directory entry */
        dptr = &fs->win[(dj->index & ((SS(dj->fs) - 1) / 32)) * 32]; 
        c = dptr[DIR_Name];
        if (c == 0 || c == 0xE5)
            { 
            /* Found an empty entry                                         */
            *dir = dptr;
            return FR_OK;
            }
        } while (next_dir_entry(dj));   /* Next directory pointer           */
    /* Reached to end of the directory table                                */

    /* Abort when it is a static table or could not stretch dynamic table   */
    if (clust == 0 || !(clust = create_chain(fs, dj->clust)))
        {
        return FR_DENIED;
        }
    if (clust == 1 || !move_window(fs, 0))
        {
        return FR_RW_ERROR;
        }

    /* Cleanup the expanded table                                           */
    fs->winsect = sector = clust2sect(fs, clust);
    memset(fs->win, 0, SS(fs));
    for (n = fs->csize; n; n--)
        {
        if (disk_write(fs->drive, fs->win, sector, 1) != RES_OK)
            {
            return FR_RW_ERROR;
            }
        sector++;
        }
    fs->winflag = 1;
    *dir = fs->win;

    return FR_OK;
}
#endif 


UINT8 check_fs (FATFS *fs, UINT32 sect)
{

    /* Load boot record */
    if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK) 
        {
        return 2;
        }
    /* Check record signature (always placed at offset 510 even if the sector 
     * size is >512) 
     */
    if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) 
        {
        return 2;
        }
    
    /* Check FAT signature */
    if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) 
        {
        return 0;
        }
    if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5)
            && !(fs->win[BPB_ExtFlags] & 0x80))
        {
        return 0;
        }

    return 1;
}


FRESULT auto_mount (const char **path, FATFS **rfs, UINT8 chk_wp)
{
    UINT32 bootsect;
    UINT32 fatsize;
    UINT32 totalsect;
    UINT32 maxclust;
    UINT8 drv;
    UINT8 fmt;
    UINT8 *tbl;
    DSTATUS stat;
    
    const char *p = *path;
    FATFS *fs;

    /* Get drive number from the path name                                  */
    while (*p == ' ')
        {
        p++;                            /* Strip leading spaces             */
        }
    drv = p[0] - '0';                   /* Is there a drive number?         */
    if (drv <= 9 && p[1] == ':')
        {
        p += 2;                         /* Found drive number, get&strip it */
        }
    else
        {
        drv = 0;                        /* No drive number is given, use    */
                                        /* drive number 0 as default        */
        }
    if (*p == '/')
        {
        p++;                            /* Strip heading slash              */
        }
    *path = p;                          /* Return pointer to the path name  */

    /* Check if the drive number is valid or not                            */
    if (drv >= _DRIVES)
        {
        return FR_INVALID_DRIVE;        /* Is the drive number valid?       */
        }
    *rfs = fs = FatFs[drv];             /* Returen pointer to the           */
                                        /* corresponding file system object */
    if (!fs)
        {
        return FR_NOT_ENABLED;          /* Is file system object registered?*/
        }

    if (fs->fs_type)
        {
        /* If the logical drive has been mounted                            */
        stat = disk_status(fs->drive);
        if (!(stat & STA_NOINIT))
            { 
            /* and physical drive is kept initialized (has not been changed)*/
#if !_FS_READONLY
            /* Check write protection if needed */
            if (chk_wp && (stat & STA_PROTECT)) 
                {
                return FR_WRITE_PROTECTED;
                }
#endif
            return FR_OK;               /* The file system object is valid  */
            }
        }

    /* The logical drive must be re-mounted. 
     * Following code attempts to mount the logical drive 
     */

    memset(fs, 0, sizeof(FATFS));       /* Clean-up the file system object  */
    fs->drive = LD2PD(drv);             /* Bind logical and physical drive  */
    stat = disk_initialize(fs->drive);  /* Initialize low level I/O layer   */
    
    if (stat & STA_NOINIT) /* Check if the drive is ready */
        {
        return FR_NOT_READY;
        }
#if S_MAX_SIZ > 512						/* Get disk sector size if needed   */
    if (disk_ioctl(drv,GET_SECTOR_SIZE,&SS(fs)) != RES_OK || SS(fs)> S_MAX_SIZ)
        {
        return FR_NO_FILESYSTEM;
        }
#endif
#if !_FS_READONLY
    if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */
        {
        return FR_WRITE_PROTECTED;
        }
#endif
    /* Search FAT partition on the drive                                    */
    fmt = check_fs(fs, bootsect = 0);   /* Check sector 0 as an SFD format  */
    if (fmt == 1)
        { 
        /* Not an FAT boot record, it may be patitioned                     */
        /* Check a partition listed in top of the partition table           */
        tbl = &fs->win[MBR_Table + LD2PT(drv) * 16]; /* Partition table     */
        if (tbl[4])
            { 
            /* Is the partition existing?                                   */
            bootsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA        */
            fmt = check_fs(fs, bootsect); /* Check the partition            */
            }
        }
    /* No valid FAT patition is found */
    if (fmt || LD_WORD(&fs->win[BPB_BytsPerSec]) != SS(fs)) 
        {
        return FR_NO_FILESYSTEM;
        }

    /* Initialize the file system object                                    */
    fatsize = LD_WORD(&fs->win[BPB_FATSz16]); /* Number of sectors per FAT  */
    if (!fatsize)
        {
        fatsize = LD_DWORD(&fs->win[BPB_FATSz32]);
        }
    fs->sects_fat = fatsize;
    /* Number of FAT copies                                                 */
    fs->n_fats = fs->win[BPB_NumFATs];
    /* (Number of sectors in FAT area)                                      */  
    fatsize *= fs->n_fats;              
    /* FAT start sector (lba)                                               */
    fs->fatbase = bootsect + LD_WORD(&fs->win[BPB_RsvdSecCnt]);
    /* Number of sectors per cluster                                        */ 
    fs->csize = fs->win[BPB_SecPerClus]; 
    /* Nmuber of root directory entries                                     */
    fs->n_rootdir = LD_WORD(&fs->win[BPB_RootEntCnt]); 
    /* Number of sectors on the file system                                 */
    totalsect = LD_WORD(&fs->win[BPB_TotSec16]); 
    if (!totalsect)
        {
        totalsect = LD_DWORD(&fs->win[BPB_TotSec32]);
        }
    
    /* max_clust = Last cluster# + 1 */
    fs->max_clust = maxclust = (totalsect - LD_WORD(&fs->win[BPB_RsvdSecCnt])
            - fatsize - fs->n_rootdir / (SS(fs)/32) ) / fs->csize + 2;

    fmt = FS_FAT12;                     /* Determine the FAT sub type       */
    if (maxclust >= 0xFF7)
        {
        fmt = FS_FAT16;
        }
    if (maxclust >= 0xFFF7)
        {
        fmt = FS_FAT32;
        }

    if (fmt == FS_FAT32)
        {
        /* Root directory start cluster                                     */
        fs->dirbase = LD_DWORD(&fs->win[BPB_RootClus]); 
        }
    else
        {
        /* Root directory start sector (lba)                                */
        fs->dirbase = fs->fatbase + fatsize; 
        }
    
    /* Data start sector (lba)                                              */
    fs->database = fs->fatbase + fatsize + fs->n_rootdir / (SS(fs)/32); 

#if !_FS_READONLY
    /* Initialize allocation information                                    */
    fs->free_clust = 0xFFFFFFFF;
#if _USE_FSINFO
    /* Get fsinfo if needed                                                 */
    if (fmt == FS_FAT32)
        {
        fs->fsi_sector = bootsect + LD_WORD(&fs->win[BPB_FSInfo]);
        if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK &&
                LD_WORD(&fs->win[BS_55AA]) == 0xAA55 &&
                LD_DWORD(&fs->win[FSI_LeadSig]) == 0x41615252 &&
                LD_DWORD(&fs->win[FSI_StrucSig]) == 0x61417272)
            {
            fs->last_clust = LD_DWORD(&fs->win[FSI_Nxt_Free]);
            fs->free_clust = LD_DWORD(&fs->win[FSI_Free_Count]);
            }
        }
#endif
#endif

    fs->fs_type = fmt;                  /* FAT syb-type                     */
    fs->id = ++fsid;                    /* File system mount ID             */
    return FR_OK;
}


FRESULT validate (const FATFS *fs, UINT16 id)
{
    if (!fs || !fs->fs_type || fs->id != id)
        {
        return FR_INVALID_OBJECT;
        }
    
    if (disk_status(fs->drive) & STA_NOINIT)
        {
        return FR_NOT_READY;
        }

    return FR_OK;
}
