/*******************************************************************************
 *
 *      A U D I O   R E C O R D E R   S D   C A R D   D R I V E R
 *
 *      Copyright A Levido 2013 - All Rights Reserved
 *
 ******************************************************************************/
#include "sdcard.h"
#include <plib.h>
#include "system.h"
#include "crc.h"

#define TRIES           300000

// Commands
struct commanddata{
    UINT8 command;
    UINT8 crc;
    UINT8 response;
    UINT8 moredata;
};
enum{R1, R1b, R2, R3};

#define CMD0_GO_IDLE                0
#define CMD1_SEND_OP_COND           1
#define CMD8_SEND_IF_COND           2
#define CMD9_SEND_CSD               3
#define CMD12_STOP_TRANSMISSION     4
#define CMD16_SET_BLOCKLEN          5
#define CMD17_READ_SINGLE_BLOCK     6    
#define CMD24_WRITE_SINGLE_BLOCK    7
#define CMD55_APP_CMD               8
#define CMD58_READ_OCR              9
#define ACMD41_SEND_OP_CMD          10
#define ACMD13_SEND_STATUS          11
#define CMD18_READ_MULTI_BLOCK      12
#define ACMD23_SET_WR_ERASE_COUNT   13
#define CMD25_WRITE_MULTI_BLOCK     14
#define CMD23_SET_BLOCK_COUNT       15
#define CMD10_SEND_CID              16

const struct commanddata comtable[] = {
    { 0, 0x95, R1,  0},
    { 1, 0xf9, R1,  0},
    { 8, 0x87, R3,  0},
    { 9, 0xff, R1,  1},
    {12, 0xc3, R1,  0},
    {16, 0xff, R1,  0},
    {17, 0xff, R1,  1},
    {24, 0xff, R1,  1},
    {55, 0x73, R1,  0},
    {58, 0xf9, R3,  0},
    {41, 0xff, R1,  0},
    {13, 0xff, R2,  1},
    {18, 0xff, R1,  1},
    {23, 0xff, R1,  0},
    {25, 0xff, R1,  1},
    {23, 0xff, R1,  0},
    {10, 0xff, R1,  1},
};
// Responses
struct {
    union{
        UINT8 byte1;
        struct{
            UINT idlestate      : 1;
            UINT erasereset     : 1;
            UINT badcommand     : 1;
            UINT crcerror       : 1;
            UINT eraseerror     : 1;
            UINT addresserror   : 1;
            UINT paramerror     : 1;
            UINT startbit       : 1;
        };
    };
    UINT8 byte2;
    UINT8 byte3;
    UINT8 byte4;
    UINT8 byte5;
}response;

// Data Tokens
#define DATA_START_TOKEN            0xfe
#define WRITE_MULTIPLE_TOKEN        0xfc
#define STOP_TRAN_TOKEN             0xfd

// Hardware
#define SD_SPI_BUF                  SPI4BUF
#define SD_SPI_STAT_RBF             SPI4STATbits.SPIRBF
#define SD_SPI_STAT_TBF             SPI4STATbits.SPITBF
#define SD_SPI_STAT_TBE             SPI4STATbits.SPITBE
#define SD_CS_LO                    PORTClearBits(IOPORT_B, BIT_15)
#define SD_CS_HI                    PORTSetBits(IOPORT_B, BIT_15)
#define SD_WRITE_PROTECT            PORTBbits.RB13
#define SD_NOT_PRESENT              PORTBbits.RB12
#define SD_POWER_ON                 PORTSetBits(IOPORT_B, BIT_11)
#define SD_POWER_OFF                PORTClearBits(IOPORT_B, BIT_11)
#define F_SLOW                      312500L
#define SD_LOW_SPEED                63// (FPB / (2 * F_SLOW)) - 1 //127 // F = FPB/(2 * (BRG+1))  -> 312.5 kHz
#define F_FAST                      20000000L
#define SD_HIGH_SPEED               0 //(FPB / (2 * F_FAST)) - 1//1   // F = FPB/(2 * (BRG+1))  -> 20 MHz

// SD card status and Type
volatile static DSTATUS SDstatus = STA_NOINIT | STA_NODISK;
static UINT8 cardtype;
UINT8 csd[16];
DWORD cardSize;
WORD blockLen;
WORD cMult;

void sdSetState(UINT32 s)
{
    if(s) SDstatus &= ~STA_NODISK;
    else SDstatus |= (STA_NOINIT | STA_NODISK);
}

