test fork

Dependencies:   SPI_TFTx2 SPI_TFTx2_ILI9341 TFT_fonts TOUCH_TFTx2 mbed

Fork of CANary_9341 by Tick Tock

Files at this revision

API Documentation at this revision

Comitter:
TickTock
Date:
Sun Jul 21 23:59:00 2013 +0000
Parent:
64:a2b3c7201faa
Parent:
128:25314f339565
Child:
130:1a9d2a6d99ce
Commit message:
Merged into default (with errors)

Changed in this revision

FatFileSystem.lib Show diff for this revision Revisions of this file
MSCFileSystem.lib Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed41.bld Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHAN_FS/diskio.c	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,180 @@
+/*-----------------------------------------------------------------------*/
+/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2007        */
+/*-----------------------------------------------------------------------*/
+/* This is a stub disk I/O module that acts as front end of the existing */
+/* disk I/O modules and attach it to FatFs module with common interface. */
+/*-----------------------------------------------------------------------*/
+
+#include "diskio.h"
+#include "usbhost_inc.h"
+#include "mbed.h"
+
+uint32_t _numBlks = 0;
+uint32_t _blkSize = 512;
+
+int initialise_msc();
+void print_inquiry(USB_INT08U *inqReply);
+
+
+int disk_sync() { return 0; }
+int disk_sectors() { return _numBlks; }
+
+DWORD get_fattime(void)
+{
+    time_t CurrentTimeStamp;
+    tm *CurrentLocalTime;
+    DWORD FATFSTimeCode;
+        
+    CurrentTimeStamp = time(NULL);
+    CurrentLocalTime = localtime(&CurrentTimeStamp);
+        
+        //Map the tm struct time into the FatFs time code    
+    FATFSTimeCode =  ((CurrentLocalTime->tm_year-80)<<25) | 
+                     ((CurrentLocalTime->tm_mon+1)<<21)   | 
+                     ((CurrentLocalTime->tm_mday)<<16)    | 
+                     ((CurrentLocalTime->tm_hour)<<11)    |
+                     ((CurrentLocalTime->tm_min)<<5)     | 
+                     ((CurrentLocalTime->tm_sec));
+
+   return FATFSTimeCode;
+}
+
+DSTATUS disk_status(BYTE Drive)
+{
+    return 0;
+}
+
+    
+DRESULT disk_ioctl (
+    BYTE drv,        /* Physical drive nmuber (0..) */
+    BYTE ctrl,        /* Control code */
+    void *buff        /* Buffer to send/receive control data */
+)
+{
+    DRESULT res;
+
+    switch(ctrl)
+    {
+        case CTRL_SYNC:
+             res = RES_OK;
+        break;
+    
+        case GET_SECTOR_SIZE:
+              res = RES_OK;
+            *(WORD *)buff = 512;
+        break;
+        
+        case GET_SECTOR_COUNT:
+            res = RES_OK;
+           *(DWORD *)buff = (WORD)disk_sectors();
+        break;
+        
+        case GET_BLOCK_SIZE:
+         res = RES_OK;
+          *(DWORD *)buff = 1;
+        break;
+        
+        default:
+        res = RES_OK;
+        break;
+    }
+    return res;
+}
+
+DSTATUS disk_initialize(BYTE Drive) {
+
+    if ( initialise_msc() != OK )
+        return 1;
+    return 0;
+}
+
+DRESULT disk_write(BYTE Drive,const BYTE * Buffer, DWORD SectorNumber, BYTE SectorCount)
+{
+   if ( OK == MS_BulkSend(SectorNumber, SectorCount, (USB_INT08U *)Buffer) )
+        return RES_OK;
+    return RES_ERROR;    
+}
+
+DRESULT disk_read(BYTE Drive, BYTE * Buffer,DWORD SectorNumber, BYTE SectorCount)
+{        
+    if ( OK == MS_BulkRecv(SectorNumber, SectorCount, (USB_INT08U *)Buffer) )
+        return RES_OK;
+    return RES_ERROR;
+}
+
+
+
+
+
+void print_inquiry(USB_INT08U *inqReply)
+{
+    // see USB Mass Storage Class &#65533; UFI Command Specification,
+    // 4.2 INQUIRY Command
+    printf("Inquiry reply:\n"); 
+    uint8_t tmp = inqReply[0]&0x1F;
+    printf("Peripheral device type: %02Xh\n", tmp);
+    if ( tmp == 0 )
+        printf("\t- Direct access (floppy)\n");
+    else if ( tmp == 0x1F )
+        printf("\t- none (no FDD connected)\n");
+    else
+        printf("\t- unknown type\n");
+    tmp = inqReply[1] >> 7;
+    printf("Removable Media Bit: %d\n", tmp);
+    tmp = inqReply[2] & 3;
+    printf("ANSI Version: %02Xh\n", tmp);
+    if ( tmp != 0 )
+        printf("\t- warning! must be 0\n");
+    tmp = (inqReply[2]>>3) & 3;
+    printf("ECMA Version: %02Xh\n", tmp);
+    if ( tmp != 0 )
+        printf("\t- warning! should be 0\n");
+    tmp = inqReply[2]>>6;
+    printf("ISO Version: %02Xh\n", tmp);
+    if ( tmp != 0 )
+        printf("\t- warning! should be 0\n");
+    tmp = inqReply[3] & 0xF;
+    printf("Response Data Format: %02Xh\n", tmp);
+    if ( tmp != 1 )
+        printf("\t- warning! should be 1\n");
+    tmp = inqReply[4];
+    printf("Additional length: %02Xh\n", tmp);
+    if ( tmp != 0x1F )
+        printf("\t- warning! should be 1Fh\n");
+    printf("Vendor Information: '%.8s'\n", &inqReply[8]);
+    printf("Product Identification: '%.16s'\n", &inqReply[16]);
+    printf("Product Revision: '%.4s'\n", &inqReply[32]);        
+}
+
+
+
+int initialise_msc()
+{
+    USB_INT32S  rc;
+    USB_INT08U  inquiryResult[INQUIRY_LENGTH];
+    
+    //print_clock();
+    Host_Init();               /* Initialize the  host controller                                    */
+    rc = Host_EnumDev();       /* Enumerate the device connected                                            */
+    if (rc != OK)
+    {
+        fprintf(stderr, "Could not enumerate device: %d\n", rc);
+        return rc;
+    }
+        
+    
+    /* Initialize the mass storage and scsi interfaces */
+    rc = MS_Init( &_blkSize, &_numBlks, inquiryResult );
+    if (rc != OK)
+    {
+        fprintf(stderr, "Could not initialize mass storage interface: %d\n", rc);
+        return rc;
+    }
+    printf("Successfully initialized mass storage interface; %d blocks of size %d\n", _numBlks, _blkSize);
+    print_inquiry(inquiryResult);
+    // FATFileSystem supports only 512-byte blocks
+    return _blkSize == 512 ? OK : 1;
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHAN_FS/diskio.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,68 @@
+/*-----------------------------------------------------------------------
+/  Low level disk interface modlue include file  R0.07   (C)ChaN, 2009
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO
+
+#define _READONLY    0    /* 1: Read-only mode */
+#define _USE_IOCTL    1
+
+#include "integer.h"
+
+/* Status of Disk Functions */
+typedef BYTE    DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+    RES_OK = 0,        /* 0: Successful */
+    RES_ERROR,        /* 1: R/W Error */
+    RES_WRPRT,        /* 2: Write Protected */
+    RES_NOTRDY,        /* 3: Not Ready */
+    RES_PARERR        /* 4: Invalid Parameter */
+} DRESULT;
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+extern BOOL assign_drives (int argc, char *argv[]);
+extern DSTATUS disk_initialize (BYTE);
+extern DSTATUS disk_status (BYTE);
+extern DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);
+extern DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
+extern DRESULT disk_ioctl (BYTE, BYTE, void*);
+
+extern int _sd_sectors();
+
+
+/* Disk Status Bits (DSTATUS) */
+
+#define STA_NOINIT        0x01    /* Drive not initialized */
+#define STA_NODISK        0x02    /* No medium in the drive */
+#define STA_PROTECT        0x04    /* Write protected */
+
+
+/* Command code for disk_ioctrl() */
+
+/* Generic command */
+#define CTRL_SYNC            0    /* Mandatory for write functions */
+#define GET_SECTOR_COUNT    1    /* Mandatory for only f_mkfs() */
+#define GET_SECTOR_SIZE        2    /* Mandatory for multiple sector size cfg */
+#define GET_BLOCK_SIZE        3    /* Mandatory for only f_mkfs() */
+#define CTRL_POWER            4
+#define CTRL_LOCK            5
+#define CTRL_EJECT            6
+/* MMC/SDC command */
+#define MMC_GET_TYPE        10
+#define MMC_GET_CSD            11
+#define MMC_GET_CID            12
+#define MMC_GET_OCR            13
+#define MMC_GET_SDSTAT        14
+/* ATA/CF command */
+#define ATA_GET_REV            20
+#define ATA_GET_MODEL        21
+#define ATA_GET_SN            22
+
+
+#define _DISKIO
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHAN_FS/ff.c	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,3154 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - FAT file system module  R0.07e                    (C)ChaN, 2009
+/-----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following trems.
+/
+/  Copyright (C) 2009, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ 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.
+/
+/ Apr 01,'09 R0.07  Merged Tiny-FatFs as a buffer configuration option.
+/                   Added long file name support.
+/                   Added multiple code page support.
+/                   Added re-entrancy for multitask operation.
+/                   Added auto cluster size selection to f_mkfs().
+/                   Added rewind option to f_readdir().
+/                   Changed result code of critical errors.
+/                   Renamed string functions to avoid name collision.
+/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
+/                   Added multiple sector size support.
+/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
+/                   Fixed wrong cache control in f_lseek().
+/                   Added relative path feature.
+/                   Added f_chdir() and f_chdrive().
+/                   Added proper case conversion to extended char.
+/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
+/                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
+/                   Fixed name matching error on the 13 char boundary.
+/                   Added a configuration option, _LFN_UNICODE.
+/                   Changed f_readdir() to return the SFN with always upper
+/                   case on non-LFN cfg.
+/---------------------------------------------------------------------------*/
+
+#include "ff.h"            /* FatFs configurations and declarations */
+#include "diskio.h"        /* Declarations of low level disk I/O functions */
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _FATFS != 0x007E
+#error Wrong include file (ff.h).
+#endif
+
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area must not be used in re-entrant configuration.
+#endif
+#define    ENTER_FF(fs)        { if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define    LEAVE_FF(fs, res)    { unlock_fs(fs, res); return res; }
+
+#else
+#define    ENTER_FF(fs)
+#define LEAVE_FF(fs, res)    return res
+
+#endif
+
+#define    ABORT(fs, res)        { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
+
+#ifndef NULL
+#define    NULL    0
+#endif
+
+/* Name status flags */
+#define NS            11        /* Offset of name status byte */
+#define NS_LOSS        0x01    /* Out of 8.3 format */
+#define NS_LFN        0x02    /* Force to create LFN entry */
+#define NS_LAST        0x04    /* Last segment */
+#define NS_BODY        0x08    /* Lower case flag (body) */
+#define NS_EXT        0x10    /* Lower case flag (ext) */
+#define NS_DOT        0x20    /* Dot entry */
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Private Work Area
+
+---------------------------------------------------------------------------*/
+
+#if _DRIVES < 1 || _DRIVES > 9
+#error Number of drives must be 1-9.
+#endif
+static
+FATFS *FatFs[_DRIVES];    /* Pointer to the file system objects (logical drives) */
+
+static
+WORD Fsid;                /* File system mount ID */
+
+#if _FS_RPATH
+static
+BYTE Drive;                /* Current drive */
+#endif
+
+
+#if _USE_LFN == 1    /* LFN with static LFN working buffer */
+static
+WCHAR LfnBuf[_MAX_LFN + 1];
+#define    NAMEBUF(sp,lp)    BYTE sp[12]; WCHAR *lp = LfnBuf
+#define INITBUF(dj,sp,lp)    dj.fn = sp; dj.lfn = lp
+
+#elif _USE_LFN > 1    /* LFN with dynamic LFN working buffer */
+#define    NAMEBUF(sp,lp)    BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
+#define INITBUF(dj,sp,lp)    dj.fn = sp; dj.lfn = lp
+
+#else                /* No LFN */
+#define    NAMEBUF(sp,lp)    BYTE sp[12]
+#define INITBUF(dj,sp,lp)    dj.fn = sp
+
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions                                                      */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, int cnt) {
+    char *d = (char*)dst;
+    const char *s = (const char *)src;
+    while (cnt--) *d++ = *s++;
+}
+
+/* Fill memory */
+static
+void mem_set (void* dst, int val, int cnt) {
+    char *d = (char*)dst;
+    while (cnt--) *d++ = (char)val;
+}
+
+/* Compare memory to memory */
+static
+int mem_cmp (const void* dst, const void* src, int cnt) {
+    const char *d = (const char *)dst, *s = (const char *)src;
+    int r = 0;
+    while (cnt-- && (r = *d++ - *s++) == 0) ;
+    return r;
+}
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr) {
+    while (*str && *str != chr) str++;
+    return *str;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume                            */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+
+static
+BOOL lock_fs (
+    FATFS *fs        /* File system object */
+)
+{
+    return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+    FATFS *fs,        /* File system object */
+    FRESULT res        /* Result code to be returned */
+)
+{
+    if (res != FR_NOT_ENABLED &&
+        res != FR_INVALID_DRIVE &&
+        res != FR_INVALID_OBJECT &&
+        res != FR_TIMEOUT) {
+        ff_rel_grant(fs->sobj);
+    }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change window offset                                                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT move_window (
+    FATFS *fs,        /* File system object */
+    DWORD sector    /* Sector number to make apperance in the fs->win[] */
+)                    /* Move to zero only writes back dirty window */
+{
+    DWORD wsect;
+
+
+    wsect = fs->winsect;
+    if (wsect != sector) {    /* Changed current window */
+#if !_FS_READONLY
+        if (fs->wflag) {    /* Write back dirty window if needed */
+            if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK)
+                return FR_DISK_ERR;
+            fs->wflag = 0;
+            if (wsect < (fs->fatbase + fs->sects_fat)) {    /* In FAT area */
+                BYTE nf;
+                for (nf = fs->n_fats; nf > 1; nf--) {    /* Refrect the change to all FAT copies */
+                    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 FR_DISK_ERR;
+            fs->winsect = sector;
+        }
+    }
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Clean-up cached data                                                  */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync (    /* FR_OK: successful, FR_DISK_ERR: failed */
+    FATFS *fs    /* File system object */
+)
+{
+    FRESULT res;
+
+
+    res = move_window(fs, 0);
+    if (res == FR_OK) {
+        /* Update FSInfo sector if needed */
+        if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
+            fs->winsect = 0;
+            mem_set(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;
+        }
+        /* Make sure that no pending write process in the physical drive */
+        if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK)
+            res = FR_DISK_ERR;
+    }
+
+    return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry                                */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD get_fat (    /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */
+    FATFS *fs,    /* File system object */
+    DWORD clst    /* Cluster# to get the link information */
+)
+{
+    UINT wc, bc;
+    DWORD fsect;
+
+
+    if (clst < 2 || clst >= fs->max_clust)    /* Range check */
+        return 1;
+
+    fsect = fs->fatbase;
+    switch (fs->fs_type) {
+    case FS_FAT12 :
+        bc = clst; bc += bc / 2;
+        if (move_window(fs, fsect + (bc / SS(fs)))) break;
+        wc = fs->win[bc & (SS(fs) - 1)]; bc++;
+        if (move_window(fs, fsect + (bc / SS(fs)))) break;
+        wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8;
+        return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
+
+    case FS_FAT16 :
+        if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break;
+        return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]);
+
+    case FS_FAT32 :
+        if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break;
+        return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF;
+    }
+
+    return 0xFFFFFFFF;    /* An error occured at the disk I/O layer */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry                              */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+
+FRESULT put_fat (
+    FATFS *fs,    /* File system object */
+    DWORD clst,    /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */
+    DWORD val    /* New value to mark the cluster */
+)
+{
+    UINT bc;
+    BYTE *p;
+    DWORD fsect;
+    FRESULT res;
+
+
+    if (clst < 2 || clst >= fs->max_clust) {    /* Range check */
+        res = FR_INT_ERR;
+
+    } else {
+        fsect = fs->fatbase;
+        switch (fs->fs_type) {
+        case FS_FAT12 :
+            bc = clst; bc += bc / 2;
+            res = move_window(fs, fsect + (bc / SS(fs)));
+            if (res != FR_OK) break;
+            p = &fs->win[bc & (SS(fs) - 1)];
+            *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+            bc++;
+            fs->wflag = 1;
+            res = move_window(fs, fsect + (bc / SS(fs)));
+            if (res != FR_OK) break;
+            p = &fs->win[bc & (SS(fs) - 1)];
+            *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+            break;
+
+        case FS_FAT16 :
+            res = move_window(fs, fsect + (clst / (SS(fs) / 2)));
+            if (res != FR_OK) break;
+            ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val);
+            break;
+
+        case FS_FAT32 :
+            res = move_window(fs, fsect + (clst / (SS(fs) / 4)));
+            if (res != FR_OK) break;
+            ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val);
+            break;
+
+        default :
+            res = FR_INT_ERR;
+        }
+        fs->wflag = 1;
+    }
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain                                 */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (
+    FATFS *fs,            /* File system object */
+    DWORD clst            /* Cluster# to remove a chain from */
+)
+{
+    FRESULT res;
+    DWORD nxt;
+
+
+    if (clst < 2 || clst >= fs->max_clust) {    /* Check the range of cluster# */
+        res = FR_INT_ERR;
+
+    } else {
+        res = FR_OK;
+        while (clst < fs->max_clust) {            /* Not a last link? */
+            nxt = get_fat(fs, clst);            /* Get cluster status */
+            if (nxt == 0) break;                /* Empty cluster? */
+            if (nxt == 1) { res = FR_INT_ERR; break; }    /* Internal error? */
+            if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
+            res = put_fat(fs, clst, 0);            /* Mark the cluster "empty" */
+            if (res != FR_OK) break;
+            if (fs->free_clust != 0xFFFFFFFF) {    /* Update FSInfo */
+                fs->free_clust++;
+                fs->fsi_flag = 1;
+            }
+            clst = nxt;    /* Next cluster */
+        }
+    }
+
+    return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch or Create a cluster chain                      */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+    FATFS *fs,            /* File system object */
+    DWORD clst            /* Cluster# to stretch. 0 means create a new chain. */
+)
+{
+    DWORD cs, ncl, scl, mcl;
+
+
+    mcl = fs->max_clust;
+    if (clst == 0) {        /* Create new chain */
+        scl = fs->last_clust;            /* Get suggested start point */
+        if (scl == 0 || scl >= mcl) scl = 1;
+    }
+    else {                    /* Stretch existing chain */
+        cs = get_fat(fs, clst);            /* Check the cluster status */
+        if (cs < 2) return 1;            /* It is an invalid cluster */
+        if (cs < mcl) return cs;        /* It is already followed by next cluster */
+        scl = clst;
+    }
+
+    ncl = scl;                /* Start cluster */
+    for (;;) {
+        ncl++;                            /* Next cluster */
+        if (ncl >= mcl) {                /* Wrap around */
+            ncl = 2;
+            if (ncl > scl) return 0;    /* No free custer */
+        }
+        cs = get_fat(fs, ncl);            /* Get the cluster status */
+        if (cs == 0) break;                /* Found a free cluster */
+        if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */
+            return cs;
+        if (ncl == scl) return 0;        /* No free custer */
+    }
+
+    if (put_fat(fs, ncl, 0x0FFFFFFF))    /* Mark the new cluster "in use" */
+        return 0xFFFFFFFF;
+    if (clst != 0) {                    /* Link it to the previous one if needed */
+        if (put_fat(fs, clst, ncl))
+            return 0xFFFFFFFF;
+    }
+
+    fs->last_clust = ncl;                /* Update FSINFO */
+    if (fs->free_clust != 0xFFFFFFFF) {
+        fs->free_clust--;
+        fs->fsi_flag = 1;
+    }
+
+    return ncl;        /* Return new cluster number */
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster#                                             */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD clust2sect (    /* !=0: Sector number, 0: Failed - invalid cluster# */
+    FATFS *fs,        /* File system object */
+    DWORD clst        /* Cluster# to be converted */
+)
+{
+    clst -= 2;
+    if (clst >= (fs->max_clust - 2)) return 0;        /* Invalid cluster# */
+    return clst * fs->csize + fs->database;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Seek directory index                             */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_seek (
+    eDIR *dj,        /* Pointer to directory object */
+    WORD idx        /* Directory index number */
+)
+{
+    DWORD clst;
+    WORD ic;
+
+
+    dj->index = idx;
+    clst = dj->sclust;
+    if (clst == 1 || clst >= dj->fs->max_clust)    /* Check start cluster range */
+        return FR_INT_ERR;
+    if (!clst && dj->fs->fs_type == FS_FAT32)    /* Replace cluster# 0 with root cluster# if in FAT32 */
+        clst = dj->fs->dirbase;
+
+    if (clst == 0) {    /* Static table */
+        dj->clust = clst;
+        if (idx >= dj->fs->n_rootdir)        /* Index is out of range */
+            return FR_INT_ERR;
+        dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32);    /* Sector# */
+    }
+    else {                /* Dynamic table */
+        ic = SS(dj->fs) / 32 * dj->fs->csize;    /* Entries per cluster */
+        while (idx >= ic) {    /* Follow cluster chain */
+            clst = get_fat(dj->fs, clst);                /* Get next cluster */
+            if (clst == 0xFFFFFFFF) return FR_DISK_ERR;    /* Disk error */
+            if (clst < 2 || clst >= dj->fs->max_clust)    /* Reached to end of table or int error */
+                return FR_INT_ERR;
+            idx -= ic;
+        }
+        dj->clust = clst;
+        dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32);    /* Sector# */
+    }
+
+    dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32;    /* Ptr to the entry in the sector */
+
+    return FR_OK;    /* Seek succeeded */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory index next                        */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next (    /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
+    eDIR *dj,        /* Pointer to directory object */
+    BOOL streach    /* FALSE: Do not streach table, TRUE: Streach table if needed */
+)
+{
+    DWORD clst;
+    WORD i;
+
+
+    i = dj->index + 1;
+    if (!i || !dj->sect)    /* Report EOT when index has reached 65535 */
+        return FR_NO_FILE;
+
+    if (!(i % (SS(dj->fs) / 32))) {    /* Sector changed? */
+        dj->sect++;                    /* Next sector */
+
+        if (dj->clust == 0) {    /* Static table */
+            if (i >= dj->fs->n_rootdir)    /* Report EOT when end of table */
+                return FR_NO_FILE;
+        }
+        else {                    /* Dynamic table */
+            if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) {    /* Cluster changed? */
+                clst = get_fat(dj->fs, dj->clust);                /* Get next cluster */
+                if (clst <= 1) return FR_INT_ERR;
+                if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                if (clst >= dj->fs->max_clust) {                /* When it reached end of dynamic table */
+#if !_FS_READONLY
+                    BYTE c;
+                    if (!streach) return FR_NO_FILE;            /* When do not streach, report EOT */
+                    clst = create_chain(dj->fs, dj->clust);        /* Streach cluster chain */
+                    if (clst == 0) return FR_DENIED;            /* No free cluster */
+                    if (clst == 1) return FR_INT_ERR;
+                    if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                    /* Clean-up streached table */
+                    if (move_window(dj->fs, 0)) return FR_DISK_ERR;    /* Flush active window */
+                    mem_set(dj->fs->win, 0, SS(dj->fs));            /* Clear window buffer */
+                    dj->fs->winsect = clust2sect(dj->fs, clst);    /* Cluster start sector */
+                    for (c = 0; c < dj->fs->csize; c++) {        /* Fill the new cluster with 0 */
+                        dj->fs->wflag = 1;
+                        if (move_window(dj->fs, 0)) return FR_DISK_ERR;
+                        dj->fs->winsect++;
+                    }
+                    dj->fs->winsect -= c;                        /* Rewind window address */
+#else
+                    return FR_NO_FILE;            /* Report EOT */
+#endif
+                }
+                dj->clust = clst;                /* Initialize data for new cluster */
+                dj->sect = clust2sect(dj->fs, clst);
+            }
+        }
+    }
+
+    dj->index = i;
+    dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};    /* Offset of LFN chars in the directory entry */
+
+
+static
+BOOL cmp_lfn (            /* TRUE:Matched, FALSE:Not matched */
+    WCHAR *lfnbuf,        /* Pointer to the LFN to be compared */
+    BYTE *dir            /* Pointer to the directory entry containing a part of LFN */
+)
+{
+    int i, s;
+    WCHAR wc, uc;
+
+
+    i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;    /* Get offset in the LFN buffer */
+    s = 0; wc = 1;
+    do {
+        uc = LD_WORD(dir+LfnOfs[s]);    /* Pick an LFN character from the entry */
+        if (wc) {    /* Last char has not been processed */
+            wc = ff_wtoupper(uc);        /* Convert it to upper case */
+            if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))    /* Compare it */
+                return FALSE;            /* Not matched */
+        } else {
+            if (uc != 0xFFFF) return FALSE;    /* Check filler */
+        }
+    } while (++s < 13);                /* Repeat until all chars in the entry are checked */
+
+    if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i])    /* Last segment matched but different length */
+        return FALSE;
+
+    return TRUE;                    /* The part of LFN matched */
+}
+
+
+
+static
+BOOL pick_lfn (            /* TRUE:Succeeded, FALSE:Buffer overflow */
+    WCHAR *lfnbuf,        /* Pointer to the Unicode-LFN buffer */
+    BYTE *dir            /* Pointer to the directory entry */
+)
+{
+    int i, s;
+    WCHAR wc, uc;
+
+
+    i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;    /* Offset in the LFN buffer */
+
+    s = 0; wc = 1;
+    do {
+        uc = LD_WORD(dir+LfnOfs[s]);            /* Pick an LFN character from the entry */
+        if (wc) {    /* Last char has not been processed */
+            if (i >= _MAX_LFN) return FALSE;    /* Buffer overflow? */
+            lfnbuf[i++] = wc = uc;                /* Store it */
+        } else {
+            if (uc != 0xFFFF) return FALSE;        /* Check filler */
+        }
+    } while (++s < 13);                        /* Read all character in the entry */
+
+    if (dir[LDIR_Ord] & 0x40) {                /* Put terminator if it is the last LFN part */
+        if (i >= _MAX_LFN) return FALSE;    /* Buffer overflow? */
+        lfnbuf[i] = 0;
+    }
+
+    return TRUE;
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+    const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
+    BYTE *dir,                /* Pointer to the directory entry */
+    BYTE ord,                /* LFN order (1-20) */
+    BYTE sum                /* SFN sum */
+)
+{
+    int i, s;
+    WCHAR wc;
+
+
+    dir[LDIR_Chksum] = sum;            /* Set check sum */
+    dir[LDIR_Attr] = AM_LFN;        /* Set attribute. LFN entry */
+    dir[LDIR_Type] = 0;
+    ST_WORD(dir+LDIR_FstClusLO, 0);
+
+    i = (ord - 1) * 13;                /* Get offset in the LFN buffer */
+    s = wc = 0;
+    do {
+        if (wc != 0xFFFF) wc = lfnbuf[i++];    /* Get an effective char */
+        ST_WORD(dir+LfnOfs[s], wc);    /* Put it */
+        if (!wc) wc = 0xFFFF;        /* Padding chars following last char */
+    } while (++s < 13);
+    if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40;    /* Bottom LFN part is the start of LFN sequence */
+    dir[LDIR_Ord] = ord;            /* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name                                                  */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+void gen_numname (
+    BYTE *dst,            /* Pointer to genartated SFN */
+    const BYTE *src,    /* Pointer to source SFN to be modified */
+    const WCHAR *lfn,    /* Pointer to LFN */
+    WORD num            /* Sequense number */
+)
+{
+    char ns[8];
+    int i, j;
+
+
+    mem_cpy(dst, src, 11);
+
+    if (num > 5) {    /* On many collisions, generate a hash number instead of sequencial number */
+        do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn);
+    }
+
+    /* itoa */
+    i = 7;
+    do {
+        ns[i--] = (num % 10) + '0';
+        num /= 10;
+    } while (num);
+    ns[i] = '~';
+
+    /* Append the number */
+    for (j = 0; j < i && dst[j] != ' '; j++) {
+        if (IsDBCS1(dst[j])) {
+            if (j == i - 1) break;
+            j++;
+        }
+    }
+    do {
+        dst[j++] = (i < 8) ? ns[i++] : ' ';
+    } while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate sum of an SFN                                               */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+    const BYTE *dir        /* Ptr to directory entry */
+)
+{
+    BYTE sum = 0;
+    int n = 11;
+
+    do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+    return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (
+    eDIR *dj            /* Pointer to the directory object linked to the file name */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN
+    BYTE a, ord, sum;
+#endif
+
+    res = dir_seek(dj, 0);            /* Rewind directory object */
+    if (res != FR_OK) return res;
+
+#if _USE_LFN
+    ord = sum = 0xFF;
+#endif
+    do {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        dir = dj->dir;                    /* Ptr to the directory entry of current index */
+        c = dir[DIR_Name];
+        if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
+#if _USE_LFN    /* LFN configuration */
+        a = dir[DIR_Attr] & AM_MASK;
+        if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
+            ord = 0xFF;
+        } else {
+            if (a == AM_LFN) {            /* An LFN entry is found */
+                if (dj->lfn) {
+                    if (c & 0x40) {        /* Is it start of LFN sequence? */
+                        sum = dir[LDIR_Chksum];
+                        c &= 0xBF; ord = c;    /* LFN start order */
+                        dj->lfn_idx = dj->index;
+                    }
+                    /* Check validity of the LFN entry and compare it with given name */
+                    ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+                }
+            } else {                    /* An SFN entry is found */
+                if (!ord && sum == sum_sfn(dir)) break;    /* LFN matched? */
+                ord = 0xFF; dj->lfn_idx = 0xFFFF;    /* Reset LFN sequence */
+                if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break;    /* SFN matched? */
+            }
+        }
+#else        /* Non LFN configuration */
+        if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
+            break;
+#endif
+        res = dir_next(dj, FALSE);        /* Next entry */
+    } while (res == FR_OK);
+
+    return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+FRESULT dir_read (
+    eDIR *dj            /* Pointer to the directory object that pointing the entry to be read */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN
+    BYTE a, ord = 0xFF, sum = 0xFF;
+#endif
+
+    res = FR_NO_FILE;
+    while (dj->sect) {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        dir = dj->dir;                    /* Ptr to the directory entry of current index */
+        c = dir[DIR_Name];
+        if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
+#if _USE_LFN    /* LFN configuration */
+        a = dir[DIR_Attr] & AM_MASK;
+        if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
+            ord = 0xFF;
+        } else {
+            if (a == AM_LFN) {            /* An LFN entry is found */
+                if (c & 0x40) {            /* Is it start of LFN sequence? */
+                    sum = dir[LDIR_Chksum];
+                    c &= 0xBF; ord = c;
+                    dj->lfn_idx = dj->index;
+                }
+                /* Check LFN validity and capture it */
+                ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+            } else {                    /* An SFN entry is found */
+                if (ord || sum != sum_sfn(dir))    /* Is there a valid LFN? */
+                    dj->lfn_idx = 0xFFFF;        /* It has no LFN. */
+                break;
+            }
+        }
+#else        /* Non LFN configuration */
+        if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL))    /* Is it a valid entry? */
+            break;
+#endif
+        res = dir_next(dj, FALSE);                /* Next entry */
+        if (res != FR_OK) break;
+    }
+
+    if (res != FR_OK) dj->sect = 0;
+
+    return res;
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register (    /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+    eDIR *dj                /* Target directory with object name to be created */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN    /* LFN configuration */
+    WORD n, ne, is;
+    BYTE sn[12], *fn, sum;
+    WCHAR *lfn;
+
+
+    fn = dj->fn; lfn = dj->lfn;
+    mem_cpy(sn, fn, 12);
+
+    if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME;    /* Cannot create dot entry */
+
+    if (sn[NS] & NS_LOSS) {            /* When LFN is out of 8.3 format, generate a numbered name */
+        fn[NS] = 0; dj->lfn = NULL;            /* Find only SFN */
+        for (n = 1; n < 100; n++) {
+            gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
+            res = dir_find(dj);                /* Check if the name collides with existing SFN */
+            if (res != FR_OK) break;
+        }
+        if (n == 100) return FR_DENIED;        /* Abort if too many collisions */
+        if (res != FR_NO_FILE) return res;    /* Abort if the result is other than 'not collided' */
+        fn[NS] = sn[NS]; dj->lfn = lfn;
+    }
+
+    if (sn[NS] & NS_LFN) {            /* When LFN is to be created, reserve reserve an SFN + LFN entries. */
+        for (ne = 0; lfn[ne]; ne++) ;
+        ne = (ne + 25) / 13;
+    } else {                        /* Otherwise reserve only an SFN entry. */
+        ne = 1;
+    }
+
+    /* Reserve contiguous entries */
+    res = dir_seek(dj, 0);
+    if (res != FR_OK) return res;
+    n = is = 0;
+    do {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        c = *dj->dir;                /* Check the entry status */
+        if (c == 0xE5 || c == 0) {    /* Is it a blank entry? */
+            if (n == 0) is = dj->index;    /* First index of the contigulus entry */
+            if (++n == ne) break;    /* A contiguous entry that requiered count is found */
+        } else {
+            n = 0;                    /* Not a blank entry. Restart to search */
+        }
+        res = dir_next(dj, TRUE);    /* Next entry with table streach */
+    } while (res == FR_OK);
+
+    if (res == FR_OK && ne > 1) {    /* Initialize LFN entry if needed */
+        res = dir_seek(dj, is);
+        if (res == FR_OK) {
+            sum = sum_sfn(dj->fn);    /* Sum of the SFN tied to the LFN */
+            ne--;
+            do {                    /* Store LFN entries in bottom first */
+                res = move_window(dj->fs, dj->sect);
+                if (res != FR_OK) break;
+                fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
+                dj->fs->wflag = 1;
+                res = dir_next(dj, FALSE);    /* Next entry */
+            } while (res == FR_OK && --ne);
+        }
+    }
+
+#else    /* Non LFN configuration */
+    res = dir_seek(dj, 0);
+    if (res == FR_OK) {
+        do {    /* Find a blank entry for the SFN */
+            res = move_window(dj->fs, dj->sect);
+            if (res != FR_OK) break;
+            c = *dj->dir;
+            if (c == 0xE5 || c == 0) break;    /* Is it a blank entry? */
+            res = dir_next(dj, TRUE);        /* Next entry with table streach */
+        } while (res == FR_OK);
+    }
+#endif
+
+    if (res == FR_OK) {        /* Initialize the SFN entry */
+        res = move_window(dj->fs, dj->sect);
+        if (res == FR_OK) {
+            dir = dj->dir;
+            mem_set(dir, 0, 32);        /* Clean the entry */
+            mem_cpy(dir, dj->fn, 11);    /* Put SFN */
+            dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT);    /* Put NT flag */
+            dj->fs->wflag = 1;
+        }
+    }
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */
+    eDIR *dj                /* Directory object pointing the entry to be removed */
+)
+{
+    FRESULT res;
+#if _USE_LFN    /* LFN configuration */
+    WORD i;
+
+    i = dj->index;    /* SFN index */
+    res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));    /* Goto the SFN or top of the LFN entries */
+    if (res == FR_OK) {
+        do {
+            res = move_window(dj->fs, dj->sect);
+            if (res != FR_OK) break;
+            *dj->dir = 0xE5;            /* Mark the entry "deleted" */
+            dj->fs->wflag = 1;
+            if (dj->index >= i) break;    /* When reached SFN, all entries of the object has been deleted. */
+            res = dir_next(dj, FALSE);    /* Next entry */
+        } while (res == FR_OK);
+        if (res == FR_NO_FILE) res = FR_INT_ERR;
+    }
+
+#else            /* Non LFN configuration */
+    res = dir_seek(dj, dj->index);
+    if (res == FR_OK) {
+        res = move_window(dj->fs, dj->sect);
+        if (res == FR_OK) {
+            *dj->dir = 0xE5;            /* Mark the entry "deleted" */
+            dj->fs->wflag = 1;
+        }
+    }
+#endif
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a segment and create the object name in directory form           */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (
+    eDIR *dj,            /* Pointer to the directory object */
+    const XCHAR **path    /* Pointer to pointer to the segment in the path string */
+)
+{
+#ifdef _EXCVT
+    static const BYTE cvt[] = _EXCVT;
+#endif
+
+#if _USE_LFN    /* LFN configuration */
+    BYTE b, cf;
+    WCHAR w, *lfn;
+    int i, ni, si, di;
+    const XCHAR *p;
+
+    /* Create LFN in Unicode */
+    si = di = 0;
+    p = *path;
+    lfn = dj->lfn;
+    for (;;) {
+        w = p[si++];                    /* Get a character */
+        if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
+        if (di >= _MAX_LFN)                /* Reject too long name */
+            return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+        w &= 0xFF;
+        if (IsDBCS1(w)) {                /* If it is a DBC 1st byte */
+            b = p[si++];                /* Get 2nd byte */
+            if (!IsDBCS2(b))            /* Reject invalid code for DBC */
+                return FR_INVALID_NAME;
+            w = (w << 8) + b;
+        }
+        w = ff_convert(w, 1);            /* Convert OEM to Unicode */
+        if (!w) return FR_INVALID_NAME;    /* Reject invalid code */
+#endif
+        if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
+            return FR_INVALID_NAME;
+        lfn[di++] = w;                    /* Store the Unicode char */
+    }
+    *path = &p[si];                        /* Rerurn pointer to the next segment */
+    cf = (w < ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
+#if _FS_RPATH
+    if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */
+        (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {
+        lfn[di] = 0;
+        for (i = 0; i < 11; i++)
+            dj->fn[i] = (i < di) ? '.' : ' ';
+        dj->fn[i] = cf | NS_DOT;        /* This is a dot entry */
+        return FR_OK;
+    }
+#endif
+    while (di) {                        /* Strip trailing spaces and dots */
+        w = lfn[di - 1];
+        if (w != ' ' && w != '.') break;
+        di--;
+    }
+    if (!di) return FR_INVALID_NAME;    /* Reject null string */
+
+    lfn[di] = 0;                        /* LFN is created */
+
+    /* Create SFN in directory form */
+    mem_set(dj->fn, ' ', 11);
+    for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;    /* Strip leading spaces and dots */
+    if (si) cf |= NS_LOSS | NS_LFN;
+    while (di && lfn[di - 1] != '.') di--;    /* Find extension (di<=si: no extension) */
+
+    b = i = 0; ni = 8;
+    for (;;) {
+        w = lfn[si++];                    /* Get an LFN char */
+        if (!w) break;                    /* Break on enf of the LFN */
+        if (w == ' ' || (w == '.' && si != di)) {    /* Remove spaces and dots */
+            cf |= NS_LOSS | NS_LFN; continue;
+        }
+
+        if (i >= ni || si == di) {        /* Extension or end of SFN */
+            if (ni == 11) {                /* Long extension */
+                cf |= NS_LOSS | NS_LFN; break;
+            }
+            if (si != di) cf |= NS_LOSS | NS_LFN;    /* Out of 8.3 format */
+            if (si > di) break;            /* No extension */
+            si = di; i = 8; ni = 11;    /* Enter extension section */
+            b <<= 2; continue;
+        }
+
+        if (w >= 0x80) {                /* Non ASCII char */
+#ifdef _EXCVT
+            w = ff_convert(w, 0);        /* Unicode -> OEM code */
+            if (w) w = cvt[w - 0x80];    /* Convert extended char to upper (SBCS) */
+#else
+            w = ff_convert(ff_wtoupper(w), 0);    /* Upper converted Unicode -> OEM code */
+#endif
+            cf |= NS_LFN;                /* Force create LFN entry */
+        }
+
+        if (_DF1S && w >= 0x100) {        /* Double byte char */
+            if (i >= ni - 1) {
+                cf |= NS_LOSS | NS_LFN; i = ni; continue;
+            }
+            dj->fn[i++] = (BYTE)(w >> 8);
+        } else {                        /* Single byte char */
+            if (!w || chk_chr("+,;[=]", w)) {        /* Replace illegal chars for SFN */
+                w = '_'; cf |= NS_LOSS | NS_LFN;    /* Lossy conversion */
+            } else {
+                if (IsUpper(w)) {        /* ASCII large capital */
+                    b |= 2;
+                } else {
+                    if (IsLower(w)) {    /* ASCII small capital */
+                        b |= 1; w -= 0x20;
+                    }
+                }
+            }
+        }
+        dj->fn[i++] = (BYTE)w;
+    }
+
+    if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05;    /* If the first char collides with deleted mark, replace it with 0x05 */
+
+    if (ni == 8) b <<= 2;
+    if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)    /* Create LFN entry when there are composite capitals */
+        cf |= NS_LFN;
+    if (!(cf & NS_LFN)) {                        /* When LFN is in 8.3 format without extended char, NT flags are created */
+        if ((b & 0x03) == 0x01) cf |= NS_EXT;    /* NT flag (Extension has only small capital) */
+        if ((b & 0x0C) == 0x04) cf |= NS_BODY;    /* NT flag (Filename has only small capital) */
+    }
+
+    dj->fn[NS] = cf;    /* SFN is created */
+
+    return FR_OK;
+
+
+#else    /* Non-LFN configuration */
+    BYTE b, c, d, *sfn;
+    int ni, si, i;
+    const char *p;
+
+    /* Create file name in directory form */
+    sfn = dj->fn;
+    mem_set(sfn, ' ', 11);
+    si = i = b = 0; ni = 8;
+    p = *path;
+#if _FS_RPATH
+    if (p[si] == '.') { /* Is this a dot entry? */
+        for (;;) {
+            c = p[si++];
+            if (c != '.' || si >= 3) break;
+            sfn[i++] = c;
+        }
+        if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+        *path = &p[si];                                    /* Rerurn pointer to the next segment */
+        sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;    /* Set last segment flag if end of path */
+        return FR_OK;
+    }
+#endif
+    for (;;) {
+        c = p[si++];
+        if (c <= ' ' || c == '/' || c == '\\') break;    /* Break on end of segment */
+        if (c == '.' || i >= ni) {
+            if (ni != 8 || c != '.') return FR_INVALID_NAME;
+            i = 8; ni = 11;
+            b <<= 2; continue;
+        }
+        if (c >= 0x80) {                /* Extended char */
+#ifdef _EXCVT
+            c = cvt[c - 0x80];            /* Convert extend char (SBCS) */
+#else
+            b |= 3;                        /* Eliminate NT flag if ext char is exist */
+#if !_DF1S    /* ASCII only cfg */
+            return FR_INVALID_NAME;
+#endif
+#endif
+        }
+        if (IsDBCS1(c)) {                /* DBC 1st byte? */
+            d = p[si++];                /* Get 2nd byte */
+            if (!IsDBCS2(d) || i >= ni - 1)    /* Reject invalid DBC */
+                return FR_INVALID_NAME;
+            sfn[i++] = c;
+            sfn[i++] = d;
+        } else {                        /* Single byte code */
+            if (chk_chr(" \"*+,[=]|\x7F", c))    /* Reject illegal chrs for SFN */
+                return FR_INVALID_NAME;
+            if (IsUpper(c)) {            /* ASCII large capital? */
+                b |= 2;
+            } else {
+                if (IsLower(c)) {        /* ASCII small capital? */
+                    b |= 1; c -= 0x20;
+                }
+            }
+            sfn[i++] = c;
+        }
+    }
+    *path = &p[si];                        /* Rerurn pointer to the next segment */
+    c = (c <= ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
+
+    if (!i) return FR_INVALID_NAME;        /* Reject null string */
+    if (sfn[0] == 0xE5) sfn[0] = 0x05;    /* When first char collides with 0xE5, replace it with 0x05 */
+
+    if (ni == 8) b <<= 2;
+    if ((b & 0x03) == 0x01) c |= NS_EXT;    /* NT flag (Extension has only small capital) */
+    if ((b & 0x0C) == 0x04) c |= NS_BODY;    /* NT flag (Filename has only small capital) */
+
+    sfn[NS] = c;        /* Store NT flag, File name is created */
+
+    return FR_OK;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+void get_fileinfo (        /* No return code */
+    eDIR *dj,            /* Pointer to the directory object */
+    FILINFO *fno         /* Pointer to the file information to be filled */
+)
+{
+    int i;
+    BYTE c, nt, *dir;
+    char *p;
+
+
+    p = fno->fname;
+    if (dj->sect) {
+        dir = dj->dir;
+        nt = dir[DIR_NTres];        /* NT flag */
+        for (i = 0; i < 8; i++) {    /* Copy name body */
+            c = dir[i];
+            if (c == ' ') break;
+            if (c == 0x05) c = 0xE5;
+            if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
+            *p++ = c;
+        }
+        if (dir[8] != ' ') {        /* Copy name extension */
+            *p++ = '.';
+            for (i = 8; i < 11; i++) {
+                c = dir[i];
+                if (c == ' ') break;
+                if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
+                *p++ = c;
+            }
+        }
+        fno->fattrib = dir[DIR_Attr];                /* Attribute */
+        fno->fsize = LD_DWORD(dir+DIR_FileSize);    /* Size */
+        fno->fdate = LD_WORD(dir+DIR_WrtDate);        /* Date */
+        fno->ftime = LD_WORD(dir+DIR_WrtTime);        /* Time */
+    }
+    *p = 0;
+
+#if _USE_LFN
+    if (fno->lfname) {
+        XCHAR *tp = fno->lfname;
+        WCHAR w, *lfn;
+
+        i = 0;
+        if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
+            lfn = dj->lfn;
+            while ((w = *lfn++) != 0) {            /* Get an LFN char */
+#if !_LFN_UNICODE
+                w = ff_convert(w, 0);            /* Unicode -> OEM conversion */
+                if (!w) { i = 0; break; }        /* Could not convert, no LFN */
+                if (_DF1S && w >= 0x100)        /* Put 1st byte if it is a DBC */
+                    tp[i++] = (XCHAR)(w >> 8);
+#endif
+                if (i >= fno->lfsize - 1) { i = 0; break; }    /* Buffer overrun, no LFN */
+                tp[i++] = (XCHAR)w;
+            }
+        }
+        tp[i] = 0;    /* Terminator */
+    }
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path (    /* FR_OK(0): successful, !=0: error code */
+    eDIR *dj,            /* Directory object to return last directory and found object */
+    const XCHAR *path    /* Full-path string to find a file or directory */
+)
+{
+    FRESULT res;
+    BYTE *dir, last;
+
+
+    while (!_USE_LFN && *path == ' ') path++;    /* Skip leading spaces */
+#if _FS_RPATH
+    if (*path == '/' || *path == '\\') { /* There is a heading separator */
+        path++;    dj->sclust = 0;        /* Strip it and start from the root dir */
+    } else {                            /* No heading saparator */
+        dj->sclust = dj->fs->cdir;    /* Start from the current dir */
+    }
+#else
+    if (*path == '/' || *path == '\\')    /* Strip heading separator if exist */
+        path++;
+    dj->sclust = 0;                        /* Start from the root dir */
+#endif
+
+    if ((UINT)*path < ' ') {            /* Null path means the start directory itself */
+        res = dir_seek(dj, 0);
+        dj->dir = NULL;
+
+    } else {                            /* Follow path */
+        for (;;) {
+            res = create_name(dj, &path);    /* Get a segment */
+            if (res != FR_OK) break;
+            res = dir_find(dj);                /* Find it */
+            last = *(dj->fn+NS) & NS_LAST;
+            if (res != FR_OK) {                /* Could not find the object */
+                if (res == FR_NO_FILE && !last)
+                    res = FR_NO_PATH;
+                break;
+            }
+            if (last) break;                /* Last segment match. Function completed. */
+            dir = dj->dir;                    /* There is next segment. Follow the sub directory */
+            if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
+                res = FR_NO_PATH; break;
+            }
+            dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+        }
+    }
+
+    return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load boot record and check if it is an FAT boot record                */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs (    /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
+    FATFS *fs,    /* File system object */
+    DWORD sect    /* Sector# (lba) to check if it is an FAT boot record or not */
+)
+{
+    if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK)    /* Load boot record */
+        return 3;
+    if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)        /* Check record signature (always placed at offset 510 even if the sector size is >512) */
+        return 2;
+
+    if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)    /* Check "FAT" string */
+        return 0;
+    if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
+        return 0;
+
+    return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Make sure that the file system is valid                               */
+/*-----------------------------------------------------------------------*/
+
+
+FRESULT chk_mounted (    /* FR_OK(0): successful, !=0: any error occured */
+    const XCHAR **path,    /* Pointer to pointer to the path name (drive number) */
+    FATFS **rfs,        /* Pointer to pointer to the found file system object */
+    BYTE chk_wp            /* !=0: Check media write protection for write access */
+)
+{
+    BYTE fmt, *tbl;
+    UINT vol;
+    DSTATUS stat;
+    DWORD bsect, fsize, tsect, mclst;
+    const XCHAR *p = *path;
+    FATFS *fs;
+
+    /* Get logical drive number from the path name */
+    vol = p[0] - '0';                /* Is there a drive number? */
+    if (vol <= 9 && p[1] == ':') {    /* Found a drive number, get and strip it */
+        p += 2; *path = p;            /* Return pointer to the path name */
+    } else {                        /* No drive number is given */
+#if _FS_RPATH
+        vol = Drive;                /* Use current drive */
+#else
+        vol = 0;                    /* Use drive 0 */
+#endif
+    }
+
+    /* Check if the logical drive is valid or not */
+    if (vol >= _DRIVES)             /* Is the drive number valid? */
+        return FR_INVALID_DRIVE;
+    *rfs = fs = FatFs[vol];            /* Returen pointer to the corresponding file system object */
+    if (!fs) return FR_NOT_ENABLED;    /* Is the file system object available? */
+
+    ENTER_FF(fs);                    /* Lock file system */
+
+    if (fs->fs_type) {                /* If the logical drive has been mounted */
+        stat = disk_status(fs->drive);
+        if (!(stat & STA_NOINIT)) {    /* and the physical drive is kept initialized (has not been changed), */
+#if !_FS_READONLY
+            if (chk_wp && (stat & STA_PROTECT))    /* Check write protection if needed */
+                return FR_WRITE_PROTECTED;
+#endif
+            return FR_OK;            /* The file system object is valid */
+        }
+    }
+
+    /* The logical drive must be mounted. Following code attempts to mount the volume */
+
+    fs->fs_type = 0;                    /* Clear the file system object */
+    fs->drive = (BYTE)LD2PD(vol);        /* Bind the logical drive and a physical drive */
+    stat = disk_initialize(fs->drive);    /* Initialize low level disk I/O layer */
+    if (stat & STA_NOINIT)                /* Check if the drive is ready */
+        return FR_NOT_READY;
+#if _MAX_SS != 512                        /* Get disk sector size if needed */
+    if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
+        return FR_NO_FILESYSTEM;
+#endif
+#if !_FS_READONLY
+    if (chk_wp && (stat & STA_PROTECT))    /* Check disk write protection if needed */
+        return FR_WRITE_PROTECTED;
+#endif
+    /* Search FAT partition on the drive */
+    fmt = check_fs(fs, bsect = 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(vol) * 16];    /* Partition table */
+        if (tbl[4]) {                                    /* Is the partition existing? */
+            bsect = LD_DWORD(&tbl[8]);                    /* Partition offset in LBA */
+            fmt = check_fs(fs, bsect);                    /* Check the partition */
+        }
+    }
+    if (fmt == 3) return FR_DISK_ERR;
+    if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))    /* No valid FAT patition is found */
+        return FR_NO_FILESYSTEM;
+
+    /* Initialize the file system object */
+    fsize = LD_WORD(fs->win+BPB_FATSz16);                /* Number of sectors per FAT */
+    if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32);
+    fs->sects_fat = fsize;
+    fs->n_fats = fs->win[BPB_NumFATs];                    /* Number of FAT copies */
+    fsize *= fs->n_fats;                                /* (Number of sectors in FAT area) */
+    fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */
+    fs->csize = fs->win[BPB_SecPerClus];                /* Number of sectors per cluster */
+    fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);    /* Nmuber of root directory entries */
+    tsect = LD_WORD(fs->win+BPB_TotSec16);                /* Number of sectors on the volume */
+    if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+    fs->max_clust = mclst = (tsect                        /* Last cluster# + 1 (Number of clusters + 2) */
+        - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32)
+        ) / fs->csize + 2;
+
+    fmt = FS_FAT12;                                        /* Determine the FAT sub type */
+    if (mclst >= 0xFF7) fmt = FS_FAT16;                    /* Number of clusters >= 0xFF5 */
+    if (mclst >= 0xFFF7) fmt = FS_FAT32;                /* Number of clusters >= 0xFFF5 */
+
+    if (fmt == FS_FAT32)
+        fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);    /* Root directory start cluster */
+    else
+        fs->dirbase = fs->fatbase + fsize;                /* Root directory start sector (lba) */
+    fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32);    /* Data start sector (lba) */
+
+#if !_FS_READONLY
+    /* Initialize allocation information */
+    fs->free_clust = 0xFFFFFFFF;
+    fs->wflag = 0;
+    /* Get fsinfo if needed */
+    if (fmt == FS_FAT32) {
+         fs->fsi_flag = 0;
+        fs->fsi_sector = bsect + 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
+    fs->fs_type = fmt;        /* FAT sub-type */
+    fs->winsect = 0;        /* Invalidate sector cache */
+#if _FS_RPATH
+    fs->cdir = 0;            /* Current directory (root dir) */
+#endif
+    fs->id = ++Fsid;        /* File system mount ID */
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/dir object is valid or not                          */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate (    /* FR_OK(0): The object is valid, !=0: Invalid */
+    FATFS *fs,        /* Pointer to the file system object */
+    WORD id            /* Member id of the target object to be checked */
+)
+{
+    if (!fs || !fs->fs_type || fs->id != id)
+        return FR_INVALID_OBJECT;
+
+    ENTER_FF(fs);        /* Lock file system */
+
+    if (disk_status(fs->drive) & STA_NOINIT)
+        return FR_NOT_READY;
+
+    return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Public Functions
+
+--------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Locical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+    BYTE vol,        /* Logical drive number to be mounted/unmounted */
+    FATFS *fs        /* Pointer to new file system object (NULL for unmount)*/
+)
+{
+    FATFS *rfs;
+
+
+    if (vol >= _DRIVES)                /* Check if the drive number is valid */
+        return FR_INVALID_DRIVE;
+    rfs = FatFs[vol];                /* Get current fs object */
+
+    if (rfs) {
+#if _FS_REENTRANT                    /* Discard sync object of the current volume */
+        if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
+#endif
+        rfs->fs_type = 0;            /* Clear old fs object */
+    }
+
+    if (fs) {
+        fs->fs_type = 0;            /* Clear new fs object */
+#if _FS_REENTRANT                    /* Create sync object for the new volume */
+        if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+    }
+    FatFs[vol] = fs;                /* Register new fs object */
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+    FIL *fp,            /* Pointer to the blank file object */
+    const XCHAR *path,    /* Pointer to the file name */
+    BYTE mode            /* Access mode and file open mode flags */
+)
+{
+    FRESULT res;
+    eDIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+
+    fp->fs = NULL;        /* Clear file object */
+#if !_FS_READONLY
+    mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW);
+    res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)));
+#else
+    mode &= FA_READ;
+    res = chk_mounted(&path, &dj.fs, 0);
+#endif
+    if (res != FR_OK) LEAVE_FF(dj.fs, res);
+    INITBUF(dj, sfn, lfn);
+    res = follow_path(&dj, path);    /* Follow the file path */
+
+#if !_FS_READONLY
+    /* Create or Open a file */
+    if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+        DWORD ps, cl;
+
+        if (res != FR_OK) {            /* No file, create new */
+            if (res == FR_NO_FILE)    /* There is no file to open, create a new entry */
+                res = dir_register(&dj);
+            if (res != FR_OK) LEAVE_FF(dj.fs, res);
+            mode |= FA_CREATE_ALWAYS;
+            dir = dj.dir;            /* Created entry (SFN entry) */
+        }
+        else {                        /* Any object is already existing */
+            if (mode & FA_CREATE_NEW)            /* Cannot create new */
+                LEAVE_FF(dj.fs, FR_EXIST);
+            dir = dj.dir;
+            if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR)))    /* Cannot overwrite it (R/O or DIR) */
+                LEAVE_FF(dj.fs, FR_DENIED);
+            if (mode & FA_CREATE_ALWAYS) {        /* Resize it to zero on over write mode */
+                cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);    /* Get start cluster */
+                ST_WORD(dir+DIR_FstClusHI, 0);    /* cluster = 0 */
+                ST_WORD(dir+DIR_FstClusLO, 0);
+                ST_DWORD(dir+DIR_FileSize, 0);    /* size = 0 */
+                dj.fs->wflag = 1;
+                ps = dj.fs->winsect;            /* Remove the cluster chain */
+                if (cl) {
+                    res = remove_chain(dj.fs, cl);
+                    if (res) LEAVE_FF(dj.fs, res);
+                    dj.fs->last_clust = cl - 1;    /* Reuse the cluster hole */
+                }
+                res = move_window(dj.fs, ps);
+                if (res != FR_OK) LEAVE_FF(dj.fs, res);
+            }
+        }
+        if (mode & FA_CREATE_ALWAYS) {
+            dir[DIR_Attr] = 0;                    /* Reset attribute */
+            ps = get_fattime();
+            ST_DWORD(dir+DIR_CrtTime, ps);        /* Created time */
+            dj.fs->wflag = 1;
+            mode |= FA__WRITTEN;                /* Set file changed flag */
+        }
+    }
+    /* Open an existing file */
+    else {
+#endif /* !_FS_READONLY */
+        if (res != FR_OK) LEAVE_FF(dj.fs, res);    /* Follow failed */
+        dir = dj.dir;
+        if (!dir || (dir[DIR_Attr] & AM_DIR))    /* It is a directory */
+            LEAVE_FF(dj.fs, FR_NO_FILE);
+#if !_FS_READONLY
+        if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+            LEAVE_FF(dj.fs, FR_DENIED);
+    }
+    fp->dir_sect = dj.fs->winsect;        /* Pointer to the directory entry */
+    fp->dir_ptr = dj.dir;
+#endif
+    fp->flag = mode;                    /* File access mode */
+    fp->org_clust =                        /* File start cluster */
+        ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+    fp->fsize = LD_DWORD(dir+DIR_FileSize);    /* File size */
+    fp->fptr = 0; fp->csect = 255;        /* File pointer */
+    fp->dsect = 0;
+    fp->fs = dj.fs; fp->id = dj.fs->id;    /* Owner file system object of the file */
+
+    LEAVE_FF(dj.fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File                                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+    FIL *fp,         /* Pointer to the file object */
+    void *buff,        /* Pointer to data buffer */
+    UINT btr,        /* Number of bytes to read */
+    UINT *br        /* Pointer to number of bytes read */
+)
+{
+    FRESULT res;
+    DWORD clst, sect, remain;
+    UINT rcnt, cc;
+    BYTE *rbuff = (BYTE *)buff;
+
+
+    *br = 0;    /* Initialize bytes read */
+
+    res = validate(fp->fs, fp->id);                    /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                        /* Check abort flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                         /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;            /* Truncate btr by remaining bytes */
+
+    for ( ;  btr;                                    /* Repeat until all data transferred */
+        rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+        if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
+            if (fp->csect >= fp->fs->csize) {        /* On the cluster boundary? */
+                clst = (fp->fptr == 0) ?            /* On the top of the file? */
+                    fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+                if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->curr_clust = clst;                /* Update current cluster */
+                fp->csect = 0;                        /* Reset sector offset in the cluster */
+            }
+            sect = clust2sect(fp->fs, fp->curr_clust);    /* Get current sector */
+            if (!sect) ABORT(fp->fs, FR_INT_ERR);
+            sect += fp->csect;
+            cc = btr / SS(fp->fs);                    /* When remaining bytes >= sector size, */
+            if (cc) {                                /* Read maximum contiguous sectors directly */
+                if (fp->csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
+                    cc = fp->fs->csize - fp->csect;
+                if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2
+#if _FS_TINY
+                if (fp->fs->wflag && fp->fs->winsect - sect < cc)        /* Replace one of the read sectors with cached data if it contains a dirty sector */
+                    mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+                if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)    /* Replace one of the read sectors with cached data if it contains a dirty sector */
+                    mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+                fp->csect += (BYTE)cc;                /* Next sector address in the cluster */
+                rcnt = SS(fp->fs) * cc;                /* Number of bytes transferred */
+                continue;
+            }
+#if !_FS_TINY
+#if !_FS_READONLY
+            if (fp->flag & FA__DIRTY) {            /* Write sector I/O buffer if needed */
+                if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            if (fp->dsect != sect) {            /* Fill sector buffer with file data */
+                if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+            }
+#endif
+            fp->dsect = sect;
+            fp->csect++;                            /* Next sector address in the cluster */
+        }
+        rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Get partial sector data from sector buffer */
+        if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+        if (move_window(fp->fs, fp->dsect))            /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
+#else
+        mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
+#endif
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+    FIL *fp,            /* Pointer to the file object */
+    const void *buff,    /* Pointer to the data to be written */
+    UINT btw,            /* Number of bytes to write */
+    UINT *bw            /* Pointer to number of bytes written */
+)
+{
+    FRESULT res;
+    DWORD clst, sect;
+    UINT wcnt, cc;
+    const BYTE *wbuff = (const BYTE *)buff;
+
+
+    *bw = 0;    /* Initialize bytes written */
+
+    res = validate(fp->fs, fp->id);                    /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                        /* Check abort flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_WRITE))                        /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+    if (fp->fsize + btw < fp->fsize) btw = 0;        /* File size cannot reach 4GB */
+
+    for ( ;  btw;                                    /* Repeat until all data transferred */
+        wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+        if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
+            if (fp->csect >= fp->fs->csize) {        /* On the cluster boundary? */
+                if (fp->fptr == 0) {                /* On the top of the file? */
+                    clst = fp->org_clust;            /* Follow from the origin */
+                    if (clst == 0)                    /* When there is no cluster chain, */
+                        fp->org_clust = clst = create_chain(fp->fs, 0);    /* Create a new cluster chain */
+                } else {                            /* Middle or end of the file */
+                    clst = create_chain(fp->fs, fp->curr_clust);            /* Follow or streach cluster chain */
+                }
+                if (clst == 0) break;                /* Could not allocate a new cluster (disk full) */
+                if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->curr_clust = clst;                /* Update current cluster */
+                fp->csect = 0;                        /* Reset sector address in the cluster */
+            }
+#if _FS_TINY
+            if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0))    /* Write back data buffer prior to following direct transfer */
+                ABORT(fp->fs, FR_DISK_ERR);
+#else
+            if (fp->flag & FA__DIRTY) {        /* Write back data buffer prior to following direct transfer */
+                if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            sect = clust2sect(fp->fs, fp->curr_clust);    /* Get current sector */
+            if (!sect) ABORT(fp->fs, FR_INT_ERR);
+            sect += fp->csect;
+            cc = btw / SS(fp->fs);                    /* When remaining bytes >= sector size, */
+            if (cc) {                                /* Write maximum contiguous sectors directly */
+                if (fp->csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
+                    cc = fp->fs->csize - fp->csect;
+                if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_TINY
+                if (fp->fs->winsect - sect < cc) {    /* Refill sector cache if it gets dirty by the direct write */
+                    mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+                    fp->fs->wflag = 0;
+                }
+#else
+                if (fp->dsect - sect < cc) {        /* Refill sector cache if it gets dirty by the direct write */
+                    mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+                    fp->flag &= ~FA__DIRTY;
+                }
+#endif
+                fp->csect += (BYTE)cc;                /* Next sector address in the cluster */
+                wcnt = SS(fp->fs) * cc;                /* Number of bytes transferred */
+                continue;
+            }
+#if _FS_TINY
+            if (fp->fptr >= fp->fsize) {            /* Avoid silly buffer filling at growing edge */
+                if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
+                fp->fs->winsect = sect;
+            }
+#else
+            if (fp->dsect != sect) {                /* Fill sector buffer with file data */
+                if (fp->fptr < fp->fsize &&
+                    disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+                        ABORT(fp->fs, FR_DISK_ERR);
+            }
+#endif
+            fp->dsect = sect;
+            fp->csect++;                            /* Next sector address in the cluster */
+        }
+        wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Put partial sector into file I/O buffer */
+        if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+        if (move_window(fp->fs, fp->dsect))            /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
+        fp->fs->wflag = 1;
+#else
+        mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
+        fp->flag |= FA__DIRTY;
+#endif
+    }
+
+    if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;    /* Update file size if needed */
+    fp->flag |= FA__WRITTEN;                        /* Set file changed flag */
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File Object                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+    FIL *fp        /* Pointer to the file object */
+)
+{
+    FRESULT res;
+    DWORD tim;
+    BYTE *dir;
+
+
+    res = validate(fp->fs, fp->id);        /* Check validity of the object */
+    if (res == FR_OK) {
+        if (fp->flag & FA__WRITTEN) {    /* Has the file been written? */
+#if !_FS_TINY    /* Write-back dirty buffer */
+            if (fp->flag & FA__DIRTY) {
+                if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                    LEAVE_FF(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            /* Update the directory entry */
+            res = move_window(fp->fs, fp->dir_sect);
+            if (res == FR_OK) {
+                dir = fp->dir_ptr;
+                dir[DIR_Attr] |= AM_ARC;                    /* Set archive bit */
+                ST_DWORD(dir+DIR_FileSize, fp->fsize);        /* Update file size */
+                ST_WORD(dir+DIR_FstClusLO, fp->org_clust);    /* Update start cluster */
+                ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16);
+                tim = get_fattime();            /* Updated time */
+                ST_DWORD(dir+DIR_WrtTime, tim);
+                fp->flag &= ~FA__WRITTEN;
+                fp->fs->wflag = 1;
+                res = sync(fp->fs);
+            }
+        }
+    }
+
+    LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+    FIL *fp        /* Pointer to the file object to be closed */
+)
+{
+    FRESULT res;
+
+
+#if _FS_READONLY
+    res = validate(fp->fs, fp->id);
+    if (res == FR_OK) fp->fs = NULL;
+    LEAVE_FF(fp->fs, res);
+#else
+    res = f_sync(fp);
+    if (res == FR_OK) fp->fs = NULL;
+    return res;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Current Drive/Directory                                        */
+/*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH
+
+FRESULT f_chdrive (
+    BYTE drv        /* Drive number */
+)
+{
+    if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+
+    Drive = drv;
+
+    return FR_OK;
+}
+
+
+
+
+FRESULT f_chdir (
+    const XCHAR *path    /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    DIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+        INITBUF(dj, sfn, lfn);
+        res = follow_path(&dj, path);        /* Follow the file path */
+        if (res == FR_OK) {                    /* Follow completed */
+            dir = dj.dir;                    /* Pointer to the entry */
+            if (!dir) {
+                dj.fs->cdir = 0;            /* No entry (root dir) */
+            } else {
+                if (dir[DIR_Attr] & AM_DIR)    /* Reached to the dir */
+                    dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+                else
+                    res = FR_NO_PATH;        /* Could not reach the dir (it is a file) */
+            }
+        }
+        if (res == FR_NO_FILE) res = FR_NO_PATH;
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+#endif
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+    FIL *fp,        /* Pointer to the file object */
+    DWORD ofs        /* File pointer from top of file */
+)
+{
+    FRESULT res;
+    DWORD clst, bcs, nsect, ifptr;
+
+
+    res = validate(fp->fs, fp->id);        /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)            /* Check abort flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+//    if (ofs > fp->fsize                    /* In read-only mode, clip offset with the file size */
+//#if !_FS_READONLY /* LAJ: Always clip offset with file size */
+//         && !(fp->flag & FA_WRITE)
+//#endif
+    if (ofs > fp->fsize)
+        ofs = fp->fsize; /* LAJ: Always clip offset with file size */
+
+    ifptr = fp->fptr;
+    fp->fptr = nsect = 0; fp->csect = 255;
+    if (ofs > 0) {
+        bcs = (DWORD)fp->fs->csize * SS(fp->fs);    /* Cluster size (byte) */
+        if (ifptr > 0 &&
+            (ofs - 1) / bcs >= (ifptr - 1) / bcs) {    /* When seek to same or following cluster, */
+            fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
+            ofs -= fp->fptr;
+            clst = fp->curr_clust;
+        } else {                                    /* When seek to back cluster, */
+            clst = fp->org_clust;                    /* start from the first cluster */
+#if !_FS_READONLY
+            if (clst == 0) {                        /* If no cluster chain, create a new chain */
+                clst = create_chain(fp->fs, 0);
+                if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->org_clust = clst;
+            }
+#endif
+            fp->curr_clust = clst;
+        }
+        if (clst != 0) {
+            while (ofs > bcs) {                        /* Cluster following loop */
+#if !_FS_READONLY
+                if (fp->flag & FA_WRITE) {            /* Check if in write mode or not */
+                    clst = create_chain(fp->fs, clst);    /* Force streached if in write mode */
+                    if (clst == 0) {                /* When disk gets full, clip file size */
+                        ofs = bcs; break;
+                    }
+                } else
+#endif
+                    clst = get_fat(fp->fs, clst);    /* Follow cluster chain if not in write mode */
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR);
+                fp->curr_clust = clst;
+                fp->fptr += bcs;
+                ofs -= bcs;
+            }
+            fp->fptr += ofs;
+            fp->csect = (BYTE)(ofs / SS(fp->fs));    /* Sector offset in the cluster */
+            if (ofs % SS(fp->fs)) {
+                nsect = clust2sect(fp->fs, clst);    /* Current sector */
+                if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+                nsect += fp->csect;
+                fp->csect++;
+            }
+        }
+    }
+    if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {
+#if !_FS_TINY
+#if !_FS_READONLY
+        if (fp->flag & FA__DIRTY) {            /* Write-back dirty buffer if needed */
+            if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                ABORT(fp->fs, FR_DISK_ERR);
+            fp->flag &= ~FA__DIRTY;
+        }
+#endif
+        if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK)
+            ABORT(fp->fs, FR_DISK_ERR);
+#endif
+        fp->dsect = nsect;
+    }
+#if !_FS_READONLY
+    if (fp->fptr > fp->fsize) {            /* Set changed flag if the file size is extended */
+        fp->fsize = fp->fptr;
+        fp->flag |= FA__WRITTEN;
+    }
+#endif
+
+    LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directroy Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+    eDIR *dj,            /* Pointer to directory object to create */
+    const XCHAR *path    /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+
+    res = chk_mounted(&path, &dj->fs, 0);
+    if (res == FR_OK) {
+        INITBUF((*dj), sfn, lfn);
+        res = follow_path(dj, path);            /* Follow the path to the directory */
+        if (res == FR_OK) {                        /* Follow completed */
+            dir = dj->dir;
+            if (dir) {                            /* It is not the root dir */
+                if (dir[DIR_Attr] & AM_DIR) {    /* The object is a directory */
+                    dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+                } else {                        /* The object is not a directory */
+                    res = FR_NO_PATH;
+                }
+            }
+            if (res == FR_OK) {
+                dj->id = dj->fs->id;
+                res = dir_seek(dj, 0);            /* Rewind dir */
+            }
+        }
+        if (res == FR_NO_FILE) res = FR_NO_PATH;
+    }
+
+    LEAVE_FF(dj->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entry in Sequense                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+    eDIR *dj,            /* Pointer to the open directory object */
+    FILINFO *fno        /* Pointer to file information to return */
+)
+{
+    FRESULT res;
+    NAMEBUF(sfn, lfn);
+
+
+    res = validate(dj->fs, dj->id);            /* Check validity of the object */
+    if (res == FR_OK) {
+        INITBUF((*dj), sfn, lfn);
+        if (!fno) {
+            res = dir_seek(dj, 0);
+        } else {
+            res = dir_read(dj);
+            if (res == FR_NO_FILE) {
+                dj->sect = 0;
+                res = FR_OK;
+            }
+            if (res == FR_OK) {                /* A valid entry is found */
+                get_fileinfo(dj, fno);        /* Get the object information */
+                res = dir_next(dj, FALSE);    /* Increment index for next */
+                if (res == FR_NO_FILE) {
+                    dj->sect = 0;
+                    res = FR_OK;
+                }
+            }
+        }
+    }
+
+    LEAVE_FF(dj->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+    const XCHAR *path,    /* Pointer to the file path */
+    FILINFO *fno        /* Pointer to file information to return */
+)
+{
+    FRESULT res;
+    eDIR dj;
+    NAMEBUF(sfn, lfn);
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+        INITBUF(dj, sfn, lfn);
+        res = follow_path(&dj, path);    /* Follow the file path */
+        if (res == FR_OK) {                /* Follwo completed */
+            if (dj.dir)    /* Found an object */
+                get_fileinfo(&dj, fno);
+            else        /* It is root dir */
+                res = FR_INVALID_NAME;
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+    const XCHAR *path,    /* Pointer to the logical drive number (root dir) */
+    DWORD *nclst,        /* Pointer to the variable to return number of free clusters */
+    FATFS **fatfs        /* Pointer to pointer to corresponding file system object to return */
+)
+{
+    FRESULT res;
+    DWORD n, clst, sect, stat;
+    UINT i;
+    BYTE fat, *p;
+
+
+    /* Get drive number */
+    res = chk_mounted(&path, fatfs, 0);
+    if (res != FR_OK) LEAVE_FF(*fatfs, res);
+
+    /* If number of free cluster is valid, return it without cluster scan. */
+    if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) {
+        *nclst = (*fatfs)->free_clust;
+        LEAVE_FF(*fatfs, FR_OK);
+    }
+
+    /* Get number of free clusters */
+    fat = (*fatfs)->fs_type;
+    n = 0;
+    if (fat == FS_FAT12) {
+        clst = 2;
+        do {
+            stat = get_fat(*fatfs, clst);
+            if (stat == 0xFFFFFFFF) LEAVE_FF(*fatfs, FR_DISK_ERR);
+            if (stat == 1) LEAVE_FF(*fatfs, FR_INT_ERR);
+            if (stat == 0) n++;
+        } while (++clst < (*fatfs)->max_clust);
+    } else {
+        clst = (*fatfs)->max_clust;
+        sect = (*fatfs)->fatbase;
+        i = 0; p = 0;
+        do {
+            if (!i) {
+                res = move_window(*fatfs, sect++);
+                if (res != FR_OK)
+                    LEAVE_FF(*fatfs, res);
+                p = (*fatfs)->win;
+                i = SS(*fatfs);
+            }
+            if (fat == FS_FAT16) {
+                if (LD_WORD(p) == 0) n++;
+                p += 2; i -= 2;
+            } else {
+                if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
+                p += 4; i -= 4;
+            }
+        } while (--clst);
+    }
+    (*fatfs)->free_clust = n;
+    if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
+    *nclst = n;
+
+    LEAVE_FF(*fatfs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+    FIL *fp        /* Pointer to the file object */
+)
+{
+    FRESULT res;
+    DWORD ncl;
+
+
+    res = validate(fp->fs, fp->id);        /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)            /* Check abort flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_WRITE))            /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+
+    if (fp->fsize > fp->fptr) {
+        fp->fsize = fp->fptr;    /* Set file size to current R/W point */
+        fp->flag |= FA__WRITTEN;
+        if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
+            res = remove_chain(fp->fs, fp->org_clust);
+            fp->org_clust = 0;
+        } else {                /* When truncate a part of the file, remove remaining clusters */
+            ncl = get_fat(fp->fs, fp->curr_clust);
+            res = FR_OK;
+            if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+            if (ncl == 1) res = FR_INT_ERR;
+            if (res == FR_OK && ncl < fp->fs->max_clust) {
+                res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF);
+                if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+            }
+        }
+    }
+    if (res != FR_OK) fp->flag |= FA__ERROR;
+
+    LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+    const XCHAR *path        /* Pointer to the file or directory path */
+)
+{
+    FRESULT res;
+    eDIR dj, sdj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+    DWORD dclst;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+    INITBUF(dj, sfn, lfn);
+    res = follow_path(&dj, path);            /* Follow the file path */
+    if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+        res = FR_INVALID_NAME;
+    if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
+
+    dir = dj.dir;
+    if (!dir)                                /* Is it the root directory? */
+        LEAVE_FF(dj.fs, FR_INVALID_NAME);
+    if (dir[DIR_Attr] & AM_RDO)                /* Is it a R/O object? */
+        LEAVE_FF(dj.fs, FR_DENIED);
+    dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+
+    if (dir[DIR_Attr] & AM_DIR) {            /* It is a sub-directory */
+        if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR);
+        mem_cpy(&sdj, &dj, sizeof(eDIR));    /* Check if the sub-dir is empty or not */
+        sdj.sclust = dclst;
+        res = dir_seek(&sdj, 2);
+        if (res != FR_OK) LEAVE_FF(dj.fs, res);
+        res = dir_read(&sdj);
+        if (res == FR_OK) res = FR_DENIED;    /* Not empty sub-dir */
+        if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res);
+    }
+
+    res = dir_remove(&dj);                    /* Remove directory entry */
+    if (res == FR_OK) {
+        if (dclst)
+            res = remove_chain(dj.fs, dclst);    /* Remove the cluster chain */
+        if (res == FR_OK) res = sync(dj.fs);
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+    const XCHAR *path        /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    eDIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir, n;
+    DWORD dsect, dclst, pclst, tim;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+    INITBUF(dj, sfn, lfn);
+    res = follow_path(&dj, path);            /* Follow the file path */
+    if (res == FR_OK) res = FR_EXIST;        /* Any file or directory is already existing */
+    if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
+        res = FR_INVALID_NAME;
+    if (res != FR_NO_FILE)                    /* Any error occured */
+        LEAVE_FF(dj.fs, res);
+
+    dclst = create_chain(dj.fs, 0);            /* Allocate a new cluster for new directory table */
+    res = FR_OK;
+    if (dclst == 0) res = FR_DENIED;
+    if (dclst == 1) res = FR_INT_ERR;
+    if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR;
+    if (res == FR_OK)
+        res = move_window(dj.fs, 0);
+    if (res != FR_OK) LEAVE_FF(dj.fs, res);
+    dsect = clust2sect(dj.fs, dclst);
+
+    dir = dj.fs->win;                        /* Initialize the new directory table */
+    mem_set(dir, 0, SS(dj.fs));
+    mem_set(dir+DIR_Name, ' ', 8+3);        /* Create "." entry */
+    dir[DIR_Name] = '.';
+    dir[DIR_Attr] = AM_DIR;
+    tim = get_fattime();
+    ST_DWORD(dir+DIR_WrtTime, tim);
+    ST_WORD(dir+DIR_FstClusLO, dclst);
+    ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+    mem_cpy(dir+32, dir, 32);             /* Create ".." entry */
+    dir[33] = '.';
+    pclst = dj.sclust;
+    if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase)
+        pclst = 0;
+    ST_WORD(dir+32+DIR_FstClusLO, pclst);
+    ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16);
+    for (n = 0; n < dj.fs->csize; n++) {    /* Write dot entries and clear left sectors */
+        dj.fs->winsect = dsect++;
+        dj.fs->wflag = 1;
+        res = move_window(dj.fs, 0);
+        if (res) LEAVE_FF(dj.fs, res);
+        mem_set(dir, 0, SS(dj.fs));
+    }
+
+    res = dir_register(&dj);
+    if (res != FR_OK) {
+        remove_chain(dj.fs, dclst);
+    } else {
+        dir = dj.dir;
+        dir[DIR_Attr] = AM_DIR;                    /* Attribute */
+        ST_DWORD(dir+DIR_WrtTime, tim);            /* Crated time */
+        ST_WORD(dir+DIR_FstClusLO, dclst);        /* Table start cluster */
+        ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+        dj.fs->wflag = 1;
+        res = sync(dj.fs);
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change File Attribute                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+    const XCHAR *path,    /* Pointer to the file path */
+    BYTE value,            /* Attribute bits */
+    BYTE mask            /* Attribute mask to change */
+)
+{
+    FRESULT res;
+    eDIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INITBUF(dj, sfn, lfn);
+        res = follow_path(&dj, path);        /* Follow the file path */
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_OK) {
+            dir = dj.dir;
+            if (!dir) {                        /* Is it a root directory? */
+                res = FR_INVALID_NAME;
+            } else {                        /* File or sub directory */
+                mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
+                dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask);    /* Apply attribute change */
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+    const XCHAR *path,    /* Pointer to the file/directory name */
+    const FILINFO *fno    /* Pointer to the timestamp to be set */
+)
+{
+    FRESULT res;
+    eDIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INITBUF(dj, sfn, lfn);
+        res = follow_path(&dj, path);    /* Follow the file path */
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_OK) {
+            dir = dj.dir;
+            if (!dir) {                /* Root directory */
+                res = FR_INVALID_NAME;
+            } else {                /* File or sub-directory */
+                ST_WORD(dir+DIR_WrtTime, fno->ftime);
+                ST_WORD(dir+DIR_WrtDate, fno->fdate);
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+    const XCHAR *path_old,    /* Pointer to the old name */
+    const XCHAR *path_new    /* Pointer to the new name */
+)
+{
+    FRESULT res;
+    eDIR dj_old, dj_new;
+    NAMEBUF(sfn, lfn);
+    BYTE buf[21], *dir;
+    DWORD dw;
+
+
+    INITBUF(dj_old, sfn, lfn);
+    res = chk_mounted(&path_old, &dj_old.fs, 1);
+    if (res == FR_OK) {
+        dj_new.fs = dj_old.fs;
+        res = follow_path(&dj_old, path_old);    /* Check old object */
+        if (_FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+    }
+    if (res != FR_OK) LEAVE_FF(dj_old.fs, res);    /* The old object is not found */
+
+    if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE);    /* Is root dir? */
+    mem_cpy(buf, dj_old.dir+DIR_Attr, 21);        /* Save the object information */
+
+    mem_cpy(&dj_new, &dj_old, sizeof(eDIR));
+    res = follow_path(&dj_new, path_new);        /* Check new object */
+    if (res == FR_OK) res = FR_EXIST;            /* The new object name is already existing */
+    if (res == FR_NO_FILE) {                     /* Is it a valid path and no name collision? */
+        res = dir_register(&dj_new);            /* Register the new object */
+        if (res == FR_OK) {
+            dir = dj_new.dir;                    /* Copy object information into new entry */
+            mem_cpy(dir+13, buf+2, 19);
+            dir[DIR_Attr] = buf[0] | AM_ARC;
+            dj_old.fs->wflag = 1;
+            if (dir[DIR_Attr] & AM_DIR) {        /* Update .. entry in the directory if needed */
+                dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO));
+                if (!dw) {
+                    res = FR_INT_ERR;
+                } else {
+                    res = move_window(dj_new.fs, dw);
+                    dir = dj_new.fs->win+32;
+                    if (res == FR_OK && dir[1] == '.') {
+                        dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust;
+                        ST_WORD(dir+DIR_FstClusLO, dw);
+                        ST_WORD(dir+DIR_FstClusHI, dw >> 16);
+                        dj_new.fs->wflag = 1;
+                    }
+                }
+            }
+            if (res == FR_OK) {
+                res = dir_remove(&dj_old);            /* Remove old entry */
+                if (res == FR_OK)
+                    res = sync(dj_old.fs);
+            }
+        }
+    }
+
+    LEAVE_FF(dj_old.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (Available on only _FS_TINY cfg)  */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+    FIL *fp,                         /* Pointer to the file object */
+    UINT (*func)(const BYTE*,UINT),    /* Pointer to the streaming function */
+    UINT btr,                        /* Number of bytes to forward */
+    UINT *bf                        /* Pointer to number of bytes forwarded */
+)
+{
+    FRESULT res;
+    DWORD remain, clst, sect;
+    UINT rcnt;
+
+
+    *bf = 0;
+
+    res = validate(fp->fs, fp->id);                    /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                        /* Check error flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                        /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;            /* Truncate btr by remaining bytes */
+
+    for ( ;  btr && (*func)(NULL, 0);                /* Repeat until all data transferred or stream becomes busy */
+        fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
+        if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
+            if (fp->csect >= fp->fs->csize) {        /* On the cluster boundary? */
+                clst = (fp->fptr == 0) ?            /* On the top of the file? */
+                    fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+                if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->curr_clust = clst;                /* Update current cluster */
+                fp->csect = 0;                        /* Reset sector address in the cluster */
+            }
+            fp->csect++;                            /* Next sector address in the cluster */
+        }
+        sect = clust2sect(fp->fs, fp->curr_clust);    /* Get current data sector */
+        if (!sect) ABORT(fp->fs, FR_INT_ERR);
+        sect += fp->csect - 1;
+        if (move_window(fp->fs, sect))                /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        fp->dsect = sect;
+        rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));    /* Forward data from sector window */
+        if (rcnt > btr) rcnt = btr;
+        rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+        if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create File System on the Drive                                       */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR    512            /* Multiple of 32 and <= 2048 */
+#define N_FATS        1            /* 1 or 2 */
+#define MAX_SECTOR    131072000UL    /* Maximum partition size */
+#define MIN_SECTOR    2000UL        /* Minimum partition size */
+
+
+FRESULT f_mkfs (
+    BYTE drv,            /* Logical drive number */
+    BYTE partition,        /* Partitioning rule 0:FDISK, 1:SFD */
+    WORD allocsize        /* Allocation unit size [bytes] */
+)
+{
+    static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000,   0 };
+    static const WORD cstbl[] =  {   32768,   16384,   8192,   4096,   2048, 16384,  8192,  4096, 2048, 1024, 512 };
+    BYTE fmt, m, *tbl;
+    DWORD b_part, b_fat, b_dir, b_data;        /* Area offset (LBA) */
+    DWORD n_part, n_rsv, n_fat, n_dir;        /* Area size */
+    DWORD n_clst, d, n;
+    WORD as;
+    FATFS *fs;
+    DSTATUS stat;
+
+
+    /* Check validity of the parameters */
+    if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+    if (partition >= 2) return FR_MKFS_ABORTED;
+
+    /* Check mounted drive and clear work area */
+    fs = FatFs[drv];
+    if (!fs) return FR_NOT_ENABLED;
+    fs->fs_type = 0;
+    drv = LD2PD(drv);
+
+    /* Get disk statics */
+    stat = disk_initialize(drv);
+    if (stat & STA_NOINIT) return FR_NOT_READY;
+    if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if _MAX_SS != 512                        /* Get disk sector size */
+    if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
+        || SS(fs) > _MAX_SS)
+        return FR_MKFS_ABORTED;
+#endif
+    if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR)
+        return FR_MKFS_ABORTED;
+    if (n_part > MAX_SECTOR) n_part = MAX_SECTOR;
+    b_part = (!partition) ? 63 : 0;        /* Boot sector */
+    n_part -= b_part;
+    for (d = 512; d <= 32768U && d != allocsize; d <<= 1) ;    /* Check validity of the allocation unit size */
+    if (d != allocsize) allocsize = 0;
+    if (!allocsize) {                    /* Auto selection of cluster size */
+        d = n_part;
+        for (as = SS(fs); as > 512U; as >>= 1) d >>= 1;
+        for (n = 0; d < sstbl[n]; n++) ;
+        allocsize = cstbl[n];
+    }
+    if (allocsize < SS(fs)) allocsize = SS(fs);
+
+    allocsize /= SS(fs);        /* Number of sectors per cluster */
+
+    /* Pre-compute number of clusters and FAT type */
+    n_clst = n_part / allocsize;
+    fmt = FS_FAT12;
+    if (n_clst >= 0xFF5) fmt = FS_FAT16;
+    if (n_clst >= 0xFFF5) fmt = FS_FAT32;
+
+    /* Determine offset and size of FAT structure */
+    switch (fmt) {
+    case FS_FAT12:
+        n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs);
+        n_rsv = 1 + partition;
+        n_dir = N_ROOTDIR * 32 / SS(fs);
+        break;
+    case FS_FAT16:
+        n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs);
+        n_rsv = 1 + partition;
+        n_dir = N_ROOTDIR * 32 / SS(fs);
+        break;
+    default:
+        n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+        n_rsv = 33 - partition;
+        n_dir = 0;
+    }
+    b_fat = b_part + n_rsv;            /* FATs start sector */
+    b_dir = b_fat + n_fat * N_FATS;    /* Directory start sector */
+    b_data = b_dir + n_dir;            /* Data start sector */
+
+    /* Align data start sector to erase block boundary (for flash memory media) */
+    if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED;
+    n = (b_data + n - 1) & ~(n - 1);
+    n_fat += (n - b_data) / N_FATS;
+    /* b_dir and b_data are no longer used below */
+
+    /* Determine number of cluster and final check of validity of the FAT type */
+    n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize;
+    if (   (fmt == FS_FAT16 && n_clst < 0xFF5)
+        || (fmt == FS_FAT32 && n_clst < 0xFFF5))
+        return FR_MKFS_ABORTED;
+
+    /* Create partition table if needed */
+    if (!partition) {
+        DWORD n_disk = b_part + n_part;
+
+        mem_set(fs->win, 0, SS(fs));
+        tbl = fs->win+MBR_Table;
+        ST_DWORD(tbl, 0x00010180);        /* Partition start in CHS */
+        if (n_disk < 63UL * 255 * 1024) {    /* Partition end in CHS */
+            n_disk = n_disk / 63 / 255;
+            tbl[7] = (BYTE)n_disk;
+            tbl[6] = (BYTE)((n_disk >> 2) | 63);
+        } else {
+            ST_WORD(&tbl[6], 0xFFFF);
+        }
+        tbl[5] = 254;
+        if (fmt != FS_FAT32)            /* System ID */
+            tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06;
+        else
+            tbl[4] = 0x0c;
+        ST_DWORD(tbl+8, 63);            /* Partition start in LBA */
+        ST_DWORD(tbl+12, n_part);        /* Partition size in LBA */
+        ST_WORD(tbl+64, 0xAA55);        /* Signature */
+        if (disk_write(drv, fs->win, 0, 1) != RES_OK)
+            return FR_DISK_ERR;
+        partition = 0xF8;
+    } else {
+        partition = 0xF0;
+    }
+
+    /* Create boot record */
+    tbl = fs->win;                                /* Clear buffer */
+    mem_set(tbl, 0, SS(fs));
+    ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB);            /* Boot code (jmp $, nop) */
+    ST_WORD(tbl+BPB_BytsPerSec, SS(fs));        /* Sector size */
+    tbl[BPB_SecPerClus] = (BYTE)allocsize;        /* Sectors per cluster */
+    ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);            /* Reserved sectors */
+    tbl[BPB_NumFATs] = N_FATS;                    /* Number of FATs */
+    ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */
+    if (n_part < 0x10000) {                        /* Number of total sectors */
+        ST_WORD(tbl+BPB_TotSec16, n_part);
+    } else {
+        ST_DWORD(tbl+BPB_TotSec32, n_part);
+    }
+    tbl[BPB_Media] = partition;                    /* Media descripter */
+    ST_WORD(tbl+BPB_SecPerTrk, 63);                /* Number of sectors per track */
+    ST_WORD(tbl+BPB_NumHeads, 255);                /* Number of heads */
+    ST_DWORD(tbl+BPB_HiddSec, b_part);            /* Hidden sectors */
+    n = get_fattime();                            /* Use current time as a VSN */
+    if (fmt != FS_FAT32) {
+        ST_DWORD(tbl+BS_VolID, n);                /* Volume serial number */
+        ST_WORD(tbl+BPB_FATSz16, n_fat);        /* Number of secters per FAT */
+        tbl[BS_DrvNum] = 0x80;                    /* Drive number */
+        tbl[BS_BootSig] = 0x29;                    /* Extended boot signature */
+        mem_cpy(tbl+BS_VolLab, "NO NAME    FAT     ", 19);    /* Volume lavel, FAT signature */
+    } else {
+        ST_DWORD(tbl+BS_VolID32, n);            /* Volume serial number */
+        ST_DWORD(tbl+BPB_FATSz32, n_fat);        /* Number of secters per FAT */
+        ST_DWORD(tbl+BPB_RootClus, 2);            /* Root directory cluster (2) */
+        ST_WORD(tbl+BPB_FSInfo, 1);                /* FSInfo record offset (bs+1) */
+        ST_WORD(tbl+BPB_BkBootSec, 6);            /* Backup boot record offset (bs+6) */
+        tbl[BS_DrvNum32] = 0x80;                /* Drive number */
+        tbl[BS_BootSig32] = 0x29;                /* Extended boot signature */
+        mem_cpy(tbl+BS_VolLab32, "NO NAME    FAT32   ", 19);    /* Volume lavel, FAT signature */
+    }
+    ST_WORD(tbl+BS_55AA, 0xAA55);                /* Signature */
+    if (SS(fs) > 512U) {
+        ST_WORD(tbl+SS(fs)-2, 0xAA55);
+    }
+    if (disk_write(drv, tbl, b_part+0, 1) != RES_OK)
+        return FR_DISK_ERR;
+    if (fmt == FS_FAT32)
+        disk_write(drv, tbl, b_part+6, 1);
+
+    /* Initialize FAT area */
+    for (m = 0; m < N_FATS; m++) {
+        mem_set(tbl, 0, SS(fs));        /* 1st sector of the FAT  */
+        if (fmt != FS_FAT32) {
+            n = (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+            n |= partition;
+            ST_DWORD(tbl, n);                /* Reserve cluster #0-1 (FAT12/16) */
+        } else {
+            ST_DWORD(tbl+0, 0xFFFFFFF8);    /* Reserve cluster #0-1 (FAT32) */
+            ST_DWORD(tbl+4, 0xFFFFFFFF);
+            ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
+        }
+        if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+            return FR_DISK_ERR;
+        mem_set(tbl, 0, SS(fs));        /* Following FAT entries are filled by zero */
+        for (n = 1; n < n_fat; n++) {
+            if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+                return FR_DISK_ERR;
+        }
+    }
+
+    /* Initialize Root directory */
+    m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir);
+    do {
+        if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+            return FR_DISK_ERR;
+    } while (--m);
+
+    /* Create FSInfo record if needed */
+    if (fmt == FS_FAT32) {
+        ST_WORD(tbl+BS_55AA, 0xAA55);
+        ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
+        ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
+        ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);
+        ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF);
+        disk_write(drv, tbl, b_part+1, 1);
+        disk_write(drv, tbl, b_part+7, 1);
+    }
+
+    return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file                                            */
+/*-----------------------------------------------------------------------*/
+char* f_gets (
+    char* buff,    /* Pointer to the string buffer to read */
+    int len,    /* Size of string buffer */
+    FIL* fil    /* Pointer to the file object */
+)
+{
+    int i = 0;
+    char *p = buff;
+    UINT rc;
+
+
+    while (i < len - 1) {            /* Read bytes until buffer gets filled */
+        f_read(fil, p, 1, &rc);
+        if (rc != 1) break;            /* Break when no data to read */
+#if _USE_STRFUNC >= 2
+        if (*p == '\r') continue;    /* Strip '\r' */
+#endif
+        i++;
+        if (*p++ == '\n') break;    /* Break when reached end of line */
+    }
+    *p = 0;
+    return i ? buff : NULL;            /* When no data read (eof or error), return with error. */
+}
+
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file                                           */
+/*-----------------------------------------------------------------------*/
+int f_putc (
+    int chr,    /* A character to be output */
+    FIL* fil    /* Ponter to the file object */
+)
+{
+    UINT bw;
+    char c;
+
+
+#if _USE_STRFUNC >= 2
+    if (chr == '\n') f_putc ('\r', fil);    /* LF -> CRLF conversion */
+#endif
+    if (!fil) {    /* Special value may be used to switch the destination to any other device */
+    /*    put_console(chr);    */
+        return chr;
+    }
+    c = (char)chr;
+    f_write(fil, &c, 1, &bw);    /* Write a byte to the file */
+    return bw ? chr : EOF;        /* Return the result */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file                                              */
+/*-----------------------------------------------------------------------*/
+int f_puts (
+    const char* str,    /* Pointer to the string to be output */
+    FIL* fil            /* Pointer to the file object */
+)
+{
+    int n;
+
+
+    for (n = 0; *str; str++, n++) {
+        if (f_putc(*str, fil) == EOF) return EOF;
+    }
+    return n;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file                                    */
+/*-----------------------------------------------------------------------*/
+int f_printf (
+    FIL* fil,            /* Pointer to the file object */
+    const char* str,    /* Pointer to the format string */
+    ...                    /* Optional arguments... */
+)
+{
+    va_list arp;
+    UCHAR c, f, r;
+    ULONG val;
+    char s[16];
+    int i, w, res, cc;
+
+
+    va_start(arp, str);
+
+    for (cc = res = 0; cc != EOF; res += cc) {
+        c = *str++;
+        if (c == 0) break;            /* End of string */
+        if (c != '%') {                /* Non escape cahracter */
+            cc = f_putc(c, fil);
+            if (cc != EOF) cc = 1;
+            continue;
+        }
+        w = f = 0;
+        c = *str++;
+        if (c == '0') {                /* Flag: '0' padding */
+            f = 1; c = *str++;
+        }
+        while (c >= '0' && c <= '9') {    /* Precision */
+            w = w * 10 + (c - '0');
+            c = *str++;
+        }
+        if (c == 'l') {                /* Prefix: Size is long int */
+            f |= 2; c = *str++;
+        }
+        if (c == 's') {                /* Type is string */
+            cc = f_puts(va_arg(arp, char*), fil);
+            continue;
+        }
+        if (c == 'c') {                /* Type is character */
+            cc = f_putc(va_arg(arp, int), fil);
+            if (cc != EOF) cc = 1;
+            continue;
+        }
+        r = 0;
+        if (c == 'd') r = 10;        /* Type is signed decimal */
+        if (c == 'u') r = 10;        /* Type is unsigned decimal */
+        if (c == 'X') r = 16;        /* Type is unsigned hexdecimal */
+        if (r == 0) break;            /* Unknown type */
+        if (f & 2) {                /* Get the value */
+            val = (ULONG)va_arg(arp, long);
+        } else {
+            val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);
+        }
+        /* Put numeral string */
+        if (c == 'd') {
+            if (val & 0x80000000) {
+                val = 0 - val;
+                f |= 4;
+            }
+        }
+        i = sizeof(s) - 1; s[i] = 0;
+        do {
+            c = (UCHAR)(val % r + '0');
+            if (c > '9') c += 7;
+            s[--i] = c;
+            val /= r;
+        } while (i && val);
+        if (i && (f & 4)) s[--i] = '-';
+        w = sizeof(s) - 1 - w;
+        while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
+        cc = f_puts(&s[i], fil);
+    }
+
+    va_end(arp);
+    return (cc == EOF) ? cc : res;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHAN_FS/ff.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,596 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module include file  R0.07e       (C)ChaN, 2009
+/----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following trems.
+/
+/  Copyright (C) 2009, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/----------------------------------------------------------------------------*/
+
+#ifndef _FATFS
+#define _FATFS    0x007E
+
+#include "integer.h"    /* Basic integer types */
+#include "ffconf.h"        /* FatFs configuration options */
+
+#if _FATFS != _FFCONFIG
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+/* DBCS code ranges and SBCS extend char conversion table */
+
+#if _CODE_PAGE == 932    /* Japanese Shift-JIS */
+#define _DF1S    0x81    /* DBC 1st byte range 1 start */
+#define _DF1E    0x9F    /* DBC 1st byte range 1 end */
+#define _DF2S    0xE0    /* DBC 1st byte range 2 start */
+#define _DF2E    0xFC    /* DBC 1st byte range 2 end */
+#define _DS1S    0x40    /* DBC 2nd byte range 1 start */
+#define _DS1E    0x7E    /* DBC 2nd byte range 1 end */
+#define _DS2S    0x80    /* DBC 2nd byte range 2 start */
+#define _DS2E    0xFC    /* DBC 2nd byte range 2 end */
+
+#elif _CODE_PAGE == 936    /* Simplified Chinese GBK */
+#define _DF1S    0x81
+#define _DF1E    0xFE
+#define _DS1S    0x40
+#define _DS1E    0x7E
+#define _DS2S    0x80
+#define _DS2E    0xFE
+
+#elif _CODE_PAGE == 949    /* Korean */
+#define _DF1S    0x81
+#define _DF1E    0xFE
+#define _DS1S    0x41
+#define _DS1E    0x5A
+#define _DS2S    0x61
+#define _DS2E    0x7A
+#define _DS3S    0x81
+#define _DS3E    0xFE
+
+#elif _CODE_PAGE == 950    /* Traditional Chinese Big5 */
+#define _DF1S    0x81
+#define _DF1E    0xFE
+#define _DS1S    0x40
+#define _DS1E    0x7E
+#define _DS2S    0xA1
+#define _DS2E    0xFE
+
+#elif _CODE_PAGE == 437    /* U.S. (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 720    /* Arabic (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 737    /* Greek (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+                0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 775    /* Baltic (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 850    /* Multilingual Latin 1 (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 852    /* Latin 2 (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+
+#elif _CODE_PAGE == 855    /* Cyrillic (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+                0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+                0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 857    /* Turkish (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 858    /* Multilingual Latin 1 + Euro (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 862    /* Hebrew (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 866    /* Russian (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 874    /* Thai (OEM, Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+                0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}
+
+#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1253 /* Greek (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
+                0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}
+
+#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}
+
+#elif _CODE_PAGE == 1    /* ASCII (for only non-LFN cfg) */
+#define _DF1S    0
+
+#else
+#error Unknown code page
+
+#endif
+
+
+
+/* Character code support macros */
+
+#define IsUpper(c)    (((c)>='A')&&((c)<='Z'))
+#define IsLower(c)    (((c)>='a')&&((c)<='z'))
+
+#if _DF1S        /* DBCS configuration */
+
+#ifdef _DF2S    /* Two 1st byte areas */
+#define IsDBCS1(c)    (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
+#else            /* One 1st byte area */
+#define IsDBCS1(c)    ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
+#endif
+
+#ifdef _DS3S    /* Three 2nd byte areas */
+#define IsDBCS2(c)    (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
+#else            /* Two 2nd byte areas */
+#define IsDBCS2(c)    (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
+#endif
+
+#else            /* SBCS configuration */
+
+#define IsDBCS1(c)    0
+#define IsDBCS2(c)    0
+
+#endif /* _DF1S */
+
+
+
+/* Definitions corresponds to multi partition */
+
+#if _MULTI_PARTITION        /* Multiple partition configuration */
+
+typedef struct _PARTITION {
+    BYTE pd;    /* Physical drive# */
+    BYTE pt;    /* Partition # (0-3) */
+} PARTITION;
+
+extern
+const PARTITION Drives[];            /* Logical drive# to physical location conversion table */
+#define LD2PD(drv) (Drives[drv].pd)    /* Get physical drive# */
+#define LD2PT(drv) (Drives[drv].pt)    /* Get partition# */
+
+#else                        /* Single partition configuration */
+
+#define LD2PD(drv) (drv)    /* Physical drive# is equal to the logical drive# */
+#define LD2PT(drv) 0        /* Always mounts the 1st partition */
+
+#endif
+
+
+
+/* Definitions corresponds to multiple sector size */
+
+#if _MAX_SS == 512        /* Single sector size */
+#define    SS(fs)    512U
+
+#elif _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096    /* Multiple sector size */
+#define    SS(fs)    ((fs)->s_size)
+
+#else
+#error Sector size must be 512, 1024, 2048 or 4096.
+
+#endif
+
+
+
+/* Type of file name on FatFs API */
+
+#if _LFN_UNICODE && _USE_LFN
+typedef WCHAR XCHAR;    /* Unicode */
+#else
+typedef char XCHAR;        /* SBCS, DBCS */
+#endif
+
+
+
+/* File system object structure */
+
+typedef struct _FATFS_ {
+    BYTE    fs_type;    /* FAT sub type */
+    BYTE    drive;        /* Physical drive number */
+    BYTE    csize;        /* Number of sectors per cluster */
+    BYTE    n_fats;        /* Number of FAT copies */
+    BYTE    wflag;        /* win[] dirty flag (1:must be written back) */
+    BYTE    fsi_flag;    /* fsinfo dirty flag (1:must be written back) */
+    WORD    id;            /* File system mount ID */
+    WORD    n_rootdir;    /* Number of root directory entries (0 on FAT32) */
+#if _FS_REENTRANT
+    _SYNC_t    sobj;        /* Identifier of sync object */
+#endif
+#if _MAX_SS != 512
+    WORD    s_size;        /* Sector size */
+#endif
+#if !_FS_READONLY
+    DWORD    last_clust;    /* Last allocated cluster */
+    DWORD    free_clust;    /* Number of free clusters */
+    DWORD    fsi_sector;    /* fsinfo sector */
+#endif
+#if _FS_RPATH
+    DWORD    cdir;        /* Current directory (0:root)*/
+#endif
+    DWORD    sects_fat;    /* Sectors per fat */
+    DWORD    max_clust;    /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */
+    DWORD    fatbase;    /* FAT start sector */
+    DWORD    dirbase;    /* Root directory start sector (Cluster# on FAT32) */
+    DWORD    database;    /* Data start sector */
+    DWORD    winsect;    /* Current sector appearing in the win[] */
+    BYTE    win[_MAX_SS];/* Disk access window for Directory/FAT */
+} FATFS;
+
+
+
+/* Directory object structure */
+
+typedef struct _DIR_ {
+    FATFS*    fs;            /* Pointer to the owner file system object */
+    WORD    id;            /* Owner file system mount ID */
+    WORD    index;        /* Current read/write index number */
+    DWORD    sclust;        /* Table start cluster (0:Static table) */
+    DWORD    clust;        /* Current cluster */
+    DWORD    sect;        /* Current sector */
+    BYTE*    dir;        /* Pointer to the current SFN entry in the win[] */
+    BYTE*    fn;            /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
+#if _USE_LFN
+    WCHAR*    lfn;        /* Pointer to the LFN working buffer */
+    WORD    lfn_idx;    /* Last matched LFN index number (0xFFFF:No LFN) */
+#endif
+} eDIR;
+
+
+
+/* File object structure */
+
+typedef struct _FIL_ {
+    FATFS*    fs;            /* Pointer to the owner file system object */
+    WORD    id;            /* Owner file system mount ID */
+    BYTE    flag;        /* File status flags */
+    BYTE    csect;        /* Sector address in the cluster */
+    DWORD    fptr;        /* File R/W pointer */
+    DWORD    fsize;        /* File size */
+    DWORD    org_clust;    /* File start cluster */
+    DWORD    curr_clust;    /* Current cluster */
+    DWORD    dsect;        /* Current data sector */
+#if !_FS_READONLY
+    DWORD    dir_sect;    /* Sector containing the directory entry */
+    BYTE*    dir_ptr;    /* Ponter to the directory entry in the window */
+#endif
+#if !_FS_TINY
+    BYTE    buf[_MAX_SS];/* File R/W buffer */
+#endif
+} FIL;
+
+
+
+/* File status structure */
+
+typedef struct _FILINFO_ {
+    DWORD    fsize;        /* File size */
+    WORD    fdate;        /* Last modified date */
+    WORD    ftime;        /* Last modified time */
+    BYTE    fattrib;    /* Attribute */
+    char    fname[13];    /* Short file name (8.3 format) */
+#if _USE_LFN
+    XCHAR*    lfname;        /* Pointer to the LFN buffer */
+    int     lfsize;        /* Size of LFN buffer [chrs] */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+    FR_OK = 0,            /* 0 */
+    FR_DISK_ERR,        /* 1 */
+    FR_INT_ERR,            /* 2 */
+    FR_NOT_READY,        /* 3 */
+    FR_NO_FILE,            /* 4 */
+    FR_NO_PATH,            /* 5 */
+    FR_INVALID_NAME,    /* 6 */
+    FR_DENIED,            /* 7 */
+    FR_EXIST,            /* 8 */
+    FR_INVALID_OBJECT,    /* 9 */
+    FR_WRITE_PROTECTED,    /* 10 */
+    FR_INVALID_DRIVE,    /* 11 */
+    FR_NOT_ENABLED,        /* 12 */
+    FR_NO_FILESYSTEM,    /* 13 */
+    FR_MKFS_ABORTED,    /* 14 */
+    FR_TIMEOUT            /* 15 */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface                           */
+
+FRESULT f_mount (BYTE, FATFS*);                        /* Mount/Unmount a logical drive */
+FRESULT f_open (FIL*, const XCHAR*, BYTE);            /* Open or create a file */
+FRESULT f_read (FIL*, void*, UINT, UINT*);            /* Read data from a file */
+FRESULT f_write (FIL*, const void*, UINT, UINT*);    /* Write data to a file */
+FRESULT f_lseek (FIL*, DWORD);                        /* Move file pointer of a file object */
+FRESULT f_close (FIL*);                                /* Close an open file object */
+FRESULT f_opendir (eDIR*, const XCHAR*);                /* Open an existing directory */
+FRESULT f_readdir (eDIR*, FILINFO*);                    /* Read a directory item */
+FRESULT f_stat (const XCHAR*, FILINFO*);            /* Get file status */
+FRESULT f_getfree (const XCHAR*, DWORD*, FATFS**);    /* Get number of free clusters on the drive */
+FRESULT f_truncate (FIL*);                            /* Truncate file */
+FRESULT f_sync (FIL*);                                /* Flush cached data of a writing file */
+FRESULT f_unlink (const XCHAR*);                    /* Delete an existing file or directory */
+FRESULT    f_mkdir (const XCHAR*);                        /* Create a new directory */
+FRESULT f_chmod (const XCHAR*, BYTE, BYTE);            /* Change attriburte of the file/dir */
+FRESULT f_utime (const XCHAR*, const FILINFO*);        /* Change timestamp of the file/dir */
+FRESULT f_rename (const XCHAR*, const XCHAR*);        /* Rename/Move a file or directory */
+FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*);    /* Forward data to the stream */
+FRESULT f_mkfs (BYTE, BYTE, WORD);                    /* Create a file system on the drive */
+FRESULT f_chdir (const XCHAR*);                        /* Change current directory */
+FRESULT f_chdrive (BYTE);                            /* Change current drive */
+
+#if _USE_STRFUNC
+int f_putc (int, FIL*);                                /* Put a character to the file */
+int f_puts (const char*, FIL*);                        /* Put a string to the file */
+int f_printf (FIL*, const char*, ...);                /* Put a formatted string to the file */
+char* f_gets (char*, int, FIL*);                    /* Get a string from the file */
+#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
+#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0)
+#ifndef EOF
+#define EOF -1
+#endif
+#endif
+
+
+
+/*--------------------------------------------------------------*/
+/* User defined functions                                       */
+
+/* Real time clock */
+#if !_FS_READONLY
+DWORD get_fattime (void);    /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
+                            /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
+#endif
+
+/* Unicode - OEM code conversion */
+#if _USE_LFN
+WCHAR ff_convert (WCHAR, UINT);
+WCHAR ff_wtoupper (WCHAR);
+#endif
+
+/* Sync functions */
+#if _FS_REENTRANT
+BOOL ff_cre_syncobj(BYTE, _SYNC_t*);
+BOOL ff_del_syncobj(_SYNC_t);
+BOOL ff_req_grant(_SYNC_t);
+void ff_rel_grant(_SYNC_t);
+#endif
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address                                     */
+
+
+/* File access control and file status flags (FIL.flag) */
+
+#define    FA_READ                0x01
+#define    FA_OPEN_EXISTING    0x00
+#if _FS_READONLY == 0
+#define    FA_WRITE            0x02
+#define    FA_CREATE_NEW        0x04
+#define    FA_CREATE_ALWAYS    0x08
+#define    FA_OPEN_ALWAYS        0x10
+#define FA__WRITTEN            0x20
+#define FA__DIRTY            0x40
+#endif
+#define FA__ERROR            0x80
+
+
+/* FAT sub type (FATFS.fs_type) */
+
+#define FS_FAT12    1
+#define FS_FAT16    2
+#define FS_FAT32    3
+
+
+/* File attribute bits for directory entry */
+
+#define    AM_RDO    0x01    /* Read only */
+#define    AM_HID    0x02    /* Hidden */
+#define    AM_SYS    0x04    /* System */
+#define    AM_VOL    0x08    /* Volume label */
+#define AM_LFN    0x0F    /* LFN entry */
+#define AM_DIR    0x10    /* Directory */
+#define AM_ARC    0x20    /* Archive */
+#define AM_MASK    0x3F    /* Mask of defined bits */
+
+
+/* FatFs refers the members in the FAT structures with byte offset instead
+/ of structure member because there are incompatibility of the packing option
+/ between various compilers. */
+
+#define BS_jmpBoot            0
+#define BS_OEMName            3
+#define BPB_BytsPerSec        11
+#define BPB_SecPerClus        13
+#define BPB_RsvdSecCnt        14
+#define BPB_NumFATs            16
+#define BPB_RootEntCnt        17
+#define BPB_TotSec16        19
+#define BPB_Media            21
+#define BPB_FATSz16            22
+#define BPB_SecPerTrk        24
+#define BPB_NumHeads        26
+#define BPB_HiddSec            28
+#define BPB_TotSec32        32
+#define BS_55AA                510
+
+#define BS_DrvNum            36
+#define BS_BootSig            38
+#define BS_VolID            39
+#define BS_VolLab            43
+#define BS_FilSysType        54
+
+#define BPB_FATSz32            36
+#define BPB_ExtFlags        40
+#define BPB_FSVer            42
+#define BPB_RootClus        44
+#define BPB_FSInfo            48
+#define BPB_BkBootSec        50
+#define BS_DrvNum32            64
+#define BS_BootSig32        66
+#define BS_VolID32            67
+#define BS_VolLab32            71
+#define BS_FilSysType32        82
+
+#define    FSI_LeadSig            0
+#define    FSI_StrucSig        484
+#define    FSI_Free_Count        488
+#define    FSI_Nxt_Free        492
+
+#define MBR_Table            446
+
+#define    DIR_Name            0
+#define    DIR_Attr            11
+#define    DIR_NTres            12
+#define    DIR_CrtTime            14
+#define    DIR_CrtDate            16
+#define    DIR_FstClusHI        20
+#define    DIR_WrtTime            22
+#define    DIR_WrtDate            24
+#define    DIR_FstClusLO        26
+#define    DIR_FileSize        28
+#define    LDIR_Ord            0
+#define    LDIR_Attr            11
+#define    LDIR_Type            12
+#define    LDIR_Chksum            13
+#define    LDIR_FstClusLO        26
+
+
+
+/*--------------------------------*/
+/* Multi-byte word access macros  */
+
+#if _WORD_ACCESS == 1    /* Enable word access to the FAT structure */
+#define    LD_WORD(ptr)        (WORD)(*(WORD*)(BYTE*)(ptr))
+#define    LD_DWORD(ptr)        (DWORD)(*(DWORD*)(BYTE*)(ptr))
+#define    ST_WORD(ptr,val)    *(WORD*)(BYTE*)(ptr)=(WORD)(val)
+#define    ST_DWORD(ptr,val)    *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
+#else                    /* Use byte-by-byte access to the FAT structure */
+#define    LD_WORD(ptr)        (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
+#define    LD_DWORD(ptr)        (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr))
+#define    ST_WORD(ptr,val)    *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8)
+#define    ST_DWORD(ptr,val)    *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24)
+#endif
+
+
+#endif /* _FATFS */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHAN_FS/ffconf.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,166 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module configuration file  R0.07e  (C)ChaN, 2009
+/----------------------------------------------------------------------------/
+/
+/ CAUTION! Do not forget to make clean the project after any changes to
+/ the configuration options.
+/
+/----------------------------------------------------------------------------*/
+#ifndef _FFCONFIG
+#define _FFCONFIG 0x007E
+
+
+/*---------------------------------------------------------------------------/
+/ Function and Buffer Configurations
+/----------------------------------------------------------------------------*/
+
+#define    _FS_TINY    0        /* 0 or 1 */
+/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system
+/  object instead of the sector buffer in the individual file object for file
+/  data transfer. This reduces memory consumption 512 bytes each file object. */
+
+
+#define _FS_READONLY    0    /* 0 or 1 */
+/* Setting _FS_READONLY to 1 defines read only configuration. This removes
+/  writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename,
+/  f_truncate and useless f_getfree. */
+
+
+#define _FS_MINIMIZE    0    /* 0, 1, 2 or 3 */
+/* The _FS_MINIMIZE option defines minimization level to remove some functions.
+/
+/   0: Full function.
+/   1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename
+/      are removed.
+/   2: f_opendir and f_readdir are removed in addition to level 1.
+/   3: f_lseek is removed in addition to level 2. */
+
+
+#define    _USE_STRFUNC    1    /* 0, 1 or 2 */
+/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
+
+
+#define    _USE_MKFS    0        /* 0 or 1 */
+/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
+
+
+#define    _USE_FORWARD    0    /* 0 or 1 */
+/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/----------------------------------------------------------------------------*/
+
+#define _CODE_PAGE    437
+/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
+/  Incorrect setting of the code page can cause a file open failure.
+/
+/   932  - Japanese Shift-JIS (DBCS, OEM, Windows)
+/   936  - Simplified Chinese GBK (DBCS, OEM, Windows)
+/   949  - Korean (DBCS, OEM, Windows)
+/   950  - Traditional Chinese Big5 (DBCS, OEM, Windows)
+/   1250 - Central Europe (Windows)
+/   1251 - Cyrillic (Windows)
+/   1252 - Latin 1 (Windows)
+/   1253 - Greek (Windows)
+/   1254 - Turkish (Windows)
+/   1255 - Hebrew (Windows)
+/   1256 - Arabic (Windows)
+/   1257 - Baltic (Windows)
+/   1258 - Vietnam (OEM, Windows)
+/   437  - U.S. (OEM)
+/   720  - Arabic (OEM)
+/   737  - Greek (OEM)
+/   775  - Baltic (OEM)
+/   850  - Multilingual Latin 1 (OEM)
+/   858  - Multilingual Latin 1 + Euro (OEM)
+/   852  - Latin 2 (OEM)
+/   855  - Cyrillic (OEM)
+/   866  - Russian (OEM)
+/   857  - Turkish (OEM)
+/   862  - Hebrew (OEM)
+/   874  - Thai (OEM, Windows)
+/    1    - ASCII only (Valid for non LFN cfg.)
+*/
+
+
+#define    _USE_LFN    1        /* 0, 1 or 2 */
+#define    _MAX_LFN    255        /* Maximum LFN length to handle (12 to 255) */
+/* The _USE_LFN option switches the LFN support.
+/
+/   0: Disable LFN. _MAX_LFN and _LFN_UNICODE have no effect.
+/   1: Enable LFN with static working buffer on the bss. NOT REENTRANT.
+/   2: Enable LFN with dynamic working buffer on the STACK.
+/
+/  The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN,
+/  two Unicode handling functions ff_convert() and ff_wtoupper() must be added
+/  to the project. */
+
+
+#define    _LFN_UNICODE    0    /* 0 or 1 */
+/* To switch the character code set on FatFs API to Unicode,
+/  enable LFN feature and set _LFN_UNICODE to 1.
+*/
+
+
+#define _FS_RPATH    0        /* 0 or 1 */
+/* When _FS_RPATH is set to 1, relative path feature is enabled and f_chdir,
+/  f_chdrive function are available.
+/  Note that output of the f_readdir fnction is affected by this option. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ Physical Drive Configurations
+/----------------------------------------------------------------------------*/
+
+#define _DRIVES        1
+/* Number of volumes (logical drives) to be used. */
+
+
+#define    _MAX_SS        1024        /* 512, 1024, 2048 or 4096 */
+/* Maximum sector size to be handled.
+/  Always set 512 for memory card and hard disk but a larger value may be
+/  required for floppy disk (512/1024) and optical disk (512/2048).
+/  When _MAX_SS is larger than 512, GET_SECTOR_SIZE command must be implememted
+/  to the disk_ioctl function. */
+
+
+#define    _MULTI_PARTITION    0    /* 0 or 1 */
+/* When _MULTI_PARTITION is set to 0, each volume is bound to the same physical
+/ drive number and can mount only first primaly partition. When it is set to 1,
+/ each volume is tied to the partitions listed in Drives[]. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/----------------------------------------------------------------------------*/
+
+#define _WORD_ACCESS    0    /* 0 or 1 */
+/* The _WORD_ACCESS option defines which access method is used to the word
+/  data on the FAT volume.
+/
+/   0: Byte-by-byte access. Always compatible with all platforms.
+/   1: Word access. Do not choose this unless following condition is met.
+/
+/  When the byte order on the memory is big-endian or address miss-aligned
+/  word access results incorrect behavior, the _WORD_ACCESS must be set to 0.
+/  If it is not the case, the value can also be set to 1 to improve the
+/  performance and code size. */
+
+
+#define _FS_REENTRANT    0        /* 0 or 1 */
+#define _FS_TIMEOUT        1000    /* Timeout period in unit of time ticks */
+#define    _SYNC_t            HANDLE    /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */
+/* The _FS_REENTRANT option switches the reentrancy of the FatFs module.
+/
+/   0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect.
+/   1: Enable reentrancy. Also user provided synchronization handlers,
+/      ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj
+/      function must be added to the project. */
+
+
+#endif /* _FFCONFIG */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHAN_FS/integer.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,39 @@
+/*-------------------------------------------*/
+/* Integer type definitions for FatFs module */
+/*-------------------------------------------*/
+
+#ifndef _INTEGER
+
+#if 0
+#include <windows.h>
+#else
+
+/* These types must be 16-bit, 32-bit or larger integer */
+typedef int                INT;
+typedef unsigned int    UINT;
+
+/* These types must be 8-bit integer */
+
+#define CHAR  char
+//typedef signed char       CHAR;
+typedef unsigned char    UCHAR;
+typedef unsigned char    BYTE;
+
+/* These types must be 16-bit integer */
+typedef short            SHORT;
+typedef unsigned short    USHORT;
+typedef unsigned short    WORD;
+typedef unsigned short    WCHAR;
+
+/* These types must be 32-bit integer */
+typedef long             LONG;
+typedef unsigned long    ULONG;
+typedef unsigned long    DWORD;
+
+/* Boolean type */
+typedef enum { FALSE = 0, TRUE } BOOL;
+
+#endif
+
+#define _INTEGER
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CHAN_FS/option/ccsbcs.c	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,538 @@
+/* (SBCS code pages)                                                      */
+/*------------------------------------------------------------------------*/
+/*  437   U.S. (OEM)
+/   720   Arabic (OEM)
+/   1256  Arabic (Windows)
+/   737   Greek (OEM)
+/   1253  Greek (Windows)
+/   1250  Central Europe (Windows)
+/   775   Baltic (OEM)
+/   1257  Baltic (Windows)
+/   850   Multilingual Latin 1 (OEM)
+/   852   Latin 2 (OEM)
+/   1252  Latin 1 (Windows)
+/   855   Cyrillic (OEM)
+/   1251  Cyrillic (Windows)
+/   866   Russian (OEM)
+/   857   Turkish (OEM)
+/   1254  Turkish (Windows)
+/   858   Multilingual Latin 1 + Euro (OEM)
+/   862   Hebrew (OEM)
+/   1255  Hebrew (Windows)
+/   874   Thai (OEM, Windows)
+/   1258  Vietnam (OEM, Windows)
+*/
+
+#include "../ff.h"
+
+
+#if _CODE_PAGE == 437
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP437(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 720
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP720(0x80-0xFF) to Unicode conversion table */
+    0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9,
+    0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642,
+    0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
+    0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 737
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP737(0x80-0xFF) to Unicode conversion table */
+    0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
+    0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+    0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,
+    0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
+    0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
+    0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD,
+    0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
+    0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 775
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP775(0x80-0xFF) to Unicode conversion table */
+    0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107,
+    0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A,
+    0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
+    0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6,
+    0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118,
+    0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
+    0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B,
+    0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144,
+    0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
+    0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E,
+    0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 850
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP850(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+    0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 852
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP852(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7,
+    0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
+    0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A,
+    0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E,
+    0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A,
+    0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE,
+    0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161,
+    0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
+    0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 855
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP855(0x80-0xFF) to Unicode conversion table */
+    0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404,
+    0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
+    0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C,
+    0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
+    0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414,
+    0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438,
+    0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E,
+    0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
+    0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443,
+    0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
+    0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D,
+    0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 857
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP857(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000,
+    0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 858
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP858(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+    0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 862
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP862(0x80-0xFF) to Unicode conversion table */
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+    0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 866
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP866(0x80-0xFF) to Unicode conversion table */
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+    0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+    0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+    0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+    0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 874
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP874(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
+    0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
+    0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
+    0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
+    0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
+    0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
+    0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
+    0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,
+    0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
+    0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
+    0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
+    0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+#elif _CODE_PAGE == 1250
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1250(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
+    0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
+    0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
+    0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
+    0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
+    0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
+    0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
+    0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
+    0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
+    0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
+};
+
+#elif _CODE_PAGE == 1251
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1251(0x80-0xFF) to Unicode conversion table */
+    0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
+    0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
+    0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
+    0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
+    0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
+    0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+    0x0428, 0x0429, 0x042A, 0x042D, 0x042C, 0x042D, 0x042E, 0x042F,
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+    0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+    0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
+};
+
+#elif _CODE_PAGE == 1252
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1252(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+};
+
+#elif _CODE_PAGE == 1253
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1253(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7,
+    0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
+    0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+    0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+    0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
+    0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+    0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+    0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+    0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+    0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
+};
+
+#elif _CODE_PAGE == 1254
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1254(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
+};
+
+#elif _CODE_PAGE == 1255
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1255(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
+    0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+    0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3,
+    0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+    0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
+};
+
+#elif _CODE_PAGE == 1256
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1256(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
+    0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
+    0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
+    0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7,
+    0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643,
+    0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
+    0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7,
+    0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
+}
+
+#elif _CODE_PAGE == 1257
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1257(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,
+    0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7,
+    0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+    0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
+    0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+    0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
+    0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+    0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
+    0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+    0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
+    0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
+};
+
+#elif _CODE_PAGE == 1258
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1258(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
+    0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
+    0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
+};
+
+#endif
+
+
+#if !_TBLDEF || !_USE_LFN
+#error This file is not needed in current configuration
+#endif
+
+
+WCHAR ff_convert (    /* Converted character, Returns zero on error */
+    WCHAR    src,    /* Character code to be converted */
+    UINT    dir        /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
+)
+{
+    WCHAR c;
+
+
+    if (src < 0x80) {    /* ASCII */
+        c = src;
+
+    } else {
+        if (dir) {        /* OEMCP to Unicode */
+            c = (src >= 0x100) ? 0 : Tbl[src - 0x80];
+
+        } else {        /* Unicode to OEMCP */
+            for (c = 0; c < 0x80; c++) {
+                if (src == Tbl[c]) break;
+            }
+            c = (c + 0x80) & 0xFF;
+        }
+    }
+
+    return c;
+}
+
+
+WCHAR ff_wtoupper (    /* Upper converted character */
+    WCHAR chr        /* Input character */
+)
+{
+    static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 };
+    static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 };
+    int i;
+
+
+    for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ;
+
+    return tbl_lower[i] ? tbl_upper[i] : chr;
+}
--- a/FatFileSystem.lib	Wed Apr 10 13:31:35 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-http://mbed.org/users/mbed_unsupported/code/FatFileSystem/#333d6e93e58f
--- a/MSCFileSystem.lib	Wed Apr 10 13:31:35 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-http://mbed.org/users/chris/code/MSCFileSystem/#dcc326e4d358
--- a/SPI_TFTx2.lib	Wed Apr 10 13:31:35 2013 +0000
+++ b/SPI_TFTx2.lib	Sun Jul 21 23:59:00 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/TickTock/code/SPI_TFTx2/#ff509eb02e37
+http://mbed.org/users/TickTock/code/SPI_TFTx2/#4ad7de7d2220
--- a/TFT_fonts.lib	Wed Apr 10 13:31:35 2013 +0000
+++ b/TFT_fonts.lib	Sun Jul 21 23:59:00 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/TickTock/code/TFT_fonts/#49b5ef080441
+http://mbed.org/users/TickTock/code/TFT_fonts/#af72b26394d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHostLite/usbhost_cpu.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,35 @@
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_cpu.h
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+
+#ifndef  USBHOST_CPU_H
+#define  USBHOST_CPU_H
+
+/*
+**************************************************************************************************************
+*                                           TYPE DEFINITIONS OF DATA TYPES
+**************************************************************************************************************
+*/
+
+typedef  unsigned int    USB_INT32U;
+typedef  signed   int    USB_INT32S;
+typedef  unsigned short  USB_INT16U;
+typedef  signed   short  USB_INT16S;
+typedef  unsigned char   USB_INT08U;
+typedef  signed   char   USB_INT08S;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHostLite/usbhost_err.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,63 @@
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_err.h
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+
+#ifndef  USBHOST_ERR_H
+#define  USBHOST_ERR_H
+
+
+/*
+**************************************************************************************************************
+*                                        GENERAL DEFINITIONS
+**************************************************************************************************************
+*/
+
+#define  OK                        0
+#define  MATCH_FOUND               0
+
+/*
+**************************************************************************************************************
+*                                HOST CONTROLLER SPECIFIC ERROR CODES
+**************************************************************************************************************
+*/
+
+#define  ERR_TD_FAIL              -1
+
+/*
+**************************************************************************************************************
+*                                  MASS STORAGE SPECIFIC ERROR CODES
+**************************************************************************************************************
+*/
+
+#define  ERR_MS_CMD_FAILED       -10
+#define  ERR_BAD_CONFIGURATION   -11
+#define  ERR_NO_MS_INTERFACE     -12
+
+/*
+**************************************************************************************************************
+*                                      FAT SPECIFIC ERROR CODES
+**************************************************************************************************************
+*/
+
+#define  MATCH_NOT_FOUND         -20
+#define  ERR_FAT_NOT_SUPPORTED   -21
+#define  ERR_OPEN_LIMIT_REACHED  -22
+#define  ERR_INVALID_BOOT_SIG    -23
+#define  ERR_INVALID_BOOT_SEC    -24
+#define  ERR_ROOT_DIR_FULL       -25
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHostLite/usbhost_inc.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,39 @@
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_inc.h
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+
+#ifndef  USBHOST_INC_H
+#define  USBHOST_INC_H
+
+/*
+**************************************************************************************************************
+*                                       INCLUDE HEADER FILES
+**************************************************************************************************************
+*/
+
+#include  "usbhost_cpu.h"
+#include  "usbhost_err.h"
+#include  "usbhost_lpc17xx.h"
+#include  "usbhost_ms.h"
+#include  "mbed.h"
+
+
+#ifdef TARGET_LPC2368
+#error "There is no USB host on the LPC2368!"
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHostLite/usbhost_lpc17xx.c	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,820 @@
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_lpc17xx.c
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+ 
+/*
+**************************************************************************************************************
+*                                            INCLUDE HEADER FILES
+**************************************************************************************************************
+*/
+
+#include  "usbhost_lpc17xx.h"
+
+/*
+**************************************************************************************************************
+*                                              GLOBAL VARIABLES
+**************************************************************************************************************
+*/
+int gUSBConnected;
+
+volatile  USB_INT32U   HOST_RhscIntr = 0;         /* Root Hub Status Change interrupt                       */
+volatile  USB_INT32U   HOST_WdhIntr  = 0;         /* Semaphore to wait until the TD is submitted            */
+volatile  USB_INT08U   HOST_TDControlStatus = 0;
+volatile  HCED        *EDCtrl;                    /* Control endpoint descriptor structure                  */
+volatile  HCED        *EDBulkIn;                  /* BulkIn endpoint descriptor  structure                  */
+volatile  HCED        *EDBulkOut;                 /* BulkOut endpoint descriptor structure                  */
+volatile  HCTD        *TDHead;                    /* Head transfer descriptor structure                     */
+volatile  HCTD        *TDTail;                    /* Tail transfer descriptor structure                     */
+volatile  HCCA        *Hcca;                      /* Host Controller Communications Area structure          */ 
+          USB_INT16U  *TDBufNonVol;               /* Identical to TDBuffer just to reduce compiler warnings */
+volatile  USB_INT08U  *TDBuffer;                  /* Current Buffer Pointer of transfer descriptor          */
+
+// USB host structures
+// AHB SRAM block 1
+#define HOSTBASEADDR 0x2007C000
+// reserve memory for the linker
+static USB_INT08U HostBuf[0x200] __attribute__((at(HOSTBASEADDR)));
+/*
+**************************************************************************************************************
+*                                         DELAY IN MILLI SECONDS
+*
+* Description: This function provides a delay in milli seconds
+*
+* Arguments  : delay    The delay required
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  Host_DelayMS (USB_INT32U  delay)
+{
+    volatile  USB_INT32U  i;
+
+
+    for (i = 0; i < delay; i++) {
+        Host_DelayUS(1000);
+    }
+}
+
+/*
+**************************************************************************************************************
+*                                         DELAY IN MICRO SECONDS
+*
+* Description: This function provides a delay in micro seconds
+*
+* Arguments  : delay    The delay required
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  Host_DelayUS (USB_INT32U  delay)
+{
+    volatile  USB_INT32U  i;
+
+
+    for (i = 0; i < (4 * delay); i++) {    /* This logic was tested. It gives app. 1 micro sec delay        */
+        ;
+    }
+}
+
+// bits of the USB/OTG clock control register
+#define HOST_CLK_EN     (1<<0)
+#define DEV_CLK_EN      (1<<1)
+#define PORTSEL_CLK_EN  (1<<3)
+#define AHB_CLK_EN      (1<<4)
+
+// bits of the USB/OTG clock status register
+#define HOST_CLK_ON     (1<<0)
+#define DEV_CLK_ON      (1<<1)
+#define PORTSEL_CLK_ON  (1<<3)
+#define AHB_CLK_ON      (1<<4)
+
+// we need host clock, OTG/portsel clock and AHB clock
+#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
+
+/*
+**************************************************************************************************************
+*                                         INITIALIZE THE HOST CONTROLLER
+*
+* Description: This function initializes lpc17xx host controller
+*
+* Arguments  : None
+*
+* Returns    : 
+*
+**************************************************************************************************************
+*/
+void  Host_Init (void)
+{
+    PRINT_Log("In Host_Init\n");
+    NVIC_DisableIRQ(USB_IRQn);                           /* Disable the USB interrupt source           */
+    
+    // turn on power for USB
+    LPC_SC->PCONP       |= (1UL<<31);
+    // Enable USB host clock, port selection and AHB clock
+    LPC_USB->USBClkCtrl |= CLOCK_MASK;
+    // Wait for clocks to become available
+    while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
+        ;
+    
+    // it seems the bits[0:1] mean the following
+    // 0: U1=device, U2=host
+    // 1: U1=host, U2=host
+    // 2: reserved
+    // 3: U1=host, U2=device
+    // NB: this register is only available if OTG clock (aka "port select") is enabled!!
+    // since we don't care about port 2, set just bit 0 to 1 (U1=host)
+    LPC_USB->OTGStCtrl |= 1;
+    
+    // now that we've configured the ports, we can turn off the portsel clock
+    LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
+    
+    // power pins are not connected on mbed, so we can skip them
+    /* P1[18] = USB_UP_LED, 01 */
+    /* P1[19] = /USB_PPWR,     10 */
+    /* P1[22] = USB_PWRD, 10 */
+    /* P1[27] = /USB_OVRCR, 10 */
+    /*LPC_PINCON->PINSEL3 &= ~((3<<4) | (3<<6) | (3<<12) | (3<<22));  
+    LPC_PINCON->PINSEL3 |=  ((1<<4)|(2<<6) | (2<<12) | (2<<22));   // 0x00802080
+    */
+
+    // configure USB D+/D- pins
+    /* P0[29] = USB_D+, 01 */
+    /* P0[30] = USB_D-, 01 */
+    LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));  
+    LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
+        
+    PRINT_Log("Initializing Host Stack\n");
+
+    Hcca       = (volatile  HCCA       *)(HostBuf+0x000);
+    TDHead     = (volatile  HCTD       *)(HostBuf+0x100);
+    TDTail     = (volatile  HCTD       *)(HostBuf+0x110);
+    EDCtrl     = (volatile  HCED       *)(HostBuf+0x120); 
+    EDBulkIn   = (volatile  HCED       *)(HostBuf+0x130);
+    EDBulkOut  = (volatile  HCED       *)(HostBuf+0x140);
+    TDBuffer   = (volatile  USB_INT08U *)(HostBuf+0x150);
+    
+    /* Initialize all the TDs, EDs and HCCA to 0  */
+    Host_EDInit(EDCtrl);
+    Host_EDInit(EDBulkIn);
+    Host_EDInit(EDBulkOut);
+    Host_TDInit(TDHead);
+    Host_TDInit(TDTail);
+    Host_HCCAInit(Hcca);
+    
+    Host_DelayMS(50);                                   /* Wait 50 ms before apply reset              */
+    LPC_USB->HcControl       = 0;                       /* HARDWARE RESET                             */
+    LPC_USB->HcControlHeadED = 0;                       /* Initialize Control list head to Zero       */
+    LPC_USB->HcBulkHeadED    = 0;                       /* Initialize Bulk list head to Zero          */
+    
+                                                        /* SOFTWARE RESET                             */
+    LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
+    LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      /* Write Fm Interval and Largest Data Packet Counter */
+
+                                                        /* Put HC in operational state                */
+    LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
+    LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            /* Set Global Power                           */
+    
+    LPC_USB->HcHCCA = (USB_INT32U)Hcca;
+    LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;                   /* Clear Interrrupt Status                    */
+
+
+    LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE |
+                         OR_INTR_ENABLE_WDH |
+                         OR_INTR_ENABLE_RHSC;
+
+    NVIC_SetPriority(USB_IRQn, 0);       /* highest priority */
+    /* Enable the USB Interrupt */
+    NVIC_EnableIRQ(USB_IRQn);
+    PRINT_Log("Host Initialized\n");
+}
+
+/*
+**************************************************************************************************************
+*                                         INTERRUPT SERVICE ROUTINE
+*
+* Description: This function services the interrupt caused by host controller
+*
+* Arguments  : None
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void USB_IRQHandler (void) __irq
+{
+    USB_INT32U   int_status;
+    USB_INT32U   ie_status;
+
+    int_status    = LPC_USB->HcInterruptStatus;                          /* Read Interrupt Status                */
+    ie_status     = LPC_USB->HcInterruptEnable;                          /* Read Interrupt enable status         */
+ 
+    if (!(int_status & ie_status)) {
+        return;
+    } else {
+
+        int_status = int_status & ie_status;
+        if (int_status & OR_INTR_STATUS_RHSC) {                 /* Root hub status change interrupt     */
+            if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC) {
+                if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE) {
+                    /*
+                     * When DRWE is on, Connect Status Change
+                     * means a remote wakeup event.
+                    */
+                    HOST_RhscIntr = 1;// JUST SOMETHING FOR A BREAKPOINT
+                }
+                else {
+                    /*
+                     * When DRWE is off, Connect Status Change
+                     * is NOT a remote wakeup event
+                    */
+                    if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) {
+                        if (!gUSBConnected) {
+                            HOST_TDControlStatus = 0;
+                            HOST_WdhIntr = 0;
+                            HOST_RhscIntr = 1;
+                            gUSBConnected = 1;
+                        }
+                        else
+                            PRINT_Log("Spurious status change (connected)?\n");
+                    } else {
+                        if (gUSBConnected) {
+                            LPC_USB->HcInterruptEnable = 0; // why do we get multiple disc. rupts???
+                            HOST_RhscIntr = 0;
+                            gUSBConnected = 0;
+                        }
+                        else
+                            PRINT_Log("Spurious status change (disconnected)?\n");
+                    }
+                }
+                LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+            }
+            if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC) {
+                LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+            }
+        }
+        if (int_status & OR_INTR_STATUS_WDH) {                  /* Writeback Done Head interrupt        */
+            HOST_WdhIntr = 1;
+            HOST_TDControlStatus = (TDHead->Control >> 28) & 0xf;
+        }            
+        LPC_USB->HcInterruptStatus = int_status;                         /* Clear interrupt status register      */
+    }
+    return;
+}
+
+/*
+**************************************************************************************************************
+*                                     PROCESS TRANSFER DESCRIPTOR
+*
+* Description: This function processes the transfer descriptor
+*
+* Arguments  : ed            Endpoint descriptor that contains this transfer descriptor
+*              token         SETUP, IN, OUT
+*              buffer        Current Buffer Pointer of the transfer descriptor
+*              buffer_len    Length of the buffer
+*
+* Returns    : OK       if TD submission is successful
+*              ERROR    if TD submission fails
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  Host_ProcessTD (volatile  HCED       *ed,
+                            volatile  USB_INT32U  token,
+                            volatile  USB_INT08U *buffer,
+                                      USB_INT32U  buffer_len)
+{
+    volatile  USB_INT32U   td_toggle;
+
+
+    if (ed == EDCtrl) {
+        if (token == TD_SETUP) {
+            td_toggle = TD_TOGGLE_0;
+        } else {
+            td_toggle = TD_TOGGLE_1;
+        }
+    } else {
+        td_toggle = 0;
+    }
+    TDHead->Control = (TD_ROUNDING    |
+                      token           |
+                      TD_DELAY_INT(0) |                           
+                      td_toggle       |
+                      TD_CC);
+    TDTail->Control = 0;
+    TDHead->CurrBufPtr   = (USB_INT32U) buffer;
+    TDTail->CurrBufPtr   = 0;
+    TDHead->Next         = (USB_INT32U) TDTail;
+    TDTail->Next         = 0;
+    TDHead->BufEnd       = (USB_INT32U)(buffer + (buffer_len - 1));
+    TDTail->BufEnd       = 0;
+
+    ed->HeadTd  = (USB_INT32U)TDHead | ((ed->HeadTd) & 0x00000002);
+    ed->TailTd  = (USB_INT32U)TDTail;
+    ed->Next    = 0;
+
+    if (ed == EDCtrl) {
+        LPC_USB->HcControlHeadED = (USB_INT32U)ed;
+        LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_CLF;
+        LPC_USB->HcControl       = LPC_USB->HcControl       | OR_CONTROL_CLE;
+    } else {
+        LPC_USB->HcBulkHeadED    = (USB_INT32U)ed;
+        LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_BLF;
+        LPC_USB->HcControl       = LPC_USB->HcControl       | OR_CONTROL_BLE;
+    }    
+
+    Host_WDHWait();
+
+//    if (!(TDHead->Control & 0xF0000000)) {
+    if (!HOST_TDControlStatus) {
+        return (OK);
+    } else {      
+        return (ERR_TD_FAIL);
+    }
+}
+
+/*
+**************************************************************************************************************
+*                                       ENUMERATE THE DEVICE
+*
+* Description: This function is used to enumerate the device connected
+*
+* Arguments  : None
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  Host_EnumDev (void)
+{
+    USB_INT32S  rc;
+
+    PRINT_Log("Connect a Mass Storage device\n");
+    while (!HOST_RhscIntr)
+        __WFI();
+    Host_DelayMS(100);                             /* USB 2.0 spec says atleast 50ms delay beore port reset */
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
+    while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
+        __WFI(); // Wait for port reset to complete...
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
+    Host_DelayMS(200);                                                 /* Wait for 100 MS after port reset  */
+
+    EDCtrl->Control = 8 << 16;                                         /* Put max pkt size = 8              */
+                                                                       /* Read first 8 bytes of device desc */
+    rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_DEVICE, 0, TDBuffer, 8);
+    if (rc != OK) {
+        PRINT_Err(rc);
+        return (rc);
+    }
+    EDCtrl->Control = TDBuffer[7] << 16;                               /* Get max pkt size of endpoint 0    */
+    rc = HOST_SET_ADDRESS(1);                                          /* Set the device address to 1       */
+    if (rc != OK) {
+        PRINT_Err(rc);
+        return (rc);
+    }
+    Host_DelayMS(2);
+    EDCtrl->Control = (EDCtrl->Control) | 1;                          /* Modify control pipe with address 1 */
+                                                                      /* Get the configuration descriptor   */
+    rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, 9);
+    if (rc != OK) {
+        PRINT_Err(rc);
+        return (rc);
+    }
+                                                                       /* Get the first configuration data  */
+    rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, ReadLE16U(&TDBuffer[2]));
+    if (rc != OK) {
+        PRINT_Err(rc);
+        return (rc);
+    }
+    rc = MS_ParseConfiguration();                                      /* Parse the configuration           */
+    if (rc != OK) {
+        PRINT_Err(rc);
+        return (rc);
+    }
+    rc = USBH_SET_CONFIGURATION(1);                                    /* Select device configuration 1     */
+    if (rc != OK) {
+        PRINT_Err(rc);
+    }
+    Host_DelayMS(100);                                               /* Some devices may require this delay */
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                        RECEIVE THE CONTROL INFORMATION
+*
+* Description: This function is used to receive the control information
+*
+* Arguments  : bm_request_type
+*              b_request
+*              w_value
+*              w_index
+*              w_length
+*              buffer
+*
+* Returns    : OK       if Success
+*              ERROR    if Failed
+*
+**************************************************************************************************************
+*/
+   
+USB_INT32S  Host_CtrlRecv (         USB_INT08U   bm_request_type,
+                                    USB_INT08U   b_request,
+                                    USB_INT16U   w_value,
+                                    USB_INT16U   w_index,
+                                    USB_INT16U   w_length,
+                          volatile  USB_INT08U  *buffer)
+{
+    USB_INT32S  rc;
+
+
+    Host_FillSetup(bm_request_type, b_request, w_value, w_index, w_length);
+    rc = Host_ProcessTD(EDCtrl, TD_SETUP, TDBuffer, 8);
+    if (rc == OK) {
+        if (w_length) {
+            rc = Host_ProcessTD(EDCtrl, TD_IN, TDBuffer, w_length);
+        }
+        if (rc == OK) {
+            rc = Host_ProcessTD(EDCtrl, TD_OUT, NULL, 0);
+        }
+    }
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                         SEND THE CONTROL INFORMATION
+*
+* Description: This function is used to send the control information
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  Host_CtrlSend (          USB_INT08U   bm_request_type,
+                                     USB_INT08U   b_request,
+                                     USB_INT16U   w_value,
+                                     USB_INT16U   w_index,
+                                     USB_INT16U   w_length,
+                           volatile  USB_INT08U  *buffer)
+{
+    USB_INT32S  rc;
+
+
+    Host_FillSetup(bm_request_type, b_request, w_value, w_index, w_length);
+
+    rc = Host_ProcessTD(EDCtrl, TD_SETUP, TDBuffer, 8);
+    if (rc == OK) {
+        if (w_length) {
+            rc = Host_ProcessTD(EDCtrl, TD_OUT, TDBuffer, w_length);
+        }
+        if (rc == OK) {
+            rc = Host_ProcessTD(EDCtrl, TD_IN, NULL, 0);
+        }
+    }
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                          FILL SETUP PACKET
+*
+* Description: This function is used to fill the setup packet
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+
+void  Host_FillSetup (USB_INT08U   bm_request_type,
+                      USB_INT08U   b_request,
+                      USB_INT16U   w_value,
+                      USB_INT16U   w_index,
+                      USB_INT16U   w_length)
+{
+    int i;
+    for (i=0;i<w_length;i++)
+        TDBuffer[i] = 0;
+    
+    TDBuffer[0] = bm_request_type;
+    TDBuffer[1] = b_request;
+    WriteLE16U(&TDBuffer[2], w_value);
+    WriteLE16U(&TDBuffer[4], w_index);
+    WriteLE16U(&TDBuffer[6], w_length);
+}
+
+
+
+/*
+**************************************************************************************************************
+*                                         INITIALIZE THE TRANSFER DESCRIPTOR
+*
+* Description: This function initializes transfer descriptor
+*
+* Arguments  : Pointer to TD structure
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  Host_TDInit (volatile  HCTD *td)
+{
+
+    td->Control    = 0;
+    td->CurrBufPtr = 0;
+    td->Next       = 0;
+    td->BufEnd     = 0;
+}
+
+/*
+**************************************************************************************************************
+*                                         INITIALIZE THE ENDPOINT DESCRIPTOR
+*
+* Description: This function initializes endpoint descriptor
+*
+* Arguments  : Pointer to ED strcuture
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  Host_EDInit (volatile  HCED *ed)
+{
+
+    ed->Control = 0;
+    ed->TailTd  = 0;
+    ed->HeadTd  = 0;
+    ed->Next    = 0;
+}
+
+/*
+**************************************************************************************************************
+*                                 INITIALIZE HOST CONTROLLER COMMUNICATIONS AREA
+*
+* Description: This function initializes host controller communications area
+*
+* Arguments  : Pointer to HCCA
+*
+* Returns    : 
+*
+**************************************************************************************************************
+*/
+
+void  Host_HCCAInit (volatile  HCCA  *hcca)
+{
+    USB_INT32U  i;
+
+
+    for (i = 0; i < 32; i++) {
+
+        hcca->IntTable[i] = 0;
+        hcca->FrameNumber = 0;
+        hcca->DoneHead    = 0;
+    }
+
+}
+
+/*
+**************************************************************************************************************
+*                                         WAIT FOR WDH INTERRUPT
+*
+* Description: This function is infinite loop which breaks when ever a WDH interrupt rises
+*
+* Arguments  : None
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  Host_WDHWait (void)
+{
+  while (!HOST_WdhIntr)
+      __WFI();
+
+  HOST_WdhIntr = 0;
+}
+
+/*
+**************************************************************************************************************
+*                                         READ LE 32U
+*
+* Description: This function is used to read an unsigned integer from a character buffer in the platform
+*              containing little endian processor
+*
+* Arguments  : pmem    Pointer to the character buffer
+*
+* Returns    : val     Unsigned integer
+*
+**************************************************************************************************************
+*/
+
+USB_INT32U  ReadLE32U (volatile  USB_INT08U  *pmem)
+{
+    USB_INT32U val = *(USB_INT32U*)pmem;
+#ifdef __BIG_ENDIAN
+    return __REV(val);
+#else
+    return val;
+#endif    
+}
+
+/*
+**************************************************************************************************************
+*                                        WRITE LE 32U
+*
+* Description: This function is used to write an unsigned integer into a charecter buffer in the platform 
+*              containing little endian processor.
+*
+* Arguments  : pmem    Pointer to the charecter buffer
+*              val     Integer value to be placed in the charecter buffer
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  WriteLE32U (volatile  USB_INT08U  *pmem,
+                            USB_INT32U   val)
+{
+#ifdef __BIG_ENDIAN
+    *(USB_INT32U*)pmem = __REV(val);
+#else
+    *(USB_INT32U*)pmem = val;
+#endif
+}
+
+/*
+**************************************************************************************************************
+*                                          READ LE 16U
+*
+* Description: This function is used to read an unsigned short integer from a charecter buffer in the platform
+*              containing little endian processor
+*
+* Arguments  : pmem    Pointer to the charecter buffer
+*
+* Returns    : val     Unsigned short integer
+*
+**************************************************************************************************************
+*/
+
+USB_INT16U  ReadLE16U (volatile  USB_INT08U  *pmem)
+{
+    USB_INT16U val = *(USB_INT16U*)pmem;
+#ifdef __BIG_ENDIAN
+    return __REV16(val);
+#else
+    return val;
+#endif    
+}
+
+/*
+**************************************************************************************************************
+*                                         WRITE LE 16U
+*
+* Description: This function is used to write an unsigned short integer into a charecter buffer in the
+*              platform containing little endian processor
+*
+* Arguments  : pmem    Pointer to the charecter buffer
+*              val     Value to be placed in the charecter buffer
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  WriteLE16U (volatile  USB_INT08U  *pmem,
+                            USB_INT16U   val)
+{
+#ifdef __BIG_ENDIAN
+    *(USB_INT16U*)pmem = (__REV16(val) & 0xFFFF);
+#else
+    *(USB_INT16U*)pmem = val;
+#endif
+}
+
+/*
+**************************************************************************************************************
+*                                         READ BE 32U
+*
+* Description: This function is used to read an unsigned integer from a charecter buffer in the platform
+*              containing big endian processor
+*
+* Arguments  : pmem    Pointer to the charecter buffer
+*
+* Returns    : val     Unsigned integer
+*
+**************************************************************************************************************
+*/
+
+USB_INT32U  ReadBE32U (volatile  USB_INT08U  *pmem)
+{
+    USB_INT32U val = *(USB_INT32U*)pmem;
+#ifdef __BIG_ENDIAN
+    return val;
+#else
+    return __REV(val);
+#endif
+}
+
+/*
+**************************************************************************************************************
+*                                         WRITE BE 32U
+*
+* Description: This function is used to write an unsigned integer into a charecter buffer in the platform
+*              containing big endian processor
+*
+* Arguments  : pmem    Pointer to the charecter buffer
+*              val     Value to be placed in the charecter buffer
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  WriteBE32U (volatile  USB_INT08U  *pmem,
+                            USB_INT32U   val)
+{
+#ifdef __BIG_ENDIAN
+    *(USB_INT32U*)pmem = val;
+#else
+    *(USB_INT32U*)pmem = __REV(val);
+#endif
+}
+
+/*
+**************************************************************************************************************
+*                                         READ BE 16U
+*
+* Description: This function is used to read an unsigned short integer from a charecter buffer in the platform
+*              containing big endian processor
+*
+* Arguments  : pmem    Pointer to the charecter buffer
+*
+* Returns    : val     Unsigned short integer
+*
+**************************************************************************************************************
+*/
+
+USB_INT16U  ReadBE16U (volatile  USB_INT08U  *pmem)
+{
+    USB_INT16U val = *(USB_INT16U*)pmem;
+#ifdef __BIG_ENDIAN
+    return val;
+#else
+    return __REV16(val);
+#endif    
+}
+
+/*
+**************************************************************************************************************
+*                                         WRITE BE 16U
+*
+* Description: This function is used to write an unsigned short integer into the charecter buffer in the
+*              platform containing big endian processor
+*
+* Arguments  : pmem    Pointer to the charecter buffer
+*              val     Value to be placed in the charecter buffer
+*
+* Returns    : None
+*
+**************************************************************************************************************
+*/
+
+void  WriteBE16U (volatile  USB_INT08U  *pmem,
+                            USB_INT16U   val)
+{
+#ifdef __BIG_ENDIAN
+    *(USB_INT16U*)pmem = val;
+#else
+    *(USB_INT16U*)pmem = (__REV16(val) & 0xFFFF);
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHostLite/usbhost_lpc17xx.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,254 @@
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_lpc17xx.h
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+
+#ifndef USBHOST_LPC17xx_H
+#define USBHOST_LPC17xx_H
+
+/*
+**************************************************************************************************************
+*                                       INCLUDE HEADER FILES
+**************************************************************************************************************
+*/
+
+#include    "usbhost_inc.h"
+
+/*
+**************************************************************************************************************
+*                                        PRINT CONFIGURATION
+**************************************************************************************************************
+*/
+
+#define  PRINT_ENABLE         0
+
+#if PRINT_ENABLE
+#define  PRINT_Log(...)       printf(__VA_ARGS__)
+#define  PRINT_Err(rc)        printf("ERROR: In %s at Line %u - rc = %d\n", __FUNCTION__, __LINE__, rc)
+
+#else 
+#define  PRINT_Log(...)       do {} while(0)
+#define  PRINT_Err(rc)        do {} while(0)
+
+#endif
+
+/*
+**************************************************************************************************************
+*                                        GENERAL DEFINITIONS
+**************************************************************************************************************
+*/
+
+#define  DESC_LENGTH(x)  x[0]
+#define  DESC_TYPE(x)    x[1]
+
+
+#define  HOST_GET_DESCRIPTOR(descType, descIndex, data, length)                      \
+         Host_CtrlRecv(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR,    \
+         (descType << 8)|(descIndex), 0, length, data)
+
+#define  HOST_SET_ADDRESS(new_addr)                                                  \
+         Host_CtrlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_ADDRESS,       \
+         new_addr, 0, 0, NULL)
+
+#define  USBH_SET_CONFIGURATION(configNum)                                           \
+         Host_CtrlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_CONFIGURATION, \
+         configNum, 0, 0, NULL)
+
+#define  USBH_SET_INTERFACE(ifNum, altNum)                                           \
+         Host_CtrlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_INTERFACE, SET_INTERFACE,  \
+         altNum, ifNum, 0, NULL)
+
+/*
+**************************************************************************************************************
+*                                  OHCI OPERATIONAL REGISTER FIELD DEFINITIONS
+**************************************************************************************************************
+*/
+
+                                            /* ------------------ HcControl Register ---------------------  */
+#define  OR_CONTROL_CLE                 0x00000010
+#define  OR_CONTROL_BLE                 0x00000020
+#define  OR_CONTROL_HCFS                0x000000C0
+#define  OR_CONTROL_HC_OPER             0x00000080
+                                            /* ----------------- HcCommandStatus Register ----------------- */
+#define  OR_CMD_STATUS_HCR              0x00000001
+#define  OR_CMD_STATUS_CLF              0x00000002
+#define  OR_CMD_STATUS_BLF              0x00000004
+                                            /* --------------- HcInterruptStatus Register ----------------- */
+#define  OR_INTR_STATUS_WDH             0x00000002
+#define  OR_INTR_STATUS_RHSC            0x00000040
+                                            /* --------------- HcInterruptEnable Register ----------------- */
+#define  OR_INTR_ENABLE_WDH             0x00000002
+#define  OR_INTR_ENABLE_RHSC            0x00000040
+#define  OR_INTR_ENABLE_MIE             0x80000000
+                                            /* ---------------- HcRhDescriptorA Register ------------------ */
+#define  OR_RH_STATUS_LPSC              0x00010000
+#define  OR_RH_STATUS_DRWE              0x00008000
+                                            /* -------------- HcRhPortStatus[1:NDP] Register -------------- */
+#define  OR_RH_PORT_CCS                 0x00000001
+#define  OR_RH_PORT_PRS                 0x00000010
+#define  OR_RH_PORT_CSC                 0x00010000
+#define  OR_RH_PORT_PRSC                0x00100000
+
+
+/*
+**************************************************************************************************************
+*                                               FRAME INTERVAL
+**************************************************************************************************************
+*/
+
+#define  FI                     0x2EDF           /* 12000 bits per frame (-1)                               */
+#define  DEFAULT_FMINTERVAL     ((((6 * (FI - 210)) / 7) << 16) | FI)
+
+/*
+**************************************************************************************************************
+*                                       TRANSFER DESCRIPTOR CONTROL FIELDS
+**************************************************************************************************************
+*/
+
+#define  TD_ROUNDING        (USB_INT32U) (0x00040000)        /* Buffer Rounding                             */
+#define  TD_SETUP           (USB_INT32U)(0)                  /* Direction of Setup Packet                   */
+#define  TD_IN              (USB_INT32U)(0x00100000)         /* Direction In                                */
+#define  TD_OUT             (USB_INT32U)(0x00080000)         /* Direction Out                               */
+#define  TD_DELAY_INT(x)    (USB_INT32U)((x) << 21)          /* Delay Interrupt                             */
+#define  TD_TOGGLE_0        (USB_INT32U)(0x02000000)         /* Toggle 0                                    */
+#define  TD_TOGGLE_1        (USB_INT32U)(0x03000000)         /* Toggle 1                                    */
+#define  TD_CC              (USB_INT32U)(0xF0000000)         /* Completion Code                             */
+
+/*
+**************************************************************************************************************
+*                                       USB STANDARD REQUEST DEFINITIONS
+**************************************************************************************************************
+*/
+
+#define  USB_DESCRIPTOR_TYPE_DEVICE                     1
+#define  USB_DESCRIPTOR_TYPE_CONFIGURATION              2
+#define  USB_DESCRIPTOR_TYPE_INTERFACE                  4
+#define  USB_DESCRIPTOR_TYPE_ENDPOINT                   5
+                                                    /*  ----------- Control RequestType Fields  ----------- */
+#define  USB_DEVICE_TO_HOST         0x80
+#define  USB_HOST_TO_DEVICE         0x00
+#define  USB_REQUEST_TYPE_CLASS     0x20
+#define  USB_RECIPIENT_DEVICE       0x00
+#define  USB_RECIPIENT_INTERFACE    0x01
+                                                    /* -------------- USB Standard Requests  -------------- */
+#define  SET_ADDRESS                 5
+#define  GET_DESCRIPTOR              6
+#define  SET_CONFIGURATION           9
+#define  SET_INTERFACE              11
+
+/*
+**************************************************************************************************************
+*                                       TYPE DEFINITIONS
+**************************************************************************************************************
+*/
+
+typedef struct hcEd {                       /* ----------- HostController EndPoint Descriptor ------------- */
+    volatile  USB_INT32U  Control;              /* Endpoint descriptor control                              */
+    volatile  USB_INT32U  TailTd;               /* Physical address of tail in Transfer descriptor list     */
+    volatile  USB_INT32U  HeadTd;               /* Physcial address of head in Transfer descriptor list     */
+    volatile  USB_INT32U  Next;                 /* Physical address of next Endpoint descriptor             */
+} HCED;
+
+typedef struct hcTd {                       /* ------------ HostController Transfer Descriptor ------------ */
+    volatile  USB_INT32U  Control;              /* Transfer descriptor control                              */
+    volatile  USB_INT32U  CurrBufPtr;           /* Physical address of current buffer pointer               */
+    volatile  USB_INT32U  Next;                 /* Physical pointer to next Transfer Descriptor             */
+    volatile  USB_INT32U  BufEnd;               /* Physical address of end of buffer                        */
+} HCTD;
+
+typedef struct hcca {                       /* ----------- Host Controller Communication Area ------------  */
+    volatile  USB_INT32U  IntTable[32];         /* Interrupt Table                                          */
+    volatile  USB_INT32U  FrameNumber;          /* Frame Number                                             */
+    volatile  USB_INT32U  DoneHead;             /* Done Head                                                */
+    volatile  USB_INT08U  Reserved[116];        /* Reserved for future use                                  */
+    volatile  USB_INT08U  Unknown[4];           /* Unused                                                   */
+} HCCA;
+
+/*
+**************************************************************************************************************
+*                                     EXTERN DECLARATIONS
+**************************************************************************************************************
+*/
+
+extern  volatile  HCED        *EDBulkIn;        /* BulkIn endpoint descriptor  structure                    */
+extern  volatile  HCED        *EDBulkOut;       /* BulkOut endpoint descriptor structure                    */
+extern  volatile  HCTD        *TDHead;          /* Head transfer descriptor structure                       */
+extern  volatile  HCTD        *TDTail;          /* Tail transfer descriptor structure                       */
+extern  volatile  USB_INT08U  *TDBuffer;        /* Current Buffer Pointer of transfer descriptor            */
+
+/*
+**************************************************************************************************************
+*                                       FUNCTION PROTOTYPES
+**************************************************************************************************************
+*/
+
+void        Host_Init     (void);
+
+extern "C" void USB_IRQHandler(void)  __irq;
+
+USB_INT32S  Host_EnumDev  (void);
+
+USB_INT32S  Host_ProcessTD(volatile  HCED       *ed,
+                           volatile  USB_INT32U  token,
+                           volatile  USB_INT08U *buffer,
+                                     USB_INT32U  buffer_len);
+
+void        Host_DelayUS  (          USB_INT32U    delay);
+void        Host_DelayMS  (          USB_INT32U    delay);
+
+
+void        Host_TDInit   (volatile  HCTD *td);
+void        Host_EDInit   (volatile  HCED *ed);
+void        Host_HCCAInit (volatile  HCCA  *hcca);
+
+USB_INT32S  Host_CtrlRecv (          USB_INT08U   bm_request_type,
+                                     USB_INT08U   b_request,
+                                     USB_INT16U   w_value,
+                                     USB_INT16U   w_index,
+                                     USB_INT16U   w_length,
+                           volatile  USB_INT08U  *buffer);
+
+USB_INT32S  Host_CtrlSend (          USB_INT08U   bm_request_type,
+                                     USB_INT08U   b_request,
+                                     USB_INT16U   w_value,
+                                     USB_INT16U   w_index,
+                                     USB_INT16U   w_length,
+                           volatile  USB_INT08U  *buffer);
+
+void        Host_FillSetup(          USB_INT08U   bm_request_type,
+                                     USB_INT08U   b_request,
+                                     USB_INT16U   w_value,
+                                     USB_INT16U   w_index,
+                                     USB_INT16U   w_length);
+
+
+void        Host_WDHWait  (void);
+
+
+USB_INT32U  ReadLE32U     (volatile  USB_INT08U  *pmem);
+void        WriteLE32U    (volatile  USB_INT08U  *pmem,
+                                     USB_INT32U   val);
+USB_INT16U  ReadLE16U     (volatile  USB_INT08U  *pmem);
+void        WriteLE16U    (volatile  USB_INT08U  *pmem,
+                                     USB_INT16U   val);
+USB_INT32U  ReadBE32U     (volatile  USB_INT08U  *pmem);
+void        WriteBE32U    (volatile  USB_INT08U  *pmem,
+                                     USB_INT32U   val);
+USB_INT16U  ReadBE16U     (volatile  USB_INT08U  *pmem);
+void        WriteBE16U    (volatile  USB_INT08U  *pmem,
+                                     USB_INT16U   val);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHostLite/usbhost_ms.c	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,455 @@
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_ms.c
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+
+/*
+**************************************************************************************************************
+*                                       INCLUDE HEADER FILES
+**************************************************************************************************************
+*/
+
+#include  "usbhost_ms.h"
+
+/*
+**************************************************************************************************************
+*                                         GLOBAL VARIABLES
+**************************************************************************************************************
+*/
+
+USB_INT32U  MS_BlkSize;
+
+/*
+**************************************************************************************************************
+*                                      INITIALIZE MASS STORAGE INTERFACE
+*
+* Description: This function initializes the mass storage interface
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S MS_Init (USB_INT32U *blkSize, USB_INT32U *numBlks, USB_INT08U *inquiryResult)
+{
+    USB_INT08U  retry;
+    USB_INT32S  rc;
+
+    MS_GetMaxLUN();                                                    /* Get maximum logical unit number   */
+    retry  = 80;
+    while(retry) {
+        rc = MS_TestUnitReady();                                       /* Test whether the unit is ready    */
+        if (rc == OK) {
+            break;
+        }
+        MS_GetSenseInfo();                                             /* Get sense information             */
+        retry--;
+    }
+    if (rc != OK) {
+        PRINT_Err(rc);
+        return (rc);
+    }
+    rc = MS_ReadCapacity(numBlks, blkSize);                         /* Read capacity of the disk         */
+    MS_BlkSize = *blkSize;                        // Set global
+    rc = MS_Inquire (inquiryResult);
+    return (rc);
+}
+/*
+**************************************************************************************************************
+*                                         PARSE THE CONFIGURATION
+*
+* Description: This function is used to parse the configuration
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  MS_ParseConfiguration (void)
+{
+    volatile  USB_INT08U  *desc_ptr;
+              USB_INT08U   ms_int_found;
+
+
+    desc_ptr     = TDBuffer;
+    ms_int_found = 0;
+
+    if (desc_ptr[1] != USB_DESCRIPTOR_TYPE_CONFIGURATION) {    
+        return (ERR_BAD_CONFIGURATION);
+    }
+    desc_ptr += desc_ptr[0];
+
+    while (desc_ptr != TDBuffer + ReadLE16U(&TDBuffer[2])) {
+
+        switch (desc_ptr[1]) {
+
+            case USB_DESCRIPTOR_TYPE_INTERFACE:                       /* If it is an interface descriptor   */
+                 if (desc_ptr[5] == MASS_STORAGE_CLASS &&             /* check if the class is mass storage */
+                     desc_ptr[6] == MASS_STORAGE_SUBCLASS_SCSI &&     /* check if the subclass is SCSI      */
+                     desc_ptr[7] == MASS_STORAGE_PROTOCOL_BO) {       /* check if the protocol is Bulk only */
+                     ms_int_found = 1;
+                     desc_ptr    += desc_ptr[0];                      /* Move to next descriptor start      */
+                 }
+                 break;
+
+            case USB_DESCRIPTOR_TYPE_ENDPOINT:                        /* If it is an endpoint descriptor    */
+                 if ((desc_ptr[3] & 0x03) == 0x02) {                  /* If it is Bulk endpoint             */
+                     if (desc_ptr[2] & 0x80) {                        /* If it is In endpoint               */
+                         EDBulkIn->Control =  1                             |      /* USB address           */
+                                              ((desc_ptr[2] & 0x7F) << 7)   |      /* Endpoint address      */
+                                              (2 << 11)                     |      /* direction             */
+                                              (ReadLE16U(&desc_ptr[4]) << 16);     /* MaxPkt Size           */
+                         desc_ptr += desc_ptr[0];                     /* Move to next descriptor start      */
+                     } else {                                         /* If it is Out endpoint              */
+                         EDBulkOut->Control = 1                             |      /* USB address           */
+                                              ((desc_ptr[2] & 0x7F) << 7)   |      /* Endpoint address      */
+                                              (1 << 11)                     |      /* direction             */
+                                              (ReadLE16U(&desc_ptr[4]) << 16);     /* MaxPkt Size           */
+                         desc_ptr += desc_ptr[0];                     /* Move to next descriptor start      */
+                     }
+                 } else {                                             /* If it is not bulk end point        */
+                     desc_ptr += desc_ptr[0];                         /* Move to next descriptor start      */
+                 }
+                 break;
+
+            default:                                 /* If the descriptor is neither interface nor endpoint */
+                 desc_ptr += desc_ptr[0];                             /* Move to next descriptor start      */
+                 break;
+        }
+    }
+    if (ms_int_found) {
+        PRINT_Log("Mass Storage device connected\n");
+        return (OK);
+    } else {
+        PRINT_Log("Not a Mass Storage device\n");
+        return (ERR_NO_MS_INTERFACE);
+    }
+}
+
+/*
+**************************************************************************************************************
+*                                         GET MAXIMUM LOGICAL UNIT
+*
+* Description: This function returns the maximum logical unit from the device
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  MS_GetMaxLUN (void)
+{
+    USB_INT32S  rc;
+
+
+    rc = Host_CtrlRecv(USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE,
+                       MS_GET_MAX_LUN_REQ,
+                       0,
+                       0,
+                       1,
+                       TDBuffer);
+    return (rc); 
+}
+
+/*
+**************************************************************************************************************
+*                                          GET SENSE INFORMATION
+*
+* Description: This function is used to get sense information from the device
+*
+* Arguments  : None
+*
+* Returns    : OK       if Success
+*              ERROR    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  MS_GetSenseInfo (void)
+{
+    USB_INT32S  rc;
+
+
+    Fill_MSCommand(0, 0, 0, MS_DATA_DIR_IN, SCSI_CMD_REQUEST_SENSE, 6);
+    rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE);
+    if (rc == OK) {
+        rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, 18);
+        if (rc == OK) {
+            rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE);
+            if (rc == OK) {
+                if (TDBuffer[12] != 0) {
+                    rc = ERR_MS_CMD_FAILED;
+                }
+            }
+        }
+    }
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                           TEST UNIT READY
+*
+* Description: This function is used to test whether the unit is ready or not
+*
+* Arguments  : None
+*
+* Returns    : OK       if Success
+*              ERROR    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  MS_TestUnitReady (void)
+{
+    USB_INT32S  rc;
+
+
+    Fill_MSCommand(0, 0, 0, MS_DATA_DIR_NONE, SCSI_CMD_TEST_UNIT_READY, 6);
+    rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE);
+    if (rc == OK) {
+        rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE);
+        if (rc == OK) {        
+            if (TDBuffer[12] != 0) {
+                rc = ERR_MS_CMD_FAILED;
+            }
+        }
+    }
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                            READ CAPACITY
+*
+* Description: This function is used to read the capacity of the mass storage device
+*
+* Arguments  : None
+*
+* Returns    : OK       if Success
+*              ERROR    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S MS_ReadCapacity (USB_INT32U *numBlks, USB_INT32U *blkSize)
+{
+    USB_INT32S  rc;
+
+
+    Fill_MSCommand(0, 0, 0, MS_DATA_DIR_IN, SCSI_CMD_READ_CAPACITY, 10);
+    rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE);
+    if (rc == OK) {
+        rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, 8);
+        if (rc == OK) {
+            if (numBlks)
+                *numBlks = ReadBE32U(&TDBuffer[0]);
+            if (blkSize)
+                *blkSize = ReadBE32U(&TDBuffer[4]);
+            rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE);
+            if (rc == OK) {
+                if (TDBuffer[12] != 0) {
+                    rc = ERR_MS_CMD_FAILED;
+                }
+            }
+        }
+    }
+    return (rc);
+}
+
+
+
+USB_INT32S MS_Inquire (USB_INT08U *response)
+{
+    USB_INT32S rc;
+    USB_INT32U i;
+
+    Fill_MSCommand(0, 0, 0, MS_DATA_DIR_IN, SCSI_CMD_INQUIRY, 6);
+    rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE);
+    if (rc == OK) {
+        rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, INQUIRY_LENGTH);
+        if (rc == OK) {
+            if (response) {
+                for ( i = 0; i < INQUIRY_LENGTH; i++ )
+                    *response++ = *TDBuffer++;
+#if 0
+                MemCpy (response, TDBuffer, INQUIRY_LENGTH);
+                StrNullTrailingSpace (response->vendorID, SCSI_INQUIRY_VENDORCHARS);
+                StrNullTrailingSpace (response->productID, SCSI_INQUIRY_PRODUCTCHARS);
+                StrNullTrailingSpace (response->productRev, SCSI_INQUIRY_REVCHARS);
+#endif
+            }
+            rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE);
+            if (rc == OK) {
+                if (TDBuffer[12] != 0) {    // bCSWStatus byte
+                    rc = ERR_MS_CMD_FAILED;
+                }
+            }
+        }
+    }
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                         RECEIVE THE BULK DATA
+*
+* Description: This function is used to receive the bulk data
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+    
+USB_INT32S  MS_BulkRecv (          USB_INT32U   block_number,
+                                   USB_INT16U   num_blocks,
+                         volatile  USB_INT08U  *user_buffer)
+{
+    USB_INT32S  rc;
+    int i;
+    volatile USB_INT08U *c = user_buffer;
+    for (i=0;i<MS_BlkSize*num_blocks;i++)
+        *c++ = 0;
+
+
+    Fill_MSCommand(block_number, MS_BlkSize, num_blocks, MS_DATA_DIR_IN, SCSI_CMD_READ_10, 10);
+
+    rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE);
+    if (rc == OK) {
+        rc = Host_ProcessTD(EDBulkIn, TD_IN, user_buffer, MS_BlkSize * num_blocks);
+        if (rc == OK) {
+            rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE);
+            if (rc == OK) {
+                if (TDBuffer[12] != 0) {
+                    rc = ERR_MS_CMD_FAILED;
+                }
+            }
+        }
+    }
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                         SEND BULK DATA
+*
+* Description: This function is used to send the bulk data
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+
+USB_INT32S  MS_BulkSend (          USB_INT32U   block_number,
+                                   USB_INT16U   num_blocks,
+                         volatile  USB_INT08U  *user_buffer)
+{
+    USB_INT32S  rc;
+
+
+    Fill_MSCommand(block_number, MS_BlkSize, num_blocks, MS_DATA_DIR_OUT, SCSI_CMD_WRITE_10, 10);
+
+    rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE);
+    if (rc == OK) {
+        rc = Host_ProcessTD(EDBulkOut, TD_OUT, user_buffer, MS_BlkSize * num_blocks);
+        if (rc == OK) {
+            rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE);
+            if (rc == OK) {
+                if (TDBuffer[12] != 0) {
+                    rc = ERR_MS_CMD_FAILED;
+                }
+            }
+        }
+    }
+    return (rc);
+}
+
+/*
+**************************************************************************************************************
+*                                         FILL MASS STORAGE COMMAND
+*
+* Description: This function is used to fill the mass storage command
+*
+* Arguments  : None
+*
+* Returns    : OK                      if Success
+*              ERR_INVALID_BOOTSIG    if Failed
+*
+**************************************************************************************************************
+*/
+
+void  Fill_MSCommand (USB_INT32U   block_number,
+                      USB_INT32U   block_size,
+                      USB_INT16U   num_blocks,
+                      MS_DATA_DIR  direction,
+                      USB_INT08U   scsi_cmd,
+                      USB_INT08U   scsi_cmd_len)
+{
+            USB_INT32U  data_len;
+    static  USB_INT32U  tag_cnt = 0;
+            USB_INT32U  cnt;
+
+
+    for (cnt = 0; cnt < CBW_SIZE; cnt++) {
+         TDBuffer[cnt] = 0;
+    }
+    switch(scsi_cmd) {
+
+        case SCSI_CMD_TEST_UNIT_READY:
+             data_len = 0;
+             break;
+        case SCSI_CMD_READ_CAPACITY:
+             data_len = 8;
+             break;
+        case SCSI_CMD_REQUEST_SENSE:
+             data_len = 18;
+             break;
+        case SCSI_CMD_INQUIRY:
+             data_len = 36;
+             break;
+        default:
+             data_len = block_size * num_blocks;
+             break;
+    }
+    WriteLE32U(TDBuffer, CBW_SIGNATURE);
+    WriteLE32U(&TDBuffer[4], tag_cnt);
+    WriteLE32U(&TDBuffer[8], data_len);
+    TDBuffer[12]     = (direction == MS_DATA_DIR_NONE) ? 0 : direction;
+    TDBuffer[14]     = scsi_cmd_len;                                   /* Length of the CBW                 */
+    TDBuffer[15]     = scsi_cmd;
+    if ((scsi_cmd     == SCSI_CMD_REQUEST_SENSE)
+     || (scsi_cmd     == SCSI_CMD_INQUIRY)) {
+        TDBuffer[19] = (USB_INT08U)data_len;
+    } else {
+        WriteBE32U(&TDBuffer[17], block_number);
+    }
+    WriteBE16U(&TDBuffer[22], num_blocks);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHostLite/usbhost_ms.h	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,101 @@
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_ms.h
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+
+#ifndef  USBHOST_MS_H
+#define  USBHOST_MS_H
+
+/*
+**************************************************************************************************************
+*                                       INCLUDE HEADER FILES
+**************************************************************************************************************
+*/
+
+#include  "usbhost_inc.h"
+
+/*
+**************************************************************************************************************
+*                               MASS STORAGE SPECIFIC DEFINITIONS
+**************************************************************************************************************
+*/
+
+#define    MS_GET_MAX_LUN_REQ            0xFE
+#define    MASS_STORAGE_CLASS            0x08
+#define    MASS_STORAGE_SUBCLASS_SCSI    0x06
+#define    MASS_STORAGE_PROTOCOL_BO      0x50
+
+#define    INQUIRY_LENGTH                36
+/*
+**************************************************************************************************************
+*                                  SCSI SPECIFIC DEFINITIONS
+**************************************************************************************************************
+*/
+
+#define  CBW_SIGNATURE               0x43425355
+#define  CSW_SIGNATURE               0x53425355
+#define  CBW_SIZE                      31
+#define  CSW_SIZE                      13
+#define  CSW_CMD_PASSED              0x00
+#define  SCSI_CMD_REQUEST_SENSE      0x03
+#define  SCSI_CMD_TEST_UNIT_READY    0x00
+#define  SCSI_CMD_INQUIRY            0x12
+#define  SCSI_CMD_READ_10            0x28
+#define  SCSI_CMD_READ_CAPACITY      0x25
+#define  SCSI_CMD_WRITE_10           0x2A
+
+/*
+**************************************************************************************************************
+*                                       TYPE DEFINITIONS
+**************************************************************************************************************
+*/
+
+typedef enum  ms_data_dir {
+
+    MS_DATA_DIR_IN     = 0x80,
+    MS_DATA_DIR_OUT    = 0x00,
+    MS_DATA_DIR_NONE   = 0x01
+
+} MS_DATA_DIR;
+
+/*
+**************************************************************************************************************
+*                                     FUNCTION PROTOTYPES
+**************************************************************************************************************
+*/
+
+USB_INT32S  MS_BulkRecv          (          USB_INT32U    block_number,
+                                            USB_INT16U    num_blocks,
+                                  volatile  USB_INT08U   *user_buffer);
+
+USB_INT32S  MS_BulkSend          (          USB_INT32U    block_number,
+                                            USB_INT16U    num_blocks,
+                                  volatile  USB_INT08U   *user_buffer);
+USB_INT32S  MS_ParseConfiguration(void);
+USB_INT32S  MS_TestUnitReady     (void);
+USB_INT32S  MS_ReadCapacity (USB_INT32U *numBlks, USB_INT32U *blkSize);
+USB_INT32S  MS_GetMaxLUN         (void);
+USB_INT32S  MS_GetSenseInfo      (void);
+USB_INT32S  MS_Init (USB_INT32U *blkSize, USB_INT32U *numBlks, USB_INT08U *inquiryResult);
+USB_INT32S  MS_Inquire (USB_INT08U *response);
+
+void        Fill_MSCommand       (          USB_INT32U    block_number,
+                                            USB_INT32U    block_size,
+                                            USB_INT16U    num_blocks,
+                                            MS_DATA_DIR   direction,
+                                            USB_INT08U    scsi_cmd,
+                                            USB_INT08U    scsi_cmd_len);
+#endif
--- a/common.h	Wed Apr 10 13:31:35 2013 +0000
+++ b/common.h	Sun Jul 21 23:59:00 2013 +0000
@@ -11,19 +11,28 @@
 #define changedScreen 10
 #define playbackScreen 11
 #define dateScreen 12
-#define config1Screen 13
+#define configScreen 13
 #define indexScreen 14
-#define maxScreens 14
+#define tripScreen 15
+#define healthScreen 16
+#define maxScreens 16
 
 #define btnGap 10
 #define ttSkin 0
 #define ggSkin 1
+#define maxSkin 1
 
-#define maxBufLen 512
+#define maxBufLen 1024
 #define canTimeout 5
 #define userTimeout 15
 
-// gg - 4x4 touch
-// #define tNavRow = 2 ; // for 3x3
-extern unsigned char tNavRow ; // gg - 4x4
+#define BatDataBaseG1 0x00 // 6 frames - SOH, SOC, Ah
+#define BatDataBaseG2 0x06 // 29 frames - Cell Pair data
+#define BatDataBaseG3 0x23 // 5 frames
+#define BatDataBaseG4 0x28 // 3 frames - Temperature data
+#define BatDataBaseG5 0x2B // 11 frames
+#define BatDataBaseG6 0x36 // 4 frames
+#define BatDataBaseG7 0x3A
+#define BatDataBufMax 0x196 // 7 x 3A bytes
 
+#define VP230Sleep 0 // Set to 0 if using VP231 (sleep disables RX)
\ No newline at end of file
--- a/displayModes.cpp	Wed Apr 10 13:31:35 2013 +0000
+++ b/displayModes.cpp	Sun Jul 21 23:59:00 2013 +0000
@@ -4,6 +4,163 @@
 char sTemp1[40];
 char sTemp2[16];
 
+void mainDisplay (bool force, bool showButtons){
+    unsigned short gids, SOC_x10, packV_x2, tireP;
+    static unsigned short lgids=0, lSOC=0, lSOH=0, lpackV_x2=0, ltireP=0, maxPS=0;
+    static float lmaxTemp=0;
+    static float lkW=0, laccV=0, lmpkWh=0;
+    static unsigned long lAh=0;
+    CANMessage msg;
+
+    msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids
+    gids = (msg.data[0]<<2)+(msg.data[1]>>6);
+    msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC
+    SOC_x10 = (msg.data[0]<<2)+(msg.data[1]>>6);
+    msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts
+    packV_x2 = (msg.data[2]<<2)+(msg.data[3]>>6);
+    msg = lastMsg[indexLastMsg[0x385]]; //Get tire pressure
+    tireP = msg.data[2]+msg.data[3]+msg.data[4]+msg.data[5];
+
+    tt.background(Navy);
+    tt.foreground(Yellow);
+    tt.set_font((unsigned char*) Arial28x28);
+    if(force) tt.cls();
+    if(skin==ttSkin){
+        if(force||gids!=lgids||mpkWh[dtePeriod]!=lmpkWh){
+            tt.locate(10,10);
+            printf("%4d gids \n",gids);
+            if(debugMode){
+                if(pointerSep>maxPS){maxPS=pointerSep;}
+                tt.locate(10,70);
+                printf("%3d sep %3d max\n",pointerSep,maxPS);
+            }
+            tt.locate(10,40);
+            printf("%4.1f kWh \n",(float)(gids-5)*0.075);
+            tt.set_font((unsigned char*) SCProSB31x55);
+            tt.foreground(Green);
+            tt.locate(60,96);            
+            printf("%4.1f %s  \n",convertDistance(mpkWh[dtePeriod]*((float)(gids-5)*.075)),distanceUnit()); //LM - add metric conversion
+            lgids=gids;
+            lmpkWh=mpkWh[dtePeriod];
+            tt.foreground(Yellow);
+            tt.set_font((unsigned char*) Arial28x28);
+        }
+        if(force||tireP!=ltireP){
+            tt.foreground(LightGrey);
+            if(msg.data[6]&0x80){
+                tt.locate(40,150);
+                printf("%3.1f\n",(float)msg.data[2]/4);
+            }
+            if(msg.data[6]&0x40){
+                tt.locate(114,150);
+                printf("%3.1f\n",(float)msg.data[3]/4);
+            }
+            if(msg.data[6]&0x20){
+                tt.locate(40,178);
+                printf("%3.1f\n",(float)msg.data[4]/4);
+            }
+            if(msg.data[6]&0x10){
+                tt.locate(114,178);
+                printf("%3.1f\n",(float)msg.data[5]/4);
+            }
+            tt.foreground(Yellow);
+            ltireP=tireP;
+        }
+        if(force||SOC_x10!=lSOC){
+            tt.locate(200,10);
+            printf("%4.1f%s\n",(float)SOC_x10/10,"% ");
+            lSOC=SOC_x10;
+        }
+        if(force||packV_x2!=lpackV_x2){
+            tt.locate(200,206);
+            printf("%4.1fV \n",(float)packV_x2/2);
+            lpackV_x2=packV_x2;
+        }
+        if(force||maxTemp!=lmaxTemp){
+            tt.locate(200,176);
+            printf("%4.1f%s\n",convertTemperature(maxTemp),temperatureUnit());
+            lmaxTemp=maxTemp;
+        }
+        if(force||accV!=laccV){
+            tt.locate(20,206);
+            printf("%3.1fV  \n",accV);
+            laccV=accV;
+        }
+        if(force||kW[0]!=lkW){
+            if(kW[0]<-10){ //Right justify
+                tt.locate(171,40);
+                printf("%4.2fkW\n",kW[0]);
+            } else if (kW[0]<0){
+                tt.locate(171,40);
+                printf(" %4.2fkW\n",kW[0]);
+            } else if (kW[0]<10){
+                tt.locate(165,40);
+                printf("  %4.2fkW\n",kW[0]);
+            } else {
+                tt.locate(165,40);
+                printf(" %4.2fkW\n",kW[0]);
+            }
+            lkW=kW[0];
+        }
+        tt.foreground(LightGrey);
+        if(force||Ah_x10000!=lAh){
+            tt.locate(10,70);
+            printf("% 4.2fAh \n",(float)Ah_x10000/10000);
+            lAh=Ah_x10000;
+        }
+        if(force||SOH_x100!=lSOH){
+            tt.locate(206,70);
+            printf("%4.1f%s\n",(float)SOH_x100/100,"% ");
+            lSOH=SOH_x100;
+        }
+    }else {//if(skin==ggSkin){
+        if(force||gids!=lgids){
+            tt.locate(10,10);
+            printf("%4d GIDs \n",gids);
+            
+            tt.locate(40,40); // gg - add GIDs Percent of 281
+            printf("%4.1f%s \n", (float)gids*0.355872, "% ") ;
+            tt.locate(20,70);
+            printf("%4.1f kwh  \n",(float)gids*0.075); // gg - closer to usable
+            
+            tt.set_font((unsigned char*) SCProSB31x55);
+            tt.foreground(Green);
+            //tt.locate(60,96);
+            tt.locate(60,116); // gg - move down a little
+            printf("%4.1f %s  \n",convertDistance((float)(gids-5)*0.31),distanceUnit()); // Approx for now - LM added metric
+            lgids=gids;
+            tt.foreground(Yellow);
+            tt.set_font((unsigned char*) Arial28x28);
+        }
+        
+        if(force||SOC_x10!=lSOC){
+            tt.locate(200,10);
+            printf("%4.1f%s\n",(float)SOC_x10/10,"% ");
+            lSOC=SOC_x10;
+        }
+        if(force||packV_x2!=lpackV_x2){
+            tt.locate(200,200);
+            printf("%4.1fV \n",(float)packV_x2/2);
+            lpackV_x2=packV_x2;
+        }
+        if(force||accV!=laccV){
+            tt.locate(20,200);
+            printf("%3.1fV  \n",accV);
+            laccV=accV;
+        }
+        if(force||kW[0]!=lkW){
+            tt.locate(160,40); // gg - move left to keep from wrap
+            printf("%3.2fkw \n",kW[0]); // use small w to save space
+            lkW=kW[0];
+        }
+    }
+    if(led4){
+        tt.fillcircle(310,10,6,Red);
+    }else{
+        tt.fillcircle(310,10,6,Navy);
+    }
+}
+
 void printLast (bool force, bool showButtons){
     CANMessage msg;
     tt.locate(0,6);
@@ -15,7 +172,7 @@
         msg = lastMsg[i+indexOffset];
         printf("%03x : %02x %02x %02x %02x %02x %02x %02x %02x    \n",msg.id,msg.data[0],msg.data[1],msg.data[2],msg.data[3],msg.data[4],msg.data[5],msg.data[6],msg.data[7]);
     }
-    if((sMode==1)&&showButtons){
+    if(showButtons){
         tt.foreground(Yellow);
         tt.background(DarkCyan);
         tt.set_font((unsigned char*) Arial12x12);
@@ -43,11 +200,10 @@
             i++;
         }// if changed
     }while(i<19&&j<99);
-    if((sMode==1)&&showButtons){
+    if(showButtons){
         tt.foreground(Yellow);
         tt.background(DarkCyan);
         tt.set_font((unsigned char*) Arial12x12);
-        
         showButton(0,0,"  <up>","",4,4);
         showButton(2,0," <down>","",4,4);
         showButton(1,0," Reset","Baseline",4,4);
@@ -55,121 +211,124 @@
 }
 
 void printLog (bool force, bool showButtons){
-    static unsigned char lastDisplayLoc = 0;
-    if(force||displayLoc!=lastDisplayLoc){ //only update if changed
+    static unsigned char lastldl = 0;
+    unsigned char ldl=displayLoc;
+    if(force||ldl!=lastldl){ //only update if changed
         tt.foreground(Amber);
         tt.background(Black);
         tt.cls();
         tt.locate(0,6);
         tt.set_font((unsigned char*) Arial12x12);
         for(int i=0; i<19; i++){
-            printf("%s",displayLog[displayLoc]);
-            displayLoc=displayLoc>17?0:displayLoc+1;
+            printf("%s",displayLog[ldl]);
+            ldl=ldl>17?0:ldl+1;
         }
     }
-    lastDisplayLoc=displayLoc;
+    lastldl=ldl;
 }
 
-void mainDisplay (bool force, bool showButtons){
-    unsigned short gids, SOC, packV;
-    static unsigned short lgids=0, lSOC=0, lpackV=0;
-    static float lkW=0, laccV=0, lmpkWh=0;
+void tripDisplay (bool force, bool showButtons){
+    static float lkWh=0;
+    tt.background(White);
+    if(force){
+        tt.cls();
+    }
+    if(force||(int)(lkWh*100)!=(int)(kWh_trip[0]*100)){ //only update if changed
+        tt.foreground(Navy);
+        tt.set_font((unsigned char*) Arial28x28);
+        tt.locate(6,210);
+        printf("kWh : %s : Eff\n",distanceUnit());
+        for(int i=0; i<3; i++){
+            tt.locate(6,20+i*60);
+            printf("%3.2f : %3.1f : %2.1f\n",kWh_trip[i],convertDistance(miles_trip[i]),convertDistance(miles_trip[i])/kWh_trip[i]);
+        }
+    }
+    if(showButtons){
+        tt.set_font((unsigned char*) Arial12x12);
+        tt.background(DarkCyan);
+        tt.foreground(Yellow);
+        showButton(3,1," Reset","   A",4,4);
+        showButton(3,2," Reset","   B",4,4);
+        lkWh=kWh_trip[0];
+    }
+}
+
+void healthDisplay (bool force, bool showButtons){
+    unsigned short gids, SOC_x10;
+    static unsigned short lgids=0, lSOC=0, lSOH=0;
+    static float lmaxTemp=0, lresr=0, lunlV=0;
+    static unsigned long lAh=0;
     CANMessage msg;
 
     msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids
     gids = (msg.data[0]<<2)+(msg.data[1]>>6);
     msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC
-    SOC = (msg.data[0]<<2)+(msg.data[1]>>6);
-    msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts
-    packV = (msg.data[2]<<2)+(msg.data[3]>>6);
+    SOC_x10 = (msg.data[0]<<2)+(msg.data[1]>>6);
 
-    tt.background(Navy);
+    tt.background(Blue);
     tt.foreground(Yellow);
     tt.set_font((unsigned char*) Arial28x28);
     if(force) tt.cls();
-    if(skin==ttSkin){
-        if(force||gids!=lgids||mpkWh[dtePeriod]!=lmpkWh){
-            tt.locate(10,10);
-            printf("%4d gids \n",gids);
-            tt.locate(10,40);
-            printf("%4.1f kWh \n",(float)(gids-5)*0.075);
-            tt.set_font((unsigned char*) SCProSB31x55);
-            tt.foreground(Green);
-            tt.locate(60,96);
-            printf("%4.1f mi  \n",mpkWh[dtePeriod]*((float)(gids-5)*.075));
-            lgids=gids;
-            lmpkWh=mpkWh[dtePeriod];
-            tt.foreground(Yellow);
-            tt.set_font((unsigned char*) Arial28x28);
-        }
-        if(force||SOC!=lSOC){
-            tt.locate(200,10);
-            printf("%4.1f%s\n",(float)SOC/10,"% ");
-            lSOC=SOC;
-        }
-        if(force||packV!=lpackV){
-            tt.locate(200,200);
-            printf("%4.1fV \n",(float)packV/2);
-            lpackV=packV;
-        }
-        if(force||accV!=laccV){
-            tt.locate(20,200);
-            printf("%3.1fV  \n",accV);
-            laccV=accV;
-        }
-        if(force||kW[0]!=lkW){
-            tt.locate(180,40);
-            printf("%3.2fkW  \n",kW[0]);
-            //printf("%3.1f mpkWh  \n",mpkWh[0]);
-            lkW=kW[0];
-        }
-    }else {//if(skin==ggSkin){
-        if(force||gids!=lgids){
-            tt.locate(10,10);
-            printf("%4d GIDs \n",gids);
-            
-            tt.locate(40,40); // gg - add GIDs Percent of 281
-            printf("%4.1f%s \n", (float)gids*0.355872, "% ") ;
-            tt.locate(20,70);
-            //printf("%4.1f kWh  \n",(float)gids*0.08); // is input, not usable
-            printf("%4.1f kwh  \n",(float)gids*0.075); // gg - closer to usable
-            
-            tt.set_font((unsigned char*) SCProSB31x55);
-            tt.foreground(Green);
-            //tt.locate(60,96);
-            tt.locate(60,116); // gg - move down a little
-            printf("%4.1f mi  \n",(float)(gids-5)*0.31); // Approx for now
-            lgids=gids;
-            tt.foreground(Yellow);
-            tt.set_font((unsigned char*) Arial28x28);
-        }
-        
-        if(force||SOC!=lSOC){
-            tt.locate(200,10);
-            printf("%4.1f%s\n",(float)SOC/10,"% ");
-            lSOC=SOC;
-        }
-        if(force||packV!=lpackV){
-            tt.locate(200,200);
-            printf("%4.1fV \n",(float)packV/2);
-            lpackV=packV;
-        }
-        if(force||accV!=laccV){
-            tt.locate(20,200);
-            printf("%3.1fV  \n",accV);
-            laccV=accV;
-        }
-        if(force||kW[0]!=lkW){
-            tt.locate(160,40); // gg - move left to keep from wrap
-            printf("%3.2fkw \n",kW[0]); // use small w to save space
-            lkW=kW[0];
-        }
+
+    /*if(force||tock){ // for esr debug
+        tt.locate(10,10);
+        printf("%d %d amps\n",Imax,Imin);
+        tt.locate(10,40);
+        printf(" %4.1f %4.1f\n",incRmax/2,incRmin/2);
+        tt.locate(10,70);
+        printf(" %4.1f %4.1f\n",redRmax/2,redRmin/2);
+        tt.locate(10,100);
+        printf(" %4.1f %4.1f\n",curRmax/2,curRmin/2);
+        //tt.locate(10,130);
+        curRmin=1000;
+        curRmax=0;
+        incRmin=1000;
+        incRmax=0;
+        redRmin=1000;
+        redRmax=0;
+        Imax=-1000;
+        Imin=1000;
+    }*/
+    if(force||gids!=lgids){
+        tt.locate(10,10);
+        printf("%4d gids \n",gids);
+        lgids=gids;
+    }
+    if(force||SOC_x10!=lSOC){
+        tt.locate(10,40);
+        printf(" %4.1f%s \n",(float)SOC_x10/10,"% SOC");
+        lSOC=SOC_x10;
+    }
+    if(force||SOH_x100!=lSOH){
+        tt.locate(10,70);
+        printf(" %4.1f%s \n",(float)SOH_x100/100,"% SOH");
+        lSOH=SOH_x100;
+    }
+    if(force||Ah_x10000!=lAh){
+        tt.locate(10,100);
+        printf(" %4.2f Ah cap \n",(float)Ah_x10000/10000);
+        lAh=Ah_x10000;
+    }
+    if(force||maxTemp!=lmaxTemp){
+        tt.locate(10,130);
+        printf(" %4.1f %s (max) \n",convertTemperature(maxTemp),temperatureUnit());
+        lmaxTemp=maxTemp;
+    }
+    if(force||unloadedV_x2!=lunlV){
+        tt.locate(10,160);
+        printf(" %4.1f V \n",unloadedV_x2/2);
+        lunlV=unloadedV_x2;
+    }
+    if(force||Resr!=lresr){
+        tt.locate(10,190);
+        printf(" %3.0f mOhms \n",Resr*1000);
+        lresr=Resr;
     }
 }
 
 void braking (bool force, bool showButtons, bool prdata=false){
     unsigned long targetBraking, regenBraking;
-    static unsigned long maxTarget = 1000, maxRegen = 1000, tarDivReg = 1000;
+    static unsigned long maxTarget = 1000, maxRegen = 1000, tardivreg_x1000 = 1000;
     unsigned long temp;
     static unsigned char lastPressure[4] = {200,200,200,200};
     unsigned char i,r,t;
@@ -191,7 +350,7 @@
     steering = (msg.data[1]<<8)+msg.data[0];
     
     if(skin==ttSkin){
-        s= (unsigned short) ((steering/10)+160)%310; // this modulo wraps display
+        s= (unsigned short) ((steering/10)+155)%310; // this modulo wraps display
     }else{// if(skin==ggSkin){    
         // do not go off screen left or right. gg - steering
         short ss = (short) ((steering/15)+160); // less gain 10 -> 15
@@ -201,18 +360,6 @@
     }
     
     //--------------
-    msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen
-    regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5);
-    targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5);
-    if ((targetBraking>50)&&(regenBraking>50)){
-        temp = targetBraking;
-        temp *= 1000;
-        temp /= regenBraking;
-        if (temp<tarDivReg) tarDivReg=temp;
-    }
-    if (targetBraking>maxTarget) maxTarget=targetBraking;
-    if (regenBraking>maxRegen) maxRegen=regenBraking;
-
     msg = lastMsg[indexLastMsg[0x1ca]]; //Get brake pressure
     tt.background(Navy);
     if (force) {
@@ -270,44 +417,63 @@
         }
     }
 
-    temp = targetBraking;
-    temp *=200;
-    temp /= maxTarget;
-    t = (char) temp;
-    if (t>200) t=200;
-    temp = regenBraking;
-    temp *= tarDivReg;
-    temp /= maxTarget;
-    temp /= 5;
-    r = (char) temp;
-    if (r>200) r=200;
-    if(lr!=r&&prdata){
-        tt.foreground(Yellow);
-        tt.set_font((unsigned char*) Arial28x28);
-        tt.locate(100,40);
-        printf("%d %d    \n",regenBraking,maxRegen);
-        tt.locate(100,70);
-        printf("%3.1f (%3.1f%s)    \n",(float)tarDivReg/10,(float)regenBraking*tarDivReg/targetBraking/10,"%");
-    }    
-    if(lt!=t&&prdata){
-        tt.foreground(Yellow);
-        tt.set_font((unsigned char*) Arial28x28);
-        tt.locate(100,10);
-        printf("%d %d    \n",targetBraking,maxTarget);
+    msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen
+    regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5);
+    targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5);
+    msg = lastMsg[indexLastMsg[0x421]]; //Get Drive Mode
+    if (msg.data[0]==0x18) { // In Neutral
+        regenBraking = 0;  // No regen when in Neutral
     }
-    if (r>t) t=r;  //Should never happen
-    if((lr!=r||lt!=t)&&!prdata){
-        tt.fillrect(190,10,260,239-t,Navy);
-        tt.fillrect(190,239-t,260,239-r,Red);
-        tt.fillrect(190,239-r,260,239,Green);
+
+    if (targetBraking<2045){
+        if ((targetBraking>50)&&(regenBraking>50)){
+            temp = targetBraking;
+            temp *= 1000;
+            temp /= regenBraking;
+            if (temp<tardivreg_x1000) tardivreg_x1000=temp;
+        }
+        if (targetBraking>maxTarget) maxTarget=targetBraking;
+        if (regenBraking>maxRegen) maxRegen=regenBraking;
+        temp = targetBraking;
+        temp *=200;
+        temp /= maxTarget;
+        t = (char) temp;
+        if (t>200) t=200;
+        temp = regenBraking;
+        temp *= tardivreg_x1000;
+        temp /= maxTarget;
+        temp /= 5; // 1000/200=5
+        r = (char) temp;
+        if (r>200) r=200;
+        if(lr!=r&&prdata){
+            tt.foreground(Yellow);
+            tt.set_font((unsigned char*) Arial28x28);
+            tt.locate(100,40);
+            printf("%d %d    \n",regenBraking,maxRegen);
+            tt.locate(100,70);
+            printf("%3.1f (%3.1f%s)    \n",(float)tardivreg_x1000/10,(float)regenBraking*tardivreg_x1000/targetBraking/10,"%");
+        }    
+        if(lt!=t&&prdata){
+            tt.foreground(Yellow);
+            tt.set_font((unsigned char*) Arial28x28);
+            tt.locate(100,10);
+            printf("%d %d    \n",targetBraking,maxTarget);
+        }
+        if (r>t) t=r;  //Should never happen
+        if((lr!=r||lt!=t)&&!prdata){
+            tt.fillrect(190,10,260,239-t,Navy);
+            tt.fillrect(190,239-t,260,239-r,Red);
+            tt.fillrect(190,239-r,260,239,Green);
+        }
+        lt=t;
+        lr=r;
     }
-    lt=t;
-    lr=r;
 }
 
 void cpData(bool force, bool showButtons){
     short unsigned max, min, jv, i, bd;
     unsigned avg;
+    static char step=0; // counter to allow incremental update
     if(force){
         tt.foreground(White);
         tt.background(Navy);
@@ -316,7 +482,7 @@
         min=9999;
         avg=0;
         for(i=0; i<96; i++){
-           bd=(battData[i*2+3]<<8)+battData[i*2+4];
+           bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
            avg+=bd;
             if(bd>max) max=bd;
             if(bd<min) min=bd;
@@ -327,29 +493,48 @@
         } else { // Only compute judgement value if min cellpair meets <= 3712mV requirement
             jv=0;
         }
-        tt.cls();
-        tt.locate(0,6);
-        printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n",max,min,avg,jv,battData[224+5],battData[224+8],battData[224+11],battData[224+14]);
-        tt.locate(0,36);
-        for(i=0; i<16; i++){
-            printf("%02d-%02d : %04d %04d %04d %04d %04d %04d\n",i*6+1,i*6+6,(battData[i*12+3]<<8)+battData[i*12+4],(battData[i*12+5]<<8)+battData[i*12+6],(battData[i*12+7]<<8)+battData[i*12+8],(battData[i*12+9]<<8)+battData[i*12+10],(battData[i*12+11]<<8)+battData[i*12+12],(battData[i*12+13]<<8)+battData[i*12+14]);
+
+        char* sTemperatureUnit = temperatureUnit();
+        switch(step){
+            case 0:
+                tt.cls();
+                showCP=true;
+                break;
+            case 1:
+                tt.locate(0,6);
+                printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n",
+                    max,min,avg,jv, convertTemperature(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+8]),sTemperatureUnit,
+                    convertTemperature(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+14]),sTemperatureUnit);
+                tt.rect(8+0*41,16,40+0*41,28,Green);
+                tt.rect(8+1*41,16,40+1*41,28,Yellow);
+                //tt.rect(8+2*41,16,40+2*41,28,White);
+                tt.rect(8+3*41,16,40+3*41,28,Red);
+                break;
+            default:
+                tt.locate(0,36+(step-2)*48);
+                for(i=(step-2)*4; i<(step-1)*4; i++){
+                    printf("%02d-%02d : %04d %04d %04d %04d %04d %04d\n",
+                        i*6+1,i*6+6,
+                        (battData[BatDataBaseG2*7+i*12+3]<<8)+battData[BatDataBaseG2*7+i*12+4],(battData[BatDataBaseG2*7+i*12+5]<<8)+battData[BatDataBaseG2*7+i*12+6],
+                        (battData[BatDataBaseG2*7+i*12+7]<<8)+battData[BatDataBaseG2*7+i*12+8],(battData[BatDataBaseG2*7+i*12+9]<<8)+battData[BatDataBaseG2*7+i*12+10],
+                        (battData[BatDataBaseG2*7+i*12+11]<<8)+battData[BatDataBaseG2*7+i*12+12],(battData[BatDataBaseG2*7+i*12+13]<<8)+battData[BatDataBaseG2*7+i*12+14]);
+                }
+                for(i=(step-2)*24; i<(step-1)*24; i++){
+                    bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
+                    if(bd>0){
+                        if(bd==max) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Green);
+                        //if(bd==avg) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,White);
+                        if(bd==min) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Yellow);
+                        if(bd<jv) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Red);
+                    }
+                }
         }
-        tt.rect(8+0*41,16,40+0*41,28,Green);
-        tt.rect(8+1*41,16,40+1*41,28,Yellow);
-        //tt.rect(8+2*41,16,40+2*41,28,White);
-        tt.rect(8+3*41,16,40+3*41,28,Red);
-        for(i=0; i<96; i++){
-            bd=(battData[i*2+3]<<8)+battData[i*2+4];
-            if(bd>0){
-                if(bd==max) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Green);
-                //if(bd==avg) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,White);
-                if(bd==min) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Yellow);
-                if(bd<jv) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Red);
-            }
+        step=step<5?step+1:0;
+        if(step==0){
+            showCP=false;
         }
-        showCP=false;
     }
-    if((sMode==1)&&showButtons){
+    if(showButtons){
         tt.foreground(Yellow);
         tt.background(DarkCyan);
         tt.set_font((unsigned char*) Arial12x12);
@@ -359,6 +544,51 @@
 }
 
 //----------------
+// gg - index
+void showIndex(bool force, bool showButtons){ 
+
+    if(force){
+        tt.foreground(White);
+        tt.background(Navy);
+        //tt.set_font((unsigned char*) Arial12x12_prop);  // select the font
+       
+        tt.cls();
+        
+        // add the buttons to GoTo to other screens
+
+        tt.foreground(Yellow);
+        tt.background(DarkCyan);
+        tt.set_font((unsigned char*) Arial12x12);
+        
+         // top row
+        showButton(0,0," GoTo"," Main",4,4);               
+        showButton(1,0," GoTo"," Brake",4,4);               
+        showButton(2,0," GoTo"," EFF",4,4);               
+        showButton(3,0," GoTo","Health",4,4);     
+        // middle row
+        showButton(0,1," GoTo","CP Data",4,4);               
+        showButton(1,1," GoTo","CP Hist",4,4);               
+        showButton(2,1," GoTo","CP Bars",4,4);     
+        showButton(3,1," GoTo"," Config",4,4);               
+        // bottom (not Nav) row        
+        showButton(0,2," GoTo","Playback",4,4);               
+        //showButton(1,2," GoTo","Set Time",4,4);             
+        showButton(2,2," GoTo"," Log",4,4);     
+        showButton(3,2," GoTo"," Trip",4,4);     
+    
+        showCP=false;
+    }
+    
+    if(sMode==1&&showButtons){
+        tt.foreground(Yellow);
+        tt.background(DarkCyan);
+        tt.set_font((unsigned char*) Arial12x12);
+        
+        // do nothing here?
+    }
+}
+
+//----------------
 // gg - cpbars
 void cpBarPlot(bool force, bool showButtons){ 
     short unsigned max, min, jv, i, bd;
@@ -375,7 +605,7 @@
        
         // calc each cell-pair voltage, find max and min
         for(i=0; i<96; i++){
-           bd=(battData[i*2+3]<<8)+battData[i*2+4];
+           bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
            nBar[i] = bd; // init to bar height
            avg+=bd;
             if(bd>max) max=bd;
@@ -429,11 +659,14 @@
         }
         
         // values, for now
+        // BatDataBaseG4 * 7 = 280
         tt.locate( 0, yWinMax+40 );
-        printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n",
-                max,min,avg,jv,
-                battData[224+5],battData[224+8],
-                battData[224+11],battData[224+14]);
+        char* sTemperatureUnit = temperatureUnit();
+        printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n",
+            max,min,avg,jv, convertTemperature(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+8]),sTemperatureUnit,
+            convertTemperature(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+14]),sTemperatureUnit);
+        //printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n",
+        //        max,min,avg,jv,  battData[BatDataBaseG4*7+5],battData[BatDataBaseG4*7+8],  battData[BatDataBaseG4*7+11],battData[BatDataBaseG4*7+14]);
         
         // label the X axis (approximate)
         tt.locate( 2, yWinMax+5); printf("%04d", min );
@@ -452,8 +685,11 @@
             if( height > 100 ) height = 100 ; // clip tops
             
             // draw the bar, is always inside x-window
-            tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Green);  
-              
+            if (shunt[i]){
+                tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Red);  
+            } else {
+                tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Green);  
+            }
             // tic mark the y axis each 5
             if(i%5 == 4){
                tt.line( xPos,yWinMax+2, xPos,yWinMax+5, White);  // a white tick mark
@@ -496,7 +732,7 @@
         min=9999;
         avg=0;
         for(i=0; i<96; i++){
-           bd=(battData[i*2+3]<<8)+battData[i*2+4];
+           bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
            avg+=bd;
             if(bd>max) max=bd;
             if(bd<min) min=bd;
@@ -534,7 +770,7 @@
         
         // do the bin counting
         for(int i=0; i<96; i++){
-          bd=(battData[i*2+3]<<8)+battData[i*2+4] - min ;
+          bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4] - min ;
           if( bd > iBinValMax ) bd = iBinValMax ;
           nBin[bd] ++ ;
         }
@@ -553,8 +789,14 @@
         }
         
         // the values, for now
+        // BatDataBaseG4 * 7 = 280        
         tt.locate( 0, yWinMax+40 );
-        printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n",max,min,avg,jv,battData[224+5],battData[224+8],battData[224+11],battData[224+14]);
+        char* sTemperatureUnit = temperatureUnit();
+        printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n",
+            max,min,avg,jv, convertTemperature(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+8]),sTemperatureUnit,
+            convertTemperature(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+14]),sTemperatureUnit);
+        //printf(" MAX  MIN  AVG CVLI T1  T2  T3  T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n",
+        //        max,min,avg,jv,  battData[BatDataBaseG2*7+BatDataBaseG4*7+5],battData[BatDataBaseG2*7+BatDataBaseG4*7+8],  battData[BatDataBaseG2*7+BatDataBaseG4*7+11],battData[BatDataBaseG2*7+BatDataBaseG4*7+14]);
         
         //---------------
         // show the bars
@@ -590,7 +832,7 @@
 }
 
 //---------------
-void config1(bool force, bool showButtons){
+void config(bool force, bool showButtons){
     if (force) {
         tt.background(Black);
         tt.cls();
@@ -599,14 +841,23 @@
     tt.background(DarkCyan);
     tt.set_font((unsigned char*) Arial12x12);
  
+    //-------- top row --------
     showButton(0,0,"Calibrate"," Touch",4,4); // gg - 4x4
-    
     showButton(1,0," Reset","",4,4);
+    showButton(2,0,"  Save"," Config",4,4);
+           
+    // a button to step to the next skin
+    unsigned int nextSkin = skin + 1 ;
+    if( nextSkin > maxSkin ) nextSkin = 0 ;
     
-    showButton(2,0," Save"," Config",4,4);
+    if( nextSkin == ttSkin ) sprintf(sTemp1,"Skin TT");
+    else if( nextSkin == ggSkin ) sprintf(sTemp1,"Skin GG");
+    else sprintf(sTemp1,"Skin %d",nextSkin);
+
+    showButton(3,0,"  Use",sTemp1,4,4);
     
     //------- second row -----
-    if (logEn) {
+    if (logEn&&usbEn) {
         sprintf(sTemp1,"Disable");
     } else {
         sprintf(sTemp1,"Enable");
@@ -627,6 +878,29 @@
         sprintf(sTemp1,"Enable");
     }
     showButton(2,1,sTemp1,"Batt Log",4,4);
+    
+    // add Enable/Disable Debug - debugMode
+    if (debugMode) {
+        sprintf(sTemp1," Disable");
+    } else {
+        sprintf(sTemp1," Enable");
+    }
+    showButton(3,1,sTemp1," Debug",4,4);    
+    
+    if(metric)
+        showButton(0,2,"Imperial","",4,4);
+    else
+       showButton(0,2,"Metric","",4,4);
+    
+    showButton(1,2,"  Set"," Time",4,4);
+    if (showHealth) {
+        sprintf(sTemp1," Hide");
+    } else {
+        sprintf(sTemp1," Show");
+    }
+    showButton(2,2,sTemp1," Health",4,4);
+    showButton(3,2,"Update","Firmware",4,4);
+    
 }
 
 void pbScreen(bool force, bool showButtons){
@@ -672,7 +946,7 @@
         tt.set_font((unsigned char*) Arial12x12);
         strftime(sTemp1, 32, "%a %m/%d/%Y %X  \n", &t);
         printf("%s",sTemp1);
-        if((sMode==1)&&showButtons){
+        if(showButtons){
             switch(dtMode){
                 case 0:
                     sprintf(sTemp1,"Year");
@@ -692,15 +966,11 @@
                 case 5:
                     sprintf(sTemp1,"Second");
                     break;
-                case 6:
-                    sprintf(sTemp1,"Select");
-                    break;
                 default:
                     break;
             }
             tt.background(DarkCyan);
-            showButton(0,1,sTemp1,"",4,4);    
-                       
+            showButton(0,1,sTemp1,"",4,4);                          
             showButton(1,1,"  UP","",4,4);               
             showButton(2,1," DOWN","",4,4);               
         }
@@ -708,35 +978,26 @@
 }
 
 void dteDisplay(bool force, bool showButtons, bool showMiles){
-    unsigned short i,x,y,lx,ly,gids,radius,color;
+    unsigned short i,x,y,lx,ly,gids,radius,color,r,t;
+    unsigned char toVal;
     static unsigned short lgids=0;
     static unsigned char leff[39]={0};
     CANMessage msg;
+    unsigned long targetBraking, regenBraking, temp;
+    static unsigned long maxTarget = 1000, maxRegen = 1000, tardivreg_x1000 = 1000;
+    static unsigned char lr=0, lt=0;
 
     msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids
     gids = (msg.data[0]<<2)+(msg.data[1]>>6);
     if(gids==0){
-        gids=281;
+        gids=281; // Display new, fully charged capacity until real data obtained
     }
 
     tt.background(Navy);
     tt.foreground(Yellow);
     if(force){
+        tt.set_font((unsigned char*) Arial12x12);   
         tt.cls();
-    }
-    if(force||lgids!=gids){
-        tt.set_font((unsigned char*) Arial12x12);
-        for(i=0;i<10;i++){
-            y=200-i*20;
-            tt.locate(10,y-8);
-            if (showMiles){
-                printf("%3.0f\n",i*((float)(gids-5)*.075));
-            }else{
-                printf("%d.0\n",i);
-            }
-            tt.line(40,y,280,y,DarkGrey);
-        }
-
         x=50+0*6;
         tt.locate(x-10,226);
         printf("sec\n");  
@@ -748,7 +1009,7 @@
         x=50+18*6;
         tt.locate(x-10,226);
         printf("hour\n");  
-        tt.line(x,10,x,220,DarkGrey);     
+        tt.line(x,10,x,220,DarkGrey);
         x=50+25*6;
         tt.locate(x-10,226);
         printf("day\n");  
@@ -758,19 +1019,35 @@
         printf("mon\n");  
         tt.line(x,10,x,220,DarkGrey);     
         x=50+38*6;
-        tt.locate(x-10,226);
-        printf("year\n");  
-        tt.line(x,10,x,220,DarkGrey);
+        //tt.locate(x-10,226);
+        //printf("year\n");  
+        //tt.line(x,10,x,220,DarkGrey);
+        toVal=33;
+    } else {
+        toVal=24;// no need to constantly update the long tc values
+    }
+    if(force||lgids!=gids){ // update Y axis when kWh changes
+        //tt.set_font((unsigned char*) Arial12x12);
+        tt.set_font((unsigned char*) Arial24x23);
+        //for(i=0;i<10;i++){
+            //y=200-i*20;
+        for(i=2;i<7;i++){
+            y=200-(i-2)*40;
+            tt.locate(0,y-8);
+            if (showMiles){
+                printf("%3.0f\n",convertDistance(i*((float)(gids-5)*.075))); // LM - Added metric support
+                //printf("%2.0f  \n",i*((float)(gids-5)*.075));
+            }else{
+                printf("%d.0\n",i);
+            }
+            tt.line(48,y,toVal*6+56,y,DarkGrey);
+        }
         lgids=gids;    
     }
-    if(force||updateDTE){
-        for(i=0;i<10;i++){
-            y=200-i*20;
-            if (y>60){
-                tt.line(40,y,280,y,DarkGrey);
-            } else {
-                tt.line(40,y,150,y,DarkGrey);
-            }
+    if(tock||force){
+        for(i=2;i<7;i++){
+            y=200-(i-2)*40;
+            tt.line(40,y,158,y,DarkGrey);
         }
 
         x=50+0*6; 
@@ -779,53 +1056,74 @@
         tt.line(x,10,x,220,DarkGrey);     
         x=50+18*6;
         tt.line(x,10,x,220,DarkGrey);     
-        x=50+25*6;
-        tt.line(x,180,x,220,DarkGrey);     
-        x=50+32*6;
-        tt.line(x,180,x,220,DarkGrey);     
-        x=50+38*6;
-        tt.line(x,180,x,220,DarkGrey);     
+        //x=50+25*6;
+        //tt.line(x,60,x,220,DarkGrey);     
+        //x=50+32*6;
+        //tt.line(x,60,x,220,DarkGrey);     
+        //x=50+38*6;
+        //tt.line(x,60,x,220,DarkGrey);     
         tt.set_font((unsigned char*) SCProSB31x55);
         tt.foreground(Green);
         if (showMiles){
-            tt.locate(160,10);
-            printf("%4.1f \n",mpkWh[dtePeriod]*((float)(gids-5)*.075));
+            float miles = mpkWh[dtePeriod]*((float)(gids-5)*.075);
+            miles = convertDistance(miles); // LM - Metric support
+            // Right justify
+            if (miles>99.9){ //space=18; num=31; . = 23
+                tt.locate(161,8);
+                printf("%4.1f\n",miles);
+            } else if (miles>9.9){
+                tt.locate(156,8);
+                printf("  %3.1f\n",miles);
+            } else {
+                tt.locate(151,8);
+                printf("    %2.1f\n",miles);
+            }
+            tt.foreground(Cyan);
+            tt.set_font((unsigned char*) Arial24x23);
+            tt.locate(198,70);
+            printf("%3.1f \n",mpkWh[dtePeriod]);
         } else {
-            tt.locate(180,10);
-            printf("%3.1f\n",mpkWh[dtePeriod]);
+            tt.locate(200,10);
+            printf("%3.1f \n",mpkWh[dtePeriod]);
         }
         lx=50;
-        ly=mpkWh[0]*20;
-        if(ly<200) {
-            ly=200-ly;
-        }else{
-            ly=0;
-        }
+        ly=mpkWh[0]*40;
         if(dtePeriod==0){
             radius=6;
-            color=Magenta;
+            color=Yellow;
         }else{
             radius=2;
             color=Green;
         }
+        if(ly<100){
+            ly=220;
+            color=Red;
+        }else if(ly<320) {
+            ly=320-ly;
+        }else{
+            ly=0;
+        }
         tt.fillcircle(lx,leff[0],radius,Navy);
         tt.fillcircle(lx,ly,radius,color);
 
-        for(i=1;i<39;i++){
+        for(i=1;i<toVal;i++){
             x=50+i*6;
-            y=mpkWh[i]*20;
-            if(y<200) {
-                y=200-y;
-            }else{
-                y=0;
-            }
+            y=mpkWh[i]*40;
             if(i==dtePeriod){
-                radius=4;
-                color=Magenta;
+                radius=6;
+                color=Yellow;
             }else{
                 radius=2;
                 color=Green;
             }
+            if(y<60){
+                y=220;
+                color=Red;
+            }else if(y<280) {
+                y=280-y;
+            }else{
+                y=0;
+            }
             tt.fillcircle(x,leff[i],radius,Navy);
             tt.line(x-6,leff[i-1],x,leff[i],Navy);
             leff[i-1]=ly;
@@ -837,53 +1135,101 @@
             ly=y;
         }
         leff[i-1]=y;
-        updateDTE=false;
+    }
+
+    msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen
+    regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5);
+    targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5);
+    msg = lastMsg[indexLastMsg[0x421]]; //Get Drive Mode
+    if (msg.data[0]==0x18) { // In Neutral
+        regenBraking = 0;  // No regen when in Neutral
+    }
+
+    if (targetBraking<2045){
+        if ((targetBraking>50)&&(regenBraking>50)){
+            temp = targetBraking;
+            temp *= 1000;
+            temp /= regenBraking;
+            if (temp<tardivreg_x1000) tardivreg_x1000=temp;
+        }
+        if (targetBraking>maxTarget) maxTarget=targetBraking;
+        if (regenBraking>maxRegen) maxRegen=regenBraking;
+    
+        temp = targetBraking;
+        temp *=200;
+        temp /= maxTarget;
+        t = (char) temp;
+        if (t>175) t=175;
+        temp = regenBraking;
+        temp *= tardivreg_x1000;
+        temp /= maxTarget;
+        temp /= 5; // 1000/200=5
+        r = (char) temp;
+        if (r>175) r=175;
+        if (r>t) t=r;  //Should never happen
+        if(lr!=r||lt!=t){
+            tt.fillrect(264,64,310,239-t,Navy);
+            tt.fillrect(264,239-t,310,239-r,Red);
+            tt.fillrect(264,239-r,310,239,Green);
+        }
+        lt=t;
+        lr=r;
     }
 }
 
 void updateDisplay(char display){
-    bool changed;
+    bool changed,showButtons;
     changed = dMode[display]!=lastDMode[display];
+    showButtons = (display==whichTouched)&&(sMode==1);
     tt.set_display(display);
     switch (dMode[display]) {
         case logScreen:
-            printLog(changed,(display==whichTouched));
+            printLog(changed,showButtons);
             break;
         case mainScreen:
-            mainDisplay(changed,(display==whichTouched));
+            mainDisplay(changed,showButtons);
             break;
         case brakeScreen:
-            braking(changed,(display==whichTouched));
+            braking(changed,showButtons);
             break;
         case dteScreen:
-            dteDisplay(changed,(display==whichTouched),true);
+            dteDisplay(changed,showButtons,true);
             break;
          case effScreen:
-            dteDisplay(changed,(display==whichTouched),false);
+            dteDisplay(changed,showButtons,false);
             break;
        case monitorScreen:
-            printLast(changed,(display==whichTouched));
+            printLast(changed,showButtons);
             break;
         case changedScreen:
-            printChanged(changed,(display==whichTouched));
+            printChanged(changed,showButtons);
             break;
         case cpScreen:
-            cpData(changed||showCP,(display==whichTouched));
+            cpData(changed||showCP,showButtons);
             break;
-        case config1Screen:
-            config1(changed,(display==whichTouched));
+        case configScreen:
+            config(changed,showButtons);
             break;
         case playbackScreen:
-            pbScreen(changed,(display==whichTouched));
+            pbScreen(changed,showButtons);
             break;
         case dateScreen:
-            showDateTime(changed,(display==whichTouched));
+            showDateTime(changed,showButtons);
             break;
         case cpHistScreen: // gg - hist
-            cpHistogram(changed||showCP,(display==whichTouched));
+            cpHistogram(changed||showCP,showButtons);
             break;
         case cpBarScreen: // gg - cpbars
-            cpBarPlot(changed||showCP,(display==whichTouched));
+            cpBarPlot(changed||showCP,showButtons);
+            break;
+        case indexScreen:
+            showIndex(changed,showButtons);
+            break;
+        case tripScreen:
+            tripDisplay(changed,showButtons);
+            break;
+        case healthScreen:
+            healthDisplay(changed,showButtons);
             break;
         default:
             if (changed){
@@ -902,10 +1248,11 @@
                 tt.set_font((unsigned char*) Arial12x12);   
                            
                 showButton(0,tNavRow," <-Prev","",4,4); // gg - 4x4
+                // col 1 see below
+                showButton(2,tNavRow," Go To"," Index",4,4); // gg - index
                 showButton(3,tNavRow," Next->","",4,4); // gg - move next
-                               
-                showButton(2,tNavRow," Go To"," Main",4,4); // gg - index
                 
+                // col 1 in Nav row                              
                 switch (dMode[display]) {
                     case offScreen:
                         sprintf(sTemp2,"  Off");
@@ -932,11 +1279,11 @@
                         sprintf(sTemp2,"DeltaMon");
                         break;
                     case cpScreen:
-                        sprintf(sTemp2,"Cell Pair");
+                        sprintf(sTemp2,"CP Data");
                         break;
-                    case config1Screen:
+                    case configScreen:
                         sprintf(sTemp2," Config");
-                        break;
+                        break;          
                     case playbackScreen:
                         sprintf(sTemp2,"Playback");
                         break;
@@ -949,6 +1296,15 @@
                     case cpBarScreen: // gg - cpbars
                         sprintf(sTemp2,"CP Bars");
                         break;
+                    case tripScreen:
+                        sprintf(sTemp2," Trip");
+                        break;
+                    case healthScreen:
+                        sprintf(sTemp2,"Health");
+                        break;
+                    case indexScreen: // gg - index
+                        sprintf(sTemp2," Index");
+                        break;
                 }
                 showButton(1,tNavRow," Select",sTemp2,4,4);
                 
@@ -981,7 +1337,8 @@
                 break;
         }
     }
-}
+    tock=false;
+} // updateDisplay
 
 //---------------------
 // gg - highlight
@@ -993,14 +1350,21 @@
     x2=(column+1)*(320/columns)-btnGap/2;
     y1=row*(240/rows)+btnGap/2;
     y2=(row+1)*(240/rows)-btnGap/2;
-    /*if( tScn == 0 )
-        // paint the whole button box
-        tt.fillrect(x1,y1,x2,y2,White); // DarkCyan);
-    else
-        tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan);*/
+    
+    tt.set_display(tScn);
+
+    if( skin == ggSkin ){
+        // paint the whole button box, for a better visual effect
+        //  especially on a screen with a yellow background
+        if( tScn == 0 )
+            tt.fillrect(x1,y1,x2,y2,White); // DarkCyan);
+        else
+            tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan);
+    } else {
+        tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan);
+    }
 
     // paint the outer pixel as a yellow frame
-    tt.set_display(tScn);
     tt.rect(x1,y1,x2,y2,Yellow) ; // DarkCyan);
 }
 
@@ -1022,97 +1386,34 @@
     printf("%s\n",text2);
 }
 
-//-------------
-// below is braking screen normalized to power rather than force
-// changed to force since power had too large a dynamic range
-/*void braking (bool force, bool showButtons, bool prdata=false){
-    unsigned long targetBraking, regenBraking, speed;
-    static unsigned long maxTarget = 20000, maxRegen = 20000, tarDivReg = 1000;
-    short rpm;
-    unsigned long temp;
-    static unsigned char lastPressure[4] = {200,200,200,200};
-    unsigned char i,r,t;
-    static unsigned char lr, lt;
-    CANMessage msg;
 
-    msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen
-    regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5);
-    targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5);
-    msg = lastMsg[indexLastMsg[0x176]]; //Get rpms - not sure what this is but scales to mph with .0725
-    rpm = ((short)msg.data[0]<<8)+msg.data[1];
-    speed =rpm>0?rpm>>3:-rpm>>3; //Take absolute to get speed; div8
-    if ((targetBraking>2039)||(speed>200)) { //Filter weird messages
-        targetBraking = 0;
-        regenBraking = 0;
-    } else {
-        if ((targetBraking>50)&&(regenBraking>50)){
-            temp = targetBraking;
-            temp *= 1000;
-            temp /= regenBraking;
-            if (temp<tarDivReg) tarDivReg=temp;
-        }
-        targetBraking *= speed;
-        regenBraking *= speed;
-        if (targetBraking>maxTarget) maxTarget=targetBraking;
-        if (regenBraking>maxRegen) maxRegen=regenBraking;
+//The temps are stored as metric, distances as imperial... I'm assuming the input based on that - LM
+float convertTemperature(float input) 
+{
+    if (!metric) {
+        //convert!
+        float output = input *1.8f;
+        output += 32.0f;
+        return output;   
     }
-
-    msg = lastMsg[indexLastMsg[0x1ca]]; //Get brake pressure
-    tt.background(Navy);
-    if (force) {
-        tt.cls();
-        tt.rect(0,111,170,239,White);
-        tt.line(0,207,170,207,White);
-        tt.line(0,175,170,175,White);
-        tt.line(0,143,170,143,White);
-        lastPressure[0] = 200;
-        lastPressure[1] = 200;
-        lastPressure[2] = 200;
-        lastPressure[3] = 200;
-    }
-    // plot bar graph for each wheel pressure
-    for (i=0; i<4; i++){
-        if (msg.data[i]<239) {
-            if (msg.data[i]>lastPressure[i]){
-                tt.fillrect(10+40*i,239-msg.data[i],40+40*i,239,Red);
-            } else if (msg.data[i]<lastPressure[i]) {
-                tt.fillrect(10+40*i,238-lastPressure[i],40+40*i,238-msg.data[i],Navy);
-            }
-            lastPressure[i]=msg.data[i];
-        }
+    return input;
+}
+float convertDistance(float input)
+{
+    if (metric) {
+        return input / 0.62137f;
     }
-
-    temp = targetBraking;
-    temp *=200;
-    temp /= maxTarget;
-    t = (char) temp;
-    if (t>200) t=200;
-    temp = regenBraking;
-    temp *= tarDivReg;
-    temp /= maxTarget;
-    temp /= 5;
-    r = (char) temp;
-    if (r>200) r=200;
-    if(lr!=r&&prdata){
-        tt.foreground(Yellow);
-        tt.set_font((unsigned char*) Arial28x28);
-        tt.locate(100,40);
-        printf("%d %d    \n",regenBraking,maxRegen);
-        tt.locate(100,70);
-        printf("%3.1f (%3.1f%s)    \n",(float)tarDivReg/10,(float)regenBraking*tarDivReg/targetBraking/10,"%");
-    }    
-    if(lt!=t&&prdata){
-        tt.foreground(Yellow);
-        tt.set_font((unsigned char*) Arial28x28);
-        tt.locate(100,10);
-        printf("%d %d    \n",targetBraking,maxTarget);
-    }
-    if (r>t) t=r;  //Should never happen
-    if((lr!=r||lt!=t)&&!prdata){
-        tt.fillrect(200,10,300,239-t,Navy);
-        tt.fillrect(200,239-t,300,239-r,Red);
-        tt.fillrect(200,239-r,300,239,Green);
-    }
-    lt=t;
-    lr=r;
-}*/
\ No newline at end of file
+    return input;
+}
+char* distanceUnit()
+{
+    if(metric)
+        return "km";
+    return "mi";
+}
+char* temperatureUnit()
+{
+    if(metric)
+        return "C";
+    return "F";
+}
--- a/displayModes.h	Wed Apr 10 13:31:35 2013 +0000
+++ b/displayModes.h	Sun Jul 21 23:59:00 2013 +0000
@@ -15,9 +15,13 @@
 extern char displayLog[20][40];
 extern unsigned char displayLoc;
 extern char indexLastMsg[0x800];
-extern unsigned char battData[256];
+
+//extern unsigned char battData[256]; // 
+extern unsigned char battData[BatDataBufMax]; // BatDataBufMax
+
 extern bool showCP;
 extern bool logEn;
+extern bool usbEn;
 extern bool yesBattLog; // gg - Batt Log
 extern bool repeatPoll;
 extern unsigned char dMode[2];
@@ -29,12 +33,31 @@
 extern bool playbackEn;
 extern bool playbackOpen;
 extern float playbackInt;
+extern float miles_trip[3];
+extern float kWh_trip[3];
 extern float kW[39];
 extern float mpkWh[39];
 extern unsigned char whichTouched;
 extern unsigned char skin;
 extern unsigned char dtePeriod;
-extern bool updateDTE;
+extern bool tock;
+extern DigitalOut led4;
+extern unsigned char tNavRow; // gg - 4x4
+extern unsigned short pointerSep; // log write buffer pointer separation
+extern float maxTemp;
+extern unsigned long Ah_x10000;
+extern unsigned long SOC_x10000;
+extern unsigned short SOH_x100;
+extern bool metric;
+extern unsigned char tNavRow ; // gg - 4x4
+extern char revStr[7]; // gg - version
+extern bool debugMode;
+extern float unloadedV_x2;
+extern float Resr;
+extern bool shunt[96];
+extern float unloadedV_x2,Resr,curRmax,curRmin,redRmax,redRmin,incRmax,incRmin;
+extern signed short Imax, Imin;
+extern bool showHealth;
 
 extern "C" {
     void printLast (bool force, bool showButtons);
@@ -46,9 +69,13 @@
     void cpData(bool force, bool showButtons);
     void cpHistogram(bool force, bool showButtons); // gg - hist
     void cpBarPlot(bool force, bool showButtons); // gg - cpbars
+    void showIndex(bool force, bool showButtons); // gg - index
     void showDateTime(bool force, bool showButtons);
     void updateDisplay(char display);
     void showButton(unsigned char column, unsigned char row, char * text1, char * text2, unsigned char columns, unsigned char rows);
     void highlightButton(unsigned char column, unsigned char row, unsigned char tScn, unsigned char columns, unsigned char rows);
-
+    float convertDistance(float input); // LM - Metric
+    float convertTemperature(float input); // LM - Metric
+    char* distanceUnit(); // LM - Metric
+    char* temperatureUnit(); // LM - Metric
 }
\ No newline at end of file
--- a/main.cpp	Wed Apr 10 13:31:35 2013 +0000
+++ b/main.cpp	Sun Jul 21 23:59:00 2013 +0000
@@ -1,33 +1,39 @@
 // main.cpp
-
+//
 //To Do:
-// * USB device detect
-// * Ability to update binary from the thumb-drive (requires file timestamp)
 // * Audible friction brake feedback
 // * User-configurable watchpoint
-// * Better graphical DTE display with historic efficiency information considered and displayed
 // * Add 50% charge option
-// * Tire Pressure Sensor display
+// * Add coasting regen to regen/braking display
+// * Change semilog efficiency graph to linear with 10 minute values
+// * Add additional 79b bank readouts
+// * Add ability to transfer settings config file to/from USB
+// * Subtract accessory power from efficiency history (add back in when displaying)
 
 #include "mbed.h"
 #include "CAN.h"
 #include "beep.h"
-#include "MSCFileSystem.h"
+#include "ff.h"
 #include "PowerControl.h"
 #include "EthernetPowerControl.h"
 #include "utility.h"
 #include "displayModes.h"
 #include "TOUCH_TFTx2.h"
 
+char revStr[7] = "128b"; // gg - revision string, max 6 characters
+
+FATFS USBdrive;
 LocalFileSystem local("local");
-
+bool waitasec = true;
+unsigned char wait5secs = 5;
 // to write to USB Flash Drives, or equivalent (SD card in Reader/Writer)
-MSCFileSystem fs("usb"); // to write to a USB Flash Drive
-
+// class10 SDcard in Reader/Writer recommended
+FRESULT mfr = f_mount(0,&USBdrive);
 time_t seconds ;
 
 Ticker autoPoll;
 Ticker playback;
+Ticker msgReq;
 Timer timer;
 
 DigitalOut led1(LED1);
@@ -45,22 +51,30 @@
 PwmOut dled(p23);
 Beep spkr(p21);
 
-bool logEn = false, logOpen = false; 
-bool yesBattLog = false ; // gg - Batt Log
-unsigned char tNavRow = 3 ; // gg - 4x4 touch
+bool debugMode = false;
+bool usbEn = false;
+bool logEn = false;
+bool logOpen = false; 
+bool yesBattLog = true; // gg - Batt Log
+unsigned char tNavRow = 3; // gg - 4x4 touch
 
-FILE *cfile;
-FILE *file;
-char fileName[35] = "" ;
-char writeBuffer[maxBufLen][13]; // buffer for USB write
+FILE *hfile; // history file
+FIL efile; // external usb file
+FRESULT efr; // external file access flags
+unsigned int bytesRW;
+char fileName[35] = "";
+char writeBuffer[maxBufLen][13] __attribute__ ((section("AHBSRAM1"))); // buffer for USB write
 char indexLastMsg[0x800]={0}; // index table for last message
 CANMessage lastMsg[100]; // table to store last message of eachtype
-unsigned char battData[256]={0};
+
+unsigned char battData[BatDataBufMax]={0}; // 7 * 0x3D = BatDataBufMax
+
 unsigned char msgChanged[100]; // inidcates which bytes changed
 char c;
 volatile int writePointer = 0;
-volatile int secsNoMsg = 0;
-volatile int secsNoTouch = 0;
+int readPointer=0;
+volatile unsigned short secsNoMsg = 0;
+volatile unsigned short secsNoTouch = 0;
 volatile bool canIdle;
 volatile bool userIdle;
 bool touched=false; //flag to read touchscreen
@@ -69,16 +83,18 @@
 unsigned char dMode[2] = {mainScreen,brakeScreen}; //display mode
 unsigned char sMode = 0; // setup mode
 unsigned char lastDMode[2] = {0,0}; //last screen mode
-unsigned char dtMode = 6;
+unsigned char dtMode = 0;
 char displayLog[20][40];
 unsigned char displayLoc = 0;
+unsigned int fwCount=1;
 unsigned char indexOffset = 1;
 bool showCP = false;
-bool pollCP = false;
 bool logCP = false; //Turbo3
-bool repeatPoll = false;
+bool logOnce = false;
+bool repeatPoll = true;
 bool headlights = false;
 bool tick = false;
+bool ZeroSecTick = false;
 float ledHi = 0.8; // Bright LED value (until config file read)
 float ledLo = 0.1; // Dim LED value (until config file read)
 unsigned short pollInt = 300; // polling interval=5 minutes (until config file read)
@@ -92,41 +108,48 @@
 bool playbackEn = false;
 bool playbackOpen = false;
 //float playbackInt = 0.05; //read messages every 50 ms
-float playbackInt = 0.005; //read messages every 50 ms
+float playbackInt = 0.005; //read messages every 5 ms
 bool step = false;
 char header[5];
 char data[8];
 signed long motorRPM;
-unsigned char skin = 0;
+unsigned char skin = ttSkin ;
 unsigned char dtePeriod = 14; //ten minute averaging interval
+float kWh_trip[3]={0};
+float miles_trip[3]={0};
 float mph[39]={0};
 float kW[39]={0};
 float mpkWh[39]={0};
+float unloadedV_x2,Resr,curRmax,curRmin,redRmax,redRmin,incRmax,incRmin;
+signed short Imax, Imin;
 // Logarithmic division scale (roughly - snapped to common units of time)
 float timeConstant[39] = {1, 1.58, 2.51, 3.98, 6.31, 10, 15.8, 25.1, 39.8, 60, // 1 minute
                      60*1.58, 60*2.51, 60*3.98, 60*6.31, 60*10, 60*15.8, 60*25.1, 60*39.8, 60*60, // 1 hour
                      60*60*1.58, 60*60*2.51, 60*60*3.98, 60*60*6.31, 60*60*10, 60*60*15.8, 60*60*24, // 1 day
                      60*60*24*1.58, 60*60*24*2.51, 60*60*24*3.98, 60*60*24*6.31, 60*60*24*10, 60*60*24*15.8, 60*60*24*30, // 1 month
                      60*60*24*39.8, 60*60*24*63.1, 60*60*24*100, 60*60*24*158, 60*60*24*251, 60*60*24*365}; // 1 year
-bool updateDTE = false;
+bool tock = false;
+unsigned short pointerSep;
+unsigned char reqMsgCnt = 99;
+unsigned long Ah_x10000 = 0;
+unsigned long SOC_x10000 = 0;
+unsigned short SOH_x100 = 0;
+float maxTemp = 0;
+bool metric = false;
+bool shunt[96]={0};
+bool charging=false;
+bool showHealth=false;
+unsigned char saveDmode=99;
+bool moving=false;
 
 int main() {
-    int readPointer=0;
+    //can1SleepMode.mode(OpenDrain);
+    //can2SleepMode.mode(OpenDrain);
     char sTemp[40];
     unsigned long secs;
     unsigned char i,j,display=0,lwt=0;
     point lastTouch;
     float average;
-
-    can1.monitor(true); // set to snoop mode
-    can2.monitor(true); // set to snoop mode
-    can1.frequency(500000);
-    can2.frequency(500000);
-    can1SleepMode = 1;         // Turn on Monitor_only Mode
-    can2SleepMode = 1;         // Turn on Monitor_only Mode
-    can1.attach(&recieve1);
-    can2.attach(&recieve2);
-    
     tt.set_orientation(1);
     tt.background(Black);
     tt.set_display(2);       // select both displays
@@ -135,12 +158,12 @@
     touchpad.rise(&touch_ISR);
     tt.wfi();               // enable interrupt on touch
     dled = 0.8; // turn on display LED 80%
-
+    Resr = 0.075; // initial guess of Resr
     timer.start() ;
     RTC_Init(); // start the RTC Interrupts that sync the timer
     struct tm t; // pointer to a static tm structure
-    NVIC_SetPriority(TIMER3_IRQn, 1); //set ticker priority
-    NVIC_SetPriority(CAN_IRQn, 2); //higher than can (so RTC sync works)
+    NVIC_SetPriority(CAN_IRQn, 2); //set can priority just below RTC
+    NVIC_SetPriority(TIMER3_IRQn, 3); //set ticker priority just below can
 
     seconds = time(NULL);
     t = *localtime(&seconds) ;
@@ -154,64 +177,27 @@
     }
     t = *localtime(&seconds) ;
     strftime(sTemp, 32, "%a %m/%d/%Y %X\n", &t);
-    logMsg(sTemp);
+    printMsg(sTemp); // record RTC
     
     // revision
-    sprintf(sTemp,"CANary firmware rev64\n");
-    logMsg(sTemp);
-
-    // Look for new binary on thumbdrive
-    // Can't make this work right now since USB doesn't attach the right timestamp (so new binary isn't loaded)
-    /*cfile = fopen("/usb/CANary.bin", "rb");
-    lastDMode[whichTouched]=99;//force refresh
-    if (cfile!=NULL){ //found a new binary on the thumbdrive so copy it over
-        sprintf(sTemp,"New binary found.\n");
-        logMsg(sTemp);
-        file = fopen("/local/CANary.bin", "wb");
-        if (file==NULL){ //failed to open destination
-            sprintf(sTemp,"Unable to open destination file.\n");
-            logMsg(sTemp);
-        } else {
-            tt.set_display(2);
-            tt.foreground(White);
-            tt.background(Black);
-            tt.cls();
-            tt.locate(1,40);
-            printf("%s\n","Copying binary - Do no remove power.");
-            tt.locate(1,80);
-            printf("CANary will reset when complete.\n");
-            wait(1); //Wait 1 sec for display DMA to finish before writing file
-            while ( int size = fread( writeBuffer, sizeof(char), maxBufLen*13, cfile )){
-                fwrite( writeBuffer, sizeof(char), size, file );
-                led4=led3;
-                led3=led2;
-                led2=led1;
-                led1=!led4;
-            }
-        fclose(cfile);
-        fclose(file);
-        remove("/usb/CANary.bin"); // delete original
-        mbed_reset(); //restart
-        }
-    }*/
+    sprintf(sTemp,"CANary firmware rev%s\n", revStr); // gg - for Logging the revision
+    printMsg(sTemp); // revision
 
     secsNoMsg = 0;
 
     //read efficiency history data
-    cfile = fopen("/local/ehist.cny", "r");
-    if (cfile!=NULL){ // found a efficiency history file
+    hfile = fopen("/local/ehist.cny", "r");
+    if (hfile!=NULL){ // found a efficiency history file
         for(i=0;i<39;i++){
-            if(!feof(cfile)){
-                fscanf(cfile,"%f %f\r\n",&mph[i],&kW[i]);
+            if(!feof(hfile)){
+                fscanf(hfile,"%f %f\r\n",&mph[i],&kW[i]);
                 mpkWh[i]=mph[i]/kW[i];
             }
         }
-        fclose(cfile);
-        sprintf(sTemp,"History Loaded.\n");
-        logMsg(sTemp);
+        fclose(hfile);
+        printMsg("History Loaded.\n"); // History loaded
     } else { // create initial file
-        sprintf(sTemp,"History not found.  Created.\n");
-        logMsg(sTemp);
+        printMsg("History not found.  Created.\n"); // history not found, created
         for(i=0;i<39;i++){
             // Pre-load with 4 mpkWh @ 40 mph
             mph[i]=40*timeConstant[i];
@@ -222,71 +208,94 @@
 
     // Read config file
     readConfig();
+    if (repeatPoll) { // enable autopolling if enabled
+        autoPoll.attach(&autoPollISR,pollInt);
+    }
+    
+    // Start monitors
+    can1.monitor(true); // set to snoop mode
+    can2.monitor(true); // set to snoop mode
+    can1.frequency(500000);
+    can2.frequency(500000);
+    can1SleepMode = VP230Sleep;         // Turn on Monitor_only Mode
+    can2SleepMode = VP230Sleep;         // Turn on Monitor_only Mode
+    can1.attach(&recieve1);
+    can2.attach(&recieve2);
 
     touched=false;
     secsNoTouch=2;
     while (true) {
         if (!logOpen) { // Open new file if one is not already open
-            if(logEn){ //logging enable
+            if(logEn&&usbEn){ //logging enabled and USB device detected
+                strftime(fileName, 32, "%m%d%H%M.alc", &t); //mmddhhmm.alc
+                efr = f_open(&efile,fileName,FA_WRITE|FA_OPEN_ALWAYS);
                 seconds = time(NULL);
                 t = *localtime(&seconds) ;
-                strftime(fileName, 32, "/usb/%m%d%H%M.alc", &t); //mmddhhmm.alc
-                //sprintf(sTemp,"Using file %s\n",fileName);
-                //logMsg(sTemp);
-                file = fopen(fileName, "ab");
-                lastDMode[whichTouched]=99;//force refresh
-                if(file==NULL){
-                    sprintf(sTemp,"\nUnable to open %s\n\n\n\n",fileName);
-                    logMsg(sTemp);
+                lastDMode[0]=99;//force refresh
+                lastDMode[1]=99;//force refresh
+                if(efr != FR_OK){
+                    sprintf(sTemp,"\nERR:%d Unable to open %s\n\n\n\n",efr,fileName);
+                    printMsg(sTemp); // cannot open alc file
                     logEn=false;
                     spkr.beep(1000,0.25);
+                    wait_ms(500);
+                    spkr.beep(1000,0.25);
                 } else {
                     logOpen = true;
                     readPointer=writePointer;
                     sprintf(sTemp,"Starting Can Log %s\n",fileName);
-                    logMsg(sTemp);
-                    logTS();
+                    printMsg(sTemp); // starting alc log file 
+                    
+                    logTS(); // Date Time at start
+                    logEvent("Starting"); // Log startup msg for testing
+                    sprintf(sTemp,"Cr%s",revStr);
+                    logEvent(sTemp); // gg - log firmware version   
                     spkr.beep(2000,0.25);
                 }
-            }//logging enabled
+            }//logging enabled and USB detected
         } else { // if (logOpen)
-            if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen/16)||canIdle||!logEn) {
+            pointerSep=(writePointer+maxBufLen-readPointer)%maxBufLen;
+            if (pointerSep>(maxBufLen/16)||canIdle||!logEn) {
                 // Dump buffer if > 1/16 full or canbus has stopped
-                if (file == NULL) {
+                //if (&efile == NULL) {
+                if (efr != FR_OK) {
                     logOpen = false;
-                    sprintf(sTemp,"Failed to append log file.\n");
-                    logMsg(sTemp);
-                    spkr.beep(1000,0.25);
+                    printMsg("Failed to append log file.\n"); // failed to append 
+                    spkr.beep(3000,0.25);
+                    spkr.beep(1500,0.25);
+                    spkr.beep(750,0.25);
+                    spkr.beep(375,0.25);
                     logEn=false;
                 } else {
-                    if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen*7/8)) { // Hi-water mark
-                        sprintf(sTemp,"Write buffer overrun.\n");
-                        logMsg(sTemp);
-                        spkr.beep(1000,0.25);
+                    while (readPointer != writePointer) {
+                        efr=f_write(&efile,&writeBuffer[readPointer][0],13,&bytesRW);
+                        if(++readPointer >= maxBufLen){
+                            readPointer=0;
+                            led4 = !led4;
+                        }
                     }
-                    while (readPointer != writePointer) {
-                        for (j = 0; j<13; j++){
-                            fprintf(file,"%c",writeBuffer[readPointer][j]);
-                        }
-                        if(++readPointer >= maxBufLen)
-                            readPointer=0;
-                    }
-                    led4 = !led4;
                 }
             } // if > 1/16 full, canbus has stopped, or logging stopped
             if (!logEn) {
-                fclose(file);
+                sprintf(sTemp,"Stopping Can Log %s\n",fileName);
+                printMsg(sTemp); // stopping alc log file 
+                f_close(&efile);
                 logOpen=false;
+                pointerSep=0;
+                led4=false;
             }
         } // if logOpen
         if (canIdle&&userIdle&&!playbackEn) { // canbus idle --> sleep to save power
-            if (logOpen){
-                fclose(file);
-            } // if (logOpen)*/
+            if (repeatPoll) { // stop autopolling if enabled
+                autoPoll.detach();
+            }
+            if (logOpen){ //close file to dump buffer
+                f_close(&efile);
+            } // if (logOpen)
             seconds = time(NULL);
             t = *localtime(&seconds) ;
             strftime(sTemp, 40, "Sleeping: %a %m/%d/%Y %X\n", &t);
-            logMsg(sTemp);
+            printMsg(sTemp); // sleeping date time
             updateDisplay(0); //Added for turbo3 who has a display override and wants to see the sleep message before going to sleep
             updateDisplay(1);
             //LPC_RTC->CIIR=0x00; // block RTC interrupts
@@ -302,20 +311,29 @@
                 //__wfi(); // freeze CPU and wait for interrupt (from canbus or touch)
                 Sleep();
             }
+            lastDMode[0]=99;
+            lastDMode[1]=99;
             secsNoTouch=2;
             canIdle=secsNoMsg>canTimeout;
-            //userIdle=!touched;
             dled=0.8; // turn on display LED
             seconds = time(NULL);
             t = *localtime(&seconds) ;
             strftime(sTemp, 40, "Waking: %a %m/%d/%Y %X\n", &t);
-            logMsg(sTemp);
+            printMsg(sTemp); // wakeup date time
             if (time(NULL)>(secs+1800)) {
-                logOpen = false; // Start new file if asleep for more than 30 minutes
+                if (logOpen){
+                    f_close(&efile);
+                    logOpen = false; // Start new file if asleep for more than 30 minutes
+                } // if (logOpen)
                 if (secsNoTouch>100) secsNoTouch = 100; // also mostly reset user Idle counter
             } else if (logOpen){ // insert timestamp on each wake if logging enabled (disabled for now)
-                file = fopen(fileName, "ab");
-                logTS();
+                efr = f_open(&efile,fileName,FA_WRITE|FA_OPEN_ALWAYS);
+                f_lseek(&efile,0xffffffff); // goto end of file (append existing)
+                logEvent("WakingUp"); // gg - use messeges
+                logTS(); // Date-Time at wakeup
+            }
+            if (repeatPoll) { // re-enable autopolling if enabled
+                autoPoll.attach(&autoPollISR,pollInt);
             }
         } // if idle
         
@@ -341,7 +359,7 @@
                 sMode = 1;
             }
             //sprintf(sTemp,"%d,%d ",lastTouch.x,lastTouch.y);
-            //logMsg(sTemp);
+            //printMsg(sTemp); // touch x,y - for debug
             touched = false; // clear interrupt flag
         }
         //---------------
@@ -355,7 +373,7 @@
                 secsNoTouch +=2; // increment to prevent double touch
                 sMode = 1;
                 //sprintf(sTemp,"button %d %d,%d %d\n",i,buttonX(lastTouch.x,3),buttonY(lastTouch.y,3),lastTouch.x);
-                //logMsg(sTemp);
+                //printMsg(sTemp); // button parms - for debug
                 switch (sMode) {
                     case 0: // no select
                         break;
@@ -372,10 +390,13 @@
                         if( tRow == tNavRow ) tRow = 7 ; // gg                   
                         switch ( (tCol*10) + tRow ) {
                             //---------------------------------
-                            case 00: // 00 on screen 0 or 1
+                            case 00: // top row, left button on screen 0 or 1
                                 if (dMode[whichTouched]==monitorScreen||dMode[whichTouched]==changedScreen) {
-                                indexOffset=indexOffset>4?indexOffset-4:1;
-                                } else if (dMode[whichTouched]==config1Screen) {
+                                    indexOffset=indexOffset>4?indexOffset-4:1;
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = mainScreen ; // GoTo Main Screen
+                                    sMode=0;
+                                } else if (dMode[whichTouched]==configScreen) {
                                     wait_ms(500);
                                     tt.background(Black);
                                     tt.calibrate();
@@ -390,17 +411,23 @@
                                 }
                                 break;
                             //-----------------------------------------------
-                            case 10: // 1,0 (col,row) on screen 0 or 1
+                            case 10: // 1,0 (col left of center,top row) on screen 0 or 1
                                 if (dMode[whichTouched]==changedScreen) {
                                     for(j=0;j<100;j++) msgChanged[j]=0; // clear changed data
                                     lastDMode[whichTouched]=99;//force refresh
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    sMode=0;
+                                    dMode[whichTouched] = brakeScreen ; // GoTo Brake Screen
                                 } else if (dMode[whichTouched]==cpScreen) {
-                                    pollCP=true;
+                                    reqMsgCnt=0;
+                                    msgReq.attach(&sendReq,0.015);
                                 } else if (dMode[whichTouched]==cpHistScreen) { // gg - hist
-                                    pollCP=true;
+                                    reqMsgCnt=0;
+                                    msgReq.attach(&sendReq,0.015);
                                 } else if (dMode[whichTouched]==cpBarScreen) { // gg - cpbars
-                                    pollCP=true;
-                                } else if (dMode[whichTouched]==config1Screen) {
+                                    reqMsgCnt=0;
+                                    msgReq.attach(&sendReq,0.015);
+                                } else if (dMode[whichTouched]==configScreen) {
                                     mbed_reset();
                                 } else if (dMode[whichTouched]==playbackScreen) { // pause/unpause
                                     playbackEn=!playbackEn;
@@ -418,9 +445,11 @@
                             case 20: // col 2 and row 0 on either screen 0 or 1
                                 if (dMode[whichTouched]==monitorScreen||dMode[whichTouched]==changedScreen) {
                                     indexOffset=indexOffset<77?indexOffset+4:80;
-                                } else if (dMode[whichTouched]==config1Screen) {
-                                    sprintf(sTemp,"Saving config file.\n");
-                                    logMsg(sTemp);
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = effScreen ; // GoTo EFF Screen
+                                    sMode=0;
+                                } else if (dMode[whichTouched]==configScreen) {
+                                    printMsg("Saving config file.\n"); // saving config
                                     saveConfig();
                                     spkr.beep(2000,0.25);
                                 } else if (dMode[whichTouched]==playbackScreen) { // faster
@@ -431,19 +460,41 @@
                                             playback.attach(&playbackISR,playbackInt);
                                         }
                                     }
-                                }else{
+                                } else {
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                }
+
+                                break;
+                                
+                            case 30: // right-most on top row
+                                
+                                if (dMode[whichTouched]==configScreen) {
+                                    // step through skins
+                                    if( skin < maxSkin ) skin += 1 ;
+                                    else skin = 0 ;
+                                    
+                                    // repaint both screens, I think
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                    // and re-paint the other screen too, to see new skin there
+                                    lastDMode[whichTouched ^ 1]=99; // repaint other screen (^ = XOR)
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = healthScreen ; // Goto health screen
+                                    sMode=0;
+                                } else {
                                     lastDMode[whichTouched]=99;//repaint to clear highlight
                                 }
 
                                 break;
                             //----------------------------------
                             //----------------------------------
-                            case 01: // col 0 row 1
-                                if (dMode[whichTouched]==config1Screen) {
+                            case 01: // left col middle row
+                                if (dMode[whichTouched]==configScreen) {
                                     logEn = !logEn;
-                                    if (!logEn) repeatPoll=false; // disable auto polling, too
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = cpScreen ; // GoTo CP Data Screen                                
+                                    sMode=0;
                                 } else if (dMode[whichTouched]==dateScreen){
-                                    dtMode=(dtMode<6)?dtMode+1:0;
+                                    dtMode=(dtMode<5)?dtMode+1:0;
                                     lastDMode[whichTouched]=99;
                                 } else {
                                     lastDMode[whichTouched]=99;//repaint to clear highlight
@@ -452,40 +503,42 @@
                                 break;
                             //------------------------------
                             case 11:
-                                if (dMode[whichTouched]==config1Screen){
-                                    repeatPoll = !repeatPoll&&logEn;
+                                if (dMode[whichTouched]==configScreen){
+                                    repeatPoll = !repeatPoll;
                                     if (repeatPoll) {
                                         autoPoll.attach(&autoPollISR,pollInt);
                                     } else {
                                         autoPoll.detach();
                                     }
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = cpHistScreen ; // GoTo CP Hist Screen
+                                    sMode=0;
                                 } else if (dMode[whichTouched]==playbackScreen) {
                                     // Start/stop playback
                                     if(!playbackOpen){
-                                        if(!logOpen){
-                                            file = fopen("/usb/playback.alc", "rb");                                          
+                                        if(!canIdle){
+                                            printMsg("Cannot playback while connected to canbus\n");
+                                        }else if(!logOpen){
+                                            efr = f_open(&efile,"playback.alc",FA_READ|FA_OPEN_EXISTING);
                                             lastDMode[whichTouched]=99;//force refresh
-                                            if(file==NULL){
-                                                sprintf(sTemp,"Unable to open /usb/playback.alc\n");
-                                                logMsg(sTemp);
+                                            if(efr != FR_OK){
+                                                printMsg("Unable to open /usb/playback.alc\n"); // no playback.alc
                                                 spkr.beep(1000,0.25);
                                             } else {
                                                 playbackOpen = true;
                                                 playbackEn=true;
                                                 playback.attach(&playbackISR,playbackInt);
-                                                sprintf(sTemp,"Starting playback\n");
-                                                logMsg(sTemp);
+                                                printMsg("Starting playback\n"); // start playback
                                                 spkr.beep(2000,0.25);
                                                 can1.attach(&doNothing);// Stop recieving CAN data
                                                 can2.attach(&doNothing);
                                             }
                                         } else {
-                                            sprintf(sTemp,"Must stop logging first\n");
-                                            logMsg(sTemp);
+                                            printMsg("Must stop logging first\n");
                                         }
                                     } else {
                                         playback.detach();
-                                        fclose(file);
+                                        f_close(&efile);
                                         playbackOpen=false;
                                         playbackEn=false;
                                         can1.attach(&recieve1);// Restore CAN data recieve
@@ -502,9 +555,11 @@
                                 break;
                             //---------------------------------
                             case 21: // col 2 row 1
-                                if (dMode[whichTouched]==config1Screen) { // gg - Batt Log Enable Button
+                                if (dMode[whichTouched]==configScreen) { // gg - Batt Log Enable Button
                                     yesBattLog = !yesBattLog;
-                                    
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = cpBarScreen ; // GoTo CP Bars Screen  
+                                    sMode=0;
                                 } else if (dMode[whichTouched]==dateScreen){
                                     upDate(dtMode,false);
                                     lastDMode[whichTouched]=99;
@@ -514,6 +569,66 @@
 
                                 break;
                                 
+                            case 31: // col 3 row 1
+                                if (dMode[whichTouched]==configScreen) { // gg - Batt Log Enable Button
+                                    debugMode = !debugMode;                                
+                                } else if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = configScreen ; // GoTo Config Screen                                   
+                                } else if (dMode[whichTouched] == tripScreen) {
+                                    miles_trip[1]=0;
+                                    kWh_trip[1]=0;
+                                    sMode=0;
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                } else {
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                }                            
+                                break;
+                                
+                            //-----------------------------------
+                            case 02: // left col, bottom row (not nav)
+                                if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = playbackScreen ; // GoTo Playback Screen                                    
+                                } else if (dMode[whichTouched]==configScreen) {
+                                    metric = !metric; // toggle metric/imperial display
+                                } else {
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                }            
+                                break;
+                                
+                             case 12: // left-middle col, bottom row (not nav)
+                                if (dMode[whichTouched] == configScreen) { // gg - index
+                                    dMode[whichTouched] = dateScreen ; // GoTo Set Date/Time Screen  
+                                } else {
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                } 
+                                break;
+                             
+                             case 22: // right-middle col, bottom row (not nav)
+                                if (dMode[whichTouched] == indexScreen) { // gg - index
+                                    dMode[whichTouched] = logScreen ;    
+                                } else if (dMode[whichTouched]==configScreen) {
+                                    showHealth = !showHealth;
+                                } else {
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                } 
+                                break;
+
+                             case 32: // right col, bottom row (not nav)  
+                                if (dMode[whichTouched] == configScreen) {
+                                    logEn=false;
+                                    updateFirmware();
+                                } else if (dMode[whichTouched] == tripScreen) {
+                                    miles_trip[2]=0;
+                                    kWh_trip[2]=0;
+                                    sMode=0;
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                } else if (dMode[whichTouched] == indexScreen) {
+                                    dMode[whichTouched] = tripScreen ;    
+                                } else {                             
+                                    lastDMode[whichTouched]=99;//repaint to clear highlight
+                                }
+                                break;
+
                             //-----------------------------------
                             //-----------------------------------
                             // Prev Navigation
@@ -530,7 +645,7 @@
                             //-----------------------------------
                             // Index Navigation
                             case 27: // col 2 row tNavRow
-                                dMode[whichTouched]= mainScreen; // indexScreen ; // gg - index
+                                dMode[whichTouched]= indexScreen ; // gg - index
                                 break;
                             //------------------------------------
                             // Next Navigation
@@ -560,63 +675,123 @@
         }
 
         if(tick){ // Executes once a second
+            tick=false;
+            headlights = (lastMsg[indexLastMsg[0x358]].data[1]&0x80)?true:false;  // headlight/turn signal indicator
             accV=floor(mon12V*scale12V*10+0.5)/10; //Round to nearest 10th
             accOn=(accV>5)?true:false;
+            moving=(mph[0]>0.1);
+            charging=!moving&&(kW[0]<-1); // not moving and generating energy so must be charging
             if(laccOn&&!accOn){ // Car turned off
+                if (repeatPoll) { // Log on shutdown if autopoll enabled
+                    tripLog(); // Write trip log on powerdown
+                }
                 //write efficiency history data
-                cfile = fopen("/local/ehist.cny", "w");
-                if (cfile!=NULL){ // found a efficiency history file
+                hfile = fopen("/local/ehist.cny", "w");
+                if (hfile!=NULL){ // found a efficiency history file
                     for(i=0;i<39;i++){
-                        fprintf(cfile,"%f %f\r\n",mph[i],kW[i]);
+                        fprintf(hfile,"%f %f\r\n",mph[i],kW[i]);
                     }
-                    fclose(cfile);
+                    fclose(hfile);
+                }
+            }
+            if(!laccOn&&accOn){ // Car turned on
+                miles_trip[0]=0;
+                kWh_trip[0]=0;
+                wait5secs=5;
+                if(showHealth){
+                    saveDmode=dMode[0];
+                    dMode[0]=healthScreen;
                 }
             }
             laccOn=accOn;
-            if(!accOn&&!logEn&&userIdle&&!playbackEn){
-                //sprintf(sTemp,"Display Off %4.2f\n",accV);
-                //logMsg(sTemp);
-                dled = 0; // turn off display if car off and logging disabled and no user activity
+            if(!accOn&&!logEn&&userIdle&&!playbackEn){ // Car off and logging disabled and no user activity
+                dled = 0; 
             }else if(!headlights){
                 dled = ledHi;
-            }else{
+            } else {
                 dled = ledLo;
             }
-            
+            if(wait5secs>0){ // Wait a few seconds after poweron to give BMS time to measure CP's
+                wait5secs-=1;
+                if (repeatPoll&&(wait5secs==0)) { // Poll on startup if autopoll enabled
+                    logOnce=true;
+                    reqMsgCnt=0;
+                    msgReq.attach(&sendReq,0.015);
+                }
+            }
+            if(moving&&(saveDmode<99)&&(wait5secs==0)){
+                dMode[0]=saveDmode;
+                saveDmode=99;
+            }
+
             //compute historic efficiency
             if(numSsamples>0){ // Avoid div0
-                mph[0]=((float) motorRPM)/numSsamples/215; // Empirically derived - may change car to car
-            }else{
+                mph[0]=((float) motorRPM)/numSsamples/220; // Empirically derived with MXV4s - may change with different wheels&tires
+            } else {
                 mph[0]=0;
             }
             if(mph[0]>99){
                 mph[0]=0;
             }
-            mpkWh[0]=mph[0];
+            numSsamples=0;
+
             if(numWsamples>0){ // Avoid div0
+                mpkWh[0]=mph[0];
                 kW[0]=((float) mWs_x4)/numWsamples/4e3;     
                 mpkWh[0]/=kW[0];
                 if (mpkWh[0]<0) {
                     mpkWh[0]=99;// negative means inf.
                 }
-            }else{
+            } else {
                 kW[0]=0;
                 mpkWh[0]=0;
             }
-            //mpkWh[0]=floor(mpkWh[0]*10+0.5)/10; // Round to nearest 10th
+            numWsamples=0;
+
+            if (!charging){
+                miles_trip[0]+=mph[0]/3600;
+                miles_trip[1]+=mph[0]/3600;
+                miles_trip[2]+=mph[0]/3600;
+                kWh_trip[0]+=kW[0]/3600;
+                kWh_trip[1]+=kW[0]/3600;
+                kWh_trip[2]+=kW[0]/3600;
+            }
+            
             motorRPM=0;
-            numSsamples=0;
             mWs_x4=0;
-            numWsamples=0;
-            if(accOn||playbackEn){
+            
+            // Compute ESR
+            if((Imax-Imin)<40){ // do nothing - insufficient delta_I to measure
+                unloadedV_x2 = (curRmax+curRmin)/2;
+            }else if ((redRmax-redRmin)<(curRmax-curRmin)) {
+                Resr-=0.001;
+                unloadedV_x2 = (redRmax+redRmin)/2;
+            } else if ((incRmax-incRmin)<(curRmax-curRmin)) {
+                Resr+=0.001;
+                unloadedV_x2 = (incRmax+incRmin)/2;
+            } else {
+                unloadedV_x2 = (curRmax+curRmin)/2;
+            }
+            curRmin=1000;
+            curRmax=0;
+            incRmin=1000;
+            incRmax=0;
+            redRmin=1000;
+            redRmax=0;
+            Imax=-1000;
+            Imin=1000;
+
+            if((accOn||playbackEn)&&!charging){
                 for(i=1;i<39;i++){
                     average=mph[i]/timeConstant[i];
                     mph[i]-=average;
                     mph[i]+=mph[0];
                     mpkWh[i]=average;
                     average=kW[i]/timeConstant[i];
-                    kW[i]-=average;
-                    kW[i]+=kW[0];
+                    if(!charging){ //Not charging - so include in efficiency data
+                        kW[i]-=average;
+                        kW[i]+=kW[0];
+                    }
                     mpkWh[i]/=average;
                     if (mpkWh[i]<0) {
                         mpkWh[i]=99;// negative means inf.
@@ -624,31 +799,33 @@
                     //mpkWh[i]=floor(mpkWh[i]*10+0.5)/10; // Round to nearest 10th
                }
             }
-            updateDTE=true;
-            if(logCP)
-                logPackVoltages(); // Turbo3
-            tick=false;
-        }
+            if(logCP&&usbEn){
+                if(logOnce){
+                    tripLog();
+                    logOnce=false;
+                }
+                logPackVoltages(); // Turbo3, only call
+            }
+            if(!usbEn&&!waitasec){
+                usbEn=detectUSB(); // Keep looking if none found
+            }
+            waitasec=false; // work around to avoid hang when USB tries to init immediately
+            tock=true;
+        } // tick
 
         display=display<1?display+1:0; // toggle display
         updateDisplay(display);
-
-        if(pollCP){ // We do this inside main loop instead of ticker so CAN RX will not be blocked
-            sendCPreq(); // send cellpair data request.
-            wait_ms(16);
-            sendTreq(); //send temperature request
-            pollCP=false;
-        }
         
         if(step){ // playback
             if(playbackOpen&&playbackEn){
                 for(i=0;i<120;i++){
-                    if(!feof(file)){
-                        fscanf(file,"%5c%8c",&header,&data);
-                        logCan(header[0],CANMessage(0x7ff&((header[4]<<8)+header[3]), data, 8));
-                    }else{
-                        fclose(file); // restart
-                        file = fopen("/usb/playback.alc", "rb");                                          
+                    if(!f_eof(&efile)){
+                        efr=f_read(&efile,&header,5,&bytesRW);
+                        efr=f_read(&efile,&data,8,&bytesRW);
+                        logCan(header[0],CANMessage(0x7ff&((header[4]<<8)+header[3]), data, 8)); // Playback
+                    } else {
+                        f_close(&efile); // restart                                       
+                        efr = f_open(&efile,"playback.alc",FA_READ|FA_OPEN_EXISTING);
                         lastDMode[whichTouched]=99;//force refresh
                         spkr.beep(2000,0.25);
                     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sun Jul 21 23:59:00 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/e2ac27c8e93e
\ No newline at end of file
--- a/mbed41.bld	Wed Apr 10 13:31:35 2013 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-http://mbed.org/users/mbed_official/code/mbed/builds/10b9abbe79a6
\ No newline at end of file
--- a/utility.cpp	Wed Apr 10 13:31:35 2013 +0000
+++ b/utility.cpp	Sun Jul 21 23:59:00 2013 +0000
@@ -9,6 +9,8 @@
     userIdle=(++secsNoTouch>userTimeout)?true:false;
     LPC_RTC->ILR |= (1<<0); // clear interrupt to prepare for next
     tick=true;
+    // will use this to generate a logTP() just before the next Message received.
+    if( (time(NULL) % 60) == 0) ZeroSecTick = true; // gg - at 0-second of each minute
 }
 
 void RTC_Init (void) {
@@ -19,7 +21,7 @@
     NVIC_EnableIRQ( RTC_IRQn );
 }
 
-void logMsg (char *msg) {
+void printMsg (char *msg) {
     strcpy(displayLog[displayLoc],msg);
     displayLoc=displayLoc>17?0:displayLoc+1;
 }
@@ -37,165 +39,414 @@
 }
 
 void logCan (char mType, CANMessage canRXmsg) {
-    char sTemp[40];
-    unsigned short ts = getTimeStamp();
-    static unsigned char ii = 0, lasti = 0; // indexindex
-    unsigned char changed,i;
+
+   // re-arranged to put static first
+    static unsigned char ii = 0;
+    static unsigned char lasti = 0; // indexindex
     static unsigned char bdi=0;
-    signed short packV;
-    signed short packA;
     static signed short imotorRPM = 0;
+    static unsigned short nLost = 0; // gg - overrun
+ 
+    char sTemp[40];    
+    unsigned char changed;
+    unsigned short i,j,k;
+    signed short packV_x2;
+    signed short packA_x2;
     signed long imWs_x4;
+    unsigned short ts;
+
     secsNoMsg=0; // reset deadman switch
-    if(logOpen){
-        if(canRXmsg.id>0) {
-            writeBuffer[writePointer][0]=mType;
-            writeBuffer[writePointer][1]=(ts&0xff00)>>8;
-            writeBuffer[writePointer][2]=(ts&0x00ff);
-            writeBuffer[writePointer][3]=canRXmsg.id&0xff;
-            writeBuffer[writePointer][4]=(canRXmsg.id>>8)+(canRXmsg.len<<4);
-            for(i=5;i<13;i++){ // Is there a better way to do this? (writeBuffer[writePointer][i]=canRXmsg.data?)
-                writeBuffer[writePointer][i]=canRXmsg.data[i-5];
+    if(debugMode||(skin==ggSkin)){ 
+        // code to insert actual number of dropped frames for overrun debug - skiped in normal mode to keep logcan short
+        if(logOpen){
+            // check to see if buffer is already full (read - write) = 1
+            // actually the last buffer location cannot be used because then 
+            //   the buffer would look empty after writePointer++
+            
+            //if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen/16)) // modulo is slow?
+ 
+            // pointers are 0 through maxBufLen-1
+            if( (readPointer - writePointer) == 1 || (writePointer - readPointer) == (maxBufLen - 1)) {
+                // the buffer is "full", so Lose this message
+                
+                // point to the last-stored message
+                int tempWritePointer = writePointer - 1 ;
+                if( tempWritePointer < 0 ) tempWritePointer = maxBufLen - 1;
+                char strLost[9] ;
+ 
+                if( nLost == 0 ) {
+                    // this is the first message lost 
+                    //   and we must overwrite the last message with an FFE comment message
+                    // So, there will be two messages lost as the comment message is laid in.
+                    nLost = 2;
+                    sprintf(strLost,"%s","Lost0002"); // indicate two messages lost
+                    
+                    // overlay the last message with a "Lost0002" comment
+                    writeBuffer[tempWritePointer][0]=0;
+                    // leave the ts of the overlaid message
+                    //writeBuffer[tempWritePointer][1]=(ts&0xff00)>>8; // Time Stamp (2 bytes_
+                    //writeBuffer[tempWritePointer][2]=(ts&0x00ff);
+                    // force the MsgID to an Event Message 
+                    writeBuffer[tempWritePointer][3]=0xfe; // MsgID, low byte
+                    writeBuffer[tempWritePointer][4]=0xff; // Len nibble, and MsgID high nibble
+                    // lay in the "Lost0002" text
+                    for(i=5;i<13;i++){ 
+                        writeBuffer[tempWritePointer][i]= strLost[i-5];
+                    }
+                } else {
+                    // at least one message was previously lost
+                    // increment the loat counter
+                    nLost += 1;
+                    
+                    // lay the new count into the comment
+                    sprintf(strLost,"%04d",nLost);
+                    for(i=9;i<13;i++){ 
+                        writeBuffer[tempWritePointer][i]= strLost[i-9];
+                    }
+                }
+            } else {
+                // there is room to insert the message
+                // get it inserted quickly
+                ts=getTimeStamp(); 
+                writeBuffer[writePointer][0]=mType;
+                writeBuffer[writePointer][1]=(ts&0xff00)>>8; // Time Stamp (2 bytes_
+                writeBuffer[writePointer][2]=(ts&0x00ff);
+                writeBuffer[writePointer][3]=canRXmsg.id&0xff; // MsgID, low byte
+                char sLen = canRXmsg.len ;
+                writeBuffer[writePointer][4]=(canRXmsg.id>>8)+(sLen<<4); // Len nibble, and MsgID high nibble
+                for(i=0;i<8;i++){ // Is there a better way to do this? (writeBuffer[writePointer][i]=canRXmsg.data?)
+                    if(i<sLen) 
+                        writeBuffer[writePointer][i+5]=canRXmsg.data[i];
+                    else // i>=sLen
+                        // force unused data bytes to FF for CAN-Do compatibility
+                        writeBuffer[writePointer][i+5]=0xFF;
+                }
+                
+                //--------------
+                // Note, this is not protected from the interrupt.
+                // Due to the nLost code above, this no longer
+                //    overflows to writePointer = readPointer
+                //    which would make the buffer look empty
+                if (++writePointer >= maxBufLen) {
+                    writePointer = 0;
+                    led3 = !led3;
+                }
+                //--------------
+                // log a local message if we had lost messages. gg - logcan
+                if( nLost > 0 ) {
+                    // We previously lost messages that did not get into the buffer
+                    sprintf(sTemp,"-- Lost %d Messages.\n", nLost);
+                    printMsg(sTemp); // write buffer overrun
+                    //spkr.beep(500,0.25);
+                    
+                    nLost = 0 ;
+                }
+                //--------------
             }
-            if (++writePointer >= maxBufLen) {
+        }
+    }else{ // not debugMode - keep code short
+        if(logOpen){
+            NVIC_DisableIRQ(CAN_IRQn); // Block interrupts until write pointer assigned
+            int localWritePointer = writePointer++; // create local copy to make logCan reentrant
+            // note that the static variables do not prevent safe reentry
+            // since they are only used for msgId<0x800 which will never interrupt
+            // another msgId<0x800 (both CANbusses are same priority)
+            if (writePointer >= maxBufLen) {
                 writePointer = 0;
                 led3 = !led3;
             }
+            NVIC_EnableIRQ(CAN_IRQn); // Unblock interrupts once local pointer set and global pointer incremented
+            ts=getTimeStamp();
+            writeBuffer[localWritePointer][0]=mType;
+            writeBuffer[localWritePointer][1]=(ts&0xff00)>>8;
+            writeBuffer[localWritePointer][2]=(ts&0x00ff);
+            writeBuffer[localWritePointer][3]=canRXmsg.id&0xff;
+            writeBuffer[localWritePointer][4]=(canRXmsg.id>>8)+(canRXmsg.len<<4);
+            for(i=5;i<13;i++){ // Is there a better way to do this? (writeBuffer[localWritePointer][5]=canRXmsg.data?)
+                writeBuffer[localWritePointer][i]=canRXmsg.data[i-5];
+            }
+            if (writePointer==readPointer) {
+                // Just caught up to read pointer
+                printMsg("Write buffer overrun.\n"); // write buffer overrun
+                spkr.beep(500,0.25);
+            }
         }
     }
 
-    if(indexLastMsg[canRXmsg.id]==0) { //Check if no entry
-        ii=ii<99?ii+1:0;
-        indexLastMsg[canRXmsg.id]=ii; //Create entry if first message
-    }
-    if(dMode[0]==changedScreen||dMode[1]==changedScreen){
-        changed=msgChanged[indexLastMsg[canRXmsg.id]];
-        for(i=0;i<8;i++){
-            if(lastMsg[indexLastMsg[canRXmsg.id]].data[i]!=canRXmsg.data[i]){
-                changed |= 1<<i;
+    if(canRXmsg.id<0x800){ // Block FFE and FFF messages
+        if(indexLastMsg[canRXmsg.id]==0) { //Check if no entry
+            //ii=ii<99?ii+1:0; // Should never wrap - less than 100 different messages ever used
+            if(ii<99) {
+                //indexLastMsg[canRXmsg.id]=ii; //Create entry if first message
+                indexLastMsg[canRXmsg.id]=++ii; //Create entry for first MsgID occurance
+                // ii max is 99 here
+            } else {
+                // the ii array is full, more than 100 MsgIDs found
+                if(ii==99) {
+                    ii++; // step to 100 to log only one error
+                    printMsg("MsgID buffer overrun.\n"); // write buffer overrun
+                }
             }
         }
-        msgChanged[indexLastMsg[canRXmsg.id]]=changed;
-    }
-
-    lastMsg[indexLastMsg[canRXmsg.id]]=canRXmsg; //Store in table
-
-    //Miscellaneous on-recieve operations below
-    if((mType==2)&&(canRXmsg.id==0x358)){ // headlight/turn signal indicator
-        headlights = (canRXmsg.data[1]&0x80)?true:false;
-    }else if((mType==1)&&(canRXmsg.id==0x7bb)){ // is battery data?  Need to store all responses
-        if(canRXmsg.data[0]<0x20){
-            if(canRXmsg.data[3]==2){//cellpair data
-                bdi=0;
-                sprintf(sTemp,"Getting cell pair data\n");
-                logMsg(sTemp);
-            }else if(canRXmsg.data[3]==4){//temperature data
-                bdi=0x20;
-                sprintf(sTemp,"Getting temperature data\n");
-                logMsg(sTemp);
-            }else bdi=0;
-            lasti=0;
-        }
-        i=canRXmsg.data[0]&0x0f; //lower nibble of D0 is index
-        if(lasti>i){ //detect rollover and offset index appropriately
-            bdi=0x10;
+        
+        //----------------
+        if(dMode[0]==changedScreen||dMode[1]==changedScreen){// Skip if not using (for execution speed)
+            changed=msgChanged[indexLastMsg[canRXmsg.id]];
+            // This is cleared in the main loop when reset button is touched
+            for(i=0;i<8;i++){
+                if(lastMsg[indexLastMsg[canRXmsg.id]].data[i]!=canRXmsg.data[i]){
+                    changed |= 1<<i;
+                }
+            }
+            msgChanged[indexLastMsg[canRXmsg.id]]=changed;
         }
-        lasti=i; //remember the msb to detect rollover next time around
-        i+=bdi;
-        //if(i==22) logCP=true; //Turbo3
-        //if( (i==22) && (yesBattLog) ) logCP=true; // only if enabled gg - Batt Log 
-        if(i==22){
-            logCP=yesBattLog; // Only log is logging enabled
-            showCP=true; // Always show
-        }
-        i*=7;
-        if(i<0xfa){ // Is there a better way to do this?
-            battData[i+0]=canRXmsg.data[1];
-            battData[i+1]=canRXmsg.data[2];
-            battData[i+2]=canRXmsg.data[3];
-            battData[i+3]=canRXmsg.data[4];
-            battData[i+4]=canRXmsg.data[5];
-            battData[i+5]=canRXmsg.data[6];
-            battData[i+6]=canRXmsg.data[7];
+    
+        lastMsg[indexLastMsg[canRXmsg.id]]=canRXmsg; //Store in table
+        
+        //-------------------
+        //Miscellaneous on-recieve operations below
+        if((mType==1)&&(canRXmsg.id==0x7bb)){ // is battery data?  Need to store all responses
+            if(canRXmsg.data[0]<0x20){
+                if(canRXmsg.data[3]==1){//Group 1 data
+                    bdi=BatDataBaseG1; // index offset for Group 1 data
+                    if(debugMode){
+                        printMsg("  Getting Group 1 data\n");
+                    }
+                    
+                }else if(canRXmsg.data[3]==2){//Group 2 = cellpair data
+                    bdi=BatDataBaseG2; // index offset for CP data
+                    if(debugMode){
+                        printMsg("  Getting cell pair data\n");
+                    }
+                    
+                }else if(canRXmsg.data[3]==3){//Group 3 data
+                    bdi=BatDataBaseG3; // index offset for Group 3 data
+                    if(debugMode){
+                        printMsg("  Getting Group 3 data\n");
+                    }
+                    
+                }else if(canRXmsg.data[3]==4){//Group 4 = temperature data
+                    bdi=BatDataBaseG4; // index offset for Temperature data
+                    if(debugMode){
+                        printMsg("  Getting temperature data\n");
+                    }
+                    
+                }else if(canRXmsg.data[3]==5){//Group 5 data
+                    bdi=BatDataBaseG5; // index offset for Group 5 data
+                    if(debugMode){
+                        printMsg("  Getting Group 5 data\n");
+                    }
+                    
+                }else if(canRXmsg.data[3]==6){//Group 6 data = shunt data
+                    bdi=BatDataBaseG6; // index offset for Group 6 data
+                    if(debugMode){
+                        printMsg("  Getting Group 6 data\n");
+                    }
+                    
+                }else bdi=0xff; // ignore other messages (for now)
+                lasti=0;
+            }
+            
+            if(bdi<0xff){
+                i=canRXmsg.data[0]&0x0f; //lower nibble of D0 is index
+                if(lasti>i){ //detect rollover and offset index appropriately
+                    bdi += 0x10; // for CP data
+                }
+                lasti=i; //remember the msb to detect rollover next time around
+                i+=bdi;
+                //-------
+                //-------
+                i*=7;
+                if(i+6 < BatDataBufMax) {
+                    battData[i+0]=canRXmsg.data[1];
+                    battData[i+1]=canRXmsg.data[2];
+                    battData[i+2]=canRXmsg.data[3];
+                    battData[i+3]=canRXmsg.data[4];
+                    battData[i+4]=canRXmsg.data[5];
+                    battData[i+5]=canRXmsg.data[6];
+                    battData[i+6]=canRXmsg.data[7];
+                }
+                if(i==(BatDataBaseG6+3)*7){ // All data loaded
+                    logCP=yesBattLog; // Only log if logging enabled
+                    showCP=true; // Always show
+                    
+                    // Find hottest temperature by finding smallest ADC value
+                    // 2013 models only have three sensors
+                    k=battData[(BatDataBaseG4*7)+3]*0x100+battData[(BatDataBaseG4*7)+4];
+                    j=battData[(BatDataBaseG4*7)+6]*0x100+battData[(BatDataBaseG4*7)+7];
+                    if(j<k)k=j;
+                    j=battData[(BatDataBaseG4*7)+9]*0x100+battData[(BatDataBaseG4*7)+10];
+                    if(j<k)k=j;
+                    j=battData[(BatDataBaseG4*7)+12]*0x100+battData[(BatDataBaseG4*7)+13];
+                    if(j<k)k=j;
+                    //interpolate from lookup table
+                    unsigned short temp_adc[8] = {1000,589,487,401,365,340,309,000};
+                    float            temp_C[8] = { -27, 13, 23, 32, 36, 39, 43, 76};
+                    char ii=0;
+                    while(k<=temp_adc[++ii]) { } // Find section in table
+                    maxTemp=(float)(k-temp_adc[ii]);
+                    maxTemp/=(float)(temp_adc[ii-1]-temp_adc[ii]);
+                    maxTemp*=(temp_C[ii-1]-temp_C[ii]);
+                    maxTemp+=temp_C[ii];
+
+                    // Get state of health
+                    SOH_x100=battData[(BatDataBaseG1*7)+29]*0x100+battData[(BatDataBaseG1*7)+30];
+                    Ah_x10000=battData[(BatDataBaseG1*7)+36]*0x10000+battData[(BatDataBaseG1*7)+37]*0x100+battData[(BatDataBaseG1*7)+38];
+                    SOC_x10000=battData[(BatDataBaseG1*7)+32]*0x10000+battData[(BatDataBaseG1*7)+33]*0x100+battData[(BatDataBaseG1*7)+34];
+                    
+                    // Save shunt data
+                    for(j=0; j<24; j++){
+                        shunt[j*4+0]=battData[BatDataBaseG6*7+j+3]&0x08;
+                        shunt[j*4+1]=battData[BatDataBaseG6*7+j+3]&0x04;
+                        shunt[j*4+2]=battData[BatDataBaseG6*7+j+3]&0x02;
+                        shunt[j*4+3]=battData[BatDataBaseG6*7+j+3]&0x01;
+                    }
+                }
+            }
+        }else if((mType==1)&&(canRXmsg.id==0x1db)){ //Battery Volts and Amps
+            packV_x2=((canRXmsg.data[2]<<2)|(canRXmsg.data[3]>>6)); // 1 LSB = 0.5V
+            packA_x2=((canRXmsg.data[0]<<3)|(canRXmsg.data[1]>>5)); // 1 LSB = 0.5A
+            if(packA_x2>0x03ff){
+                packA_x2|=0xf800;//extend sign;
+            }
+            packA_x2 -= 1; //Slight correction to value required (unique to my Leaf?)
+            if (-packA_x2<Imin){
+                Imin=-packA_x2;
+            } else if (-packA_x2>Imax){
+                Imax=-packA_x2;
+            }
+            imWs_x4 = packV_x2; // Volts*milliSeconds*2
+            imWs_x4 *= -packA_x2; // milliWattseconds*4
+            mWs_x4 += imWs_x4; // total mWs_x4
+            float temp;
+            temp = Resr;
+            temp *= (float) -packA_x2;
+            temp += (float) packV_x2;
+            if(temp>curRmax){
+                curRmax=temp;
+            } else if(temp<curRmin){
+                curRmin=temp;
+            }
+            temp = Resr-0.001;
+            temp *= (float) -packA_x2;
+            temp += (float) packV_x2;
+            if(temp>redRmax){
+                redRmax=temp;
+            } else if(temp<redRmin){
+                redRmin=temp;
+            }
+            temp = Resr+0.001;
+            temp *= (float) -packA_x2;
+            temp += (float) packV_x2;
+            if(temp>incRmax){
+                incRmax=temp;
+            } else if(temp<incRmin){
+                incRmin=temp;
+            }
+            numWsamples++;
+        }else if((mType==1)&&(canRXmsg.id==0x1da)){ //Motor Speed
+            imotorRPM=((canRXmsg.data[4]<<8)|(canRXmsg.data[5]));
+            if(imotorRPM<0){ // take absolute value
+                imotorRPM=-imotorRPM;
+            }
+            motorRPM+=imotorRPM;
+            numSsamples++;
         }
-    }else if((mType==1)&&(canRXmsg.id==0x1db)){ //Battery Volts and Amps
-        packV=((canRXmsg.data[2]<<2)|(canRXmsg.data[3]>>6)); // 1 LSB = 0.5V
-        packA=((canRXmsg.data[0]<<3)|(canRXmsg.data[1]>>5)); // 1 LSB = 0.5A
-        if(packA>0x03ff){
-            packA|=0xf800;//extend sign;
-        }
-        imWs_x4 = packV; // Volts*milliSeconds*2
-        imWs_x4 *= -packA; // milliWattseconds*4
-        if (!((imotorRPM<2)&&(imWs_x4<0))){ //Ignore if charging from wall
-            mWs_x4 += imWs_x4; // total mWs_x4
-            numWsamples++;
-        }
-    }else if((mType==1)&&(canRXmsg.id==0x1da)){ //Motor Speed
-        imotorRPM=((canRXmsg.data[4]<<8)|(canRXmsg.data[5]));
-        if(imotorRPM<0){ // take absolute value
-            imotorRPM=-imotorRPM;
-        }
-        motorRPM+=imotorRPM;
-        numSsamples++;
     }
 }
 
+//-----------------------------
 void logTS () {
     CANMessage tsMsg;
     unsigned long secs = time(NULL); // seconds past 12:00:00 AM 1 Jan 1900
+    // NOTE: In Mbed, I believe that this is seconds past start of 1970, not 1900
+    //   but this is good, since seconds past 1970 is what CAN-Do expects. GG - Date Time
     tsMsg.id=0xfff;
     tsMsg.len=0xf;
-    tsMsg.data[0]=secs&0xff;
+    tsMsg.data[0]=secs&0xff; 
     tsMsg.data[1]=(secs>>8)&0xff;
     tsMsg.data[2]=(secs>>16)&0xff;
-    tsMsg.data[3]=secs>>24;
-    tsMsg.data[4]=0xff;
-    tsMsg.data[5]=0xff;
-    tsMsg.data[6]=0xff;
+    tsMsg.data[3]=(secs>>24)&0xff;
+    tsMsg.data[4]=0; // 0xff; gg - Date Time
+    tsMsg.data[5]=0; // 0xff; for CAN-Do
+    tsMsg.data[6]=0; // 0xff;
     tsMsg.data[7]=0xff;
-    logCan(0,tsMsg);
+    logCan(0,tsMsg); // Date-Time
+}
+
+void logEvent (char * errMsg) {
+    // log CAN-Do 8-character Pseudo Message
+    CANMessage tsMsg;
+    tsMsg.id=0xffe; // pseudo Message to CAN-Do log
+    tsMsg.len=0xf;
+    int iMsgLen = strlen(errMsg);
+    // 8 character message compatible with CAN-Do
+    for(int i=0; i<8; i++){
+      tsMsg.data[i]=' '; 
+      if( i < iMsgLen ) tsMsg.data[i]=errMsg[i];
+    }
+    logCan(0,tsMsg); // FFE Comment Message
 }
 
-void sendCPreq() {
-    char i;
-    char data[8] = {0x02, 0x21, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff};
-    can1.monitor(false); // set to active mode
-    can1SleepMode = 0; // enable TX
-    can1.write(CANMessage(0x79b, data, 8));
-    logCan(1,CANMessage(0x79b, data, 8));
-    data[0]=0x30; //change to request next line message
-    data[1]=0x01;
-    data[2]=0x00;
-    for(i=0;i<29;i++){
-        wait_ms(16); //wait 16ms
+void sendReq() {
+    static char data[8] = {0x02, 0x21, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff};
+    if(reqMsgCnt<99){
+        switch (reqMsgCnt){
+            case BatDataBaseG1:
+                can1.monitor(false); // set to active mode
+                can1SleepMode = 0; // enable TX
+                data[0]=0x02; //change to request group 1
+                data[1]=0x21;
+                data[2]=0x01;
+                break;
+            case BatDataBaseG2: // group 1 has 6 frames
+                data[0]=0x02; //change to request group 2 (cp data)
+                data[1]=0x21;
+                data[2]=0x02;
+                break;
+            case BatDataBaseG3: // group 2 has 29 frames
+                data[0]=0x02; //change to request group 3
+                data[1]=0x21;
+                data[2]=0x03;
+                break;
+            case BatDataBaseG4: // group 3 has 5 frames
+                data[0]=0x02; //change to request group 4 (temperature)
+                data[1]=0x21;
+                data[2]=0x04;
+                break;
+            case BatDataBaseG5: // group 4 has 3 frames
+                data[0]=0x02; //change to request group 5
+                data[1]=0x21;
+                data[2]=0x05;
+                break;
+            case BatDataBaseG6: // group 4 has 3 frames
+                data[0]=0x02; //change to request group 5
+                data[1]=0x21;
+                data[2]=0x06;
+                break;
+            case BatDataBaseG7: // group 5 has 11 frames
+                reqMsgCnt = 99;
+                can1SleepMode = VP230Sleep; // disable TX
+                can1.monitor(true); // set to snoop mode
+                msgReq.detach(); // stop ticker
+            default:
+                data[0]=0x30; //change to request next line message
+                data[1]=0x01;
+                data[2]=0x00;
+        }
         can1.write(CANMessage(0x79b, data, 8));
+        reqMsgCnt++;
     }
-    can1SleepMode = 1; // disable TX
-    can1.monitor(true); // set to snoop mode
 }
 
-void sendTreq() {
-    char i;
-    char data[8] = {0x02, 0x21, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff};
-    can1.monitor(false); // set to active mode
-    can1SleepMode = 0; // enable TX
-    can1.write(CANMessage(0x79b, data, 8));
-    logCan(1,CANMessage(0x79b, data, 8));
-    data[0]=0x30; //change to request next line message
-    data[1]=0x01;
-    data[2]=0x00;
-    for(i=0;i<3;i++){
-        wait_ms(16); //wait 16ms
-        can1.write(CANMessage(0x79b, data, 8));
-    }
-    can1SleepMode = 1; // disable TX
-    can1.monitor(true); // set to snoop mode
+void autoPollISR(){
+    //char sTemp[40]; // just for debug
+    //sprintf(sTemp,"Requesting cp data\n"); // just for debug
+    //printMsg(sTemp); // just for debug
+    reqMsgCnt = 0; //reset message counter
+    msgReq.attach(&sendReq,0.015);
 }
 
-void autoPollISR() {  //This is the ticker ISR for auto-polling
-    pollCP=true;    //Set a flag to do in main loop instead of here
-}                   //since ticker blocks other interrupts
-
 void playbackISR() { //Used for autoplayback
     step=true;
 }
@@ -206,15 +457,24 @@
 void recieve1() {
     CANMessage msg1;
     can1.read(msg1);
-    logCan(1, msg1); //EVcan
-    led1 = !led1;
+    
+    if( ZeroSecTick ) { ZeroSecTick = false; logTS(); } // gg - 0-second EV bus
+    if(msg1.id>0) {
+        logCan(1, msg1); // EVcan Message Received
+        led1 = !led1;
+    }
 }
 
 void recieve2() {
     CANMessage msg2;
     can2.read(msg2);
-    logCan(2, msg2); //CARcan
-    led2 = !led2;
+    
+    if( ZeroSecTick ) { ZeroSecTick = false; logTS(); } // gg - 0-second EV bus
+    
+    if(msg2.id>0) {
+        logCan(2, msg2); // CARcan Message Received
+        led2 = !led2;
+    }
 }
 
 unsigned char buttonX(unsigned short X, unsigned char columns) {
@@ -229,8 +489,8 @@
 
 void saveConfig(){
     FILE *cfile;
-    cfile = fopen("/local/config.txt", "w");
-    fprintf(cfile,"format 3\r\n");
+    cfile = fopen("/local/config.txt", "w");    
+    fprintf(cfile,"format 6\r\n");
     fprintf(cfile,"x0_off %d\r\n",tt.x0_off);
     fprintf(cfile,"y0_off %d\r\n",tt.y0_off);
     fprintf(cfile,"x0_pp %d\r\n",tt.x0_pp);
@@ -240,11 +500,11 @@
     fprintf(cfile,"x1_pp %d\r\n",tt.x1_pp);
     fprintf(cfile,"y1_pp %d\r\n",tt.y1_pp);
     fprintf(cfile,"x_mid %d\r\n",tt.x_mid);
-    if (dMode[0]==config1Screen)
+    if (dMode[0]==configScreen)
         fprintf(cfile,"dMode0 %d\r\n",mainScreen);
     else
         fprintf(cfile,"dMode0 %d\r\n",dMode[0]);
-    if (dMode[1]==config1Screen)
+    if (dMode[1]==configScreen)
         fprintf(cfile,"dMode1 %d\r\n",mainScreen);
     else
         fprintf(cfile,"dMode1 %d\r\n",dMode[1]);
@@ -254,59 +514,76 @@
     fprintf(cfile,"scale12V %4.2f\r\n",scale12V);
     fprintf(cfile,"skin %d\r\n",skin);
     fprintf(cfile,"dtePeriod %d\r\n",dtePeriod);
+    fprintf(cfile,"DebugMode %d\r\n",(debugMode?1:0));
+    fprintf(cfile,"metric %d\r\n",(metric?1:0));
+    fprintf(cfile, "firmware %d\r\n", fwCount );            
+    fprintf(cfile,"showHealth %d\r\n",(showHealth?1:0));
     fclose(cfile);
 }
 
- void readConfig(){
+void readConfig(){
     FILE *cfile;
     int ff;
-    char sTemp[40];
+
     cfile = fopen("/local/config.txt", "r");
     if (cfile==NULL){ // if doesn't exist --> create
-        sprintf(sTemp,"No config file found.\n");
-        logMsg(sTemp);
-        sprintf(sTemp,"Calibrating touch screen.\n");
-        logMsg(sTemp);
+        printMsg("No config file found.\n"); // no config file
+        printMsg("Calibrating touch screen.\n"); // calibrating
         //tt.setcal(5570, 34030, 80, 108, 33700, 5780, 82, 108, 32500);// bypass calibration using my values
         tt.calibrate();   // run touchscreen calibration routine
         // NOTE: calibrates screen 1 first, then screen 0.
         saveConfig();
     } else {
-        ledHi = 0.823;
+        ledHi = 0.8;
         ledLo = 0.1;
-        pollInt = 300;
+        pollInt = 60;
         scale12V = 16.2;
         skin = ttSkin;
-        fscanf(cfile, "format %d\r\n", &ff ) ;
-        fscanf(cfile, "x0_off %d\r\n", &tt.x0_off ) ;
-        fscanf(cfile, "y0_off %d\r\n", &tt.y0_off ) ;
-        fscanf(cfile, "x0_pp %d\r\n", &tt.x0_pp ) ;
-        fscanf(cfile, "y0_pp %d\r\n", &tt.y0_pp ) ;
-        fscanf(cfile, "x1_off %d\r\n", &tt.x1_off ) ;
-        fscanf(cfile, "y1_off %d\r\n", &tt.y1_off ) ;
-        fscanf(cfile, "x1_pp %d\r\n", &tt.x1_pp ) ;
-        fscanf(cfile, "y1_pp %d\r\n", &tt.y1_pp ) ;
-        fscanf(cfile, "x_mid %d\r\n", &tt.x_mid ) ;
-        fscanf(cfile, "dMode0 %d\r\n", &dMode[0] ) ;
-        fscanf(cfile, "dMode1 %d\r\n", &dMode[1] ) ;
+        fscanf(cfile, "format %d\r\n", &ff );
+        fscanf(cfile, "x0_off %d\r\n", &tt.x0_off );
+        fscanf(cfile, "y0_off %d\r\n", &tt.y0_off );
+        fscanf(cfile, "x0_pp %d\r\n", &tt.x0_pp );
+        fscanf(cfile, "y0_pp %d\r\n", &tt.y0_pp );
+        fscanf(cfile, "x1_off %d\r\n", &tt.x1_off );
+        fscanf(cfile, "y1_off %d\r\n", &tt.y1_off );
+        fscanf(cfile, "x1_pp %d\r\n", &tt.x1_pp );
+        fscanf(cfile, "y1_pp %d\r\n", &tt.y1_pp );
+        fscanf(cfile, "x_mid %d\r\n", &tt.x_mid );
+        fscanf(cfile, "dMode0 %d\r\n", &dMode[0] );
+        fscanf(cfile, "dMode1 %d\r\n", &dMode[1] );
         if(ff>1){
-            fscanf(cfile, "ledHi %f\r\n", &ledHi ) ;
-            fscanf(cfile, "ledLo %f\r\n", &ledLo ) ;
-            fscanf(cfile, "pollInt %d\r\n", &pollInt ) ;
-            fscanf(cfile, "scale12V %f\r\n", &scale12V ) ;
+            fscanf(cfile, "ledHi %f\r\n", &ledHi );
+            fscanf(cfile, "ledLo %f\r\n", &ledLo );
+            fscanf(cfile, "pollInt %d\r\n", &pollInt );
+            fscanf(cfile, "scale12V %f\r\n", &scale12V );
         }
         if(ff>2){
-            fscanf(cfile, "skin %d\r\n", &skin ) ;
-            fscanf(cfile, "dtePeriod %d\r\n", &dtePeriod ) ;
+            fscanf(cfile, "skin %d\r\n", &skin );
+            fscanf(cfile, "dtePeriod %d\r\n", &dtePeriod );
+        }
+        if(ff>3){
+            int iDebug;
+            fscanf(cfile, "DebugMode %d\r\n", &iDebug );            
+            debugMode = (bool)iDebug;
+        }
+        if(ff>4) {
+            int iMetric;
+            fscanf(cfile, "metric %d\r\n", &iMetric );            
+            metric = (bool)iMetric; 
+            fscanf(cfile, "firmware %d\r\n", &iMetric );
+            fwCount = iMetric;
+        }
+        if(ff>5){
+            int ishowHealth;
+            fscanf(cfile, "showHealth %d\r\n", &ishowHealth );            
+            showHealth = (bool)ishowHealth;
         }
         fclose(cfile);
-        if(ff<3){//If not latest format, save as latest format
+        if(ff<6){//If not latest format, save as latest format
             saveConfig();
-            sprintf(sTemp,"Config file format updated.\n");
-            logMsg(sTemp);
+            printMsg("Config file format updated.\n"); // config forat updates
         }
-        sprintf(sTemp,"Config file loaded.\n");
-        logMsg(sTemp);
+        printMsg("Config file loaded.\n"); // config file loaded
     }
 }
 
@@ -369,8 +646,69 @@
     struct tm t; // pointer to a static tm structure
     short unsigned max, min, jv, i, bd;
     unsigned avg;
-    unsigned short gids, SOC, packV;
-    signed short packA;
+    unsigned short gids, SOC, packV_x2;
+    signed short packA_x2;
+    time_t seconds ;
+    
+    CANMessage msg;
+    
+    seconds = time(NULL); // Turbo3
+    t = *localtime(&seconds) ; // Turbo3 
+    
+    msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids
+    gids = (msg.data[0]<<2)+(msg.data[1]>>6);
+    msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC
+    SOC = (msg.data[0]<<2)+(msg.data[1]>>6);
+    msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts
+    packV_x2 = (msg.data[2]<<2)+(msg.data[3]>>6);
+    packA_x2 = (msg.data[0]<<3)+(msg.data[1]>>5);
+    if (packA_x2 & 0x400) packA_x2 |= 0xf800;
+    
+    max=0;
+    min=9999;
+    avg=0;
+    for(i=0; i<96; i++) {
+        bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
+        avg+=bd;
+        if(bd>max) max=bd;
+        if(bd<min) min=bd;
+    }
+    avg /= 96;
+    if(min<3713) {
+        jv=avg-(max-avg)*1.5;
+    } else { // Only compute judgement value if min cellpair meets <= 3712mV requirement
+        jv=0;
+    }
+    
+    FIL bfile;
+    FRESULT bfr;
+    bfr = f_open(&bfile,"batvolt.txt",FA_WRITE|FA_OPEN_ALWAYS);
+    if(bfr==FR_OK) {
+        f_lseek(&bfile,0xffffffff); // go to end of file to append
+        strftime(sTemp, 40, "%a %m/%d/%Y %X", &t);
+        f_printf(&bfile,"%s,",sTemp);
+        sprintf(sTemp,"%d,%5.1f%%,%5.1f,%5.1f,%d,%d,%d,%d,%d",gids,(float)SOC/10,(float)packV_x2/2,(float)packA_x2/2,max,min,avg,max-min,jv);
+        f_printf(&bfile,"%s,",sTemp);           
+        f_printf(&bfile,"%d,%d,%d,%d,",(battData[(BatDataBaseG4*7)+ 3]<<8)+battData[(BatDataBaseG4*7)+ 4],battData[(BatDataBaseG4*7)+ 5],(battData[(BatDataBaseG4*7)+ 6]<<8)+battData[(BatDataBaseG4*7)+ 7],battData[(BatDataBaseG4*7)+ 8]);
+        f_printf(&bfile,"%d,%d,%d,%d", (battData[(BatDataBaseG4*7)+ 9]<<8)+battData[(BatDataBaseG4*7)+10],battData[(BatDataBaseG4*7)+11],(battData[(BatDataBaseG4*7)+12]<<8)+battData[(BatDataBaseG4*7)+13],battData[(BatDataBaseG4*7)+14]);
+        for(i=0; i<96; i++) {
+            bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
+            f_printf(&bfile,",%d",bd);
+        }
+        f_printf(&bfile,"\r\n");
+        f_close(&bfile);
+    }
+    logCP=false;
+    showCP=true;
+}
+
+void tripLog() { // Daily log
+    char sTemp[40];
+    struct tm t; // pointer to a static tm structure
+    short unsigned max, min, jv, i, bd;
+    unsigned avg;
+    unsigned short gids, SOC, packV_x2;
+    signed short packA_x2;
     time_t seconds ;
     
     CANMessage msg;
@@ -383,15 +721,15 @@
     msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC
     SOC = (msg.data[0]<<2)+(msg.data[1]>>6);
     msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts
-    packV = (msg.data[2]<<2)+(msg.data[3]>>6);
-    packA = (msg.data[0]<<3)+(msg.data[1]>>5);
-    if (packA & 0x400) packA |= 0xf800;
+    packV_x2 = (msg.data[2]<<2)+(msg.data[3]>>6);
+    packA_x2 = (msg.data[0]<<3)+(msg.data[1]>>5);
+    if (packA_x2 & 0x400) packA_x2 |= 0xf800;
     
     max=0;
     min=9999;
     avg=0;
     for(i=0; i<96; i++) {
-        bd=(battData[i*2+3]<<8)+battData[i*2+4];
+        bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
         avg+=bd;
         if(bd>max) max=bd;
         if(bd<min) min=bd;
@@ -403,22 +741,167 @@
         jv=0;
     }
     
-    FILE *bfile;
-    //bfile = fopen("/local/batvolt.txt", "a");
-    bfile = fopen("/usb/batvolt.txt", "a");
-    if(bfile!=NULL) {
+    FIL bfile;
+    FRESULT bfr;
+    bfr = f_open(&bfile,"triplog.txt",FA_WRITE|FA_OPEN_ALWAYS);
+    if(bfr==FR_OK) {
+        f_lseek(&bfile,0xffffffff); // go to end of file to append
         strftime(sTemp, 40, "%a %m/%d/%Y %X", &t);
-        fprintf(bfile,"%s,%d,%5.1f%%,%5.1f,%5.1f,%d,%d,%d,%d,%d",sTemp,gids,(float)SOC/10,(float)packV/2,(float)packA/2,max,min,avg,max-min,jv);
-        fprintf(bfile,"%d,%d,%d,%d,",(battData[224+ 3]<<8)+battData[224+ 4],battData[224+ 5],(battData[224+ 6]<<8)+battData[224+ 7],battData[224+ 8]);
-        fprintf(bfile,"%d,%d,%d,%d", (battData[224+ 9]<<8)+battData[224+10],battData[224+11],(battData[224+12]<<8)+battData[224+13],battData[224+14]);
-        for(i=0; i<96; i++) {
-            bd=(battData[i*2+3]<<8)+battData[i*2+4];
-            fprintf(bfile,",%d",bd);
+        f_printf(&bfile,"%s,",sTemp);
+        sprintf(sTemp,"%3.1f,%d,%5.1f%%,%5.1f%%, %4.2f, %5.1f,%4.1f,%d,%d,%d,%d,%d",accV,gids,(float)SOC/10, (float)SOH_x100/100,(float)Ah_x10000/10000,(float)packV_x2/2,(float)packA_x2/2,max,min,avg,max-min,jv);      
+        f_printf(&bfile,"%s,",sTemp);           
+        f_printf(&bfile,"%d,%d,%d,%d,",(battData[(BatDataBaseG4*7)+ 3]<<8)+battData[(BatDataBaseG4*7)+ 4],battData[(BatDataBaseG4*7)+ 5],(battData[(BatDataBaseG4*7)+ 6]<<8)+battData[(BatDataBaseG4*7)+ 7],battData[(BatDataBaseG4*7)+ 8]);
+        f_printf(&bfile,"%d,%d,%d,%d", (battData[(BatDataBaseG4*7)+ 9]<<8)+battData[(BatDataBaseG4*7)+10],battData[(BatDataBaseG4*7)+11],(battData[(BatDataBaseG4*7)+12]<<8)+battData[(BatDataBaseG4*7)+13],battData[(BatDataBaseG4*7)+14]);
+        /*for(i=0; i<96; i++) {
+            bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4];
+            f_printf(&bfile,",%d",bd);
+        }*/
+        // temporariliy dump everything
+        for(i=0; i<BatDataBufMax; i++) {
+            sprintf(sTemp,",%02x",battData[i]);
+            f_printf(&bfile,"%s",sTemp);
         }
-        fprintf(bfile,"\r\n");
-        fclose(bfile);
+
+        f_printf(&bfile,"\r\n");
+        f_close(&bfile);
     }
-    logCP=false;
-    showCP=true;
 }
 
+//LM - updates firmware off a usb key, eliminating the need to plug
+//the CANary into a computer for updates.
+void updateFirmware()
+{
+    FIL sfile; // external usb file
+    FRESULT sfr; // external file access flags
+    unsigned int bytesRW;
+    char sTemp[40];
+    const int bufSize = 2048;
+    char buffer[bufSize];
+    FILE *destFile;    
+
+    // Check for config file on USB drive
+    sfr = f_open(&sfile,"CONFIG.TXT",FA_READ|FA_OPEN_EXISTING);    
+    if(sfr == FR_OK)
+    {        
+        printf("Copy config file from USB\n");
+        destFile = fopen("/local/CONFIG.TXT", "w");
+        while (!f_eof(&sfile))
+        {
+            sfr=f_read(&sfile,&buffer,bufSize,&bytesRW);
+            fwrite(buffer, 1, bytesRW, destFile);
+        }
+        fflush(destFile);
+        fclose(destFile);
+        f_close(&sfile);
+    }    
+    
+    sfr = f_open(&sfile,"firmware.bin",FA_READ|FA_OPEN_EXISTING);    
+    if(sfr != FR_OK)
+    {        
+        sprintf(sTemp,"Couldn't find firmware.bin\n");
+        printf(sTemp);
+        wait(3);
+        lastDMode[whichTouched]=99;//force refresh
+        return;
+    }
+    fwCount ++;
+    saveConfig();    
+    tt.cls();
+    printf("Saved Configuration\n");
+    //delete all bin files in /local
+    DIR *dir;
+    struct dirent *ent;
+    printf("Starting update\n");
+    printf("deleting old firmware files\n");
+    if ((dir = opendir ("/local/")) != NULL) {
+      // print all the files and directories within directory
+      while ((ent = readdir (dir)) != NULL) {
+            //printf("FILE: %s\n",ent->d_name);
+            char dest[4] = "";
+            strncat(dest, &ent->d_name[strlen(ent->d_name)-3],3);            
+            dest[0] = tolower(dest[0]);
+            dest[1] = tolower(dest[1]);
+            dest[2] = tolower(dest[2]);                        
+            if(strcmp(dest,"bin")==0)
+            {                            
+                sprintf(sTemp,"/local/%s",ent->d_name);
+                int result = remove(sTemp);       
+                printf("REMOVED: %s",ent->d_name);
+            }
+      }
+      closedir (dir);
+    } else {
+      //could not open directory
+        printf("Couldnt open folder");
+        wait(3);
+        return;
+    }        
+    printf("copying new firmware\n");
+    tt.cls();
+    //Copy the new firmware from usb->local
+    //The newest bin file is the one that is used by the mbed  
+    sprintf(sTemp,"/local/fw%d.bin",fwCount);
+    printf("Writing %s\n",sTemp);
+    wait(2);
+    destFile = fopen(sTemp, "wb");
+    if(destFile == NULL)
+    {
+        printf("Couldn't Open Destination\n");
+        wait(3);
+        return;
+    }
+
+    while (!f_eof(&sfile))
+    {
+        sfr=f_read(&sfile,&buffer,bufSize,&bytesRW);
+        fwrite(buffer, 1, bytesRW, destFile);
+    }
+
+    fflush(destFile);
+    fclose(destFile);
+    f_close(&sfile);
+    tt.cls();
+    printf("Succesful\n\n");
+    printf("Rebooting in 3 seconds\n");
+    wait(3);
+    //Now run new firmware
+    mbed_reset();
+}
+
+bool detectUSB(void){
+    /*FIL tfile; // external usb file
+    bool usbEn = (f_open(&tfile,"usb.det",FA_WRITE|FA_OPEN_ALWAYS)==FR_OK);
+    if(usbEn){
+        f_close(&tfile);
+        f_unlink("usb.det");
+    }
+    return(usbEn);*/
+    return(true
+    );
+}
+
+//Sample CONFIG.TXT
+/*
+format 5
+x0_off 5732
+y0_off 34009
+x0_pp 77
+y0_pp 106
+x1_off 33955
+y1_off 6310
+x1_pp 80
+y1_pp 104
+x_mid 31986
+dMode0 4
+dMode1 2
+ledHi 0.800
+ledLo 0.100
+pollInt 300
+scale12V 16.20
+skin 0
+dtePeriod 14
+DebugMode 0
+metric 0
+firmware 11
+showHealth 1
+*/
\ No newline at end of file
--- a/utility.h	Wed Apr 10 13:31:35 2013 +0000
+++ b/utility.h	Sun Jul 21 23:59:00 2013 +0000
@@ -1,13 +1,16 @@
 // utility.h
 
-//#include "mbed.h"
 #include "CAN.h"
 #include "common.h"
 #include "TOUCH_TFTx2.h"
+#include "beep.h"
+#include <cctype>
+#include "ff.h"
 
 extern Timer timer;
-extern volatile int secsNoMsg;
-extern volatile int secsNoTouch;
+extern Ticker msgReq;
+extern volatile unsigned short secsNoMsg;
+extern volatile unsigned short secsNoTouch;
 extern volatile bool canIdle;
 extern volatile bool userIdle;
 extern char displayLog[20][40];
@@ -19,16 +22,18 @@
 extern bool showCP;
 extern char writeBuffer[maxBufLen][13];
 extern volatile int writePointer;
+extern int readPointer;
 extern DigitalOut led1,led2,led3,led4;
 extern char indexLastMsg[0x800];
 extern unsigned char dMode[2];
 extern unsigned char msgChanged[100];
-extern unsigned char battData[256];
+extern unsigned char battData[BatDataBufMax]; // BatDataBufMax
 extern CANMessage lastMsg[100];
 extern CAN can1,can2;
+extern unsigned int fwCount;
 extern DigitalOut can1SleepMode,can2SleepMode;
-extern bool pollCP;
 extern bool tick;
+extern bool ZeroSecTick;
 extern bool headlights;
 extern TOUCH_TFTx2 tt;
 extern float ledHi;
@@ -45,18 +50,37 @@
 extern unsigned short numSsamples;
 extern unsigned char skin;
 extern unsigned char dtePeriod;
+extern Beep spkr;
+extern unsigned char reqMsgCnt;
+extern float maxTemp;
+extern unsigned long Ah_x10000;
+extern unsigned long SOC_x10000;
+extern unsigned short SOH_x100;
+extern bool metric;
+extern unsigned char lastDMode[2];
+extern unsigned char whichTouched;
+extern float unloadedV_x2,Resr,curRmax,curRmin,redRmax,redRmin,incRmax,incRmin;
+extern signed short Imax, Imin;
+extern unsigned char tNavRow ; // gg - 4x4
+extern char revStr[7]; // gg - version
+extern bool debugMode;
+extern bool shunt[96];
+extern float accV;
+extern bool showHealth;
 
 extern "C" {
     void mbed_reset();
     void RTC_IRQHandler();
     void RTC_Init (void);    
-    void logMsg (char *msg);
+    void printMsg (char *msg);
     void touch_ISR();
     unsigned short getTimeStamp();
     void logCan (char mType, CANMessage canRXmsg);
     void logTS ();    
-    void sendCPreq();    
-    void sendTreq();    
+    void logEvent (char * errMsg); // gg - messeges
+    //void sendCPreq();    
+    //void sendTreq();    
+    void sendReq();    
     void autoPollISR();
     void playbackISR();
     void recieve1();
@@ -68,6 +92,9 @@
     void readConfig();
     void upDate(unsigned char field, bool updownbar);
     void logPackVoltages(); // Turbo3
+    void tripLog(); // Turbo3
+    void updateFirmware(); // LM - Update firmware off USB
+    bool detectUSB();
 }
 
 //LEAF OBD