From ab2c65b9f252ae95826b32366b5e2dab060275fd Mon Sep 17 00:00:00 2001 From: bsongis Date: Mon, 14 May 2012 17:36:18 +0000 Subject: [PATCH] SD card for ARM board (first part) --- src/Makefile | 14 +- src/board_gruvin9x.cpp | 2 + src/drivers.cpp | 9 +- src/ersky9x/AT91SAM3S4.h | 6 + src/ersky9x/diskio.cpp | 1010 +++++++++ src/ersky9x/diskio.h | 145 ++ src/ersky9x/ff.cpp | 3819 +++++++++++++++++++++++++++++++++ src/ersky9x/ff.h | 575 +++++ src/ersky9x/ffconf.h | 221 ++ src/ersky9x/gtime.cpp | 495 +++++ src/ersky9x/gtime.h | 63 + src/ersky9x/integer.h | 70 + src/ersky9x/sdcard_driver.cpp | 1 + src/ersky9x/sdcard_driver.h | 3 + src/ersky9x/sdmmc_spi.c | 1410 ------------ src/ersky9x/sdmmc_spi.h | 443 ---- src/ersky9x/sdspi.c | 781 ------- src/ersky9x/sdspi.h | 175 -- src/general_menus.cpp | 6 +- src/gruvin9x/diskio.cpp | 2 +- src/gruvin9x/ff.cpp | 25 + src/gruvin9x/ff.h | 3 + src/gruvin9x/sdcard.h | 9 +- src/open9x.cpp | 34 +- src/open9x.h | 15 +- 25 files changed, 6462 insertions(+), 2874 deletions(-) create mode 100644 src/ersky9x/diskio.cpp create mode 100644 src/ersky9x/diskio.h create mode 100644 src/ersky9x/ff.cpp create mode 100644 src/ersky9x/ff.h create mode 100644 src/ersky9x/ffconf.h create mode 100644 src/ersky9x/gtime.cpp create mode 100644 src/ersky9x/gtime.h create mode 100644 src/ersky9x/integer.h delete mode 100644 src/ersky9x/sdmmc_spi.c delete mode 100644 src/ersky9x/sdmmc_spi.h delete mode 100644 src/ersky9x/sdspi.c delete mode 100644 src/ersky9x/sdspi.h diff --git a/src/Makefile b/src/Makefile index f3c719e29..961fe5612 100644 --- a/src/Makefile +++ b/src/Makefile @@ -296,6 +296,10 @@ ifeq ($(EEPROM_PROGRESS_BAR), YES) CPPDEFS += -DEEPROM_PROGRESS_BAR endif +ifeq ($(SDCARD), YES) + CPPDEFS += -DSDCARD +endif + RUN_FROM_FLASH = 1 ifeq ($(PCB), ARM) @@ -305,8 +309,7 @@ ifeq ($(PCB), ARM) EXTRAINCDIRS += ersky9x BOARDSRC = board_ersky9x.cpp EXTRABOARDSRC = ersky9x/core_cm3.c ersky9x/board_lowlevel.c ersky9x/crt.c ersky9x/vectors_sam3s.c - # ersky9x/ff.c ersky9x/diskio_sam3s.c ersky9x/Media.c ersky9x/ccsbcs.c ersky9x/sdcard.c ersky9x/MEDSdcard.c - # CPPSRC += ersky9x/sdmmc_spi.c ersky9x/sdspi.c + CPPSRC += ersky9x/ff.cpp ersky9x/diskio.cpp ersky9x/gtime.cpp EEPROMSRC = eeprom_arm.cpp PULSESSRC = pulses_arm.cpp CPPSRC += ersky9x/audio.cpp haptic.cpp @@ -322,14 +325,9 @@ ifeq ($(PCB), V4) EEPROMSRC = eeprom_avr.cpp PULSESSRC = pulses_avr.cpp CPPSRC += stock/audio.cpp haptic.cpp - CPPSRC += gruvin9x/gtime.cpp - CPPSRC += gruvin9x/rtc.cpp - CPPSRC += gruvin9x/ff.cpp - CPPSRC += gruvin9x/diskio.cpp ifeq ($(SDCARD), YES) - CPPDEFS += -DSDCARD - CPPSRC += gruvin9x/logs.cpp + CPPSRC += gruvin9x/logs.cpp gruvin9x/gtime.cpp gruvin9x/rtc.cpp gruvin9x/ff.cpp gruvin9x/diskio.cpp MODS:=${MODS}S endif diff --git a/src/board_gruvin9x.cpp b/src/board_gruvin9x.cpp index 930d39b66..4b277e1c1 100644 --- a/src/board_gruvin9x.cpp +++ b/src/board_gruvin9x.cpp @@ -71,6 +71,7 @@ inline void board_init() TCCR0B = (1< + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/*-----------------------------------------------------------------------*/ +/* MMCv3/SDv1/SDv2 (in SPI mode) control module (C)ChaN, 2010 */ +/*-----------------------------------------------------------------------*/ +/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */ +/* are platform dependent. */ +/*-----------------------------------------------------------------------*/ + +#include "../open9x.h" +#include "diskio.h" + +// SD Card routines + +#define MCI_INITIAL_SPEED 400000 + +// States for initialising card +#define SD_ST_EMPTY 0 +#define SD_ST_IDLE 1 +#define SD_ST_READY 2 +#define SD_ST_IDENT 3 +#define SD_ST_STBY 4 +#define SD_ST_TRAN 5 +#define SD_ST_DATA 6 + + +uint32_t Card_ID[4] ; +uint32_t Card_SCR[2] ; +uint32_t Card_CSD[4] ; +uint32_t Card_state = SD_ST_EMPTY ; +uint32_t Sd_128_resp[4] ; +uint32_t Sd_rca ; +//uint32_t Cmd_55_resp ; + +/** + * Initializes a MCI peripheral. + */ +void SD_Init() +{ + unsigned short clkDiv; + Hsmci *pMciHw = HSMCI ; + + /* Enable the MCI peripheral */ + PMC->PMC_PCER0 |= 1 << ID_HSMCI ; // Enable peripheral clock to HSMCI + pMciHw->HSMCI_CR = HSMCI_CR_SWRST; /* Reset the MCI */ + pMciHw->HSMCI_CR = HSMCI_CR_MCIDIS | HSMCI_CR_PWSDIS; /* Disable the MCI */ + pMciHw->HSMCI_IDR = 0xFFFFFFFF; /* Disable all the interrupts */ + pMciHw->HSMCI_DTOR = HSMCI_DTOR_DTOCYC | HSMCI_DTOR_DTOMUL ; /* Set the Data Timeout Register */ + pMciHw->HSMCI_CSTOR = HSMCI_CSTOR_CSTOCYC | HSMCI_CSTOR_CSTOMUL ; /* CSTOR ? */ + /* Set the Mode Register: 400KHz for MCK = 48MHz (CLKDIV = 58) */ + clkDiv = (Master_frequency / (MCI_INITIAL_SPEED * 2)) - 1; + pMciHw->HSMCI_MR = clkDiv | (7 << 8) ; + + /* Set the SDCard Register 1-bit, slot A */ + pMciHw->HSMCI_SDCR = HSMCI_SDCR_SDCSEL_SLOTA | HSMCI_SDCR_SDCBUS_1 ; + /* Enable the MCI and the Power Saving */ + pMciHw->HSMCI_CR = HSMCI_CR_MCIEN | HSMCI_CR_PWSEN ; + /* Configure MCI */ + pMciHw->HSMCI_CFG = HSMCI_CFG_FIFOMODE | ((1 << 4) & HSMCI_CFG_FERRCTRL); +} + + +/** + * Configure the MCI SDCBUS in the MCI_SDCR register. Only two modes available + * + * \param busWidth MCI bus width mode. 00: 1-bit, 10: 4-bit. + */ +uint32_t SD_SetBusWidth( uint32_t busWidth) +{ + uint32_t mciSdcr; + + if( (busWidth != HSMCI_SDCR_SDCBUS_1) && (busWidth != HSMCI_SDCR_SDCBUS_4) ) + { + return (uint32_t)-1; + } + + busWidth &= HSMCI_SDCR_SDCBUS ; + + mciSdcr = (HSMCI->HSMCI_SDCR & ~(uint32_t)(HSMCI_SDCR_SDCBUS)); + HSMCI->HSMCI_SDCR = mciSdcr | busWidth; + + return 0; +} + +/** + * Configure the MCI_CFG to enable the HS mode + * \param hsEnable 1 to enable, 0 to disable HS mode. + */ +void SD_EnableHsMode( uint8_t hsEnable) +{ + uint32_t cfgr; + + cfgr = HSMCI->HSMCI_CFG; + if (hsEnable) + { + cfgr |= HSMCI_CFG_HSMODE; + } + else + { + cfgr &= ~(uint32_t)HSMCI_CFG_HSMODE; + } + + HSMCI->HSMCI_CFG = cfgr; +} + + +/** + * Configure the MCI CLKDIV in the MCI_MR register. The max. for MCI clock is + * MCK/2 and corresponds to CLKDIV = 0 + * \param mciSpeed MCI clock speed in Hz, 0 will not change current speed. + * \return The actual speed used, 0 for fail. + */ +uint32_t SD_SetSpeed( uint32_t mciSpeed ) +{ + uint32_t mciMr; + uint32_t clkdiv; + + mciMr = HSMCI->HSMCI_MR & (~(uint32_t)HSMCI_MR_CLKDIV); + /* Multimedia Card Interface clock (MCCK or MCI_CK) is Master Clock (MCK) + * divided by (2*(CLKDIV+1)) + * mciSpeed = MCK / (2*(CLKDIV+1)) */ + if (mciSpeed > 0) + { + clkdiv = (Master_frequency / 2 / mciSpeed); + /* Speed should not bigger than expired one */ + if (mciSpeed < Master_frequency/2/clkdiv) + { + clkdiv++; + } + + if ( clkdiv > 0 ) + { + clkdiv -= 1; + } + } + else + { + clkdiv = 0 ; + } + + /* Actual MCI speed */ + mciSpeed = Master_frequency / 2 / (clkdiv + 1); + /* Modify MR */ + HSMCI->HSMCI_MR = mciMr | clkdiv; + + return (mciSpeed); +} + +//void SD_SetBlklen( uint32_t blklen ) +//{ +// uint32_t mciMr; +// uint32_t clkdiv; + +// mciMr = HSMCI->HSMCI_MR & (~(uint32_t)HSMCI_MR_BLKLEN); +// /* Modify MR */ +// HSMCI->HSMCI_MR = mciMr | blklen << 16 ; +//} + + +/** + * Reset MCI HW interface and disable it. + * \param keepSettings Keep old register settings, including + * _MR, _SDCR, _DTOR, _CSTOR, _DMA and _CFG. + */ +void SD_Reset( uint8_t keepSettings) +{ + Hsmci *pMciHw = HSMCI ; + + if (keepSettings) + { + uint32_t mr, sdcr, dtor, cstor; + uint32_t cfg; + mr = pMciHw->HSMCI_MR; + sdcr = pMciHw->HSMCI_SDCR; + dtor = pMciHw->HSMCI_DTOR; + cstor = pMciHw->HSMCI_CSTOR; + cfg = pMciHw->HSMCI_CFG; + pMciHw->HSMCI_CR = HSMCI_CR_SWRST ; // Reset + pMciHw->HSMCI_CR = HSMCI_CR_MCIDIS; // Disable + pMciHw->HSMCI_MR = mr; + pMciHw->HSMCI_SDCR = sdcr; + pMciHw->HSMCI_DTOR = dtor; + pMciHw->HSMCI_CSTOR = cstor; + pMciHw->HSMCI_CFG = cfg; + } + else + { + pMciHw->HSMCI_CR = HSMCI_CR_SWRST ; // Reset + pMciHw->HSMCI_CR = HSMCI_CR_MCIDIS; // Disable + } +} + + +void sd_cmd55() +{ + uint32_t i ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + phsmci->HSMCI_ARGR = Sd_rca ; + phsmci->HSMCI_CMDR = 0x00001077 ; + + for ( i = 0 ; i < 30000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_CMDRDY ) + { + break ; + } + } +// Cmd_55_resp = phsmci->HSMCI_RSPR[0] ; + } +} + + + +uint32_t sd_acmd41() +{ + uint32_t i ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + sd_cmd55() ; + phsmci->HSMCI_ARGR = 0X001F8000 ; + phsmci->HSMCI_CMDR = 0x00001069 ; + + for ( i = 0 ; i < 30000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_CMDRDY ) + { + break ; + } + } + return phsmci->HSMCI_RSPR[0] ; + } + else + { + return 0 ; + } +} + +// Get Card ID +uint32_t sd_cmd2() +{ + uint32_t i ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + phsmci->HSMCI_ARGR = 0 ; + phsmci->HSMCI_CMDR = 0x00001082 ; + + for ( i = 0 ; i < 30000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_CMDRDY ) + { + break ; + } + } + Sd_128_resp[0] = phsmci->HSMCI_RSPR[0] ; + Sd_128_resp[1] = phsmci->HSMCI_RSPR[1] ; + Sd_128_resp[2] = phsmci->HSMCI_RSPR[2] ; + Sd_128_resp[3] = phsmci->HSMCI_RSPR[3] ; + return 1 ; + } + else + { + return 0 ; + } +} + +// Get new RCA +uint32_t sd_cmd3() +{ + uint32_t i ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + phsmci->HSMCI_ARGR = 0 ; + phsmci->HSMCI_CMDR = 0x00001043 ; + + for ( i = 0 ; i < 30000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_CMDRDY ) + { + break ; + } + } + return phsmci->HSMCI_RSPR[0] ; + } + else + { + return 0 ; + } +} + +// Get CSD +uint32_t sd_cmd9() +{ + uint32_t i ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + phsmci->HSMCI_ARGR = Sd_rca ; + phsmci->HSMCI_CMDR = 0x00001089 ; + + for ( i = 0 ; i < 30000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_CMDRDY ) + { + break ; + } + } + Sd_128_resp[0] = phsmci->HSMCI_RSPR[0] ; + Sd_128_resp[1] = phsmci->HSMCI_RSPR[1] ; + Sd_128_resp[2] = phsmci->HSMCI_RSPR[2] ; + Sd_128_resp[3] = phsmci->HSMCI_RSPR[3] ; + return 1 ; + } + else + { + return 0 ; + } +} + +// Select Card +uint32_t sd_cmd7() +{ + uint32_t i ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + phsmci->HSMCI_ARGR = Sd_rca ; + phsmci->HSMCI_CMDR = 0x00001047 ; + + for ( i = 0 ; i < 30000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_CMDRDY ) + { + break ; + } + } + return 1 ; + } + else + { + return 0 ; + } +} + +#define HSMCI_CMDR_SPCMD_STD ( 0 << 8 ) +#define HSMCI_CMDR_RSPTYP_48_BIT ( 1 << 6 ) +#define HSMCI_CMDR_TRCMD_START_DATA ( 1 << 16 ) +#define HSMCI_CMDR_TRDIR_READ ( 1 << 18 ) +#define HSMCI_CMDR_TRTYP_SINGLE ( 0 << 19 ) + +#define SD_SEND_SCR (51 | HSMCI_CMDR_SPCMD_STD | HSMCI_CMDR_RSPTYP_48_BIT \ + | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_READ \ + | HSMCI_CMDR_TRTYP_SINGLE | HSMCI_CMDR_MAXLAT) + +#define SD_READ_SINGLE_BLOCK (17 | HSMCI_CMDR_SPCMD_STD | HSMCI_CMDR_RSPTYP_48_BIT \ + | HSMCI_CMDR_TRCMD_START_DATA | HSMCI_CMDR_TRDIR_READ \ + | HSMCI_CMDR_TRTYP_SINGLE | HSMCI_CMDR_MAXLAT) + +// Get SCR +uint32_t sd_acmd51( uint32_t *presult ) +{ + uint32_t i ; + uint32_t j = 0 ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + sd_cmd55() ; + // Block size = 64/ 8, nblocks = 1 + phsmci->HSMCI_BLKR = ( ( 64 / 8 ) << 16 ) | 1 ; + phsmci->HSMCI_ARGR = 0 ; + phsmci->HSMCI_CMDR = SD_SEND_SCR ; + + for ( i = 0 ; i < 50000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_RXRDY ) + { + *presult++ = __REV(phsmci->HSMCI_RDR) ; + j += 0x10000000 ; + } + if ( ( phsmci->HSMCI_SR & ( HSMCI_SR_CMDRDY | HSMCI_SR_XFRDONE ) ) == ( HSMCI_SR_CMDRDY | HSMCI_SR_XFRDONE ) ) + { + break ; + } + + } + if ( i >= 50000 ) + { + *presult = phsmci->HSMCI_SR ; + } + return i | j ; //phsmci->HSMCI_RSPR[0] ; + } + else + { + return 0 ; + } +} + +#define SD_SET_BUS_WIDTH (6 | HSMCI_CMDR_SPCMD_STD | HSMCI_CMDR_RSPTYP_48_BIT \ + | HSMCI_CMDR_MAXLAT) + +// Set bus width to 4 bits, set speed to 9 MHz +uint32_t sd_acmd6() +{ + uint32_t i ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + sd_cmd55() ; + phsmci->HSMCI_ARGR = 2 ; // Bus width 4 bits + phsmci->HSMCI_CMDR = SD_SET_BUS_WIDTH ; + + for ( i = 0 ; i < 300000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_CMDRDY ) + { + break ; + } + } + if ( i >= 300000 ) + { + return 0 ; + } + SD_SetBusWidth( HSMCI_SDCR_SDCBUS_4 ) ; + SD_SetSpeed( 9000000 ) ; + return i ; //phsmci->HSMCI_RSPR[0] ; + } + else + { + return 0 ; + } +} + + +// Read SD card from (byte) address, for 512 bytes +uint32_t sd_cmd17( uint32_t address, uint32_t *presult ) +{ + uint32_t i ; + uint32_t j = 0 ; + Hsmci *phsmci = HSMCI ; + + if ( CardIsConnected() ) + { + // Block size = 512, nblocks = 1 + phsmci->HSMCI_BLKR = ( ( 512 ) << 16 ) | 1 ; + phsmci->HSMCI_ARGR = address ; + phsmci->HSMCI_CMDR = SD_READ_SINGLE_BLOCK ; + + for ( i = 0 ; i < 50000 ; i += 1 ) + { + if ( phsmci->HSMCI_SR & HSMCI_SR_RXRDY ) + { + *presult++ = phsmci->HSMCI_RDR ; + j += 1 ; + } + if ( ( phsmci->HSMCI_SR & ( HSMCI_SR_CMDRDY | HSMCI_SR_XFRDONE ) ) == ( HSMCI_SR_CMDRDY | HSMCI_SR_XFRDONE ) ) + { + break ; + } + if ( j >= 128 ) + { + break ; + } + } + return i | (j << 16) ; //phsmci->HSMCI_RSPR[0] ; + } + else + { + return 0 ; + } +} + +// This routine is called every 10 mS, and checks for card +// insertion and removal. +// When a card is inserted, it initialises it ready for read/write access. +void sd_poll_10mS() +{ + uint32_t i ; + + if ( !CardIsConnected() ) + { + Card_state = SD_ST_EMPTY ; + Sd_rca = 0 ; + + } + + switch ( Card_state ) + { + case SD_ST_EMPTY : + if ( CardIsConnected() ) + { + Card_state = SD_ST_IDLE ; + } + break ; + + case SD_ST_IDLE : + i = sd_acmd41() ; + if ( i & 0x80000000 ) + { + Card_state = SD_ST_READY ; + } + break ; + + case SD_ST_READY : + i = sd_cmd2() ; + if ( i ) + { + Card_ID[0] = Sd_128_resp[0] ; + Card_ID[1] = Sd_128_resp[1] ; + Card_ID[2] = Sd_128_resp[2] ; + Card_ID[3] = Sd_128_resp[3] ; + Card_state = SD_ST_IDENT ; + } + break ; + + case SD_ST_IDENT : + i = sd_cmd3() ; // Get new RCA + Sd_rca = i ; + Card_state = SD_ST_STBY ; + break ; + + case SD_ST_STBY : + i = sd_cmd9() ; + Card_CSD[0] = Sd_128_resp[0] ; + Card_CSD[1] = Sd_128_resp[1] ; + Card_CSD[2] = Sd_128_resp[2] ; + Card_CSD[3] = Sd_128_resp[3] ; + i = sd_cmd7() ; // Select Card +// txmit( '+' ) ; +// p8hex( i ) ; +// crlf() ; + Card_state = SD_ST_TRAN ; + break ; + + case SD_ST_TRAN : + i = sd_acmd51( Sd_128_resp ) ; + Card_SCR[0] = Sd_128_resp[0] ; + Card_SCR[1] = Sd_128_resp[1] ; + Card_state = SD_ST_DATA ; + i = sd_acmd6() ; // Set bus width to 4 bits, and speed to 9 MHz +// txmit( '-' ) ; +// p8hex( i ) ; +// crlf() ; + // Should check the card can do this **** + break ; + } +} + +// Checks for card ready for read/write +// returns 1 for YES, 0 for NO +uint32_t sd_card_ready( void ) +{ + if ( CardIsConnected() ) + { + if ( Card_state == SD_ST_DATA ) + { + return 1 ; + } + } + return 0 ; +} + +uint32_t sd_read_block( uint32_t block_no, uint32_t *data ) +{ + if ( Card_state == SD_ST_DATA ) + { + return sd_cmd17( block_no << 9, data ) ; + } + else + { + return 0 ; + } +} + + +/* + Notes on SD card: + +On card present +1. Send CMD 55 (resp 48bit) + followed by ACMD41 (resp48bit) + until bit 31 of response is '1' + +2. Send CMD 2 (resp 136bit) - card ID + +3. Send CMD 3 (resp 48 bit) - new RCA returned + +4. Send CMD 9 (resp 136) - CSD + +5. Send CMD 7 (resp 48) - + +6. Send CMD 55 (resp 48bit) + followed by ACMD51 (resp48bit) - SCR stored as 2 32 bit values + +Now decide what the card can do! + +7. Set block length + +8. Set bus width + +9. Read block 0 + +*/ + + +#include "ff.h" +#include "diskio.h" + +/* FAT sub-type boundaries */ +/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ +#define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */ +#define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because there are incompatibility of the packing option +/ between 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_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 BS_55AA 510 + +#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 + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ + ) +{ + // TODO BSS BYTE n, cmd, ty, ocr[4]; + + + if (drv) return STA_NOINIT; /* Supports only single drive */ + +#if 0 + // TODO BSS + if (Stat & STA_NODISK) return Stat; /* No card in the socket */ + + power_on(); /* Force socket power on */ + FCLK_SLOW(); + for (n = 10; n; n--) rcvr_spi(); /* 80 dummy clocks */ + + ty = 0; + if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ + Timer1 = 100; /* Initialization timeout of 1000 msec */ + if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); /* Get trailing return value of R7 resp */ + if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ + while (Timer1 && send_cmd(ACMD41, 1UL << 30)); /* Wait for leaving idle state (ACMD41 with HCS bit) */ + if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */ + for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); + ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */ + } + } + } else { /* SDv1 or MMCv3 */ + if (send_cmd(ACMD41, 0) <= 1) { + ty = CT_SD1; cmd = ACMD41; /* SDv1 */ + } else { + ty = CT_MMC; cmd = CMD1; /* MMCv3 */ + } + while (Timer1 && send_cmd(cmd, 0)); /* Wait for leaving idle state */ + if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */ + ty = 0; + } + } + CardType = ty; + deselect(); + + if (ty) { /* Initialization succeded */ + Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */ + FCLK_FAST(); + } else { /* Initialization failed */ + power_off(); + } + + return Stat; +#endif + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE drv /* Physical drive number (0) */ + ) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return 0; // TODO BSS return Stat; +} + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ + ) +{ + uint32_t result ; + if (drv || !count) return RES_PARERR; + + if ( sd_card_ready() == 0 ) return RES_NOTRDY; + + if (count == 1) + { /* Single block read */ + result = sd_read_block( sector, ( uint32_t *)buff ) ; + if ( result ) + { + count = 0 ; + } +// if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */ +// && rcvr_datablock(buff, 512)) +// count = 0; + } + else + { /* Multiple block read */ + do + { + result = sd_read_block( sector, ( uint32_t *)buff ) ; + if ( result ) + { + sector += 1 ; + buff += 512 ; + count -= 1 ; + } + else + { + count = 1 ; // Flag error + break ; + } + } while ( count ) ; + } + return count ? RES_ERROR : RES_OK; +} + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ + ) +{ + if (drv || !count) return RES_PARERR; + +#if 0 + if (Stat & STA_NOINIT) return RES_NOTRDY; + if (Stat & STA_PROTECT) return RES_WRPRT; + + if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */ + + if (count == 1) { /* Single block write */ + if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */ + && xmit_datablock(buff, 0xFE)) + count = 0; + } + else { /* Multiple block write */ + if (CardType & CT_SDC) send_cmd(ACMD23, count); + if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */ + do { + if (!xmit_datablock(buff, 0xFC)) break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */ + count = 1; + } + } + deselect(); +#endif + return count ? RES_ERROR : RES_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + BYTE *buff /* Buffer to send/receive control data */ + ) +{ + DRESULT res; + // TODO BSS BYTE n, csd[16], *ptr = buff; + // TODO BSS WORD csize; + + if (drv) return RES_PARERR; + + res = RES_ERROR; +#if 0 + if (ctrl == CTRL_POWER) { + switch (ptr[0]) { + case 0: /* Sub control code (POWER_OFF) */ + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code (POWER_GET) */ + ptr[1] = (BYTE)power_status(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + switch (ctrl) { + case CTRL_SYNC : /* Make sure that no pending write process. Do not remove this or written sector might not left updated. */ + if (select()) { + deselect(); + res = RES_OK; + } + break; + + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { + if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */ + csize = csd[9] + ((WORD)csd[8] << 8) + 1; + *(DWORD*)buff = (DWORD)csize << 10; + } else { /* SDC ver 1.XX or MMC*/ + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(DWORD*)buff = (DWORD)csize << (n - 9); + } + res = RES_OK; + } + break; + + case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */ + if (CardType & CT_SD2) { /* SDv2? */ + if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */ + rcvr_spi(); + if (rcvr_datablock(csd, 16)) { /* Read partial block */ + for (n = 64 - 16; n; n--) rcvr_spi(); /* Purge trailing data */ + *(DWORD*)buff = 16UL << (csd[10] >> 4); + res = RES_OK; + } + } + } else { /* SDv1 or MMCv3 */ + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */ + if (CardType & CT_SD1) { /* SDv1 */ + *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1); + } else { /* MMCv3 */ + *(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1); + } + res = RES_OK; + } + } + break; + + case MMC_GET_TYPE : /* Get card type flags (1 byte) */ + *ptr = CardType; + res = RES_OK; + break; + + case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */ + if (send_cmd(CMD9, 0) == 0 /* READ_CSD */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */ + if (send_cmd(CMD10, 0) == 0 /* READ_CID */ + && rcvr_datablock(ptr, 16)) + res = RES_OK; + break; + + case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */ + if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ + for (n = 4; n; n--) *ptr++ = rcvr_spi(); + res = RES_OK; + } + break; + + case MMC_GET_SDSTAT : /* Receive SD statsu as a data block (64 bytes) */ + if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */ + rcvr_spi(); + if (rcvr_datablock(ptr, 64)) + res = RES_OK; + } + break; + + default: + res = RES_PARERR; + } + + deselect(); + } +#endif + return res; +} + + + + + + + + + + diff --git a/src/ersky9x/diskio.h b/src/ersky9x/diskio.h new file mode 100644 index 000000000..ce0ddf093 --- /dev/null +++ b/src/ersky9x/diskio.h @@ -0,0 +1,145 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file (C)ChaN, 2010 +/-----------------------------------------------------------------------*/ + +#ifndef DEF_DISKIO +#define DEF_DISKIO + +#include "integer.h" + +#define DN_MCI 0 /* Physical drive number for MCI */ +#define DN_NAND 1 /* Physical drive number for NAND flash */ + + +/* These functions are defined in asmfunc.S */ +void Copy_al2un (BYTE *dst, const DWORD *src, int count); /* Copy aligned to unaligned. */ +void Copy_un2al (DWORD *dst, const BYTE *src, int count); /* Copy unaligned to aligned. */ + + +/* 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 */ + +DSTATUS disk_initialize (BYTE); +DSTATUS disk_status (BYTE); +DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +DRESULT disk_ioctl (BYTE, BYTE, BYTE*); +void disk_timerproc (void); + + +/* 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 fucntion */ + +/* Generic ioctl command (defined for FatFs) */ +#define CTRL_SYNC 0 /* Flush disk cache (for write functions) */ +#define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */ +#define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */ + +/* Generic ioctl command */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +/* NAND specific ioctl command */ +#define NAND_FORMAT 30 /* Create physical format */ + + +volatile extern BYTE Timer1, Timer2; /* 100Hz decrement timer */ + +//------------------------------------------------------------------------------ +/// Detect if SD card is connected +//------------------------------------------------------------------------------ +#define CardIsConnected() ( (PIOB->PIO_PDSR & PIO_PB7) == 0 ) + +extern uint32_t Card_ID[4] ; +extern uint32_t Card_SCR[2] ; +extern uint32_t Card_CSD[4] ; +extern uint32_t Sd_128_resp[4] ; +extern uint32_t Sd_rca ; +//extern uint32_t Cmd_55_resp ; + +extern uint32_t SD_SetBusWidth( uint32_t busWidth) ; +extern void SD_EnableHsMode( uint8_t hsEnable) ; +extern uint32_t SD_SetSpeed( uint32_t mciSpeed ) ; +extern void SD_Reset( uint8_t keepSettings) ; +extern void sd_cmd55( void ) ; +extern uint32_t sd_acmd41( void ) ; +extern uint32_t sd_cmd2( void ) ; +extern uint32_t sd_cmd3( void ) ; +extern uint32_t sd_cmd7( void ) ; +extern uint32_t sd_cmd9( void ) ; +extern uint32_t sd_cmd17( uint32_t address, uint32_t *presult ) ; +extern uint32_t sd_acmd6( void ) ; +extern uint32_t sd_acmd51( uint32_t *presult ) ; +extern void sd_poll_10mS( void ) ; +extern uint32_t sd_card_ready( void ) ; +extern uint32_t sd_read_block( uint32_t block_no, uint32_t *data ) ; + + +#endif diff --git a/src/ersky9x/ff.cpp b/src/ersky9x/ff.cpp new file mode 100644 index 000000000..b6e66396c --- /dev/null +++ b/src/ersky9x/ff.cpp @@ -0,0 +1,3819 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.08a (C)ChaN, 2010 +/-----------------------------------------------------------------------------/ +/ 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 terms. +/ +/ Copyright (C) 2010, 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) partition. +/ 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 algorithm 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 physical 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. (_FS_TINY) +/ 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. +/ +/ May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3) +/ Added file lock feature. (_FS_SHARE) +/ Added fast seek feature. (_USE_FASTSEEK) +/ Changed some types on the API, XCHAR->TCHAR. +/ Changed fname member in the FILINFO structure on Unicode cfg. +/ String functions support UTF-8 encoding files on Unicode cfg. +/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) +/ Added sector erase feature. (_USE_ERASE) +/ Moved file lock semaphore table from fs object to the bss. +/ Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. +/ Fixed f_mkfs() creates wrong FAT32 volume. +/---------------------------------------------------------------------------*/ + +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ +#include "gtime.h" + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 8255 +#error Wrong include file (ff.h). +#endif + + +/* Definitions on sector size */ +#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 +#error Wrong sector size. +#endif +#if _MAX_SS != 512 +#define SS(fs) ((fs)->ssize) /* Multiple sector size */ +#else +#define SS(fs) 512U /* Fixed sector size */ +#endif + + +/* Reentrancy related */ +#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); } + + +/* File shareing feature */ +#if _FS_SHARE +#if _FS_READONLY +#error _FS_SHARE must be 0 on read-only cfg. +#endif +typedef struct { + FATFS *fs; /* File ID 1, volume (NULL:blank entry) */ + DWORD clu; /* File ID 2, directory */ + WORD idx; /* File ID 3, directory index */ + WORD ctr; /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */ +} FILESEM; +#endif + + +/* Misc definitions */ +#define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO)) +#define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);} + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S /* Code page is DBCS */ + +#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 /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* 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 */ + + +/* FAT sub-type boundaries */ +/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ +#define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */ +#define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because there are incompatibility of the packing option +/ between 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_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 BS_55AA 510 + +#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 + + + +/*------------------------------------------------------------*/ +/* Work area */ + +#if _VOLUMES +static +FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +#else +#error Number of drives must not be 0. +#endif + +static +WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH +static +BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_SHARE +static +FILESEM Files[_FS_SHARE]; /* File lock semaphores */ +#endif + +#if _USE_LFN == 0 /* No LFN */ +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() + +#elif _USE_LFN == 1 /* LFN with static LFN working buffer */ +static WCHAR LfnBuf[_MAX_LFN+1]; +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } +#define FREE_BUF() + +#elif _USE_LFN == 2 /* LFN with dynamic LFN working buffer on the stack */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } +#define FREE_BUF() + +#elif _USE_LFN == 3 /* LFN with dynamic LFN working buffer on the heap */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn +#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ + if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ + (dobj).lfn = lfn; (dobj).fn = sfn; } +#define FREE_BUF() ff_memfree(lfn) + +#else +#error Wrong LFN configuration. +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + +#if _WORD_ACCESS == 1 + while (cnt >= sizeof(int)) { + *(int*)d = *(int*)s; + d += sizeof(int); s += sizeof(int); + cnt -= sizeof(int); + } +#endif + while (cnt--) + *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + while (cnt--) + *d++ = (BYTE)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)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 +int 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 + + + +/*-----------------------------------------------------------------------*/ +/* File shareing control functions */ +/*-----------------------------------------------------------------------*/ +#if _FS_SHARE + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dj, /* Directory object pointing the file to be checked */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_SHARE; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dj->fs && /* Check if the file matched with an open file */ + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } else { /* Blank entry */ + be++; + } + } + if (i == _FS_SHARE) /* The file is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new file? */ + + /* The file has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock ( /* Check if an entry is available for a new file */ + FATFS* fs /* File system object */ +) +{ + UINT i; + + for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; + return (i == _FS_SHARE) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */ + DIR* dj, /* Directory object pointing the file to register or increment */ + int acc /* Desired access mode (0:Read, !0:Write) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_SHARE; i++) { /* Find the file */ + if (Files[i].fs == dj->fs && + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } + + if (i == _FS_SHARE) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; + if (i == _FS_SHARE) return 0; /* No space to register (int err) */ + Files[i].fs = dj->fs; + Files[i].clu = dj->sclust; + Files[i].idx = dj->index; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement file open counter */ + UINT i /* Semaphore index */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_SHARE) { + n = Files[i].ctr; + if (n == 0x100) n = 0; + if (n) n--; + Files[i].ctr = n; + if (!n) Files[i].fs = 0; + res = FR_OK; + } else { + res = FR_INT_ERR; + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_SHARE; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make appearance 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->drv, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->fsize)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf > 1; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drv, 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->drv, 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->drv, CTRL_SYNC, (BYTE*)0) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* 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->n_fatent - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + BYTE *p; + + + if (clst < 2 || clst >= fs->n_fatent) /* Chack range */ + return 1; + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc = fs->win[bc % SS(fs)]; bc++; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc |= fs->win[bc % SS(fs)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break; + p = &fs->win[clst * 2 % SS(fs)]; + return LD_WORD(p); + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break; + p = &fs->win[clst * 4 % SS(fs)]; + return LD_DWORD(p) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occurred 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->n_fatent - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + FRESULT res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + ST_WORD(p, (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val |= LD_DWORD(p) & 0xF0000000; + ST_DWORD(p, 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 _USE_ERASE + DWORD scl = clst, ecl = clst, resion[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->n_fatent) { /* 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; + } +#if _USE_ERASE + if (ecl + 1 == nxt) { /* Next cluster is contiguous */ + ecl = nxt; + } else { /* End of contiguous clusters */ + resion[0] = clust2sect(fs, scl); /* Start sector */ + resion[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, resion); /* Erase the block */ + scl = ecl = nxt; + } +#endif + 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; + FRESULT res; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (!scl || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch the current chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ + return cs; + if (ncl == scl) return 0; /* No free cluster */ + } + + res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ + } + if (res == FR_OK) { + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + } + + return ncl; /* Return new cluster number or error code */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( + DIR *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->n_fatent) /* 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 (root-dir in FAT12/16) */ + 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 (sub-dirs or root-dir in FAT32) */ + 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->n_fatent) /* 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 stretch */ + DIR *dj, /* Pointer to directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch 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->n_fatent) { /* When it reached end of dynamic table */ +#if !_FS_READONLY + BYTE c; + if (!stretch) return FR_NO_FILE; /* When do not stretch, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Stretch 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 stretched 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 +int cmp_lfn ( /* 1:Matched, 0:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + UINT 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 0; /* Not matched */ + } else { + if (uc != 0xFFFF) return 0; /* 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 0; + + return 1; /* The part of LFN matched */ +} + + + +static +int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + UINT 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 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* 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 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; +} + + +#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 */ +) +{ + UINT 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 generated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa */ + i = 7; + do { + c = (seq % 16) + '0'; + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + 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; + UINT 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 ( + DIR *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_sdi(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, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +FRESULT dir_read ( + DIR *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, 0); /* 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 */ + DIR *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)) /* Cannot create dot entry */ + return FR_INVALID_NAME; + + if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = 0; /* 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 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_sdi(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 contiguous entry */ + if (++n == ne) break; /* A contiguous entry that required count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_sdi(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, 0); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_sdi(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, 1); /* Next entry with table stretch */ + } 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 */ +#if _USE_LFN + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + 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 */ + DIR *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_sdi(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, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_sdi(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 ( + DIR *dj, /* Pointer to the directory object */ + const TCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */ +#endif + +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *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)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) + return FR_INVALID_NAME; /* Reject invalid sequence */ + w = (w << 8) + b; /* Create a DBC */ + } + w = ff_convert(w, 1); /* Convert ANSI/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]; /* Return 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 nul 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 end 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 = excvt[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 (always false on SBCS cfg) */ + 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; + UINT 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 = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Return 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 = (BYTE)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? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = excvt[c-0x80]; /* Upper conversion (SBCS) */ +#else +#if !_DF1S /* ASCII only cfg */ + return FR_INVALID_NAME; +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + d = (BYTE)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]; /* Return 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 nul 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 (Name extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body 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 */ + DIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to the file information to be filled */ +) +{ + UINT i; + BYTE nt, *dir; + TCHAR *p, c; + + + 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 = (TCHAR)0xE5; + if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *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; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *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; /* Terminate SFN str by a \0 */ + +#if _USE_LFN + if (fno->lfname && fno->lfsize) { + TCHAR *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 (always false on SBCS cfg) */ + tp[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */ + tp[i++] = (TCHAR)w; + } + } + tp[i] = 0; /* Terminate the LFN str by a \0 */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dj, /* Directory object to return last directory and found object */ + const TCHAR *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, ns; + + +#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 separator */ + 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 < ' ') { /* Nul path means the start directory itself */ + res = dir_sdi(dj, 0); + dj->dir = 0; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + ns = *(dj->fn+NS); + if (res != FR_OK) { /* Failed to find the object */ + if (res != FR_NO_FILE) break; /* Abort if any hard error occured */ + /* Object not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exit */ + dj->sclust = 0; dj->dir = 0; /* It is the root dir */ + res = FR_OK; + if (!(ns & NS_LAST)) continue; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; + } + break; + } + if (ns & NS_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 = LD_CLUST(dir); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk 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->drv, 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; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file system object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR **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, b, *tbl; + UINT vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat; + WORD nrsv; + const TCHAR *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 = CurrVol; /* Use current drive */ +#else + vol = 0; /* Use drive 0 */ +#endif + } + + /* Check if the logical drive is valid or not */ + if (vol >= _VOLUMES) /* Is the drive number valid? */ + return FR_INVALID_DRIVE; + *rfs = fs = FatFs[vol]; /* Return 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->drv); + 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 a volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no media or hard error */ +#if _MAX_SS != 512 /* Get disk sector size (variable sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK) + return FR_DISK_ERR; +#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. Supports only generic partitionings, FDISK and SFD. */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 if it is a VBR */ + if (fmt == 1) { /* Not an FAT-VBR, the disk may be partitioned */ + /* Check the 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) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* Following code initializes the file system object */ + + if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + fasize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = b = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + if (b != 1 && b != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= b; /* Number of sectors for FAT area */ + + fs->csize = b = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / 32)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */ + + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + + nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (!nrsv) return FR_NO_FILESYSTEM; /* (BPB_RsvdSecCnt must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / 32); /* RSV+FAT+DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT12; + if (nclst >= MIN_FAT16) fmt = FS_FAT16; + if (nclst >= MIN_FAT32) fmt = FS_FAT32; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->database = bsect + sysect; /* Data start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + if (fmt == FS_FAT32) { + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Required FAT size) */ + } else { + if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Required FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (FAT size must not be less than FAT sectors */ + return FR_NO_FILESYSTEM; + +#if !_FS_READONLY + /* Initialize cluster allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->last_clust = 0; + + /* Get fsinfo if available */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs->drv, 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->id = ++Fsid; /* File system mount ID */ + fs->winsect = 0; /* Invalidate sector cache */ + fs->wflag = 0; +#if _FS_RPATH + fs->cdir = 0; /* Current directory (root dir) */ +#endif +#if _FS_SHARE /* Clear file lock semaphores */ + clear_lock(fs); +#endif + + 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->drv) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical 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 >= _VOLUMES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current fs object */ + + if (rfs) { +#if _FS_SHARE + clear_lock(rfs); +#endif +#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; +} + + +/*---------------------------------------------------------*/ +/* User Provided Date/Time Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ +/* This is not required in read-only configuration. */ + +gtime_t g_unixTime; // Global date/time register, incremented each second in per10ms() + +uint32_t o9x_get_fattime(void) +{ + struct gtm t; + filltm(&g_unixTime, &t); // create a struct tm date/time structure from global unix time stamp + + /* Pack date and time into a DWORD variable */ + return ((DWORD)(t.tm_year - 80) << 25) + | ((uint32_t)(t.tm_mon+1) << 21) + | ((uint32_t)t.tm_mday << 16) + | ((uint32_t)t.tm_hour << 11) + | ((uint32_t)t.tm_min << 5) + | ((uint32_t)t.tm_sec >> 1); +} + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const TCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + fp->fs = 0; /* 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_READ)); +#else + mode &= FA_READ; + res = chk_mounted(&path, &dj.fs, 0); +#endif + INIT_BUF(dj); + if (res == FR_OK) + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; + +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (!dir) /* Current dir itself */ + res = FR_INVALID_NAME; +#if _FS_SHARE + else + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD dw, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_SHARE + res = enq_lock(dj.fs) ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + dir = dj.dir; /* New entry */ + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) { /* Cannot create new */ + res = FR_EXIST; + } else { + if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = o9x_get_fattime(); /* Created time */ + ST_DWORD(dir+DIR_CrtTime, dw); + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + cl = LD_CLUST(dir); /* Get start cluster */ + ST_CLUST(dir, 0); /* cluster = 0 */ + dj.fs->wflag = 1; + if (cl) { /* Remove the cluster chain if exist */ + dw = dj.fs->winsect; + res = remove_chain(dj.fs, cl); + if (res == FR_OK) { + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + res = move_window(dj.fs, dw); + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Follow succeeded */ + if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + res = FR_DENIED; + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA__WRITTEN; + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#if _FS_SHARE + fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->lockid) res = FR_INT_ERR; +#endif + } + +#else /* R/O configuration */ + if (res == FR_OK) { /* Follow succeeded */ + if (!dir) { /* Current dir itself */ + res = FR_INVALID_NAME; + } else { + if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + res = FR_NO_FILE; + } + } +#endif + FREE_BUF(); + + if (res == FR_OK) { + fp->flag = mode; /* File access mode */ + fp->org_clust = LD_CLUST(dir); /* File start cluster */ + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; /* File pointer */ + fp->dsect = 0; +#if _USE_FASTSEEK + fp->cltbl = 0; /* No cluster link map table */ +#endif + fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */ + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + BYTE *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 csect, *rbuff = buff; + + + *br = 0; /* Initialize byte counter */ + + 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? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* 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 */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) + 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) + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + 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->drv, 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->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + 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 BYTE *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 = buff; + BYTE csect; + + + *bw = 0; /* Initialize byte counter */ + + 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? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* 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 stretch 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 */ + } +#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->drv, 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 += csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_write(fp->fs->drv, 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 + 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->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + 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 change 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->drv, 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_CLUST(dir, fp->org_clust); /* Update start cluster */ + tim = o9x_get_fattime(); /* Update 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 + FATFS *fs = fp->fs; + res = validate(fs, fp->id); + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + LEAVE_FF(fs, res); + +#else + res = f_sync(fp); /* Flush cached data */ +#if _FS_SHARE + if (res == FR_OK) { /* Decrement open counter */ +#if _FS_REENTRANT + res = validate(fp->fs, fp->id); + if (res == FR_OK) { + res = dec_lock(fp->lockid); + unlock_fs(fp->fs, FR_OK); + } +#else + res = dec_lock(fp->lockid); +#endif + } +#endif + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + return res; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Current Drive/Directory Handlings */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH >= 1 + +FRESULT f_chdrive ( + BYTE drv /* Drive number */ +) +{ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + + CurrVol = drv; + + return FR_OK; +} + + + +FRESULT f_chdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the path */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (!dj.dir) { + dj.fs->cdir = dj.sclust; /* Start directory itself */ + } else { + if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ + dj.fs->cdir = LD_CLUST(dj.dir); + else + res = FR_NO_PATH; /* Reached but a file */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR *path, /* Pointer to the directory path */ + UINT sz_path /* Size of path */ +) +{ + FRESULT res; + DIR dj; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMEBUF; + + + *path = 0; + res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */ + if (res == FR_OK) { + INIT_BUF(dj); + i = sz_path; /* Bottom of buffer (dir stack base) */ + dj.sclust = dj.fs->cdir; /* Start to follow upper dir from current dir */ + while ((ccl = dj.sclust) != 0) { /* Repeat while current dir is a sub-dir */ + res = dir_sdi(&dj, 1); /* Get parent dir */ + if (res != FR_OK) break; + res = dir_read(&dj); + if (res != FR_OK) break; + dj.sclust = LD_CLUST(dj.dir); /* Goto parent dir */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child dir */ + res = dir_read(&dj); + if (res != FR_OK) break; + if (ccl == LD_CLUST(dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; +#if _USE_LFN + fno.lfname = path; + fno.lfsize = i; +#endif + get_fileinfo(&dj, &fno); /* Get the dir name and push it to the buffer */ + tp = fno.fname; + if (_USE_LFN && *path) tp = path; + for (n = 0; tp[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) path[--i] = tp[--n]; + path[--i] = '/'; + } + tp = path; + if (res == FR_OK) { + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; + if (i == sz_path) { /* Root-dir */ + *tp++ = '/'; + } else { /* Sub-dir */ + do /* Add stacked path str */ + *tp++ = path[i++]; + while (i < sz_path); + } + } + *tp = 0; + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#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; + + + 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 _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + DWORD cl, pcl, ncl, tcl, dsc, tlen, *tbl = fp->cltbl; + BYTE csc; + + tlen = *tbl++; + if (ofs == CREATE_LINKMAP) { /* Create link map table */ + cl = fp->org_clust; + if (cl) { + do { + if (tlen < 4) { /* Not enough table items */ + res = FR_NOT_ENOUGH_CORE; break; + } + tcl = cl; ncl = 0; + do { /* Get a fragment and store the top and length */ + pcl = cl; ncl++; + cl = get_fat(fp->fs, cl); + if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + } while (cl == pcl + 1); + *tbl++ = ncl; *tbl++ = tcl; + tlen -= 2; + } while (cl < fp->fs->n_fatent); + } + *tbl = 0; /* Terminate table */ + + } else { /* Fast seek */ + if (ofs > fp->fsize) /* Clip offset at the file size */ + ofs = fp->fsize; + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + dsc = (ofs - 1) / SS(fp->fs); + cl = dsc / fp->fs->csize; + for (;;) { + ncl = *tbl++; + if (!ncl) ABORT(fp->fs, FR_INT_ERR); + if (cl < ncl) break; + cl -= ncl; tbl++; + } + fp->curr_clust = cl + *tbl; + csc = (BYTE)(dsc & (fp->fs->csize - 1)); + dsc = clust2sect(fp->fs, fp->curr_clust); + if (!dsc) ABORT(fp->fs, FR_INT_ERR); + dsc += csc; + if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Flush dirty buffer if needed */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { + DWORD clst, bcs, nsect, ifptr; + + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + 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 stretch 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->n_fatent) ABORT(fp->fs, FR_INT_ERR); + fp->curr_clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += ofs / SS(fp->fs); + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Flush dirty buffer if needed */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, 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 change 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 ( + DIR *dj, /* Pointer to directory object to create */ + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj->fs, 0); + if (res == FR_OK) { + INIT_BUF(*dj); + res = follow_path(dj, path); /* Follow the path to the directory */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (dj->dir) { /* It is not the root dir */ + if (dj->dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = LD_CLUST(dj->dir); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dj->id = dj->fs->id; + res = dir_sdi(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 ( + DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DEF_NAMEBUF; + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dj, 0); /* Rewind the directory object */ + } else { + INIT_BUF(*dj); + res = dir_read(dj); /* Read an directory item */ + if (res == FR_NO_FILE) { /* Reached end of dir */ + 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, 0); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + FREE_BUF(); + } + } + + LEAVE_FF(dj->fs, res); +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR *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) { + /* If free_clust is valid, return it without full cluster scan */ + if ((*fatfs)->free_clust <= (*fatfs)->n_fatent - 2) { + *nclst = (*fatfs)->free_clust; + } else { + /* 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) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) n++; + } while (++clst < (*fatfs)->n_fatent); + } else { + clst = (*fatfs)->n_fatent; + sect = (*fatfs)->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(*fatfs, sect++); + if (res != FR_OK) break; + 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, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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) { + if (fp->flag & FA__ERROR) { /* Check abort flag */ + res = FR_INT_ERR; + } else { + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + res = FR_DENIED; + } + } + if (res == FR_OK) { + 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->n_fatent) { + 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 TCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + BYTE *dir; + DWORD dclst; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; /* Cannot remove dot entry */ +#if _FS_SHARE + if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open file */ +#endif + if (res == FR_OK) { /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = FR_INVALID_NAME; /* Cannot remove the start directory */ + } else { + if (dir[DIR_Attr] & AM_RDO) + res = FR_DENIED; /* Cannot remove R/O object */ + } + dclst = LD_CLUST(dir); + if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */ + if (dclst < 2) { + res = FR_INT_ERR; + } else { + mem_cpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); /* Exclude dot entries */ + if (res == FR_OK) { + res = dir_read(&sdj); + if (res == FR_OK /* Not empty dir */ +#if _FS_RPATH + || dclst == sdj.fs->cdir /* Current dir */ +#endif + ) res = FR_DENIED; + if (res == FR_NO_FILE) res = FR_OK; /* Empty */ + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK) { + if (dclst) /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res == FR_OK) res = sync(dj.fs); + } + } + } + FREE_BUF(); + } + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, n; + DWORD dsc, dcl, pcl, tim = o9x_get_fattime(); + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) /* Flush FAT */ + res = move_window(dj.fs, 0); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(dj.fs, dcl); + dir = dj.fs->win; + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir+DIR_WrtTime, tim); + ST_CLUST(dir, dcl); + mem_cpy(dir+32, dir, 32); /* Create ".." entry */ + dir[33] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + ST_CLUST(dir+32, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res != FR_OK) break; + mem_set(dir, 0, SS(dj.fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res != FR_OK) { + remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */ + ST_CLUST(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + 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 TCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + 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 TCHAR *path_old, /* Pointer to the old name */ + const TCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEF_NAMEBUF; + + + res = chk_mounted(&path_old, &djo.fs, 1); + if (res == FR_OK) { + djn.fs = djo.fs; + INIT_BUF(djo); + res = follow_path(&djo, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; +#if _FS_SHARE + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Old object is found */ + if (!djo.dir) { /* Is root dir? */ + res = FR_NO_FILE; + } else { + mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */ + mem_cpy(&djn, &djo, sizeof(DIR)); /* Check new object */ + res = follow_path(&djn, path_new); + 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? */ +/* Start critical section that any interruption or error can cause cross-link */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy object information except for name */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { /* Update .. entry in the directory if needed */ + dw = clust2sect(djn.fs, LD_CLUST(dir)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(djn.fs, dw); + dir = djn.fs->win+32; /* .. entry */ + if (res == FR_OK && dir[1] == '.') { + dw = (djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase) ? 0 : djn.sclust; + ST_CLUST(dir, dw); + djn.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) + res = sync(djo.fs); + } + } +/* End critical section */ + } + } + } + FREE_BUF(); + } + LEAVE_FF(djo.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 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; + BYTE csect; + + + *bf = 0; /* Initialize byte counter */ + + 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)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (!csect) { /* 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 */ + } + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + 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 */ +#define N_FATS 1 /* 1 or 2 */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ + UINT au /* Allocation unit size [bytes] */ +) +{ + static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; + static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; + BYTE fmt, md, *tbl; + DWORD n_clst, vs, n, wsect; + UINT i; + DWORD b_vol, b_fat, b_dir, b_data; /* Offset (LBA) */ + DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ + FATFS *fs; + DSTATUS stat; + + + /* Check mounted drive and clear work area */ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + 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) + return FR_DISK_ERR; +#endif + if (disk_ioctl(drv, GET_SECTOR_COUNT, (BYTE*)&n_vol) != RES_OK || n_vol < 128) + return FR_DISK_ERR; + b_vol = (sfd) ? 0 : 63; /* Volume start sector */ + n_vol -= b_vol; + if (au & (au - 1)) au = 0; /* Check validity of the allocation unit size */ + if (!au) { /* AU auto selection */ + vs = n_vol / (2000 / (SS(fs) / 512)); + for (i = 0; vs < vst[i]; i++) ; + au = cst[i]; + } + au /= SS(fs); /* Number of sectors per cluster */ + if (au == 0) au = 1; + if (au > 128) au = 128; + + /* Pre-compute number of clusters and FAT syb-type */ + n_clst = n_vol / au; + fmt = FS_FAT12; + if (n_clst >= MIN_FAT16) fmt = FS_FAT16; + if (n_clst >= MIN_FAT32) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + if (fmt == FS_FAT32) { + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 32; + n_dir = 0; + } else { + n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; + n_fat = (n_fat + SS(fs) - 1) / SS(fs); + n_rsv = 1; + n_dir = N_ROOTDIR * 32UL / SS(fs); + } + b_fat = b_vol + n_rsv; /* FAT area start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ + b_data = b_dir + n_dir; /* Data area start sector */ + if (n_vol < b_data + au) return FR_MKFS_ABORTED; /* Too small volume */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, (BYTE*)&n) != RES_OK || !n || n > 32768) n = 1; + n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ + n = (n - b_data) / N_FATS; + if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ + n_rsv += n; + b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + n_fat += n; + } + + /* Determine number of cluster and final check of validity of the FAT sub-type */ + n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; + if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) + || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) + return FR_MKFS_ABORTED; + + /* Create partition table if required */ + if (sfd) { + md = 0xF0; + } else { + DWORD n_disk = b_vol + n_vol; + + 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_vol < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */ + ST_WORD(tbl+64, 0xAA55); /* Signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) + return FR_DISK_ERR; + md = 0xF8; + } + + /* Create volume boot record */ + tbl = fs->win; /* Clear sector */ + mem_set(tbl, 0, SS(fs)); + mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot code, OEM name */ + i = SS(fs); /* Sector size */ + ST_WORD(tbl+BPB_BytsPerSec, i); + tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of rootdir entries */ + ST_WORD(tbl+BPB_RootEntCnt, i); + if (n_vol < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_vol); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_vol); + } + tbl[BPB_Media] = md; /* Media descriptor */ + 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_vol); /* Hidden sectors */ + n = o9x_get_fattime(); /* Use current time as VSN */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl+BS_VolID32, n); /* VSN */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (VBR+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID, n); /* VSN */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors 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 label, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ + if (disk_write(drv, tbl, b_vol, 1) != RES_OK)/* Write original (VBR) */ + return FR_DISK_ERR; + if (fmt == FS_FAT32) /* Write backup (VBR+6) */ + disk_write(drv, tbl, b_vol + 6, 1); + + /* Initialize FAT area */ + wsect = b_fat; + for (i = 0; i < N_FATS; i++) { + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + n = md; /* Media descriptor byte */ + if (fmt != FS_FAT32) { + n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + n |= 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* 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, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ + for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector write */ + if (disk_write(drv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize root directory */ + i = (fmt == FS_FAT32) ? au : n_dir; + do { + if (disk_write(drv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--i); + +#if _USE_ERASE /* Erase data area if needed */ + { + DWORD eb[2]; + + eb[0] = wsect; eb[1] = wsect + n_clst * au - 1; + disk_ioctl(drv, CTRL_ERASE_SECTOR, eb); + } +#endif + + /* Create FSInfo 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_vol + 1, 1); /* Write original (VBR+1) */ + disk_write(drv, tbl, b_vol + 7, 1); /* Write backup (VBR+7) */ + } + + return (disk_ioctl(drv, CTRL_SYNC, (BYTE*)0) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + +#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ +/*------------------------------------------------------------------------*/ +/* Allocate a memory block */ +/*------------------------------------------------------------------------*/ +/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. + */ + +void* ff_memalloc ( /* Returns pointer to the allocated memory block */ + UINT size /* Number of bytes to allocate */ + ) +{ + return malloc(size); +} + + +/*------------------------------------------------------------------------*/ +/* Free a memory block */ +/*------------------------------------------------------------------------*/ + +void ff_memfree( + void* mblock /* Pointer to the memory block to free */ + ) +{ + free(mblock); +} + +#endif + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fil /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; /* Break on EOF or error */ + c = s[0]; +#if _LFN_UNICODE /* Read a character in UTF-8 encoding */ + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequense */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; + c = ((c & 0x1F) << 6) | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequense */ + f_read(fil, s, 2, &rc); + if (rc != 2) break; + c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequense */ + c = '?'; + } + } + } +#endif +#if _USE_STRFUNC >= 2 + if (c == '\r') continue; /* Strip '\r' */ +#endif + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + UINT bw, btw; + BYTE s[3]; + + +#if _USE_STRFUNC >= 2 + if (c == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + +#if _LFN_UNICODE /* Write the character in UTF-8 encoding */ + if (c < 0x80) { /* 7-bit */ + s[0] = (BYTE)c; + btw = 1; + } else { + if (c < 0x800) { /* 11-bit */ + s[0] = (BYTE)(0xC0 | (c >> 6)); + s[1] = (BYTE)(0x80 | (c & 0x3F)); + btw = 2; + } else { /* 16-bit */ + s[0] = (BYTE)(0xE0 | (c >> 12)); + s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F)); + s[2] = (BYTE)(0x80 | (c & 0x3F)); + btw = 3; + } + } +#else /* Write the character without conversion */ + s[0] = (BYTE)c; + btw = 1; +#endif + f_write(fil, s, btw, &bw); /* Write the char to the file */ + return (bw == btw) ? 1 : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const TCHAR* 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 TCHAR* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + BYTE f, r; + UINT i, w; + ULONG val; + TCHAR c, d, s[16]; + int 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 character */ + 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 (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *str++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + cc = f_puts(va_arg(arp, TCHAR*), fil); continue; + case 'C' : /* Character */ + cc = f_putc((TCHAR)va_arg(arp, int), fil); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown */ + cc = f_putc(c, fil); continue; + } + + /* Get an argument */ + val = (f & 2) ? va_arg(arp, long) : ((d == 'D') ? (long)va_arg(arp, int) : va_arg(arp, unsigned int)); + if (d == 'D' && (val & 0x80000000)) { + val = 0 - val; + f |= 4; + } + /* Put it in numeral string */ + i = 0; + do { + d = (TCHAR)(val % r); val /= r; + if (d > 9) { + d += 7; + if (c == 'x') d += 0x20; + } + s[i++] = d + '0'; + } while (val && i < sizeof(s) / sizeof(s[0])); + if (f & 4) s[i++] = '-'; + cc = 0; + while (i < w-- && cc != EOF) { + cc = f_putc((TCHAR)((f & 1) ? '0' : ' '), fil); + res++; + } + do { + cc = f_putc(s[--i], fil); + res++; + } while (i && cc != EOF); + if (cc != EOF) cc = 0; + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/src/ersky9x/ff.h b/src/ersky9x/ff.h new file mode 100644 index 000000000..6178492bb --- /dev/null +++ b/src/ersky9x/ff.h @@ -0,0 +1,575 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.08a (C)ChaN, 2010 +/----------------------------------------------------------------------------/ +/ 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) 2010, 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 8255 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inttypes.h" +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#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 + + + +/* Definitions of volume management */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ +#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive# */ +#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition# */ +typedef struct { + BYTE pd; /* Physical drive# */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; +extern const PARTITION VolToPart[]; /* Volume - Physical location resolution table */ + +#else /* Single partition configuration */ +#define LD2PD(vol) (vol) /* Logical drive# is bound to the same physical drive# */ +#define LD2PT(vol) 0 /* Always mounts the 1st partition */ + +#endif + + + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode string */ +#if !_USE_LFN +#error _LFN_UNICODE must be 0 in non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif + +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* FAT sub-type (0:Not mounted) */ + BYTE drv; /* Physical drive number */ + BYTE csize; /* Sectors per cluster (1,2,4...128) */ + BYTE n_fats; /* Number of FAT copies (1,2) */ + 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 (FAT12/16) */ +#if _MAX_SS != 512 + WORD ssize; /* Bytes per sector (512,1024,2048,4096) */ +#endif +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector (FAT32) */ +#endif +#if _FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#endif + DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */ + DWORD fsize; /* Sectors per FAT */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */ +} FATFS; + + + +/* File object structure (FIL) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE pad1; + DWORD fptr; /* File read/write pointer (0 on file open) */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster (0 when fsize==0) */ + 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 _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (null on file open) */ +#endif +#if _FS_SHARE + UINT lockid; /* File lock ID (index of file semaphore table) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File data read/write buffer */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + 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:Root dir) */ + 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 +} DIR; + + + +/* File status structure (FILINFO) */ + +typedef struct { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + TCHAR fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + TCHAR* lfname; /* Pointer to the LFN buffer */ + UINT lfsize; /* Size of LFN buffer in TCHAR */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, // (0) Succeeded + FR_DISK_ERR, // (1) A hard error occured in the low level disk I/O layer + FR_INT_ERR, // (2) Assertion failed + FR_NOT_READY, // (3) The physical drive cannot work + FR_NO_FILE, // (4) Could not find the file + FR_NO_PATH, // (5) Could not find the path + FR_INVALID_NAME, // (6) The path name format is invalid + FR_DENIED, // (7) Acces denied due to prohibited access or directory full + FR_EXIST, // (8) Acces denied due to prohibited access + FR_INVALID_OBJECT, // (9) The file/directory object is invalid + FR_WRITE_PROTECTED, // (10) The physical drive is write protected + FR_INVALID_DRIVE, // (11) The logical drive number is invalid + FR_NOT_ENABLED, // (12) The volume has no work area + FR_NO_FILESYSTEM, // (13) There is no valid FAT volume on the physical drive + FR_MKFS_ABORTED, // (14) The f_mkfs() aborted due to any parameter error + FR_TIMEOUT, // (15) Could not get a grant to access the volume within defined period + FR_LOCKED, // (16) The operation is rejected according to the file shareing policy + FR_NOT_ENOUGH_CORE, // (17) LFN working buffer could not be allocated + FR_TOO_MANY_OPEN_FILES // (18) Number of open files > _FS_SHARE +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const TCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, BYTE*, UINT, UINT*); /* Read data from 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 (DIR*, const TCHAR*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const TCHAR*, FILINFO*); /* Get file status */ + +#if !_FS_READONLY +FRESULT f_write (FIL*, const BYTE *, UINT, UINT*); /* Write data to a file */ +FRESULT f_getfree (const TCHAR*, 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 TCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const TCHAR*); /* Create a new directory */ +FRESULT f_chmod (const TCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const TCHAR*, const FILINFO*); /* Change timestamp of the file/dir */ +FRESULT f_rename (const TCHAR*, const TCHAR*); /* Rename/Move a file or directory */ +#endif + +#if _USE_FORWARD +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +#endif + +#if _USE_MKFS +FRESULT f_mkfs (BYTE, BYTE, UINT); /* Create a file system on the drive */ +#endif + +#if _FS_RPATH +FRESULT f_chdrive (BYTE); /* Change current drive */ +FRESULT f_chdir (const TCHAR*); /* Change current directory */ +FRESULT f_getcwd (TCHAR*, UINT); /* Get current directory */ +#endif + +#if _USE_STRFUNC +int f_putc (TCHAR, FIL*); /* Put a character to the file */ +int f_puts (const TCHAR*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const TCHAR*, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR*, int, FIL*); /* Get a string from the file */ +#ifndef EOF +#define EOF (-1) +#endif +#endif + +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->fsize) + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY +extern uint32_t o9x_get_fattime (void); /* Returns 32-bit packed date and time */ +#endif + +/* Unicode support functions */ +#if _USE_LFN /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT); /* Allocate memory block */ +void ff_memfree (void*); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */ +int ff_del_syncobj (_SYNC_t); /* Delete a sync object */ +int ff_req_grant (_SYNC_t); /* Lock sync object */ +void ff_rel_grant (_SYNC_t); /* Unlock sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#define FA__ERROR 0x80 + +#if !_FS_READONLY +#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 + + +/* 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 */ + + +/* Fast seek function */ +#define CREATE_LINKMAP 0xFFFFFFFF + + +/*--------------------------------*/ +/* 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 + +#ifdef __cplusplus +} +#endif + +#endif /* _FATFS */ diff --git a/src/ersky9x/ffconf.h b/src/ersky9x/ffconf.h new file mode 100644 index 000000000..4c8f2641b --- /dev/null +++ b/src/ersky9x/ffconf.h @@ -0,0 +1,221 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.08a (C)ChaN, 2010 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONF +#define _FFCONF 8255 /* Revision ID */ + + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 1 /* 0:Normal or 1:Tiny */ +/* 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:Read/Write or 1:Read only */ +/* 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 to 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 1. +/ 3: f_lseek is removed in addition to 2. */ + + +#define _USE_STRFUNC 1 /* 0:Disable or 1/2:Enable */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 1 /* 0:Disable or 1:Enable */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + +#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */ +/* To enable fast seek feature, set _USE_FASTSEEK to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 932 +/* 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 0 /* 0 to 3 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN, +/ Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. When enable to use heap, memory control functions +/ ff_memalloc() and ff_memfree() must be added to the project. */ + + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. */ + + +#define _FS_RPATH 2 /* 0 to 2 */ +/* The _FS_RPATH option configures relative path feature. +/ +/ 0: Disable relative path feature and remove related functions. +/ 1: Enable relative path. f_chdrive() and f_chdir() are available. +/ 2: f_getcwd() is available in addition to 1. +/ +/ Note that output of the f_readdir fnction is affected by this option. */ + + + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 /* 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:Single partition or 1:Multiple partition */ +/* When set to 0, each volume is bound to the same physical drive number and +/ it can mount only first primaly partition. When it is set to 1, each volume +/ is tied to the partitions listed in VolToPart[]. */ + + +#define _USE_ERASE 0 /* 0:Disable or 1:Enable */ +/* To enable sector erase feature, set _USE_ERASE to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS +/ option defines which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. +/ 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. */ + + +/* Include a header file here to define sync object types on the O/S */ +/* #include , , or ohters. */ + +#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ +#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. */ + + +#define _FS_SHARE 0 /* 0:Disable or >=1:Enable */ +/* To enable file shareing feature, set _FS_SHARE to 1 or greater. The value + defines how many files can be opened simultaneously. */ + + +#endif /* _FFCONFIG */ diff --git a/src/ersky9x/gtime.cpp b/src/ersky9x/gtime.cpp new file mode 100644 index 000000000..ebe0069dd --- /dev/null +++ b/src/ersky9x/gtime.cpp @@ -0,0 +1,495 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "gtime.h" + +#define LEAP_SECONDS_POSSIBLE 0 + +/* Shift A right by B bits portably, by dividing A by 2**B and + truncating towards minus infinity. A and B should be free of side + effects, and B should be in the range 0 <= B <= INT_BITS - 2, where + INT_BITS is the number of useful bits in an int. GNU code can + assume that INT_BITS is at least 32. + + ISO C99 says that A >> B is implementation-defined if A < 0. Some + implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift + right in the usual way when A < 0, so SHR falls back on division if + ordinary A >> B doesn't seem to be the usual signed shift. */ +#define SHR(a, b) \ + (-1 >> 1 == -1 \ + ? (a) >> (b) \ + : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) + +/* The extra casts in the following macros work around compiler bugs, + e.g., in Cray C 5.0.3.0. */ + +/* True if the arithmetic type T is an integer type. bool counts as + an integer. */ +#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1) + +/* True if negative values of the signed integer type T use two's + complement, ones' complement, or signed magnitude representation, + respectively. Much GNU code assumes two's complement, but some + people like to be portable to all possible C hosts. */ +#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1) +#define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0) +#define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1) + +/* True if the arithmetic type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* The maximum and minimum values for the integer type T. These + macros have undefined behavior if T is signed and has padding bits. + If this is a problem for you, please let us know how to fix it for + your host. */ +#define TYPE_MINIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) 0 \ + : TYPE_SIGNED_MAGNITUDE (t) \ + ? ~ (t) 0 \ + : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))) +#define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))) + +#ifndef TIME_T_MIN +# define TIME_T_MIN TYPE_MINIMUM (gtime_t) +#endif +#ifndef TIME_T_MAX +# define TIME_T_MAX TYPE_MAXIMUM (gtime_t) +#endif +#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1) + +/* Verify a requirement at compile-time (unlike assert, which is runtime). */ +#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } + +verify (gtime_t_is_integer, TYPE_IS_INTEGER (gtime_t)); +verify (twos_complement_arithmetic, TYPE_TWOS_COMPLEMENT (int)); +/* The code also assumes that signed integer overflow silently wraps + around, but this assumption can't be stated without causing a + diagnostic on some hosts. */ + +#define EPOCH_YEAR 1970 +#define TM_YEAR_BASE 1900 +verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0); + +#define SECS_PER_HOUR 3600ul +#define SECS_PER_DAY 86400ul + +/* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */ +static inline int +leapyear (long int year) +{ + /* Don't add YEAR to TM_YEAR_BASE, as that might overflow. + Also, work even if YEAR is negative. */ + return + ((year & 3) == 0 + && (year % 100 != 0 + || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3))); +} + +const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + +/* Compute the `struct tm' representation of *T, + offset OFFSET seconds east of UTC, + and store year, yday, mon, mday, wday, hour, min, sec into *TP. + Return nonzero if successful. */ +int +__offtime ( + gtime_t *t, + long int offset, + struct gtm *tp) +{ + long int days, rem, y; + const unsigned short int *ip; + + days = *t / SECS_PER_DAY; + rem = *t % SECS_PER_DAY; + rem += offset; + while (rem < 0) + { + rem += SECS_PER_DAY; + --days; + } + while (rem >= (long int)SECS_PER_DAY) + { + rem -= SECS_PER_DAY; + ++days; + } + tp->tm_hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + tp->tm_min = rem / 60; + tp->tm_sec = rem % 60; + /* January 1, 1970 was a Thursday. */ + tp->tm_wday = (4 + days) % 7; + if (tp->tm_wday < 0) + tp->tm_wday += 7; + y = 1970; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) + + while (days < 0 || days >= (leapyear (y) ? 366 : 365)) + { + /* Guess a corrected year, assuming 365 days per year. */ + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + tp->tm_year = y - 1900; + if (tp->tm_year != y - 1900) + { + /* The year cannot be represented due to overflow. */ + // __set_errno (EOVERFLOW); + return 0; + } + tp->tm_yday = days; + ip = __mon_yday[leapyear(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + tp->tm_mon = y; + tp->tm_mday = days + 1; + return 1; +} + +/* time_r function implementations */ +// G: No time zones in our implementation so just do the converion from gtime_t to struct tm +struct gtm * +__localtime_r (gtime_t * t, struct gtm * tp) +{ + __offtime(t, 0, tp); + return tp; +} + + +/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) - + (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks + were not adjusted between the time stamps. + + The YEAR values uses the same numbering as TP->tm_year. Values + need not be in the usual range. However, YEAR1 must not be less + than 2 * INT_MIN or greater than 2 * INT_MAX. + + The result may overflow. It is the caller's responsibility to + detect overflow. */ + +static inline gtime_t +ydhms_diff (long int year1, long int yday1, int hour1, int min1, int sec1, + int year0, int yday0, int hour0, int min0, int sec0) +{ + verify (C99_integer_division, -1 / 2 == 0); + verify (long_int_year_and_yday_are_wide_enough, + INT_MAX <= LONG_MAX / 2 || TIME_T_MAX <= UINT_MAX); + + /* Compute intervening leap days correctly even if year is negative. + Take care to avoid integer overflow here. */ + int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3); + int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3); + int a100 = a4 / 25 - (a4 % 25 < 0); + int b100 = b4 / 25 - (b4 % 25 < 0); + int a400 = SHR (a100, 2); + int b400 = SHR (b100, 2); + int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); + + /* Compute the desired time in gtime_t precision. Overflow might + occur here. */ + gtime_t tyear1 = year1; + gtime_t years = tyear1 - year0; + gtime_t days = 365 * years + yday1 - yday0 + intervening_leap_days; + gtime_t hours = 24 * days + hour1 - hour0; + gtime_t minutes = 60 * hours + min1 - min0; + gtime_t seconds = 60 * minutes + sec1 - sec0; + return seconds; +} + +/* Return a gtime_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC), + assuming that *T corresponds to *TP and that no clock adjustments + occurred between *TP and the desired time. + If TP is null, return a value not equal to *T; this avoids false matches. + If overflow occurs, yield the minimal or maximal value, except do not + yield a value equal to *T. */ +static gtime_t +guess_time_tm (long int year, long int yday, int hour, int min, int sec, + gtime_t *t, struct gtm *tp) +{ + if (tp) + { + gtime_t d = ydhms_diff (year, yday, hour, min, sec, + tp->tm_year, tp->tm_yday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + gtime_t t1 = *t + d; + if ((t1 < *t) == (TYPE_SIGNED (gtime_t) ? d < 0 : TIME_T_MAX / 2 < d)) + return t1; + } + + /* Overflow occurred one way or another. Return the nearest result + that is actually in range, except don't report a zero difference + if the actual difference is nonzero, as that would cause a false + match; and don't oscillate between two values, as that would + confuse the spring-forward gap detector. */ + return (*t < TIME_T_MIDPOINT + ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN) + : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX)); +} + +/* Use CONVERT to convert *T to a broken down time in *TP. + If *T is out of range for conversion, adjust it so that + it is the nearest in-range value and then convert that. */ +static struct gtm * +ranged_convert (struct gtm *(*convert) (gtime_t *, struct gtm *), + gtime_t *t, struct gtm *tp) +{ + struct gtm *r = convert (t, tp); + + if (!r && *t) + { + gtime_t bad = *t; + gtime_t ok = 0; + + /* BAD is a known unconvertible gtime_t, and OK is a known good one. + Use binary search to narrow the range between BAD and OK until + they differ by 1. */ + while (bad != ok + (bad < 0 ? -1 : 1)) + { + gtime_t mid = *t = (bad < 0 + ? bad + ((ok - bad) >> 1) + : ok + ((bad - ok) >> 1)); + r = convert (t, tp); + if (r) + ok = mid; + else + bad = mid; + } + + if (!r && ok) + { + /* The last conversion attempt failed; + revert to the most recent successful attempt. */ + *t = ok; + r = convert (t, tp); + } + } + + return r; +} + +/* Convert *TP to a gtime_t value, inverting + the monotonic and mostly-unit-linear conversion function CONVERT. + Use *OFFSET to keep track of a guess at the offset of the result, + compared to what the result would be for UTC without leap seconds. + If *OFFSET's guess is correct, only one CONVERT call is needed. + This function is external because it is used also by timegm.c. */ +gtime_t +__mktime_internal (struct gtm *tp, + struct gtm *(*convert) (gtime_t *, struct gtm *), + gtime_t *offset) +{ + gtime_t t, gt, t0, t1, t2; + struct gtm tm; + + /* The maximum number of probes (calls to CONVERT) should be enough + to handle any combinations of time zone rule changes, solar time, + leap seconds, and oscillations around a spring-forward gap. + POSIX.1 prohibits leap seconds, but some hosts have them anyway. */ + int remaining_probes = 6; + + /* Time requested. Copy it in case CONVERT modifies *TP; this can + occur if TP is localtime's returned value and CONVERT is localtime. */ + int sec = tp->tm_sec; + int min = tp->tm_min; + int hour = tp->tm_hour; + int mday = tp->tm_mday; + int mon = tp->tm_mon; + int year_requested = tp->tm_year; + + /* Ensure that mon is in range, and set year accordingly. */ + int mon_remainder = mon % 12; + int negative_mon_remainder = mon_remainder < 0; + int mon_years = mon / 12 - negative_mon_remainder; + long int lyear_requested = year_requested; + long int year = lyear_requested + mon_years; + + /* The other values need not be in range: + the remaining code handles minor overflows correctly, + assuming int and gtime_t arithmetic wraps around. + Major overflows are caught at the end. */ + + /* Calculate day of year from year, month, and day of month. + The result need not be in range. */ + int mon_yday = ((__mon_yday[leapyear (year)] + [mon_remainder + 12 * negative_mon_remainder]) + - 1); + long int lmday = mday; + long int yday = mon_yday + lmday; + + gtime_t guessed_offset = *offset; + + int sec_requested = sec; + +/* + if (LEAP_SECONDS_POSSIBLE) + { + // Handle out-of-range seconds specially, + // since ydhms_tm_diff assumes every minute has 60 seconds. + if (sec < 0) + sec = 0; + if (59 < sec) + sec = 59; + } +*/ + + /* Invert CONVERT by probing. First assume the same offset as last + time. */ + + t0 = ydhms_diff (year, yday, hour, min, sec, + EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset); + + if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) + { + /* gtime_t isn't large enough to rule out overflows, so check + for major overflows. A gross check suffices, since if t0 + has overflowed, it is off by a multiple of TIME_T_MAX - + TIME_T_MIN + 1. So ignore any component of the difference + that is bounded by a small value. */ + + /* Approximate log base 2 of the number of time units per + biennium. A biennium is 2 years; use this unit instead of + years to avoid integer overflow. For example, 2 average + Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds, + which is 63113904 seconds, and rint (log2 (63113904)) is + 26. */ + int ALOG2_SECONDS_PER_BIENNIUM = 26; + int ALOG2_MINUTES_PER_BIENNIUM = 20; + int ALOG2_HOURS_PER_BIENNIUM = 14; + int ALOG2_DAYS_PER_BIENNIUM = 10; + int LOG2_YEARS_PER_BIENNIUM = 1; + + int approx_requested_biennia = + (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM) + - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM) + + SHR (mday, ALOG2_DAYS_PER_BIENNIUM) + + SHR (hour, ALOG2_HOURS_PER_BIENNIUM) + + SHR (min, ALOG2_MINUTES_PER_BIENNIUM) + + (LEAP_SECONDS_POSSIBLE + ? 0 + : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM))); + + int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM); + int diff = approx_biennia - approx_requested_biennia; + int abs_diff = diff < 0 ? - diff : diff; + + /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously + gives a positive value of 715827882. Setting a variable + first then doing math on it seems to work. + (ghazi@caip.rutgers.edu) */ + gtime_t time_t_max = TIME_T_MAX; + gtime_t time_t_min = TIME_T_MIN; + gtime_t overflow_threshold = + (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM; + + if (overflow_threshold < abs_diff) + { + /* Overflow occurred. Try repairing it; this might work if + the time zone offset is enough to undo the overflow. */ + gtime_t repaired_t0 = -1 - t0; + approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM); + diff = approx_biennia - approx_requested_biennia; + abs_diff = diff < 0 ? - diff : diff; + if (overflow_threshold < abs_diff) + return -1; + guessed_offset += repaired_t0 - t0; + t0 = repaired_t0; + } + } + + /* Repeatedly use the error to improve the guess. */ + + for (t = t1 = t2 = t0; + (gt = guess_time_tm (year, yday, hour, min, sec, &t, + ranged_convert (convert, &t, &tm)), + t != gt); + t1 = t2, t2 = t, t = gt) + if (t == t1 && t != t2) + goto offset_found; + else if (--remaining_probes == 0) + return -1; + + offset_found: + *offset = guessed_offset + t - t0; + + if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec) + { + /* Adjust time to reflect the tm_sec requested, not the normalized value. + Also, repair any damage from a false match due to a leap second. */ + int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec; + t1 = t + sec_requested; + t2 = t1 + sec_adjustment; + if (((t1 < t) != (sec_requested < 0)) + | ((t2 < t1) != (sec_adjustment < 0)) + | ! convert (&t2, &tm)) + return -1; + t = t2; + } + + *tp = tm; + return t; +} + +/* Convert *TP to a gtime_t value. */ +gtime_t +mktime (struct gtm *tp) +{ + // no time zone stuff. Just do the math ;) + static gtime_t localtime_offset; + return __mktime_internal (tp, __localtime_r, &localtime_offset); +} + +/* Fill a (struct tm) TP* from a given gtime_t time stamp */ +gtime_t +filltm(gtime_t *t, struct gtm *tp) +{ + return __offtime(t, 0, tp); +} + diff --git a/src/ersky9x/gtime.h b/src/ersky9x/gtime.h new file mode 100644 index 000000000..b275827cf --- /dev/null +++ b/src/ersky9x/gtime.h @@ -0,0 +1,63 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef g9x_time_h +#define g9x_time_h + +#include +#include + +#undef CHAR_BIT +#define CHAR_BIT 8 + +typedef long int gtime_t; + +struct gtm +{ + int8_t tm_sec; /* Seconds. [0-60] (1 leap second) */ + int8_t tm_min; /* Minutes. [0-59] */ + int8_t tm_hour; /* Hours. [0-23] */ + int8_t tm_mday; /* Day. [1-31] */ + int8_t tm_mon; /* Month. [0-11] */ + int8_t tm_year; /* Year - 1900. Limited to the year 2115. Oh no! :P */ + int8_t tm_wday; /* Day of week. [0-6] */ + int16_t tm_yday; /* Day of year. [0-365] Needed internally for calculations */ +}; + +extern const unsigned short int __mon_yday[2][13]; + +extern gtime_t mktime(struct gtm *tp); +extern gtime_t filltm(gtime_t *t, struct gtm *tp); + +#endif + diff --git a/src/ersky9x/integer.h b/src/ersky9x/integer.h new file mode 100644 index 000000000..81fcb123a --- /dev/null +++ b/src/ersky9x/integer.h @@ -0,0 +1,70 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * open9x is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER +#define _INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include + +#else /* Embedded platform */ + +/* 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 */ +typedef 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; + +#endif + +#endif diff --git a/src/ersky9x/sdcard_driver.cpp b/src/ersky9x/sdcard_driver.cpp index a90f9d0ee..762dd5d30 100644 --- a/src/ersky9x/sdcard_driver.cpp +++ b/src/ersky9x/sdcard_driver.cpp @@ -36,6 +36,7 @@ void init_SDcard() #ifndef SIMU configure_pins( 0xFC000000, PIN_PERIPHERAL | PIN_INPUT | PIN_PER_C | PIN_PORTA | PIN_NO_PULLUP ) ; configure_pins( PIO_PB7, PIN_INPUT | PIN_PORTB | PIN_NO_PULLUP | PIN_NO_PULLDOWN ) ; + SD_Init() ; #endif } diff --git a/src/ersky9x/sdcard_driver.h b/src/ersky9x/sdcard_driver.h index a7d6bd53d..61907b2a9 100644 --- a/src/ersky9x/sdcard_driver.h +++ b/src/ersky9x/sdcard_driver.h @@ -31,5 +31,8 @@ * */ +#include "diskio.h" +#include "ff.h" + extern void init_SDcard( void ) ; diff --git a/src/ersky9x/sdmmc_spi.c b/src/ersky9x/sdmmc_spi.c deleted file mode 100644 index 0e7bba831..000000000 --- a/src/ersky9x/sdmmc_spi.c +++ /dev/null @@ -1,1410 +0,0 @@ -/* ---------------------------------------------------------------------------- - * ATMEL Microcontroller Software Support - * ---------------------------------------------------------------------------- - * Copyright (c) 2008, Atmel Corporation - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the disclaimer below. - * - * Atmel's name may not be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE - * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ---------------------------------------------------------------------------- - */ - -//------------------------------------------------------------------------------ -// Headers -//------------------------------------------------------------------------------ - -#include "sdmmc_spi.h" -#include "sdspi.h" -//#include -//#include -//#include - -#define TRACE_ERROR(x...) -#define TRACE_DEBUG(x...) -#define TRACE_INFO(x...) -#define SANITY_CHECK(x...) - -#include - -//------------------------------------------------------------------------------ -// Local constants -//------------------------------------------------------------------------------ - -// SD card operation states -#define SD_STATE_STBY 0 -#define SD_STATE_DATA 1 -#define SD_STATE_RCV 2 - -// Card type -#define UNKNOWN_CARD 0 -#define CARD_SD 1 -#define CARD_SDHC 2 -#define CARD_MMC 3 - -// Delay between sending MMC commands -#define MMC_DELAY 0x4FF - -#define SD_ADDRESS(pSd, address) (((pSd)->cardType == CARD_SDHC) ? \ - (address):((address) << SD_BLOCK_SIZE_BIT)) - -//----------------------------------------------------------------------------- -/// MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS -/// R1 is the low order byte; R2 is the next highest byte, when present. -//----------------------------------------------------------------------------- -#define R1_SPI_IDLE (1 << 0) -#define R1_SPI_ERASE_RESET (1 << 1) -#define R1_SPI_ILLEGAL_COMMAND (1 << 2) -#define R1_SPI_COM_CRC (1 << 3) -#define R1_SPI_ERASE_SEQ (1 << 4) -#define R1_SPI_ADDRESS (1 << 5) -#define R1_SPI_PARAMETER (1 << 6) -// R1 bit 7 is always zero -#define R2_SPI_CARD_LOCKED (1 << 0) -#define R2_SPI_WP_ERASE_SKIP (1 << 1) -#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP -#define R2_SPI_ERROR (1 << 2) -#define R2_SPI_CC_ERROR (1 << 3) -#define R2_SPI_CARD_ECC_ERROR (1 << 4) -#define R2_SPI_WP_VIOLATION (1 << 5) -#define R2_SPI_ERASE_PARAM (1 << 6) -#define R2_SPI_OUT_OF_RANGE (1 << 7) -#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE - -// Status register constants -#define STATUS_READY_FOR_DATA (1 << 8) -#define STATUS_IDLE (0 << 9) -#define STATUS_READY (1 << 9) -#define STATUS_IDENT (2 << 9) -#define STATUS_STBY (3 << 9) -#define STATUS_TRAN (4 << 9) -#define STATUS_DATA (5 << 9) -#define STATUS_RCV (6 << 9) -#define STATUS_PRG (7 << 9) -#define STATUS_DIS (8 << 9) -#define STATUS_STATE (0xF << 9) - -//----------------------------------------------------------------------------- -/// OCR Register -//----------------------------------------------------------------------------- -#define AT91C_VDD_16_17 (1 << 4) -#define AT91C_VDD_17_18 (1 << 5) -#define AT91C_VDD_18_19 (1 << 6) -#define AT91C_VDD_19_20 (1 << 7) -#define AT91C_VDD_20_21 (1 << 8) -#define AT91C_VDD_21_22 (1 << 9) -#define AT91C_VDD_22_23 (1 << 10) -#define AT91C_VDD_23_24 (1 << 11) -#define AT91C_VDD_24_25 (1 << 12) -#define AT91C_VDD_25_26 (1 << 13) -#define AT91C_VDD_26_27 (1 << 14) -#define AT91C_VDD_27_28 (1 << 15) -#define AT91C_VDD_28_29 (1 << 16) -#define AT91C_VDD_29_30 (1 << 17) -#define AT91C_VDD_30_31 (1 << 18) -#define AT91C_VDD_31_32 (1 << 19) -#define AT91C_VDD_32_33 (1 << 20) -#define AT91C_VDD_33_34 (1 << 21) -#define AT91C_VDD_34_35 (1 << 22) -#define AT91C_VDD_35_36 (1 << 23) -#define AT91C_CARD_POWER_UP_BUSY (1 << 31) - -#define AT91C_MMC_HOST_VOLTAGE_RANGE (AT91C_VDD_27_28 +\ - AT91C_VDD_28_29 +\ - AT91C_VDD_29_30 +\ - AT91C_VDD_30_31 +\ - AT91C_VDD_31_32 +\ - AT91C_VDD_32_33) -#define AT91C_CCS (1 << 30) - -// SPI_CMD Register Value -#define AT91C_POWER_ON_INIT (0) - -//----------------------------------------------------------------------------- -// Command Classes -//----------------------------------------------------------------------------- -// -// Class 0, 2, 4, 5, 7 and 8 are mandatory and shall be supported by all SD Memory Cards. -// Basic Commands (class 0) -// -// Cmd0 MCI + SPI -#define AT91C_GO_IDLE_STATE_CMD (0) -// Cmd1 SPI -#define AT91C_MMC_SEND_OP_COND_CMD (1) -// Cmd2 MCI -#define AT91C_ALL_SEND_CID_CMD (2) -// Cmd3 MCI -#define AT91C_SET_RELATIVE_ADDR_CMD (3) -// Cmd4 MCI -//#define AT91C_SET_DSR_CMD (4) -// cmd7 MCI -#define AT91C_SEL_DESEL_CARD_CMD (7) -// Cmd8 MCI + SPI -#define AT91C_SEND_IF_COND (8) -// Cmd9 MCI + SPI -#define AT91C_SEND_CSD_CMD (9) -// Cmd10 MCI + SPI -#define AT91C_SEND_CID_CMD (10) -// Cmd12 MCI + SPI -#define AT91C_STOP_TRANSMISSION_CMD (12) -// Cmd13 MCI + SPI -#define AT91C_SEND_STATUS_CMD (13) -// Cmd15 MCI -//#define AT91C_GO_INACTIVE_STATE_CMD (15) -// Cmd58 SPI -#define AT91C_READ_OCR_CMD (58) -// Cmd59 SPI -#define AT91C_CRC_ON_OFF_CMD (59) -//#define AT91C_MMC_ALL_SEND_CID_CMD (2) -//#define AT91C_MMC_SET_RELATIVE_ADDR_CMD (3) -//#define AT91C_MMC_READ_DAT_UNTIL_STOP_CMD (11) -//#define AT91C_STOP_TRANSMISSION_SYNC_CMD (12) - -//*------------------------------------------------ -//* Class 2 commands: Block oriented Read commands -//*------------------------------------------------ -// Cmd16 -#define AT91C_SET_BLOCKLEN_CMD (16) -// Cmd17 -#define AT91C_READ_SINGLE_BLOCK_CMD (17) -// Cmd18 -#define AT91C_READ_MULTIPLE_BLOCK_CMD (18) - -//*------------------------------------------------ -//* Class 4 commands: Block oriented write commands -//*------------------------------------------------ -// Cmd24 -#define AT91C_WRITE_BLOCK_CMD (24) -// Cmd25 -#define AT91C_WRITE_MULTIPLE_BLOCK_CMD (25) -// Cmd27 -//#define AT91C_PROGRAM_CSD_CMD (27) - -//*---------------------------------------- -//* Class 5 commands: Erase commands -//*---------------------------------------- -// Cmd32 -//#define AT91C_TAG_SECTOR_START_CMD (32) -// Cmd33 -//#define AT91C_TAG_SECTOR_END_CMD (33) -// Cmd38 -//#define AT91C_ERASE_CMD (38) - -//*---------------------------------------- -//* Class 7 commands: Lock commands -//*---------------------------------------- -// Cmd42 -//#define AT91C_LOCK_UNLOCK (42) - -//*----------------------------------------------- -// Class 8 commands: Application specific commands -//*----------------------------------------------- -// Cmd55 -#define AT91C_APP_CMD (55) -// cmd 56 -//#define AT91C_GEN_CMD (56) -// ACMD6 -#define AT91C_SDCARD_SET_BUS_WIDTH_CMD (6) -// ACMD13 -//#define AT91C_SDCARD_STATUS_CMD (13) -// ACMD22 -//#define AT91C_SDCARD_SEND_NUM_WR_BLOCKS_CMD (22) -// ACMD23 -//#define AT91C_SDCARD_SET_WR_BLK_ERASE_COUNT_CMD (23) -// ACMD41 -#define AT91C_SDCARD_APP_OP_COND_CMD (41) -// ACMD42 -//#define AT91C_SDCARD_SET_CLR_CARD_DETECT_CMD (42) -// ACMD51 -#define AT91C_SDCARD_SEND_SCR_CMD (51) - -//------------------------------------------------------------------------------ -// Local functions -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -/// Sends the current SD card driver command to the card. -/// Returns 0 if successful; Otherwise, returns the transfer status code or -/// SD_ERROR_DRIVER if there was a problem with the SD transfer. -/// \param pSd Pointer to a SdCard driver instance. -//------------------------------------------------------------------------------ -static unsigned char SendCommand(SdCard *pSd) -{ - SdCmd *pCommand = &(pSd->command); - SdDriver *pSdDriver = pSd->pSdDriver; - unsigned char error; - unsigned int i; - - // Send command - SDSPI_NCS((SdSpi *)pSdDriver); - - error = SDSPI_SendCommand((SdSpi *)pSdDriver, (SdSpiCmd *)pCommand); - if (error) { - TRACE_ERROR("SPI SendCommand: Failed to send command (%d)\n\r", error); - return SD_ERROR_DRIVER; - } - - // Wait for command to complete - while (!SDSPI_IsTxComplete((SdSpiCmd *)pCommand)); - if(pCommand->cmd == AT91C_STOP_TRANSMISSION_CMD) { - if( 1 == SDSPI_Wait((SdSpi *)pSdDriver, 2) ) { - TRACE_ERROR("SPI \n\r"); - } - while (SDSPI_WaitDataBusy((SdSpi *)pSdDriver) == 1); - } - - // Delay between sending commands, only for MMC card test. - if((pSd->cardType == CARD_MMC) - ||(pSd->cardType == UNKNOWN_CARD) - ||(pSd->cardType == CARD_SD)) { - - for(i=0; i < MMC_DELAY; i++); - } - - return pCommand->status; -} - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -void DecodeR1(unsigned char R1) -{ - if( (R1 & R1_SPI_IDLE)==R1_SPI_IDLE) { - TRACE_DEBUG("R1_SPI_IDLE\n\r"); - } - if( (R1 & R1_SPI_ERASE_RESET)==R1_SPI_ERASE_RESET) { - TRACE_DEBUG("R1_SPI_ERASE_RESET\n\r"); - } - if( (R1 & R1_SPI_ILLEGAL_COMMAND)==R1_SPI_ILLEGAL_COMMAND) { - TRACE_DEBUG("R1_SPI_ILLEGAL_COMMAND\n\r"); - } - if( (R1 & R1_SPI_COM_CRC)==R1_SPI_COM_CRC) { - TRACE_DEBUG("R1_SPI_COM_CRC\n\r"); - } - if( (R1 & R1_SPI_ERASE_SEQ)==R1_SPI_ERASE_SEQ) { - TRACE_DEBUG("R1_SPI_ERASE_SEQ\n\r"); - } - if( (R1 & R1_SPI_ADDRESS)==R1_SPI_ADDRESS) { - TRACE_DEBUG("R1_SPI_ADDRESS\n\r"); - } - if( (R1 & R1_SPI_PARAMETER)==R1_SPI_PARAMETER) { - TRACE_DEBUG("R1_SPI_PARAMETER\n\r"); - } -} - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -void DecodeR2(unsigned char R2) -{ - if( (R2 & R2_SPI_CARD_LOCKED)==R2_SPI_CARD_LOCKED) { - TRACE_DEBUG("R2_SPI_CARD_LOCKED\n\r"); - } - if( (R2 & R2_SPI_WP_ERASE_SKIP)==R2_SPI_WP_ERASE_SKIP) { - TRACE_DEBUG("R2_SPI_WP_ERASE_SKIP/R2_SPI_LOCK_UNLOCK_FAIL\n\r"); - } - if( (R2 & R2_SPI_ERROR)==R2_SPI_ERROR) { - TRACE_DEBUG("R2_SPI_ERROR\n\r"); - } - if( (R2 & R2_SPI_CC_ERROR)==R2_SPI_CC_ERROR) { - TRACE_DEBUG("R2_SPI_CC_ERROR\n\r"); - } - if( (R2 & R2_SPI_CARD_ECC_ERROR)==R2_SPI_CARD_ECC_ERROR) { - TRACE_DEBUG("R2_SPI_CARD_ECC_ERROR\n\r"); - } - if( (R2 & R2_SPI_WP_VIOLATION)==R2_SPI_WP_VIOLATION) { - TRACE_DEBUG("R2_SPI_WP_VIOLATION\n\r"); - } - if( (R2 & R2_SPI_ERASE_PARAM)==R2_SPI_ERASE_PARAM) { - TRACE_DEBUG("R2_SPI_ERASE_PARAM\n\r"); - } - if( (R2 & R2_SPI_OUT_OF_RANGE)==R2_SPI_OUT_OF_RANGE) { - TRACE_DEBUG("R2_SPI_OUT_OF_RANGE/R2_SPI_CSD_OVERWRITE\n\r"); - } -} - -//------------------------------------------------------------------------------ -/// Check SPI mode response 1. -/// Returns 0 if no error; Otherwise, returns error. -/// \param pResp Pointer to response token. -//------------------------------------------------------------------------------ -static unsigned char SD_SPI_R1(unsigned char *pResp) -{ - DecodeR1(*pResp); - - if((*pResp & 0x7E) !=0) { - // An error occured - return SD_ERROR_NORESPONSE; - } - else { - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Check SPI mode response 1b. -/// Returns 0 if no error; Otherwise, returns error. -/// \param pResp Pointer to response token. -//------------------------------------------------------------------------------ -static unsigned char SD_SPI_R1b(unsigned char *pResp) -{ - // A zero value indicates card is busy. - // A non-zero value indicates the card is ready for the next command. - if( (*pResp) == 0 ) { - TRACE_INFO("Card is busy\n\r"); - } - - DecodeR1(*(pResp+1)); - if(((*(pResp+1)) & 0x7E) !=0) { - // An error occured - return SD_ERROR_NORESPONSE; - } - else { - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Check SPI mode response 2. -/// Returns 0 if no error; Otherwise, returns error. -/// \param pResp Pointer to response token. -//------------------------------------------------------------------------------ -static unsigned char SD_SPI_R2(unsigned char *pResp) -{ - DecodeR1(*pResp); - DecodeR2(*(pResp+1)); - - if((( *pResp & 0x7e ) != 0) && (*(pResp+1) != 0)) { - return SD_ERROR_NORESPONSE; - } - else { - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Check SPI mode response 3. -/// Returns 0 if no error; Otherwise, returns error. -/// \param pResp Pointer to response token. -//------------------------------------------------------------------------------ -static unsigned char SD_SPI_R3(unsigned char *pResp, unsigned int *ocr) -{ - // *pResp: bit 32-39: R1 - if(( *pResp & 0x7e ) != 0) { - return SD_ERROR_NORESPONSE; - } - else { - // bit 0-31: OCR - *ocr = ((*(pResp+1) << 24) \ - |(*(pResp+2) << 16) \ - |(*(pResp+3) << 8) \ - | *(pResp+4)); - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Check SPI mode response 7. -/// Returns 0 if no error; Otherwise, returns error. -/// \param pResp Pointer to response token. -//------------------------------------------------------------------------------ -static unsigned char SD_SPI_R7(unsigned char *pResp, unsigned char *isSdhc) -{ - *isSdhc = 0; - - if(( *pResp & 0x7e ) != 0) { - return SD_ERROR_NORESPONSE; - } - else { - // *(pResp+4): bit 0- 7: check pattern - // *(pResp+3): bit 8-11: voltage accepted: 0x01: 2.7-3.6V - if ((*(pResp+3) == 0x1) && (*(pResp+4) == 0xAA)) { - *isSdhc = 1; - } - else { - *isSdhc = 0; - } - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp -/// up time. -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SdCard driver instance. -//------------------------------------------------------------------------------ -static unsigned char Pon(SdCard *pSd) -{ - SdCmd *pCommand = &(pSd->command); - unsigned int response; - unsigned char error; - - TRACE_DEBUG("Pon()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_POWER_ON_INIT; - pCommand->resType = 1; - pCommand->pResp = &response; - - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - error = SendCommand(pSd); - if( error == 0 ) { - error = SD_SPI_R1((unsigned char *)&response); - } - return error; -} - -//------------------------------------------------------------------------------ -/// Resets all cards to idle state -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SdCard driver instance. -//------------------------------------------------------------------------------ -static unsigned char Cmd0(SdCard *pSd) -{ - SdCmd *pCommand = &(pSd->command); - unsigned int response; - unsigned char error; - - TRACE_DEBUG("Cmd0()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_GO_IDLE_STATE_CMD; - pCommand->resType = 1; - pCommand->pResp = &response; - - // Set SD command state - pSd->state = SD_STATE_STBY; - - // send command - error = SendCommand(pSd); - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// MMC send operation condition command. -/// Sends host capacity support information and activates the card's -/// initialization process. -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SdCard driver instance. -//------------------------------------------------------------------------------ -static unsigned char Cmd1(SdCard *pSd) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - TRACE_DEBUG("Cmd1()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_MMC_SEND_OP_COND_CMD; - pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; - pCommand->resType = 1; - pCommand->pResp = &response; - - // Set SD command state - pSd->state = SD_STATE_STBY; - - // send command - error = SendCommand(pSd); - if (error) { - return error; - } - - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// Sends SD Memory Card interface -/// condition, which includes host supply -/// voltage information and asks the card -/// whether card supports voltage. -/// Returns 0 if successful; otherwise returns SD_ERROR_NORESPONSE if the card did -/// not answer the command, or SD_ERROR_DRIVER. -/// \param pSd Pointer to a SD card driver instance. -/// \param supplyVoltage Expected supply voltage. -//------------------------------------------------------------------------------ -static unsigned char Cmd8(SdCard *pSd, unsigned char supplyVoltage) -{ - SdCmd *pCommand = &(pSd->command); - unsigned int response[2]; - unsigned char error; - unsigned char isSdhc; - - TRACE_DEBUG("Cmd8()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_SEND_IF_COND; - pCommand->arg = (supplyVoltage << 8) | (0xAA); - pCommand->resType = 7; - pCommand->pResp = &response[0]; - // Set SD command state - pSd->state = SD_STATE_STBY; - - TRACE_DEBUG("supplyVoltage: 0x%x\n\r", supplyVoltage); - - // Send command - error = SendCommand(pSd); - - - TRACE_DEBUG("SD_R7[0]: 0x%x\n\r", response[0]); - TRACE_DEBUG("SD_R7[1]: 0x%x\n\r", response[1]); - error = SD_SPI_R7((unsigned char *)&response, &isSdhc); - if( error == SD_ERROR_NORESPONSE ) { - TRACE_DEBUG("Cmd8 R7 error:%d \n\r", error); - return error; - } - else { - if(isSdhc == 1) { - TRACE_DEBUG("Cmd8 Ver 2.00 isSdhc:%d\n\r", isSdhc); - return 0; - } - else { - TRACE_DEBUG("Cmd8 Ver 1.X isSdhc:%d\n\r", isSdhc); - return error; - } - } -} - -//------------------------------------------------------------------------------ -/// Addressed card sends its card-specific -/// data (CSD) on the CMD line. -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SD card driver instance. -//------------------------------------------------------------------------------ -static unsigned char Cmd9(SdCard *pSd) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response = 0; - unsigned char csdData[16]; - unsigned int i; - - TRACE_DEBUG("Cmd9()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_SEND_CSD_CMD; - pCommand->resType = 1; - pCommand->blockSize = 16; - pCommand->pData = csdData; - pCommand->isRead = 1; - pCommand->pResp = &response; - - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - error = SendCommand(pSd); - - // In SPI mode, reading CSD is the same as reading data. - for (i = 0; i < 4; i++) { - pSd->csd[i] = csdData[i*4] << 24 | - csdData[i*4+1] << 16 | - csdData[i*4+2] << 8 | - csdData[i*4+3]; - } - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// Forces the card to stop transmission -/// \param pSd Pointer to a SD card driver instance. -/// \param pStatus Pointer to a status variable. -//------------------------------------------------------------------------------ -static unsigned char Cmd12(SdCard *pSd) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - TRACE_DEBUG("Cmd12()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_STOP_TRANSMISSION_CMD; - pCommand->conTrans = SPI_NEW_TRANSFER; - pCommand->resType = 1; - pCommand->pResp = &response; - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - error = SendCommand(pSd); - - //TRACE_DEBUG("cmd12 resp 0x%X\n\r",response); - error = SD_SPI_R1b((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// Addressed card sends its status register. -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SD card driver instance. -/// \param pStatus Pointer to a status variable. -//------------------------------------------------------------------------------ -static unsigned char Cmd13(SdCard *pSd, unsigned int *pStatus) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - - TRACE_DEBUG("Cmd13()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_SEND_STATUS_CMD; - pCommand->resType = 2; - pCommand->pResp = pStatus; - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - error = SendCommand(pSd); - error = SD_SPI_R2((unsigned char *)pStatus); - return error; -} - -//------------------------------------------------------------------------------ -/// In the case of a Standard Capacity SD Memory Card, this command sets the -/// block length (in bytes) for all following block commands (read, write, lock). -/// Default block length is fixed to 512 Bytes. -/// Set length is valid for memory access commands only if partial block read -/// operation are allowed in CSD. -/// In the case of a High Capacity SD Memory Card, block length set by CMD16 -/// command does not affect the memory read and write commands. Always 512 -/// Bytes fixed block length is used. This command is effective for LOCK_UNLOCK command. -/// In both cases, if block length is set larger than 512Bytes, the card sets the -/// BLOCK_LEN_ERROR bit. -/// \param pSd Pointer to a SD card driver instance. -/// \param blockLength Block length in bytes. -//------------------------------------------------------------------------------ -static unsigned char Cmd16(SdCard *pSd, unsigned short blockLength) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - TRACE_DEBUG("Cmd16()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_SET_BLOCKLEN_CMD; - pCommand->arg = blockLength; - pCommand->resType = 1; - pCommand->pResp = &response; - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - //return SendCommand(pSd); - error = SendCommand(pSd); - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// Continously transfers datablocks from card to host until interrupted by a -/// STOP_TRANSMISSION command. -/// \param pSd Pointer to a SD card driver instance. -/// \param blockSize Block size (shall be set to 512 in case of high capacity). -/// \param pData Pointer to the application buffer to be filled. -/// \param address SD card address. -//------------------------------------------------------------------------------ -static unsigned char Cmd18(SdCard *pSd, - unsigned short nbBlock, - unsigned char *pData, - unsigned int address) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - //TRACE_DEBUG("Cmd18()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_READ_MULTIPLE_BLOCK_CMD; - pCommand->arg = address; - pCommand->blockSize = SD_BLOCK_SIZE; - pCommand->nbBlock = nbBlock; - pCommand->pData = pData; - pCommand->isRead = 1; - pCommand->conTrans = SPI_NEW_TRANSFER; - pCommand->resType = 1; - pCommand->pResp = &response; - // Set SD command state - pSd->state = SD_STATE_DATA; - - // Send command - // return SendCommand(pSd); - error = SendCommand(pSd); - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// Write block command -/// \param pSd Pointer to a SD card driver instance. -/// \param blockSize Block size (shall be set to 512 in case of high capacity). -/// \param pData Pointer to the application buffer to be filled. -/// \param address SD card address. -//------------------------------------------------------------------------------ -static unsigned char Cmd25(SdCard *pSd, - unsigned short nbBlock, - unsigned char *pData, - unsigned int address) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - TRACE_DEBUG("Cmd25()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_WRITE_MULTIPLE_BLOCK_CMD; - pCommand->arg = address; - pCommand->blockSize = SD_BLOCK_SIZE; - pCommand->nbBlock = nbBlock; - pCommand->pData = (unsigned char *) pData; - pCommand->conTrans = SPI_NEW_TRANSFER; - pCommand->resType = 1; - pCommand->pResp = &response; - - // Set SD command state - pSd->state = SD_STATE_RCV; - - // Send command - //return SendCommand(pSd); - error = SendCommand(pSd); - - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - - -//------------------------------------------------------------------------------ -/// Initialization delay: The maximum of 1 msec, 74 clock cycles and supply -/// ramp up time. -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SD card driver instance. -//------------------------------------------------------------------------------ -static unsigned char Cmd55(SdCard *pSd) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - TRACE_DEBUG("Cmd55()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_APP_CMD; - pCommand->resType = 1; - pCommand->pResp = &response; - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - //return SendCommand(pSd); - error = SendCommand(pSd); - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// SPI Mode, Reads the OCR register of a card -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SD card driver instance. -/// \param pOcr OCR value of the card -//------------------------------------------------------------------------------ -static unsigned char Cmd58(SdCard *pSd, unsigned int *pOcr) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response[2]; - - TRACE_DEBUG("Cmd58()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_READ_OCR_CMD; - pCommand->resType = 3; - pCommand->pResp = &response[0]; - - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - error = SendCommand(pSd); - error = SD_SPI_R3((unsigned char *)&response, pOcr); - return error; -} - -//------------------------------------------------------------------------------ -/// SPI Mode, Set CRC option of a card -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SD card driver instance. -/// \param option CRC option, 1 to turn on, 0 to trun off -//------------------------------------------------------------------------------ -static unsigned char Cmd59(SdCard *pSd, unsigned char option) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - TRACE_DEBUG("Cmd59()\n\r"); - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_CRC_ON_OFF_CMD; - pCommand->arg = (option & 0x1); - pCommand->resType = 1; - pCommand->pResp = &response; - - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - error = SendCommand(pSd); - error = SD_SPI_R1((unsigned char *)&response); - return error; -} - -//------------------------------------------------------------------------------ -/// Asks to all cards to send their operations conditions. -/// Returns the command transfer result (see SendCommand). -/// \param pSd Pointer to a SD card driver instance. -/// \param hcs Shall be true if Host support High capacity. -/// \param pCCS Set the pointed flag to 1 if hcs != 0 and SD OCR CCS flag is set. -//------------------------------------------------------------------------------ -static unsigned char Acmd41(SdCard *pSd, unsigned char hcs, unsigned char *pCCS) -{ - SdCmd *pCommand = &(pSd->command); - unsigned char error; - unsigned int response; - - do { - error = Cmd55(pSd); - if (error) { - return error; - } - - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->cmd = AT91C_SDCARD_APP_OP_COND_CMD; - pCommand->arg = AT91C_MMC_HOST_VOLTAGE_RANGE; - if (hcs) { - pCommand->arg |= AT91C_CCS; - } - - pCommand->resType = 1; - pCommand->pResp = &response; - - // Set SD command state - pSd->state = SD_STATE_STBY; - - // Send command - TRACE_DEBUG("Acmd41()\n\r"); - error = SendCommand(pSd); - if (error) { - return error; - } - error = SD_SPI_R1((unsigned char *)&response); - if (error) { - return error; - } - // continue if in idle mode - if ((response & 0x1) != 0) { // R1_SPI_IDLE - continue; - } - *pCCS = ((response & AT91C_CCS) != 0); - return 0; - } - while ((response & AT91C_CARD_POWER_UP_BUSY) != AT91C_CARD_POWER_UP_BUSY); - - return 0; -} - - -//------------------------------------------------------------------------------ -/// Continue to transfer datablocks from card to host until interrupted by a -/// STOP_TRANSMISSION command. -/// \param pSd Pointer to a SD card driver instance. -/// \param blockSize Block size (shall be set to 512 in case of high capacity). -/// \param pData Pointer to the application buffer to be filled. -/// \param address SD card address. -//------------------------------------------------------------------------------ -static unsigned char ContinuousRead(SdCard *pSd, - unsigned short nbBlock, - unsigned char *pData, - unsigned int address) -{ - SdCmd *pCommand = &(pSd->command); - - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->blockSize = SD_BLOCK_SIZE; - pCommand->nbBlock = nbBlock; - pCommand->pData = pData; - pCommand->isRead = 1; - pCommand->conTrans = SPI_CONTINUE_TRANSFER; - // Set SD command state - pSd->state = SD_STATE_DATA; - - // Send command - return SendCommand(pSd); -} - -//------------------------------------------------------------------------------ -/// Continue to transfer datablocks from host to card until interrupted by a -/// STOP_TRANSMISSION command. -/// \param pSd Pointer to a SD card driver instance. -/// \param blockSize Block size (shall be set to 512 in case of high capacity). -/// \param pData Pointer to the application buffer to be filled. -/// \param address SD card address. -//------------------------------------------------------------------------------ -static unsigned char ContinuousWrite(SdCard *pSd, - unsigned short nbBlock, - const unsigned char *pData, - unsigned int address) -{ - SdCmd *pCommand = &(pSd->command); - - memset(pCommand, 0, sizeof(SdCmd)); - // Fill command information - pCommand->blockSize = SD_BLOCK_SIZE; - pCommand->nbBlock = nbBlock; - pCommand->pData = (unsigned char *) pData; - pCommand->isRead = 0; - pCommand->conTrans = SPI_CONTINUE_TRANSFER; - // Set SD command state - pSd->state = SD_STATE_RCV; - - // Send command - return SendCommand(pSd); -} - -//------------------------------------------------------------------------------ -/// Move SD card to transfer state. The buffer size must be at -/// least 512 byte long. This function checks the SD card status register and -/// address the card if required before sending the transfer command. -/// Returns 0 if successful; otherwise returns an code describing the error. -/// \param pSd Pointer to a SD card driver instance. -/// \param address Address of the block to transfer. -/// \param nbBlocks Number of blocks to be transfer. -/// \param pData Data buffer whose size is at least the block size. -/// \param isRead 1 for read data and 0 for write data. -//------------------------------------------------------------------------------ -static unsigned char MoveToTransferState(SdCard *pSd, - unsigned int address, - unsigned short nbBlocks, - unsigned char *pData, - unsigned char isRead) -{ - unsigned int status; - unsigned char error; - SdDriver *pSdDriver = pSd->pSdDriver; - - if((pSd->state == SD_STATE_DATA) - || (pSd->state == SD_STATE_RCV)) { - - // SD SPI mode uses stop transmission token to stop multiple block write. - if ((pSd->state == SD_STATE_RCV) ) { //&& (pSd->mode == SD_SPI_MODE)) { - SDSPI_StopTranToken((SdSpi *)pSdDriver); - pSd->state = SD_STATE_STBY; - while (SDSPI_WaitDataBusy((SdSpi *)pSdDriver) == 1); - while (SDSPI_WaitDataBusy((SdSpi *)pSdDriver) == 1); - } - else { - error = Cmd12(pSd); - if (error) { - return error; - } - } - } - pSd->preBlock = address + (nbBlocks-1); - - if(isRead) { - //TRACE_DEBUG("Read\n\r"); - // Wait for card to be ready for data transfers - do { - //TRACE_DEBUG("state = 0x%X\n\r", (status & STATUS_STATE) >> 9); - error = Cmd13(pSd, &status); - if (error) { - TRACE_DEBUG("Pb MTTS cmd13\n\r"); - return error; - } - break; - } - while (((status & STATUS_READY_FOR_DATA) == 0) || - ((status & STATUS_STATE) != STATUS_TRAN)); - // Read data - // Move to Sending data state - error = Cmd18(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); - if (error) { - return error; - } - } - else { - //TRACE_DEBUG("Write\n\r"); - // Wait for card to be ready for data transfers - do { - error = Cmd13(pSd, &status); - if (error) { - TRACE_DEBUG("error cmd 13\n\r"); - return error; - } - break; - } - while ((status & STATUS_READY_FOR_DATA) == 0); - - // Move to Sending data state - error = Cmd25(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); - if (error) { - TRACE_DEBUG("error cmd 25\n\r"); - return error; - } - } - - return error; -} - -//------------------------------------------------------------------------------ -// Global functions -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -/// Read Block of data in a buffer pointed by pData. The buffer size must be at -/// least 512 byte long. This function checks the SD card status register and -/// address the card if required before sending the read command. -/// Returns 0 if successful; otherwise returns an code describing the error. -/// \param pSd Pointer to a SD card driver instance. -/// \param address Address of the block to read. -/// \param nbBlocks Number of blocks to be read. -/// \param pData Data buffer whose size is at least the block size. -//------------------------------------------------------------------------------ -unsigned char SD_ReadBlock(SdCard *pSd, - unsigned int address, - unsigned short nbBlocks, - unsigned char *pData) -{ - unsigned char error; - - SANITY_CHECK(pSd); - SANITY_CHECK(pData); - SANITY_CHECK(nbBlocks); - - if((pSd->state == SD_STATE_DATA) - && ((pSd->preBlock + 1) == address)) { - -#if defined(at91rm9200) - error = Cmd12(pSd); - if (error) { - return error; - } -#else - TRACE_DEBUG("SD_ReadBlock:ContinuousRead\n\r"); - error = ContinuousRead(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); - pSd->preBlock = address + (nbBlocks-1); -#endif - } - else { - error = MoveToTransferState(pSd, address, nbBlocks, pData, 1); - } - return error; -} - -//------------------------------------------------------------------------------ -/// Write Block of data pointed by pData. The buffer size must be at -/// least 512 byte long. This function checks the SD card status register and -/// address the card if required before sending the read command. -/// Returns 0 if successful; otherwise returns an SD_ERROR code. -/// \param pSd Pointer to a SD card driver instance. -/// \param address Address of block to write. -/// \param nbBlocks Number of blocks to be read -/// \param pData Pointer to a 512 bytes buffer to be transfered -//------------------------------------------------------------------------------ -unsigned char SD_WriteBlock(SdCard *pSd, - unsigned int address, - unsigned short nbBlocks, - const unsigned char *pData) -{ - unsigned char error; - - SANITY_CHECK(pSd); - SANITY_CHECK(pData); - SANITY_CHECK(nbBlocks); - - if((pSd->state == SD_STATE_RCV) - && ((pSd->preBlock + 1) == address)) { - - TRACE_DEBUG("SD_WriteBlock:ContinuousWrite\n\r"); - error = ContinuousWrite(pSd, nbBlocks, pData, SD_ADDRESS(pSd,address)); - pSd->preBlock = address + (nbBlocks-1); - } - else { - - //TRACE_FATAL("SD_WriteBlock:MoveToTransferState\n\r"); - error = MoveToTransferState(pSd, address, nbBlocks, - (unsigned char *)pData, 0); - } - return error; -} - - -//------------------------------------------------------------------------------ -/// Run the SDcard SPI Mode initialization sequence. This function runs the -/// initialisation procedure and the identification process, then it sets the SD -/// card in transfer state to set the block length. -/// Returns 0 if successful; otherwise returns an SD_ERROR code. -/// \param pSd Pointer to a SD card driver instance. -/// \param pSdDriver Pointer to SD driver already initialized. -//------------------------------------------------------------------------------ -unsigned char SD_SPI_Init(SdCard *pSd, SdDriver *pSpi) -{ - unsigned char isCCSet; - unsigned char error; - unsigned char cmd8Retries = 2; - unsigned char cmd1Retries = 1; - unsigned int pOCR; - - // The command GO_IDLE_STATE (CMD0) is the software reset command and sets card into Idle State - // regardless of the current card state. - error = Cmd0(pSd); - if (error) { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - - // CMD8 is newly added in the Physical Layer Specification Version 2.00 to support multiple voltage - // ranges and used to check whether the card supports supplied voltage. The version 2.00 host shall - // issue CMD8 and verify voltage before card initialization. - // The host that does not support CMD8 shall supply high voltage range... - do { - error = Cmd8(pSd, 1); - } - while ((error == SD_ERROR_NORESPONSE) && (cmd8Retries-- > 0)); - - if (error == SD_ERROR_NORESPONSE) { - // No response : Ver2.00 or later SD Memory Card(voltage mismatch) - // or Ver1.X SD Memory Card - // or not SD Memory Card - - TRACE_DEBUG("No response to Cmd8\n\r"); - - // CMD58 ? ! - error = Cmd58(pSd, &pOCR); - if (error) { - TRACE_ERROR("Error during initialization (%d), 8\n\r", error); - return error; - } - - // ACMD41 is a synchronization command used to negotiate the operation voltage range and to poll the - // cards until they are out of their power-up sequence. - error = Acmd41(pSd, 0, &isCCSet); - if (error) { - // Acmd41 failed : MMC card or unknown card - error = Cmd0(pSd); - if (error) { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - do { - error = Cmd1(pSd); - } - while ((error) && (cmd1Retries-- > 0)); - - if (error) { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - else { - pSd->cardType = CARD_MMC; - } - } - else { - if(isCCSet == 0) { - TRACE_DEBUG("CARD SD\n\r"); - pSd->cardType = CARD_SD; - } - } - } - else if (!error) { - error = Cmd58(pSd, &pOCR); - if (error) { - TRACE_ERROR("Error during initialization (%d), 8\n\r", error); - return error; - } - - // Valid response : Ver2.00 or later SD Memory Card - error = Acmd41(pSd, 1, &isCCSet); - if (error) { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - error = Cmd58(pSd, &pOCR); - if (error) { - TRACE_ERROR("Error during initialization (%d), 8\n\r", error); - return error; - } - if (isCCSet) { - TRACE_DEBUG("CARD SDHC\n\r"); - pSd->cardType = CARD_SDHC; - } - else { - TRACE_DEBUG("CARD SD\n\r"); - pSd->cardType = CARD_SD; - } - } - else { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - - if (pSd->cardType != CARD_MMC) { - // The host issues CRC_ON_OFF (CMD59) to set data CRC on/off - // The host can turn the CRC option on and off using the CRC_ON_OFF command (CMD59). - // Host should enable CRC verification before issuing ACMD41. - error = Cmd59(pSd,0); // turn crc option OFF - if (error) { - - TRACE_ERROR("Error during initialization (%d)\n\r, 59", error); - return error; - } - } - - // The host issues SEND_CSD (CMD9) to obtain the Card Specific Data (CSD register), - // e.g. block length, card storage capacity, etc... - error = Cmd9(pSd); - if (error) { - - TRACE_ERROR("Error during initialization (%d), 9\n\r", error); - return error; - } - return 0; -} -// - -//------------------------------------------------------------------------------ -/// Run the SDcard initialization sequence. This function runs the initialisation -/// procedure and the identification process, then it sets the SD card in transfer -/// state to set the block length and the bus width. -/// Returns 0 if successful; otherwise returns an SD_ERROR code. -/// \param pSd Pointer to a SD card driver instance. -/// \param pSdDriver Pointer to SD driver already initialized. -/// \param mode Select SD or SPI access mode -//------------------------------------------------------------------------------ -unsigned char SD_Init(SdCard *pSd, SdDriver *pSdDriver) -{ - unsigned char error; - - //TRACE_DEBUG("SD_Init()\n\r"); - - // Initialize SdCard structure - pSd->pSdDriver = pSdDriver; - pSd->cardAddress = 0; - pSd->preBlock = 0xffffffff; - pSd->state = SD_STATE_STBY; - pSd->cardType = UNKNOWN_CARD; - memset(&(pSd->command), 0, sizeof(SdCmd)); - - // Initialization delay: The maximum of 1 msec, 74 clock cycles and supply ramp up time - // ‘Supply ramp up time’ provides the time that the power is built up to the operating level (the bus - // master supply voltage) and the time to wait until the SD card can accept the first command - - // Power On Init Special Command - //TRACE_DEBUG("Pon()\n\r"); - error = Pon(pSd); - if (error) { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - - // After power-on or CMD0, all cards’ CMD lines are in input mode, waiting for start bit of the next command. - // The cards are initialized with a default relative card address (RCA=0x0000) and with a default - // driver stage register setting (lowest speed, highest driving current capability). - - error = SD_SPI_Init(pSd, pSdDriver); - if (error) { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - - // In the case of a Standard Capacity SD Memory Card, this command sets the - // block length (in bytes) for all following block commands (read, write, lock). - // Default block length is fixed to 512 Bytes. - // Set length is valid for memory access commands only if partial block read - // operation are allowed in CSD. - // In the case of a High Capacity SD Memory Card, block length set by CMD16 - // command does not affect the memory read and write commands. Always 512 - // Bytes fixed block length is used. This command is effective for LOCK_UNLOCK command. - // In both cases, if block length is set larger than 512Bytes, the card sets the - // BLOCK_LEN_ERROR bit. - if (pSd->cardType == CARD_SD) { - error = Cmd16(pSd, SD_BLOCK_SIZE); - if (error) { - TRACE_ERROR("Error during initialization (%d)\n\r", error); - return error; - } - } - - // If SD CSD v2.0 - if((pSd->cardType != CARD_MMC) && (SD_CSD_STRUCTURE(pSd) == 1)) { - pSd->totalSize = SD_CSD_TOTAL_SIZE_HC(pSd); - pSd->blockNr = SD_CSD_BLOCKNR_HC(pSd); - } - else { - pSd->totalSize = SD_CSD_TOTAL_SIZE(pSd); - pSd->blockNr = SD_CSD_BLOCKNR(pSd); - } - - if (pSd->cardType == UNKNOWN_CARD) { - return SD_ERROR_NOT_INITIALIZED; - } - else { - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Stop the SDcard. This function stops all SD operations. -/// Returns 0 if successful; otherwise returns an SD_ERROR code. -/// \param pSd Pointer to a SD card driver instance. -/// \param pSdDriver Pointer to MCI driver already initialized. -//------------------------------------------------------------------------------ -unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver) -{ - unsigned char error; - SdCmd *pCommand = &(pSd->command); - - SANITY_CHECK(pSd); - SANITY_CHECK(pSdDriver); - - if(pCommand->conTrans == SPI_CONTINUE_TRANSFER) - { - TRACE_DEBUG("SD_StopTransmission()\n\r"); - - error = Cmd12(pSd); - if(error) { - return error; - } - } - - SDSPI_Close((SdSpi *)pSdDriver); - return 0; -} - - diff --git a/src/ersky9x/sdmmc_spi.h b/src/ersky9x/sdmmc_spi.h deleted file mode 100644 index efb740881..000000000 --- a/src/ersky9x/sdmmc_spi.h +++ /dev/null @@ -1,443 +0,0 @@ -/* ---------------------------------------------------------------------------- - * ATMEL Microcontroller Software Support - * ---------------------------------------------------------------------------- - * Copyright (c) 2008, Atmel Corporation - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the disclaimer below. - * - * Atmel's name may not be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE - * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ---------------------------------------------------------------------------- - */ - -//------------------------------------------------------------------------------ -/// \page "sdmmc_spi" -/// -/// !Purpose -/// -/// Implementation for sdcard spi mode physical layer driver. Supply a set of sdcard spi mode's -/// interface. -/// -/// !Usage -/// -/// -# SD_Init: Run the SDcard initialization sequence -/// -# SD_SPI_Init : Run the SDcard SPI Mode initialization sequence -/// -# SD_Stop: Stop the SDcard by sending Cmd12 -/// -# SD_ReadBlock : Read blocks of data -/// -# SD_WriteBlock : Write blocks of data -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -/// \page "sdcard spi mode initialization and identification" -/// -/// !Purpose -/// -/// sdcard spi mode initialization and identification sequence -/// -/// !Description -/// - Host sends Cmd0 to do card reset, card is in "idle state". -/// - Host sends Cmd8 and checks the response of the card, only sdcard supports physical -/// layer version 2.00 will response correctly to Cmd8, and this command is mandatory to be -/// sent before ACmd41 for sdcard which support physical layer version 2.00, to enable new -/// functions or to validate a dual-voltage card. -/// - refer to "If Cmd8 response ok" branch for the initialize of sdcard 2.0. -/// - refer to "If Cmd8 response fail" branch for the initialize of sdcard 1.x, mmc card, sdcard2.0 -/// with invalid voltage. -/// - If Cmd8 response ok -/// - Host sends Cmd58 to read OCR register to validate the voltage. -/// - If the response to Cmd58 is fail, initialize ends, the card is put into inactive state. -/// - If the response to Cmd58 is ok, continue to the next step. -/// - Host sends ACmd41* with argument "HCS" equal to "1". -/// - If the response to ACmd41 failed, it means the card does not match the voltage -/// desired by the host, the card will be put into inactive state, initialize ends. -/// - If the response with "CCS" equal to "1", the card is a version 2.0 high capacity sdcard, -/// refer to "Card Initialize" for the succeeding initialize sequence. -/// - If the response with "CCS" equal to "0", the card is a version 2.0 standard capacity sdcard. -/// refer to "Card Initialize" for the succeeding initialize sequence. -/// - If Cmd8 response fail -/// - Host sends Cmd58 to read OCR register to validate the voltage. -/// - If the response to Cmd58 is fail, initialize ends, the card is put into inactive state. -/// - If the response to Cmd58 is ok, continue to the next step. -/// - Host sends ACmd41* argument "HCS" equal to "0". -/// - If the response to ACmd41 ok, the card is a version 1.x sdcard, refer to "Card Initialize" for -/// the succeeding initialize sequence. -/// - If the response to ACmd41 fails -/// - Host sends Cmd0 to reset card. -/// - Host sends Cmd1 to card. -/// - If card has response to Cmd1, the card is a MMC card, refer to "Card Initialize" for the -/// succeeding initialize sequence. -/// - If card has no response to Cmd1, the card is either an unknown card or a card does -/// not match host's voltage, the initialize ends. -/// - Card Initialize -/// - At this stage, the initialization and identification process is over, the following steps are done -/// for the sdcard's succeeding operation. -/// - Host sends Cmd59 to turn sdcard's CRC option off. -/// - Host sends Cmd9 to get the Card Specific Data (CSD). -/// -/// \note Send Cmd55 before send ACmd41. \endnote -/// \note sdcard include ver 1.x sdcard, ver2.0 standard capacity sdcard, ver2.0 high capacity sdcard \endnote -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -/// \page "sdcard spi mode write" -/// -/// !Purpose -/// -/// sdcard spi mode write process -/// -/// !Description -/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send -/// Cmd12 or use stop transmission token to stop multiple block write, and to transit sdcard to -/// "stand-by state". -/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". -/// - Host sends Cmd25 to do multiple blocks write, the address here is different between high capacity -/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's -/// address is equal to block number times 512. -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -/// \page "sdcard spi mode read" -/// -/// !Purpose -/// -/// sdcard spi mode read process -/// -/// !Description -/// - Make sure sdcard is under "transfer state", if the sdcard is under other state, host will send -/// Cmd12 or use stop transmission token to stop multiple block read and to transit sdcard to -/// "stand-by state". -/// - Host sends Cmd13 to check sdcard's status, to make sure sdcard is "ready-for-data". -/// - Host sends Cmd18 to do multiple blocks read, the address here is different between high capacity -/// sdcard and normal sdcard, the address of SDHC is equal to the block number, while normal sdcard's -/// address is equal to block number times 512. -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -/// \page "sdhc" -/// -/// !Purpose -/// -/// highlight of sdhc -/// -/// !Sdhc initialization and identification -/// -/// - Refer to page "sdcard spi mode initialization and identification" for the initialization and identification -/// sequence of a sdhc. -/// -/// !Functional difference between sdhc and standard capacity sdcard -/// -/// - Command argument is different: -/// - Sdhc uses block address format in memory access commands*, block length is fixed to 512 bytes. -/// - Standard capacity sdcard uses byte address format in memory access commands, block length -/// is defined in Cmd16. -/// - Partial access and misalign access are disabled in sdhc as the block address is used. -/// - Sdhc does not support write-protected commands (Cmd28, Cmd29, Cmd30). -/// -/// \note Memory access commands means block read commands (CMD17, CMD18), block write commands -/// (CMD24, CMD25), and block erase commands (CMD32, CMD33). -/// -//------------------------------------------------------------------------------ - - -#ifndef SDCARD_H -#define SDCARD_H - -//------------------------------------------------------------------------------ -// Constants -//------------------------------------------------------------------------------ - - -/// There was an error with the SD driver. -#define SD_ERROR_DRIVER 1 -/// The SD card did not answer the command. -#define SD_ERROR_NORESPONSE 2 -/// The SD card did not answer the command. -#define SD_ERROR_NOT_INITIALIZED 3 -/// The SD card is busy -#define SD_ERROR_BUSY 4 -/// The operation is not supported -#define SD_ERROR_NOT_SUPPORT 5 - -/// SD card block size in bytes. -#define SD_BLOCK_SIZE 512 -/// SD card block size binary shift value -#define SD_BLOCK_SIZE_BIT 9 - -//- MMC Card Command Types -/// Broadcast commands (bc), no response -#define MMC_CCT_BC 0 -/// Broadcase commands with response (bcr) -#define MMC_CCT_BCR 1 -/// Addressed commands (ac), no data transfer on DAT lines -#define MMC_CCT_AC 2 -/// Addressed data transfer commands (adtc), data transfer on DAT lines -#define MMC_CCT_ADTC 3 - -//- MMC Card Command Classes (CCC) -/// Class 0: basic -#define MMC_CCC_BASIC 0 -/// Class 1: stream read -#define MMC_CCC_STREAM_READ 1 -/// Class 2: block read -#define MMC_CCC_BLOCK_READ 2 -/// Class 3: stream write -#define MMC_CCC_STREAM_WRITE 3 -/// Class 4: block write -#define MMC_CCC_BLOCK_WRITE 4 -/// Class 5: erase -#define MMC_CCC_ERASE 5 -/// Class 6: write protection -#define MMC_CCC_WRITE_PROTECTION 6 -/// Class 7: lock card -#define MMC_CCC_LOCK_CARD 7 -/// Class 8: application specific -#define MMC_CCC_APP_SPEC 8 -/// Class 9: I/O mode -#define MMC_CCC_IO_MODE 9 - -//- MMC/SD Card Command Response Type -/// R1 (normal response command), 48bits -#define MMC_RESP_R1 1 -#define SD_RESP_R1 MMC_RESP_R1 -/// R1b: busy signal transmitted on the data line DAT0 -#define MMC_RESP_R1b 1 -#define SD_RESP_R1b MMC_RESP_R1b -/// R2: 136bits, CID, CSD register -#define MMC_RESP_R2 2 -#define SD_RESP_R2 MMC_RESP_R2 -/// R3: 48bits, OCR -#define MMC_RESP_R3 3 -#define SD_RESP_R3 MMC_RESP_R3 -/// R4 (Fast I/O), 48bits -#define MMC_RESP_R4 4 -/// R5 (Interrupt request), 48bits -#define MMC_RESP_R5 5 -/// R6 (Published RCA response), 48bits -#define SD_RESP_R6 6 -/// R7 (Card interface condition), 48bits -#define SD_RESP_R7 7 - -//- MMC Card CMD6 access mode -#define MMC_SWITCH_CMDSET 0 -#define MMC_SWITCH_SETBITS 1 -#define MMC_SWITCH_CLRBITS 2 -#define MMC_SWITCH_WRITE 3 - -//-MMC Boot partition enable -/// Boot partition 1 enabled for boot -#define MMC_BOOT_PART_1 1 -/// Boot partition 2 enabled for boot -#define MMC_BOOT_PART_2 2 -/// User area enabled for boot -#define MMC_BOOT_PART_USER 7 - -//-MMC Boot partition access -/// R/W boot partition 1 -#define MMC_BOOT_ACC_PART1 1 -/// R/W boot partition 2 -#define MMC_BOOT_ACC_PART2 2 - -//------------------------------------------------------------------------------ -// Macros -//------------------------------------------------------------------------------ - -// CID register access macros. -#define SD_CID(pSd, bitfield, bits) ( (pSd->cid[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) -#define SD_CID_MID(pSd) SD_CID(pSd, 120, 8) ///< Manufacturer ID -#define SD_CID_BGA(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) -#define SD_CID_CBS(pSd) SD_CID(pSd, 112, 2) ///< Card/BGA(eMMC) -#define SD_CID_OID_BYTE_1(pSd) SD_CID(pSd, 112, 8) ///< OEM/Application ID byte 1 -#define SD_CID_OID_BYTE_0(pSd) SD_CID(pSd, 104, 8) ///< OEM/Application ID byte 0 -#define SD_CID_PNM_BYTE_4(pSd) SD_CID(pSd, 96, 8) ///< Product revision byte 4 -#define SD_CID_PNM_BYTE_3(pSd) SD_CID(pSd, 88, 8) ///< Product revision byte 3 -#define SD_CID_PNM_BYTE_2(pSd) SD_CID(pSd, 80, 8) ///< Product revision byte 2 -#define SD_CID_PNM_BYTE_1(pSd) SD_CID(pSd, 72, 8) ///< Product revision byte 1 -#define SD_CID_PNM_BYTE_0(pSd) SD_CID(pSd, 64, 8) ///< Product revision byte 0 -#define SD_CID_PRV_1(pSd) SD_CID(pSd, 24, 8) ///< Product serial number 1 -#define SD_CID_PRV_2(pSd) SD_CID(pSd, 32,24) ///< Product serial number 2 -#define SD_CID_MDT_YEAR(pSd) (SD_CID(pSd, 12, 8))+2000///< Manufacturing date year -#define SD_CID_MDT_MONTH(pSd) SD_CID(pSd, 8, 4) ///< Manufacturing date month -#define SD_CID_CRC(pSd) SD_CID(pSd, 1, 7) ///< CRC7 checksum - -// CSD register access macros. -#define SD_CSD(pSd, bitfield, bits) ((((pSd)->csd)[3-(bitfield)/32] >> ((bitfield)%32)) & ((1 << (bits)) - 1)) -#define SD_CSD_STRUCTURE(pSd) SD_CSD(pSd, 126, 2) ///< CSD structure 00b Version 1.0 01b version 2.0 High Cap -#define SD_CSD_SPEC_VERS(pSd) SD_CSD(pSd, 122, 4) ///< System Specification Version Number -#define SD_CSD_TAAC(pSd) SD_CSD(pSd, 112, 8) ///< Data read-access-time-1 -#define SD_CSD_NSAC(pSd) SD_CSD(pSd, 104, 8) ///< Data read access-time-2 in CLK cycles -#define SD_CSD_TRAN_SPEED(pSd) SD_CSD(pSd, 96, 8) ///< Max. data transfer rate -#define SD_CSD_CCC(pSd) SD_CSD(pSd, 84, 12) ///< Card command class -#define SD_CSD_READ_BL_LEN(pSd) SD_CSD(pSd, 80, 4) ///< Max. read data block length -#define SD_CSD_READ_BL_PARTIAL(pSd) SD_CSD(pSd, 79, 1) ///< Bartial blocks for read allowed -#define SD_CSD_WRITE_BLK_MISALIGN(pSd) SD_CSD(pSd, 78, 1) ///< Write block misalignment -#define SD_CSD_READ_BLK_MISALIGN(pSd) SD_CSD(pSd, 77, 1) ///< Read block misalignment -#define SD_CSD_DSR_IMP(pSd) SD_CSD(pSd, 76, 1) ///< DSP implemented -#define SD_CSD_C_SIZE(pSd) ((SD_CSD(pSd, 72, 2) << 10) + \ - (SD_CSD(pSd, 64, 8) << 2) + \ - SD_CSD(pSd, 62, 2)) ///< Device size -#define SD_CSD_C_SIZE_HC(pSd) ((SD_CSD(pSd, 64, 6) << 16) + \ - (SD_CSD(pSd, 56, 8) << 8) + \ - SD_CSD(pSd, 48, 8)) ///< Device size v2.0 High Capacity -#define SD_CSD_VDD_R_CURR_MIN(pSd) SD_CSD(pSd, 59, 3) ///< Max. read current @VDD min -#define SD_CSD_VDD_R_CURR_MAX(pSd) SD_CSD(pSd, 56, 3) ///< Max. read current @VDD max -#define SD_CSD_VDD_W_CURR_MIN(pSd) SD_CSD(pSd, 53, 3) ///< Max. write current @VDD min -#define SD_CSD_VDD_W_CURR_MAX(pSd) SD_CSD(pSd, 50, 3) ///< Max. write current @VDD max -#define SD_CSD_C_SIZE_MULT(pSd) SD_CSD(pSd, 47, 3) ///< Device size multiplier -#define SD_CSD_ERASE_BLK_EN(pSd) SD_CSD(pSd, 46, 1) ///< Erase single block enable -#define SD_CSD_SECTOR_SIZE(pSd) ((SD_CSD(pSd, 40, 6) << 1) + SD_CSD(pSd, 39, 1)) ///< Erase sector size -#define SD_CSD_WP_GRP_SIZE(pSd) SD_CSD(pSd, 32, 7) ///< Write protect group size -#define SD_CSD_WP_GRP_ENABLE(pSd) SD_CSD(pSd, 31, 1) ///< write protect group enable -#define SD_CSD_R2W_FACTOR(pSd) SD_CSD(pSd, 26, 3) ///< Write speed factor -#define SD_CSD_WRITE_BL_LEN(pSd) ((SD_CSD(pSd, 24, 2) << 2) + SD_CSD(pSd, 22, 2)) ///< Max write block length -#define SD_CSD_WRITE_BL_PARTIAL(pSd) SD_CSD(pSd, 21, 1) ///< Partial blocks for write allowed -#define SD_CSD_CONTENT_PROT_APP(pSd) SD_CSD(pSd, 16, 1) ///< File format group -#define SD_CSD_FILE_FORMAT_GRP(pSd) SD_CSD(pSd, 15, 1) ///< File format group -#define SD_CSD_COPY(pSd) SD_CSD(pSd, 14, 1) ///< Copy flag (OTP) -#define SD_CSD_PERM_WRITE_PROTECT(pSd) SD_CSD(pSd, 13, 1) ///< Permanent write protect -#define SD_CSD_TMP_WRITE_PROTECT(pSd) SD_CSD(pSd, 12, 1) ///< Temporary write protection -#define SD_CSD_FILE_FORMAT(pSd) SD_CSD(pSd, 11, 2) ///< File format -#define SD_CSD_ECC(pSd) SD_CSD(pSd, 8, 2) ///< CRC -#define SD_CSD_CRC(pSd) SD_CSD(pSd, 1, 7) ///< CRC -#define SD_CSD_MULT(pSd) (1 << (SD_CSD_C_SIZE_MULT(pSd) + 2)) -#define SD_CSD_BLOCKNR(pSd) ((SD_CSD_C_SIZE(pSd) + 1) * SD_CSD_MULT(pSd)) -#define SD_CSD_BLOCKNR_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 1024) -#define SD_CSD_BLOCK_LEN(pSd) (1 << SD_CSD_READ_BL_LEN(pSd)) -#define SD_CSD_TOTAL_SIZE(pSd) (SD_CSD_BLOCKNR(pSd) * SD_CSD_BLOCK_LEN(pSd)) -#define SD_CSD_TOTAL_SIZE_HC(pSd) ((SD_CSD_C_SIZE_HC(pSd) + 1) * 512* 1024) -#define SD_TOTAL_SIZE(pSd) ((pSd)->totalSize) -#define SD_TOTAL_BLOCK(pSd) ((pSd)->blockNr) - -// SCR register access macros. -#define SD_SCR_BUS_WIDTHS(pScr) ((pScr[1] >> 16) & 0xF) ///< Describes all the DAT bus that are supported by this card -#define SD_SCR_BUS_WIDTH_4BITS (1 << 1) ///< 4bit Bus Width is supported -#define SD_SCR_BUS_WIDTH_1BIT (1 << 0) ///< 1bit Bus Width is supported - -//------------------------------------------------------------------------------ -// Types -//------------------------------------------------------------------------------ - -/// SD end-of-transfer callback function. -typedef void (*SdCallback)(unsigned char status, void *pCommand); - -//------------------------------------------------------------------------------ -/// SD Transfer Request prepared by the application upper layer. This structure -/// is sent to the SD_SendCommand function to start the transfer. At the end of -/// the transfer, the callback is invoked by the interrupt handler. -//------------------------------------------------------------------------------ -typedef struct _SdCmd { - - /// Command status. - volatile char status; - /// Command code. - unsigned int cmd; - /// Command argument. - unsigned int arg; - /// Data buffer. - unsigned char *pData; - /// Size of data buffer in bytes. - unsigned short blockSize; - /// Number of blocks to be transfered - unsigned short nbBlock; - /// Indicate if continue to transfer data - unsigned char conTrans; - /// Indicates if the command is a read operation. - unsigned char isRead; - /// Response buffer. - unsigned int *pResp; - /// SD card response type. - unsigned char resType; - /// Optional user-provided callback function. - SdCallback callback; - /// Optional argument to the callback function. - void *pArg; - -} SdCmd; - -//------------------------------------------------------------------------------ -/// SD driver structure. Holds the internal state of the SD driver and -/// prevents parallel access to a SPI peripheral. -//------------------------------------------------------------------------------ -typedef struct { - - /// Pointer to a SPI peripheral. - //AT91S_MCI *pSdHw; - /// SPI peripheral identifier. - unsigned char spiId; - /// MCI HW mode - unsigned char mciMode; - /// Pointer to currently executing command. - SdCmd *pCommand; - /// Mutex. - volatile char semaphore; - -} SdDriver; - -//------------------------------------------------------------------------------ -/// Sdcard driver structure. It holds the current command being processed and -/// the SD card address. -//------------------------------------------------------------------------------ -typedef struct _SdCard { - - /// Pointer to the underlying MCI driver. - SdDriver *pSdDriver; - /// Current MCI command being processed. - SdCmd command; - /// SD card current address. - unsigned short cardAddress; - /// Card-specific data. - unsigned int csd[4]; - /// Previous access block number. - unsigned int preBlock; - /// State after sd command complete - unsigned char state; - /// Card type - unsigned char cardType; - /// Card total size - unsigned int totalSize; - /// Card block number - unsigned int blockNr; - /// Card access mode - unsigned char mode; - -} SdCard; - -//------------------------------------------------------------------------------ -// Global functions -//------------------------------------------------------------------------------ - -extern unsigned char SD_Init(SdCard *pSd, - SdDriver *pSdDriver); - -extern unsigned char SD_ReadBlock( - SdCard *pSd, - unsigned int address, - unsigned short nbBlocks, - unsigned char *pData); - -extern unsigned char SD_WriteBlock( - SdCard *pSd, - unsigned int address, - unsigned short nbBlocks, - const unsigned char *pData); - -extern unsigned char SD_Stop(SdCard *pSd, SdDriver *pSdDriver); - -#endif //#ifndef SDCARD_H - diff --git a/src/ersky9x/sdspi.c b/src/ersky9x/sdspi.c deleted file mode 100644 index 52e9789b2..000000000 --- a/src/ersky9x/sdspi.c +++ /dev/null @@ -1,781 +0,0 @@ -/* ---------------------------------------------------------------------------- - * ATMEL Microcontroller Software Support - * ---------------------------------------------------------------------------- - * Copyright (c) 2008, Atmel Corporation - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the disclaimer below. - * - * Atmel's name may not be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE - * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ---------------------------------------------------------------------------- - */ - -//------------------------------------------------------------------------------ -// Headers -//------------------------------------------------------------------------------ - -#include "sdspi.h" -//#include -//#include -//#include -//#include -//#include -//#include -//#include -#include - -//------------------------------------------------------------------------------ -// Macros -//------------------------------------------------------------------------------ - -/// Transfer is pending. -#define SDSPI_STATUS_PENDING 1 -/// Transfer has been aborted because an error occured. -#define SDSPI_STATUS_ERROR 2 - -/// SPI driver is currently in use. -#define SDSPI_ERROR_LOCK 1 - -// Data Tokens -#define SDSPI_START_BLOCK_1 0xFE // Single/Multiple read, single write -#define SDSPI_START_BLOCK_2 0xFC // Multiple block write -#define SDSPI_STOP_TRAN 0xFD // Cmd12 - -//------------------------------------------------------------------------------ -// Exported functions -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -/// Initializes the SD Spi structure and the corresponding SPI hardware. -/// \param pSpid Pointer to a Spid instance. -/// \param pSpiHw Associated SPI peripheral. -/// \param spiId SPI peripheral identifier. -//------------------------------------------------------------------------------ -void SDSPI_Configure(SdSpi *pSdSpi, - AT91PS_SPI pSpiHw, - unsigned char spiId) -{ - // Initialize the SPI structure - pSdSpi->pSpiHw = pSpiHw; - pSdSpi->spiId = spiId; - pSdSpi->semaphore = 1; - - // Enable the SPI clock - // BSS replaced by below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); - PMC->PMC_PCER0 = (1 << pSdSpi->spiId); - - // Execute a software reset of the SPI twice - pSpiHw->SPI_CR = SPI_CR_SWRST; // BSS AT91C_SPI_SWRST; - pSpiHw->SPI_CR = SPI_CR_SWRST; // BSS AT91C_SPI_SWRST; - - // Configure SPI in Master Mode with No CS selected !!! - pSpiHw->SPI_MR = SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PCS; // BSS AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS; - - // Disables the receiver PDC transfer requests - // Disables the transmitter PDC transfer requests. - pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; - - // Enable the SPI - pSpiHw->SPI_CR = SPI_CR_SPIEN; // BSS AT91C_SPI_SPIEN; - - // Disable the SPI clock - // BSS replaced by below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); - PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); -} - -//------------------------------------------------------------------------------ -/// Configures the parameters for the device corresponding to the cs. -/// \param pSdSpi Pointer to a SdSpi instance. -/// \param cs number corresponding to the SPI chip select. -/// \param csr SPI_CSR value to setup. -//------------------------------------------------------------------------------ -void SDSPI_ConfigureCS(SdSpi *pSdSpi, unsigned char cs, unsigned int csr) -{ - unsigned int spiMr; - AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; - - // Enable the SPI clock - // BSS below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); - PMC->PMC_PCER0 = (1 << pSdSpi->spiId); - - //TRACE_DEBUG("CSR[%d]=0x%8X\n\r", cs, csr); - pSpiHw->SPI_CSR[cs] = csr; - -//jcb to put in sendcommand - // Write to the MR register - spiMr = pSpiHw->SPI_MR; - spiMr |= SPI_MR_PCS; // TODO AT91C_SPI_PCS; - spiMr &= ~((1 << cs) << 16); - pSpiHw->SPI_MR = spiMr; - - // Disable the SPI clock - // BSS below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); - PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); -} - -//------------------------------------------------------------------------------ -/// Use PDC for SPI data transfer. -/// Return 0 if no error, otherwise return error status. -/// \param pSdSpi Pointer to a SdSpi instance. -/// \param pData Data pointer. -/// \param size Data transfer byte count. -//------------------------------------------------------------------------------ -unsigned char SDSPI_PDC(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) -{ - AT91PS_SPI pSpiHw = pSdSpi->pSpiHw; - unsigned int spiIer; - - if (pSdSpi->semaphore == 0) { - TRACE_DEBUG("No semaphore\n\r"); - return SDSPI_ERROR_LOCK; - } - pSdSpi->semaphore--; - - // Enable the SPI clock - // BSS below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); - PMC->PMC_PCER0 = (1 << pSdSpi->spiId); - - // Disable transmitter and receiver - pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; - - // Receive Pointer Register - pSpiHw->SPI_RPR = (int)pData; - // Receive Counter Register - pSpiHw->SPI_RCR = size; - // Transmit Pointer Register - pSpiHw->SPI_TPR = (int) pData; - // Transmit Counter Register - pSpiHw->SPI_TCR = size; - - spiIer = SPI_IER_RXBUFF; // TODO AT91C_SPI_RXBUFF; - - // Enable transmitter and receiver - pSpiHw->SPI_PTCR = SPI_PTCR_RXTEN | SPI_PTCR_TXTEN; // BSS AT91C_PDC_RXTEN | AT91C_PDC_TXTEN; - - // Interrupt enable shall be done after PDC TXTEN and RXTEN - pSpiHw->SPI_IER = spiIer; - - return 0; -} - -//! Should be moved to a new file -//------------------------------------------------------------------------------ -/// Read data on SPI data bus; -/// Returns 1 if read fails, returns 0 if no error. -/// \param pSdSpi Pointer to a SD SPI driver instance. -/// \param pData Data pointer. -/// \param size Data size. -//------------------------------------------------------------------------------ -unsigned char SDSPI_Read(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) -{ - unsigned char error; - - // MOSI should hold high during read, or there will be wrong data in received data. - memset(pData, 0xff, size); - - error = SDSPI_PDC(pSdSpi, pData, size); - - while(SDSPI_IsBusy(pSdSpi) == 1); - - if( error == 0 ) { - return 0; - } - else { - TRACE_DEBUG("PB SDSPI_Read\n\r"); - return 1; - } -} - -//------------------------------------------------------------------------------ -/// Write data on SPI data bus; -/// Returns 1 if write fails, returns 0 if no error. -/// \param pSdSpi Pointer to a SD SPI driver instance. -/// \param pData Data pointer. -/// \param size Data size. -//------------------------------------------------------------------------------ -unsigned char SDSPI_Write(SdSpi *pSdSpi, unsigned char *pData, unsigned int size) -{ - unsigned char error; - - error = SDSPI_PDC(pSdSpi, pData, size); - - while(SDSPI_IsBusy(pSdSpi) == 1); - - if( error == 0 ) { - return 0; - } - else { - TRACE_DEBUG("PB SDSPI_Write\n\r"); - return 1; - } -} - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -unsigned char SDSPI_WaitDataBusy(SdSpi *pSdSpi) -{ - unsigned char busyData; - - SDSPI_Read(pSdSpi, &busyData, 1); - - if (busyData != 0xff) { - return 1; - } - else { - return 0; - } -} - -/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */ -const uint8_t crc7_syndrome_table[256] = { - 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, - 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, - 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, - 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, - 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, - 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, - 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, - 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, - 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, - 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, - 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, - 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, - 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, - 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, - 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, - 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, - 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, - 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, - 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, - 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, - 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, - 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, - 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, - 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, - 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, - 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, - 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, - 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, - 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, - 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, - 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, - 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 -}; - -// BSS crc functions added -static inline uint8_t crc7_byte(uint8_t crc, uint8_t data) -{ - return crc7_syndrome_table[(crc << 1) ^ data]; -} - -uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len) -{ - while (len--) - crc = crc7_byte(crc, *buffer++); - return crc; -} - -//------------------------------------------------------------------------------ -/// Convert SD MCI command to a SPI mode command token. -/// \param pCmdToken Pointer to the SD command token. -/// \param arg SD command argument -//------------------------------------------------------------------------------ -void SDSPI_MakeCmd(unsigned char *pCmdToken, unsigned int arg) -{ - unsigned char sdCmdNum; - unsigned char crc = 0; - unsigned char crcPrev = 0; - - sdCmdNum = 0x3f & *pCmdToken; - *pCmdToken = sdCmdNum | 0x40; - *(pCmdToken+1) = (arg >> 24) & 0xff; - *(pCmdToken+2) = (arg >> 16) & 0xff; - *(pCmdToken+3) = (arg >> 8) & 0xff; - *(pCmdToken+4) = arg & 0xff; - - crc = crc7(crcPrev, (unsigned char *)(pCmdToken), 5); - - *(pCmdToken+5) = (crc << 1) | 1; -} - -//------------------------------------------------------------------------------ -/// Get response after send SD command. -/// Return 0 if no error, otherwise indicate an error. -/// \param pSdSpi Pointer to the SD SPI instance. -/// \param pCommand Pointer to the SD command -//------------------------------------------------------------------------------ -unsigned char SDSPI_GetCmdResp(SdSpi *pSdSpi, SdSpiCmd *pCommand) -{ - unsigned char resp[8]; // response - unsigned char error; - unsigned int respRetry = 8; //NCR max 8, refer to card datasheet - - memset(resp, 0, 8); - - // Wait for response start bit. - do { - error = SDSPI_Read(pSdSpi, &resp[0], 1); - if (error) { - TRACE_DEBUG("\n\rpb SDSPI_GetCmdResp: 0x%X\n\r", error); - return error; - } - if ((resp[0]&0x80) == 0) { - break; - } - respRetry--; - } while(respRetry > 0); - - switch (pCommand->resType) { - case 1: - *(pCommand->pResp) = resp[0]; - break; - - case 2: - error = SDSPI_Read(pSdSpi, &resp[1], 1); - if (error) { - return error; - } - *(pCommand->pResp) = resp[0] - | (resp[1] << 8); - break; - - // Response 3, get OCR - case 3: - error = SDSPI_Read(pSdSpi, &resp[1], 4); - if (error) { - return error; - } - *(pCommand->pResp) = resp[0] - | (resp[1] << 8) - | (resp[2] << 16) - | (resp[3] << 24); - *(pCommand->pResp+1) = resp[4]; - break; - - case 7: - TRACE_DEBUG("case 7\n\r"); - error = SDSPI_Read(pSdSpi, &resp[1], 4); - if (error) { - return error; - } - *(pCommand->pResp) = resp[0] - | (resp[1] << 8) - | (resp[2] << 16) - | (resp[3] << 24); - *(pCommand->pResp+1) = resp[4]; - break; - - default: - TRACE_DEBUG("PB default\n\r"); - break; - } - - return 0; -} - -//------------------------------------------------------------------------------ -/// Get response after send data. -/// Return 0 if no error, otherwise indicate an error. -/// \param pSdSpi Pointer to the SD SPI instance. -/// \param pCommand Pointer to the SD command -//------------------------------------------------------------------------------ -unsigned char SDSPI_GetDataResp(SdSpi *pSdSpi, SdSpiCmd *pCommand) -{ - unsigned char resp = 0; // response - unsigned char error; - unsigned int respRetry = 18; //NCR max 8, refer to card datasheet - - // Wait for response start bit. - do { - error = SDSPI_Read(pSdSpi, &resp, 1); - if (error) { - return error; - } - if (((resp & 0x11) == 0x1) || ((resp & 0xf0) == 0)) - break; - - respRetry--; - } while(respRetry > 0); - //TRACE_DEBUG("SDSPI_GetDataResp 0x%X\n\r",resp); - return resp; -} - -// BSS crc16 function added - -const uint16_t crc_itu_t_table[256] = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - -static inline uint16_t crc_itu_t_byte(uint16_t crc, const uint8_t data) -{ - return (crc << 8) ^ crc_itu_t_table[((crc >> 8) ^ data) & 0xff]; -} - -uint16_t crc_itu_t(uint16_t crc, const uint8_t *buffer, size_t len) -{ - while (len--) - crc = crc_itu_t_byte(crc, *buffer++); - return crc; -} - -//------------------------------------------------------------------------------ -/// Starts a SPI master transfer. This is a non blocking function. It will -/// return as soon as the transfer is started. -/// Returns 0 if the transfer has been started successfully; otherwise returns -/// error. -/// \param pSdSpi Pointer to a SdSpi instance. -/// \param pCommand Pointer to the SPI command to execute. -//------------------------------------------------------------------------------ -unsigned char SDSPI_SendCommand(SdSpi *pSdSpi, SdSpiCmd *pCommand) -{ - // BSS AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; - unsigned char CmdToken[6]; - unsigned char *pData; - unsigned int blockSize; - unsigned int i; - unsigned char error; - unsigned char dataHeader; - unsigned int dataRetry1 = 100; - unsigned int dataRetry2 = 100; - unsigned char crc[2]; - unsigned char crcPrev = 0; - unsigned char crcPrev2 = 0; - - SANITY_CHECK(pSdSpi); - // BSS SANITY_CHECK(pSpiHw); - SANITY_CHECK(pCommand); - - CmdToken[0] = pCommand->cmd & 0x3F; - pData = pCommand->pData; - blockSize = pCommand->blockSize; - - SDSPI_MakeCmd((unsigned char *)&CmdToken, pCommand->arg); - - // Command is now being executed - pSdSpi->pCommand = pCommand; - pCommand->status = SDSPI_STATUS_PENDING; - - // Send the command - if((pCommand->conTrans == SPI_NEW_TRANSFER) || (blockSize == 0)) { - - for(i = 0; i < 6; i++) { - error = SDSPI_Write(pSdSpi, &CmdToken[i], 1); - if (error) { - TRACE_DEBUG("Error: %d\n\r", error); - return error; - } - } - // Specific for Cmd12() - if ((pCommand->cmd & 0x3F) == 12) { - if( 1 == SDSPI_Wait(pSdSpi, 2) ) { - TRACE_DEBUG("Pb Send command 12\n\r"); - } - } - if (pCommand->pResp) { - error = SDSPI_GetCmdResp(pSdSpi, pCommand); - if (error) { - TRACE_DEBUG("Error: %d\n\r", error); - return error; - } - } - } - - if( (blockSize > 0) && (pCommand->nbBlock == 0) ) { - pCommand->nbBlock = 1; - } - - // For data block operations - while (pCommand->nbBlock > 0) { - - // If data block size is invalid, return error - if (blockSize == 0) { - TRACE_DEBUG("Block Size = 0\n\r"); - return 1; - } - - // DATA transfer from card to host - if (pCommand->isRead) { - do { - SDSPI_Read(pSdSpi, &dataHeader, 1); - dataRetry1 --; - if (dataHeader == SDSPI_START_BLOCK_1) { - break; - } - else if((dataHeader & 0xf0) == 0x00) { - pCommand->status = SDSPI_STATUS_ERROR; - TRACE_DEBUG("Data Error 0x%X!\n\r", dataHeader); - return 1; - } - } while(dataRetry1 > 0); - - if (dataRetry1 == 0) { - TRACE_DEBUG("Timeout dataretry1\n\r"); - return 1; - } - - SDSPI_Read(pSdSpi, pData, blockSize); - - // Specific for Cmd9() - if ((pCommand->cmd & 0x3f) != 0x9) { - - SDSPI_Read(pSdSpi, crc, 2); -#ifdef SDSPI_CRC_ON - // Check data CRC - TRACE_DEBUG("Check Data CRC\n\r"); - crcPrev = 0; - crcPrev2 = 0; - if (crc[0] != ((crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8 ) - || crc[1] != (crc_itu_t(crcPrev2, pData, blockSize) & 0xff)) { - TRACE_ERROR("CRC error 0x%X 0x%X 0x%X\n\r", \ - crc[0], crc[1], crc_itu_t(pData, blockSize)); - return 1; - } -#endif - } - } - - // DATA transfer from host to card - else { - SDSPI_NCS(pSdSpi); - if ((pCommand->conTrans == SPI_CONTINUE_TRANSFER) || ((pCommand->cmd & 0x3f) == 25)) { - dataHeader = SDSPI_START_BLOCK_2; - } - else { - dataHeader = SDSPI_START_BLOCK_1; - } - - crcPrev = 0; - crc[0] = (crc_itu_t(crcPrev, pData, blockSize) & 0xff00) >> 8; - crcPrev2 = 0; - crc[1] = (crc_itu_t(crcPrev2, pData, blockSize) & 0xff); - SDSPI_Write(pSdSpi, &dataHeader, 1); - SDSPI_Write(pSdSpi, pData, blockSize); - SDSPI_Write(pSdSpi, crc, 2); - - // If status bits in data response is not "data accepted", return error - if ((SDSPI_GetDataResp(pSdSpi, pCommand) & 0xe) != 0x4) { - TRACE_ERROR("Write resp error!\n\r"); - return 1; - } - - do { - if (SDSPI_WaitDataBusy(pSdSpi) == 0) { - break; - } - dataRetry2--; - } while(dataRetry2 > 0); - } - pData += blockSize; - pCommand->nbBlock--; - } - - if (pCommand->status == SDSPI_STATUS_PENDING) { - pCommand->status = 0; - } - - //TRACE_DEBUG("end SDSPI_SendCommand\n\r"); - return 0; -} -//! - -//------------------------------------------------------------------------------ -/// The SPI_Handler must be called by the SPI Interrupt Service Routine with the -/// corresponding Spi instance. -/// The SPI_Handler will unlock the Spi semaphore and invoke the upper application -/// callback. -/// \param pSdSpi Pointer to a SdSpi instance. -//------------------------------------------------------------------------------ -void SDSPI_Handler(SdSpi *pSdSpi) -{ - SdSpiCmd *pCommand = pSdSpi->pCommand; - AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; - volatile unsigned int spiSr; - - // Read the status register - spiSr = pSpiHw->SPI_SR; - if(spiSr & SPI_SR_RXBUFF/*BSS AT91C_SPI_RXBUFF*/) { - - if (pCommand->status == SDSPI_STATUS_PENDING) { - pCommand->status = 0; - } - // Disable transmitter and receiver - pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; - - // Disable the SPI clock - // BSS below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); - PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); - - // Disable buffer complete interrupt - pSpiHw->SPI_IDR = SPI_IDR_RXBUFF | SPI_IDR_ENDTX; // BSS AT91C_SPI_RXBUFF | AT91C_SPI_ENDTX; - - // Release the SPI semaphore - pSdSpi->semaphore++; - } - - // Invoke the callback associated with the current command - if (pCommand && pCommand->callback) { - pCommand->callback(0, pCommand); - } -} - -//------------------------------------------------------------------------------ -/// Returns 1 if the given SPI transfer is complete; otherwise returns 0. -/// \param pCommand Pointer to a SdSpiCmd instance. -//------------------------------------------------------------------------------ -unsigned char SDSPI_IsTxComplete(SdSpiCmd *pCommand) -{ - if (pCommand->status != SDSPI_STATUS_PENDING) { - if (pCommand->status != 0){ - TRACE_DEBUG("SPI_IsTxComplete %d\n\r", pCommand->status); - } - return 1; - } - else { - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Close a SPI driver instance and the underlying peripheral. -/// \param pSdSpi Pointer to a SD SPI driver instance. -//------------------------------------------------------------------------------ -void SDSPI_Close(SdSpi *pSdSpi) -{ - AT91S_SPI *pSpiHw = pSdSpi->pSpiHw; - - SANITY_CHECK(pSdSpi); - SANITY_CHECK(pSpiHw); - - // Enable the SPI clock - // BSS below AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId); - PMC->PMC_PCER0 = (1 << pSdSpi->spiId); - - // Disable the PDC transfer - pSpiHw->SPI_PTCR = SPI_PTCR_RXTDIS | SPI_PTCR_TXTDIS; // BSS AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; - - // Enable the SPI - pSpiHw->SPI_CR = SPI_CR_SPIDIS; // BSS AT91C_SPI_SPIDIS; - - // Disable the SPI clock - // BSS below AT91C_BASE_PMC->PMC_PCDR = (1 << pSdSpi->spiId); - PMC->PMC_PCDR0 = (1 << pSdSpi->spiId); - - // Disable all the interrupts - pSpiHw->SPI_IDR = 0xFFFFFFFF; -} - -//------------------------------------------------------------------------------ -/// Returns 1 if the SPI driver is currently busy programming; -/// otherwise returns 0. -/// \param pSdSpi Pointer to a SD SPI driver instance. -//------------------------------------------------------------------------------ -unsigned char SDSPI_IsBusy(SdSpi *pSdSpi) -{ - if (pSdSpi->semaphore == 0) { - return 1; - } - else { - return 0; - } -} - -//------------------------------------------------------------------------------ -/// Wait several cycles on SPI bus; -/// Returns 0 to indicates no error, otherwise return 1. -/// \param pSdSpi Pointer to a SD SPI driver instance. -/// \param cycles Wait data cycles. -//------------------------------------------------------------------------------ -unsigned char SDSPI_Wait(SdSpi *pSdSpi, unsigned int cycles) -{ - unsigned int i = cycles; - unsigned char data = 0xff; - - for (; i > 0; i--) { - if (SDSPI_Read(pSdSpi, &data, 1)) { - return 1; - } - } - return 0; -} - -//------------------------------------------------------------------------------ -/// Send stop transfer data token; -/// Returns 0 to indicates no error, otherwise return 1. -/// \param pSdSpi Pointer to a SD SPI driver instance. -//------------------------------------------------------------------------------ -unsigned char SDSPI_StopTranToken(SdSpi *pSdSpi) -{ - unsigned char stopToken = SDSPI_STOP_TRAN; - - TRACE_DEBUG("SDSPI_StopTranToken\n\r"); - return SDSPI_Write(pSdSpi, &stopToken, 1); -} - -//------------------------------------------------------------------------------ -/// Wait, SD card Ncs cycles; -/// Returns 0 to indicates no error, otherwise return 1. -/// \param pSdSpi Pointer to a SD SPI driver instance. -//------------------------------------------------------------------------------ -unsigned char SDSPI_NCS(SdSpi *pSdSpi) -{ - unsigned int i; - unsigned char ncs; - - for(i = 0; i < 15; i++) { - ncs = 0xff; - if (SDSPI_Write(pSdSpi, &ncs, 1)) { - return 1; - } - } - return 0; -} - - diff --git a/src/ersky9x/sdspi.h b/src/ersky9x/sdspi.h deleted file mode 100644 index 0f89157e8..000000000 --- a/src/ersky9x/sdspi.h +++ /dev/null @@ -1,175 +0,0 @@ -/* ---------------------------------------------------------------------------- - * ATMEL Microcontroller Software Support - * ---------------------------------------------------------------------------- - * Copyright (c) 2008, Atmel Corporation - - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the disclaimer below. - * - * Atmel's name may not be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE - * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * ---------------------------------------------------------------------------- - */ - -//------------------------------------------------------------------------------ -/// \page "sdspi" -/// -/// !Purpose -/// -/// sdcard spi-bus driver -/// -/// !Usage -/// -/// -# SDSPI_Configure: Initializes the SD Spi structure and the corresponding SPI hardware -/// -# SDSPI_ConfigureCS : Configures the parameters for the device corresponding to the cs -/// -# SDSPI_Read: Read data on SPI data bus -/// -# SDSPI_Write : Write data on SPI data bus -/// -# SDSPI_SendCommand : Starts a SPI master transfer -/// -# SDSPI_StopTranToken : Send stop transfer data token -//------------------------------------------------------------------------------ - -#ifndef SDSPI_H -#define SDSPI_H - -//------------------------------------------------------------------------------ -// Headers -//------------------------------------------------------------------------------ - -#ifdef REVA -#include "AT91SAM3S2.h" -#else -#include "AT91SAM3S4.h" -#endif - -//#include -//#include - -/// SPI CSR value -#define SDSPI_CSR(scbr) ( AT91C_SPI_CPOL \ - | AT91C_SPI_BITS_8 \ - | (((scbr)<< 8) & AT91C_SPI_SCBR) \ - | ( (0x08 << 16) & AT91C_SPI_DLYBS)\ - | ( (0x01 << 24) & AT91C_SPI_DLYBCT) ) - -/// Calculates the value of the SCBR field of the Chip Select Register given -/// MCK and SPCK. -#define SPID_CSR_SCBR(mck, spck) ((((mck) / (spck)) << 8) & AT91C_SPI_SCBR) - -/// Calculates the value of the DLYBS field of the Chip Select Register given -/// the delay in ns and MCK. -#define SPID_CSR_DLYBS(mck, delay) \ - ((((((delay) * ((mck) / 1000000)) / 1000) + 1) << 16) & AT91C_SPI_DLYBS) - -/// Calculates the value of the DLYBCT field of the Chip Select Register given -/// the delay in ns and MCK. -#define SPID_CSR_DLYBCT(mck, delay) \ - ((((((delay) / 32 * ((mck) / 1000000)) / 1000) + 1) << 24) & AT91C_SPI_DLYBCT) - -#define AT45_CSR(mck, spck) \ - (AT91C_SPI_NCPHA | SPID_CSR_DLYBCT(mck, 250) \ - | SPID_CSR_DLYBS(mck, 250) | SPID_CSR_SCBR(mck, spck)) - - -/// Start new data transfer -#define SPI_NEW_TRANSFER 0 -/// Continue data transfer -#define SPI_CONTINUE_TRANSFER 1 - -/// SD end-of-transfer callback function. -typedef void (*SdSpiCallback)(unsigned char status, void *pCommand); - -//------------------------------------------------------------------------------ -/// SPI Transfer Request prepared by the application upper layer. This structure -/// is sent to the SDSPI_SendCommand function to start the transfer. At the end of -/// the transfer, the callback is invoked by the interrupt handler. -//------------------------------------------------------------------------------ -typedef struct _SdSpiCmd { - - /// Command status. - volatile char status; - /// Command code. - unsigned int cmd; - /// Command argument. - unsigned int arg; - /// Data buffer. - unsigned char *pData; - /// Size of data buffer in bytes. - unsigned short blockSize; - /// Number of blocks to be transfered - unsigned short nbBlock; - /// Indicate if continue to transfer data - unsigned char conTrans; - /// Indicates if the command is a read operation. - unsigned char isRead; - /// Response buffer. - unsigned int *pResp; - /// Size of SD card response in bytes. - unsigned char resType; - /// Optional user-provided callback function. - SdSpiCallback callback; - /// Optional argument to the callback function. - void *pArg; - -} SdSpiCmd; - -/// Above should be put into SD card header file. - -//------------------------------------------------------------------------------ -/// SPI driver structure. Holds the internal state of the SPI driver and -/// prevents parallel access to a SPI peripheral. -//------------------------------------------------------------------------------ -typedef struct { - - /// Pointer to a SPI peripheral. - AT91S_SPI *pSpiHw; - /// SPI peripheral identifier. - unsigned char spiId; - /// Pointer to currently executing command. - SdSpiCmd *pCommand; - /// Mutex. - volatile char semaphore; - -} SdSpi; - -extern void SDSPI_Configure(SdSpi *pSdSpi,AT91PS_SPI pSpiHw,unsigned char spiId); - -extern void SDSPI_SetSpeed(SdSpi *pSdSpi, unsigned int spiSpeed); - -extern unsigned char SDSPI_SendCommand(SdSpi *pSdSpi, SdSpiCmd *pSdSpiCmd); - -extern void SDSPI_Handler(SdSpi *pSdSpi); - -extern unsigned char SDSPI_IsTxComplete(SdSpiCmd *pSdSpiCmd); - -extern unsigned char SDSPI_IsBusy(SdSpi *pSdSpi); - -extern unsigned char SDSPI_NCS(SdSpi *pSdSpi); - -extern void SDSPI_Close(SdSpi *pSdSpi); - -extern void SDSPI_ConfigureCS(SdSpi *pSdSpi, unsigned char cs, unsigned int csr); - -extern unsigned char SDSPI_StopTranToken(SdSpi *pSdSpi); - -extern unsigned char SDSPI_Wait(SdSpi *pSdSpi, unsigned int cycles); - -extern unsigned char SDSPI_WaitDataBusy(SdSpi *pSdSpi); - -#endif - diff --git a/src/general_menus.cpp b/src/general_menus.cpp index 30cdfae5a..eb7177bb0 100644 --- a/src/general_menus.cpp +++ b/src/general_menus.cpp @@ -50,7 +50,7 @@ enum EnumTabDiag { }; void menuProcSetup(uint8_t event); -#if defined(PCBV4) +#if defined(PCBV4) && defined(SDCARD) void menuProcTime(uint8_t event); #endif void menuProcTrainer(uint8_t event); @@ -61,7 +61,7 @@ void menuProcDiagCalib(uint8_t event); const MenuFuncP_PROGMEM menuTabDiag[] PROGMEM = { menuProcSetup, -#if defined(PCBV4) +#if defined(PCBV4) && defined(SDCARD) menuProcTime, #endif menuProcTrainer, @@ -392,7 +392,7 @@ void menuProcSetup(uint8_t event) } -#if defined(PCBV4) +#if defined(PCBV4) && defined(SDCARD) // SD card interface contains Real-Time-Clock chip void menuProcTime(uint8_t event) { diff --git a/src/gruvin9x/diskio.cpp b/src/gruvin9x/diskio.cpp index 3723ffed6..c7f286a27 100644 --- a/src/gruvin9x/diskio.cpp +++ b/src/gruvin9x/diskio.cpp @@ -395,7 +395,7 @@ DSTATUS disk_initialize ( /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( - BYTE drv /* Physical drive nmuber (0) */ + BYTE drv /* Physical drive number (0) */ ) { if (drv) return STA_NOINIT; /* Supports only single drive */ diff --git a/src/gruvin9x/ff.cpp b/src/gruvin9x/ff.cpp index c49111ef0..668c22ed1 100644 --- a/src/gruvin9x/ff.cpp +++ b/src/gruvin9x/ff.cpp @@ -120,6 +120,7 @@ #include "ff.h" /* FatFs configurations and declarations */ #include "diskio.h" /* Declarations of low level disk I/O functions */ +#include "gtime.h" /*-------------------------------------------------------------------------- @@ -2027,6 +2028,30 @@ FRESULT f_mount ( } +/*---------------------------------------------------------*/ +/* User Provided Date/Time Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ +/* This is not required in read-only configuration. */ + +uint8_t g_ms100 = 0; // global to allow time set function to reset to zero +gtime_t g_unixTime; // global date/time register, incremented each second in per10ms() + +uint32_t o9x_get_fattime(void) +{ + struct gtm t; + filltm(&g_unixTime, &t); // create a struct tm date/time structure from global unix time stamp + + /* Pack date and time into a DWORD variable */ + return ((DWORD)(t.tm_year - 80) << 25) + | ((uint32_t)(t.tm_mon+1) << 21) + | ((uint32_t)t.tm_mday << 16) + | ((uint32_t)t.tm_hour << 11) + | ((uint32_t)t.tm_min << 5) + | ((uint32_t)t.tm_sec >> 1); +} /*-----------------------------------------------------------------------*/ diff --git a/src/gruvin9x/ff.h b/src/gruvin9x/ff.h index 6178492bb..892b3cd27 100644 --- a/src/gruvin9x/ff.h +++ b/src/gruvin9x/ff.h @@ -57,6 +57,7 @@ extern "C" { #include "inttypes.h" #include "integer.h" /* Basic integer types */ #include "ffconf.h" /* FatFs configuration options */ +#include "gtime.h" #if _FATFS != _FFCONF #error Wrong configuration file (ffconf.h). @@ -572,4 +573,6 @@ void ff_rel_grant (_SYNC_t); /* Unlock sync object */ } #endif +extern gtime_t g_unixTime; // Global date/time register, incremented each second in per10ms() + #endif /* _FATFS */ diff --git a/src/gruvin9x/sdcard.h b/src/gruvin9x/sdcard.h index f390df897..c54c0ffca 100644 --- a/src/gruvin9x/sdcard.h +++ b/src/gruvin9x/sdcard.h @@ -34,9 +34,12 @@ #ifndef sdcard_h #define sdcard_h +#include "ff.h" +#include "rtc.h" +#include "gtime.h" + #define LOGS_PATH "/9XLOGS" // no trailing slash = important #define MODELS_PATH "/9XMODELS" - #define MODELS_EXT ".bin" extern char g_logFilename[21]; // "/9XLOGS/M00_000.CSV\0" max required length = 21 @@ -49,5 +52,9 @@ extern void writeLogs(); const pm_char *SDCARD_ERROR(FRESULT result); +// MM/SD card Disk IO Support +extern void disk_timerproc(void); +extern uint8_t g_ms100; // defined in drivers.cpp + #endif diff --git a/src/open9x.cpp b/src/open9x.cpp index a537b063f..7ae9873f3 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -42,11 +42,6 @@ const pm_uchar * s9xsplash = splashdata+4; #include "menus.h" -// MM/SD card Disk IO Support -#if defined (PCBV4) -gtime_t g_unixTime; // Global date/time register, incremented each second in per10ms() -#endif - EEGeneral g_eeGeneral; ModelData g_model; @@ -1875,6 +1870,7 @@ void perMain() ee32_process(); else if (TIME_TO_WRITE) eeCheck(); + sd_poll_10mS(); } #else if (!eeprom_buffer_size) { @@ -2078,7 +2074,7 @@ void perMain() if((inacCounter&0x3F)==10) AUDIO_INACTIVITY(); } -#if defined(SDCARD) +#if defined(PCBV4) && defined(SDCARD) writeLogs(); #endif @@ -2258,7 +2254,7 @@ ISR(TIMER0_COMP_vect, ISR_NOBLOCK) //10ms timer per10ms(); -#if defined (PCBV4) +#if defined (PCBV4) && defined(SDCARD) disk_timerproc(); #endif @@ -2415,30 +2411,6 @@ ISR(USART0_UDRE_vect) #endif } #endif -#endif - -#if defined (PCBV4) -/*---------------------------------------------------------*/ -/* User Provided Date/Time Function for FatFs module */ -/*---------------------------------------------------------*/ -/* This is a real time clock service to be called from */ -/* FatFs module. Any valid time must be returned even if */ -/* the system does not support a real time clock. */ -/* This is not required in read-only configuration. */ - -uint32_t o9x_get_fattime(void) // TODO why not in ff.cpp? -{ - struct gtm t; - filltm(&g_unixTime, &t); // create a struct tm date/time structure from global unix time stamp - - /* Pack date and time into a DWORD variable */ - return ((DWORD)(t.tm_year - 80) << 25) - | ((uint32_t)(t.tm_mon+1) << 21) - | ((uint32_t)t.tm_mday << 16) - | ((uint32_t)t.tm_hour << 11) - | ((uint32_t)t.tm_min << 5) - | ((uint32_t)t.tm_sec >> 1); -} #endif void instantTrim() diff --git a/src/open9x.h b/src/open9x.h index 649c6d032..7208cc2b4 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -41,11 +41,6 @@ #include #include -#if defined(PCBV4) -#include "gruvin9x/ff.h" -#include "gruvin9x/gtime.h" -#endif - #if defined(PCBARM) #include "board_ersky9x.h" #endif @@ -761,14 +756,6 @@ extern uint8_t beepAgain; extern uint16_t g_LightOffCounter; extern uint8_t mixWarning; -// MM/SD card Disk IO Support -#if defined (PCBV4) -#include "gruvin9x/rtc.h" -extern void disk_timerproc(void); -extern gtime_t g_unixTime; // global unix timestamp -- hold current time in seconds since 1970-01-01 00:00:00 -extern uint8_t g_ms100; // defined in drivers.cpp -#endif - extern PhaseData *phaseaddress(uint8_t idx); extern ExpoData *expoaddress(uint8_t idx); extern MixData *mixaddress(uint8_t idx); @@ -880,7 +867,7 @@ enum AUDIO_SOUNDS { #endif #if defined(SDCARD) -#include "sdcard.h" +#include "gruvin9x/sdcard.h" #endif #if defined(SOMO)