/* Local Functions & Macros */
inline void writespi(unsigned char data)
{
  SD_SPI_BUF = data;
  while(!SD_SPI_STAT_RBF);
  data = SD_SPI_BUF;
}
inline UINT8 readspi(void)
{
    SD_SPI_BUF = 0xFF;
    while(!SD_SPI_STAT_RBF);
    return(SD_SPI_BUF);
}
void sendcommand(unsigned int cmd, unsigned long address)
{
    DWORD counter;

    SD_CS_LO;
    writespi(comtable[cmd].command | 0x40);
    writespi((address >> 24) & 0x000000ff);
    writespi((address >> 16) & 0x000000ff);
    writespi((address >> 8) & 0x000000ff);
    writespi(address & 0x000000ff);
    writespi(comtable[cmd].crc);

    //if(cmd == CMD12_STOP_TRANSMISSION) readspi(); // discard stuff byte
    counter = 8;
    do{
        response.byte1 = readspi();
        counter--;
    } while((response.byte1 == 0xFF) && counter != 0);
    switch(comtable[cmd].response){
        case R2:
            response.byte2 = readspi();
            break;
        case R3:
            response.byte2 = readspi();
            response.byte3 = readspi();
            response.byte4 = readspi();
            response.byte5 = readspi();
            break;
        default:
            break;
    }
    if(comtable[cmd].moredata == 0) {
        SD_CS_HI;
        writespi(0xff);
    }
}

void sdShutdown()
{
    SD_POWER_OFF;
    SPI4CONbits.ON = 0;
}

void sdWakeup()
{
    disk_initialize(0);
}
/* API functions **************************************************************/
DSTATUS disk_initialize(BYTE drive)
{
    UINT16 type;
    DWORD count;
    UINT32 index;

    if(drive) return STA_NOINIT;                // Only support a single drive 0

    // open SPI4 in slow mode
    SPI4CONbits.MSTEN = 1;                      // Master

    SPI4CONbits.MODE16 = 0;                     // 8-bit
    SPI4CONbits.CKE = 1;         //0
    SPI4CONbits.CKP = 0;        // 1
    SPI4CONbits.SMP = 1;
    SPI4BRG = SD_LOW_SPEED;
    SPI4CONbits.ON = 1;
    SD_CS_HI;

    SD_POWER_OFF;
    count = 1000000;
    while(count--);
    SD_POWER_ON;

    /* Wait ~20ms then send at least 74 dummy clocks */
    count = 800000;
    while(count--);
    for(count = 0; count < 10; count++) { writespi(0xFF); }
    
    /* Reset the card and check it goes into the idle state */
    count = 10;
    do{
        SD_CS_HI;
        sendcommand(CMD0_GO_IDLE,  0x0000);
        count--;
    }while(response.byte1 != 0x01 && count != 0);
    if(count == 0) return (STA_NOINIT);         // Can't reset card
    
    /* Check if it likes the voltage range 2.7 - 3.6 */ 
    sendcommand(CMD8_SEND_IF_COND, 0x000001aa);
    if(response.byte5 == 0x000000aa && response.badcommand == 0){
        // Must be a v2 device so initialise it
        type = CT_SD2;
        count = TRIES;
        do{
            sendcommand(CMD55_APP_CMD, 0x0000);
            sendcommand(ACMD41_SEND_OP_CMD, 0x40000000);
            count--;
        }while(response.byte1 != 0 && count != 0);
        if(count == 0) return(STA_NOINIT);
        /* Get the Operating Condition Register */
        sendcommand(CMD58_READ_OCR, 0x00000000);
        if(response.byte2 == 0xc0) type |= CT_BLOCK; // HC or XC Card
        
    }
    else{ /* Must be a v1 SD or MMC v3 card so initialise it */
        count = TRIES;
        do{
            sendcommand(CMD55_APP_CMD, 0x0000);
            sendcommand(ACMD41_SEND_OP_CMD, 0x00000000);
            count--;
        }while(response.byte1 != 0 && count != 0);         
        if(response.byte1 != 0x01){ // must be MMC v3 or faulty
            count = TRIES;
            do{
                sendcommand(CMD1_SEND_OP_COND, 0x000000);
                count--;
            }while(response.byte1 != 0 && count != 0);
            if(count == 0)return(STA_NOINIT);
            type = CT_MMC;
        }
        else type = CT_SD1;
    }
    /* open SPI4 in fast mode */ 
    SPI4CONbits.ON = 0;
    SPI4BRG = SD_HIGH_SPEED;
    SPI4CONbits.ON = 1;

    /* Read CSD & Calculate the disk size */
    sendcommand(CMD9_SEND_CSD, 0x00000000);
    if(response.byte1 != 0x00) {
        SD_CS_HI;
        return STA_NOINIT;
    }
    index = 0;
    for (count = 0; count < 20u; count ++) {
        csd[index] = readspi();
        index++;
        /* Hopefully the first byte is the datatoken, however, some cards do
        not send the response token before the CSD register.*/
        if((count == 0) && (csd[0] == DATA_START_TOKEN)) {  index = 0; }
    }

    if(csd[0] & 0xC0) {             //V2
        cardSize = (((DWORD)csd[7] & 0x3F) << 16) | ((WORD)csd[8] << 8) | csd[9];
        cardSize = ((DWORD)(cardSize + 1) * (WORD)(1024u)) - 1;
    }
    else {                          // V1
        cardSize = ((DWORD)csd[6] << 16) | ((WORD)csd[7] << 8) | csd[8];
        cardSize &= 0x0003FFC0;
        cardSize = cardSize >> 6;
        cMult = ((WORD)((csd[9] & 0x03) << 1)) | ((WORD)((csd[10] & 0x80) >> 7));
        blockLen = csd[5] & 0x0F;
        blockLen = 1 << (blockLen - 9);
        cardSize = ((DWORD)(cardSize + 1) * (WORD)((WORD)1 << (cMult + 2)) * blockLen) - 1;
    }

    SD_CS_HI;

    cardtype = type;                // set card type
    SDstatus &= ~STA_NOINIT;          // clear disk status
    return(SDstatus);
}

DRESULT disk_read (BYTE drv, BYTE *buff, DWORD sector, BYTE count)
{
    DWORD j;
    unsigned int i;

    if(drv || !count) { return RES_PARERR;  }
    if(SDstatus & STA_NOINIT) { return RES_NOTRDY; }
    if(!(cardtype & CT_BLOCK)) sector *= 512;

    SD_CS_LO;
    while(readspi() != 0xff);   // wait til not busy
    SD_CS_HI;

    if (count == 1) {		// Single block read
        sendcommand(CMD17_READ_SINGLE_BLOCK, sector);
        if(response.byte1 != 0x00) {
            SD_CS_HI;
            return(RES_ERROR);
        }
        /* wait  till we get start token */
        j = TRIES;
        
        while(1){
            if(readspi() == DATA_START_TOKEN) break;
            if(--j == 0){
                SD_CS_HI;
                return(RES_ERROR);
            }
        }
        /* read 512 bytes then 2 CRC */
        i = 512;
        while(1){
            SD_SPI_BUF = 0xff;
            while(!SD_SPI_STAT_RBF);
            *buff++ = SD_SPI_BUF;
            if(--i) continue;
            SD_SPI_BUF = 0xff;
            while(!SD_SPI_STAT_RBF);   
            SD_SPI_BUF;
            SD_SPI_BUF = 0xff;
            while(!SD_SPI_STAT_RBF);
            SD_SPI_BUF;
            break;
        }
        SD_CS_HI;
        count = 0;
    }
    else {			// Multi-block read
        sendcommand(CMD18_READ_MULTI_BLOCK, sector);
        if(response.byte1 != 0x00) {
            SD_CS_HI;
            return(RES_ERROR);
        }
        /* read n blocks of 512 bytes each */
        do{
            /* wait  till we get start token */
            j = TRIES;
            i = 512;
            while(1){
                if(readspi() == DATA_START_TOKEN) break;
                if(--j == 0){
                    SD_CS_HI;
                    return(RES_ERROR);
                }
            }
            /* read 512 bytes then 2 CRC */
            i = 512;
            while(1){
                SD_SPI_BUF = 0xff;
                while(!SD_SPI_STAT_RBF);
                *buff++ = SD_SPI_BUF;
                if(--i) continue;
                SD_SPI_BUF = 0xff;
                while(!SD_SPI_STAT_RBF);   
                SD_SPI_BUF;
                SD_SPI_BUF = 0xff;
                while(!SD_SPI_STAT_RBF);
                SD_SPI_BUF;
                break;
            }
        } while(--count);
        sendcommand(CMD12_STOP_TRANSMISSION, 0);    
    }
    SD_CS_HI;
    return RES_OK;
}

DRESULT disk_write (BYTE drv, const BYTE *buff, DWORD sector, BYTE count)
{
    DWORD j;
    unsigned int i;
    
    if (drv || !count) return RES_PARERR;
    if (SDstatus & STA_NOINIT) return RES_NOTRDY;
    if (SDstatus & STA_PROTECT) return RES_WRPRT;
    if (!(cardtype & CT_BLOCK)) sector *= 512;

    SD_CS_LO;
    while(readspi() != 0xff);   // wait til not busy
    SD_CS_HI;

    if (count == 1) {                  /* Single Bock write */
        unsigned short crc = CRC16(buff, 512);
        sendcommand(CMD24_WRITE_SINGLE_BLOCK, sector);
        if(response.byte1 != 0x00) {
            SD_CS_HI;
            return(RES_ERROR);	
        }
        readspi();          /* Wait one byte */
        /* send start token then write 512 bytes & dummy crc */
        SPI4BRG = 1;
        writespi(DATA_START_TOKEN);
        i = 512;
        while(1){
            SD_SPI_BUF = *buff++;
            while(!SD_SPI_STAT_RBF);
            SD_SPI_BUF;
            if(--i) continue;

            SD_SPI_BUF = crc>>8;
            while(!SD_SPI_STAT_RBF);
            SD_SPI_BUF;
            SD_SPI_BUF = crc&0xFF;
            while(!SD_SPI_STAT_RBF);
            SD_SPI_BUF;
            break;
        }
        SPI4BRG = SD_HIGH_SPEED;
        /* read the response token */
        if((readspi() & 0x0f) != 0x05){
            SD_CS_HI;
            return(RES_ERROR);
        }
    } 
    else {				/* Multi-block write */
        if(cardtype & CT_SDC){
            sendcommand(CMD55_APP_CMD, 0);
            sendcommand(ACMD23_SET_WR_ERASE_COUNT, cardtype & CT_BLOCK ? count : count << 9);
            if(response.byte1 != 0x00) {
                SD_CS_HI;
                return(RES_ERROR);
            }
        }
        else{
            sendcommand(CMD23_SET_BLOCK_COUNT, count << 9);
            if(response.byte1 != 0x00) {
                SD_CS_HI;
                return(RES_ERROR);
            }
        }
        sendcommand(CMD25_WRITE_MULTI_BLOCK, sector);
        if(response.byte1 != 0x00) {
            SD_CS_HI;
            return(RES_ERROR);
        }
        /* write n blocks of 512 bytes each */
        do{
            unsigned short crc = CRC16(buff, 512);
            /* wait till not busy */
            j = TRIES;
            while(readspi() != 0xff && --j > 0);
            if(j == 0) {
                SD_CS_HI;
                return(RES_ERROR);
            }
            if(count == 0) break; // done

            SPI4BRG = 1;
/*
            SPI4CONbits.ON = 0;
            SPI4CONbits.ON = 1;
*/
            /* send start token then write 512 bytes & dummy crc */
            writespi(WRITE_MULTIPLE_TOKEN);
            i = 512;
            while(1){
                SD_SPI_BUF = *buff++;
                while(!SD_SPI_STAT_RBF);
                SD_SPI_BUF;
                if(--i) continue;

                SD_SPI_BUF = crc>>8;
                while(!SD_SPI_STAT_RBF);
                SD_SPI_BUF;
                SD_SPI_BUF = crc&0xFF;
                while(!SD_SPI_STAT_RBF);
                SD_SPI_BUF;
                break;
            }

            SPI4BRG = SD_HIGH_SPEED;
/*
            SPI4CONbits.ON = 0;
            SPI4CONbits.ON = 1;
*/
            /* read the response token */
            if((readspi() & 0x0f) != 0x05){
                SD_CS_HI;
                return(RES_ERROR);
            }
            count--;
   
        }while(1);
        /* wait for not busy */ 
        while(readspi() != 0xff);   // wait til not busy
        writespi(STOP_TRAN_TOKEN);
    }
    SD_CS_HI;
    return RES_OK;
}

DSTATUS disk_status (BYTE drv)
{
    if(drv) return STA_NOINIT;
    return SDstatus;
}

void sdDetectStatus(void)
{
    BYTE s, t;
   
    t = s = SDstatus;
    if (SD_WRITE_PROTECT) s |= STA_PROTECT;
    else s &= ~STA_PROTECT;

    if (SD_NOT_PRESENT) {            // Card removed
        s |= (STA_NODISK | STA_NOINIT);
        SDstatus = s;
        if((t & STA_NODISK) == 0) {
            enqueueEvent(SD_CARD, SD_OUT, 0, 0);
        }
    }
    else {                          // Card inserted
        s &= ~STA_NODISK;
        SDstatus = s;
        if(t & STA_NODISK) {            
            enqueueEvent(SD_CARD, SD_IN, 0, 0);
        }
    }
}

UINT32 sdCardPresent(void)
{
    if(SD_NOT_PRESENT) return 0;
    else return 1;
}

DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void *buff)
{
	DRESULT res;
	BYTE n, *ptr = (BYTE*)buff;

	if (drv) return RES_PARERR;
	if (SDstatus & STA_NOINIT) return RES_NOTRDY;

	res = RES_ERROR;
	switch (ctrl) {
            case CTRL_SYNC :            // Flush cache (Not applicable)
                res = RES_OK;
                break;
            case GET_SECTOR_SIZE :      // Get sector size
		*(UINT*)ptr = 512;
		res = RES_OK;
		break;
            case MMC_GET_TYPE :         // Get card type flags
		*ptr = cardtype;
		res = RES_OK;
		break;
            case MMC_GET_CSD :          //Receive CSD as a data block
                sendcommand(CMD9_SEND_CSD, 0);
                if(response.byte1 == 0x00) {
                    n = 16;
                    while(n--) *ptr++ = readspi();
                    SD_CS_HI;
                    res = RES_OK;
                }
                break;
            case MMC_GET_CID :          // Receive CID as a data block
		sendcommand(CMD10_SEND_CID, 0);
                if(response.byte1 == 0x00) {
                    n = 16;
                    while(n--) *ptr++ = readspi();
                    SD_CS_HI;
                    res = RES_OK;
                }
                break;
            case MMC_GET_OCR :          // Receive OCR as an R3 resp
		sendcommand(CMD58_READ_OCR, 0);
                if(response.byte1 == 0x00) {
                    *ptr++ = response.byte2;
                    *ptr++ = response.byte3;
                    *ptr++ = response.byte4;
                    *ptr++ = response.byte5;
                    res = RES_OK;
                }
                break;

            case MMC_GET_SDSTAT :	// Receive SD statsu as a data block
		if (cardtype & CT_SD2) {
                    sendcommand(CMD55_APP_CMD, 0);
                    sendcommand(ACMD13_SEND_STATUS, 0);
                    if(response.byte1 == 0x00) {
                        n = 64;
                        while(n--) *ptr++ = readspi();
                        SD_CS_HI;
                        res = RES_OK;
                    }
                }
                break;
            default: res = RES_PARERR;
	}
	return res;
}

////// Unicode/OEMCP Conversion ////////////////////////////////////////////////
static const UINT16 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
};

UINT16 ff_convert(UINT16 chr, UINT dir)
{
    UINT16 c;

    if(chr < 0x80) c = chr;     // ASCII
    else{
        if(dir){
            c = (chr >= 0x100) ? 0 : tbl[chr - 0x80];
        }
        else{
            for(c = 0; c < 0x80; c++){
                if(chr == tbl[c]) break;
            }
            c = (c + 0x80) & 0x00ff;
        }

    }
    return c;
}

////// Unicode Upper Case Conversion  //////////////////////////////////////////
static const UINT16 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 UINT16 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 };
	
UINT16 ff_wtoupper(UINT16 chr)
{
    int i;
    for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ;
    return tbl_lower[i] ? tbl_upper[i] : chr;
}

/* USB MSD Functions **********************************************************/
MEDIA_INFORMATION info;

DWORD msdReadCapacity(void)
{
    return cardSize;
}
BYTE msdSectorRead(DWORD addr, BYTE* buff)
{
    if(disk_read(0, buff, addr, 1) == RES_OK) { return TRUE; }
    else { return FALSE; }
}

BYTE msdSectorWrite(DWORD addr, BYTE* buff, BYTE wtz)
{
    if(disk_write(0, (const BYTE*)buff, addr, 1) == RES_OK) { return TRUE; }
    else { return FALSE; }
}
BYTE msdReadWP(void)
{
    return SD_WRITE_PROTECT;
}
MEDIA_INFORMATION* msdInitMedia(void)
{
    if(SDstatus & STA_NOINIT){
        if(disk_initialize(0) & STA_NOINIT) { return NULL; }
    }
    return &info; 
}
BYTE msdMediaDetect(void)
{
    if(SD_NOT_PRESENT) return FALSE;
    return TRUE;
}
WORD msdReadSectorSize(void)
{
    return 512;
}
