├── Makefile ├── README.md ├── asmfunc.S ├── binary_sketches ├── Blink2GPS.bin ├── PTU.bin ├── README.txt ├── blink.bin └── fastblink.bin ├── burn.bat ├── diskio.h ├── integer.h ├── main.c ├── mmc.c ├── pff.c ├── pff.h └── sch.jpg /Makefile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------ 2 | # Makefile for stand-alone MMC boot strap loader 3 | #------------------------------------------------------------------ 4 | # Change these three defs for the target device 5 | 6 | MCU_TARGET = atmega328p # Target device to be used (32K or lager) 7 | BOOT_ADR = 0x7000 # Boot loader start address [byte] 8 | F_CPU =16000000 # CPU clock frequency [Hz] 9 | 10 | #------------------------------------------------------------------ 11 | 12 | TARGET = avr_boot 13 | CSRC = main.c pff.c mmc.c 14 | ASRC = asmfunc.S 15 | OPTIMIZE = -Os -mcall-prologues 16 | DEFS = -DBOOT_ADR=$(BOOT_ADR) -DF_CPU=$(F_CPU) 17 | LIBS = 18 | DEBUG = dwarf-2 19 | 20 | ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs $(DEFS) 21 | ALL_ASFLAGS = -mmcu=$(MCU_TARGET) -I. -x assembler-with-cpp $(ASFLAGS) 22 | CFLAGS = -g$(DEBUG) -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) 23 | LDFLAGS = -Wl,-Map,$(TARGET).map -Wl,--section-start,.text=$(BOOT_ADR) 24 | OBJ = $(CSRC:.c=.o) $(ASRC:.S=.o) 25 | 26 | CC = avr-gcc 27 | OBJCOPY = avr-objcopy 28 | OBJDUMP = avr-objdump 29 | SIZE = avr-size 30 | 31 | 32 | all: $(TARGET).elf lst text size 33 | 34 | $(TARGET).elf: $(OBJ) 35 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) 36 | 37 | 38 | clean: 39 | rm -rf *.o $(TARGET).elf *.eps *.bak *.a 40 | rm -rf *.lst *.map $(EXTRA_CLEAN_FILES) 41 | rm -rf $(TARGET).hex 42 | 43 | size: $(TARGET).elf 44 | $(SIZE) -C --mcu=$(MCU_TARGET) $(TARGET).elf 45 | 46 | lst: $(TARGET).lst 47 | %.lst: %.elf 48 | $(OBJDUMP) -h -S $< > $@ 49 | 50 | %.o : %.S 51 | $(CC) -c $(ALL_ASFLAGS) $< -o $@ 52 | 53 | 54 | text: $(TARGET).hex 55 | 56 | %.hex: %.elf 57 | $(OBJCOPY) -j .text -j .data -j .fuse -O ihex $< $@ 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | avr_boot 2 | ======== 3 | 4 | SD card Bootloader for atmega processors -------------------------------------------------------------------------------- /asmfunc.S: -------------------------------------------------------------------------------- 1 | ;---------------------------------------------------------------------------; 2 | ; MMC hardware controls and Flash controls (C)ChaN, 2010 3 | ;---------------------------------------------------------------------------; 4 | ; Hardware dependent macros to be modified 5 | 6 | //sparkfun microsd 7 | //#define DDR_CS _SFR_IO_ADDR(DDRB), 0 // MMC CS pin (DDR, PORT) 8 | //#define PORT_CS _SFR_IO_ADDR(PORTB), 0 9 | // i2GPS 10 | //#define DDR_CS _SFR_IO_ADDR(DDRB), 2 // MMC CS pin (DDR, PORT) 11 | //#define PORT_CS _SFR_IO_ADDR(PORTB), 2 12 | 13 | // Arduino Ethernet 14 | #define DDR_CS _SFR_IO_ADDR(DDRD), 4 // MMC CS pin (DDR, PORT) 15 | #define PORT_CS _SFR_IO_ADDR(PORTD), 4 16 | 17 | 18 | #define DDR_CK _SFR_IO_ADDR(DDRB), 5 // MMC SCLK pin (DDR, PORT) 19 | #define PORT_CK _SFR_IO_ADDR(PORTB), 5 20 | 21 | #define DDR_DI _SFR_IO_ADDR(DDRB), 3 // MMC DI pin (DDR, PORT) 22 | #define PORT_DI _SFR_IO_ADDR(PORTB), 3 23 | 24 | #define PIN_DO _SFR_IO_ADDR(PINB), 4 // MMC DO pin (PIN, PORT) 25 | #define PORT_DO _SFR_IO_ADDR(PORTB), 4 26 | 27 | #define DDR_SS _SFR_IO_ADDR(DDRB), 2 // SS pin (PIN, PORT) 28 | #define PORT_SS _SFR_IO_ADDR(PORTB),2 29 | 30 | 31 | 32 | 33 | ;---------------------------------------------------------------------------; 34 | .nolist 35 | #include 36 | .list 37 | .text 38 | 39 | 40 | ;---------------------------------------------------------------------------; 41 | ; Initialize MMC port 42 | ; 43 | ; void init_spi (void); 44 | 45 | .global init_spi 46 | .func init_spi 47 | init_spi: 48 | sbi DDR_CS ; CS: output 49 | sbi DDR_DI ; DI: output 50 | sbi DDR_CK ; SCLK: output 51 | sbi DDR_SS ; Set SS to output, Atmega is SPI master 52 | sbi PORT_SS ; set it to high 53 | sbi PORT_DO ; DO: pull-up 54 | ret 55 | .endfunc 56 | 57 | 58 | 59 | ;---------------------------------------------------------------------------; 60 | ; Delay 100 microseconds 61 | ; 62 | ; void dly_us (UINT n); 63 | 64 | .global dly_100us 65 | .func dly_100us 66 | dly_100us: 67 | ldi r24, lo8(F_CPU / 100000) /* Loop counter */ 68 | 1: sbiw r30, 1 /* 10 clocks per loop */ 69 | sbiw r30, 1 70 | sbiw r30, 1 71 | nop 72 | dec r24 73 | brne 1b 74 | ret 75 | .endfunc 76 | 77 | 78 | 79 | ;---------------------------------------------------------------------------; 80 | ; Select MMC 81 | ; 82 | ; void select (void); 83 | 84 | .global select 85 | .func select 86 | select: 87 | rcall deselect 88 | cbi PORT_CS 89 | rjmp rcv_spi 90 | .endfunc 91 | 92 | 93 | 94 | ;---------------------------------------------------------------------------; 95 | ; Deselect MMC 96 | ; 97 | ; void deselect (void); 98 | 99 | .global deselect 100 | .func deselect 101 | deselect: 102 | sbi PORT_CS 103 | ; Goto next function 104 | .endfunc 105 | 106 | 107 | 108 | ;---------------------------------------------------------------------------; 109 | ; Receive a byte 110 | ; 111 | ; BYTE rcv_spi (void); 112 | 113 | .global rcv_spi 114 | .func rcv_spi 115 | rcv_spi: 116 | ldi r24, 0xFF ; Send 0xFF to receive data 117 | ; Goto next function 118 | .endfunc 119 | 120 | 121 | 122 | ;---------------------------------------------------------------------------; 123 | ; Transmit a byte 124 | ; 125 | ; void xmit_spi (BYTE); 126 | 127 | .global xmit_spi 128 | .func xmit_spi 129 | xmit_spi: 130 | ldi r25, 8 131 | 1: sbrc r24, 7 ; DI = Bit to sent 132 | sbi PORT_DI ; 133 | sbrs r24, 7 ; 134 | cbi PORT_DI ; / 135 | lsl r24 ; Get DO from MMC 136 | sbic PIN_DO ; 137 | inc r24 ; / 138 | sbi PORT_CK ; A positive pulse to SCLK 139 | cbi PORT_CK ; / 140 | dec r25 ; Repeat 8 times 141 | brne 1b ; / 142 | ret 143 | .endfunc 144 | 145 | 146 | 147 | ;--------------------------------------------------------------------------- 148 | ; Erase a flash page 149 | ; 150 | ; void flash_erase (DWORD flash_addr); 151 | 152 | .global flash_erase 153 | .func flash_erase 154 | flash_erase: 155 | 156 | movw ZL, r22 157 | #if FLASHEND >= 0x10000 158 | out _SFR_IO_ADDR(RAMPZ), r24 159 | #endif 160 | 161 | ; Initiate erase operation 162 | ldi r24, 0b00000011 163 | sts _SFR_MEM_ADDR(SPMCSR), r24 164 | spm 165 | 166 | ; Wait for end of erase operation 167 | 1: lds r24, _SFR_MEM_ADDR(SPMCSR) 168 | sbrc r24, 0 169 | rjmp 1b 170 | 171 | ; Re-enable read access to the flash 172 | ldi r24, 0b00010001 173 | sts _SFR_MEM_ADDR(SPMCSR), r24 174 | spm 175 | 176 | 9: ret 177 | .endfunc 178 | 179 | 180 | 181 | ;--------------------------------------------------------------------------- 182 | ; Write a flash page 183 | ; 184 | ; void flash_write (DWORD flash_addr, const BYTE* data); 185 | 186 | .global flash_write 187 | .func flash_write 188 | flash_write: 189 | push r0 190 | push r1 191 | 192 | #if FLASHEND >= 0x10000 193 | out _SFR_IO_ADDR(RAMPZ), r24 194 | #endif 195 | 196 | ; Fill page buffer 197 | movw ZL, r22 198 | movw XL, r20 199 | ldi r25, lo8(SPM_PAGESIZE/2) 200 | 1: ld r0, X+ 201 | ld r1, X+ 202 | ldi r24, 0b00000001 203 | sts _SFR_MEM_ADDR(SPMCSR), r24 204 | spm 205 | adiw ZL, 2 206 | dec r25 207 | brne 1b 208 | 209 | ; Initiate write operation 210 | movw ZL, r22 211 | ldi r24, 0b00000101 212 | sts _SFR_MEM_ADDR(SPMCSR), r24 213 | spm 214 | 215 | ; Wait for end of write operation 216 | 2: lds r24, _SFR_MEM_ADDR(SPMCSR) 217 | sbrc r24, 0 218 | rjmp 2b 219 | 220 | ; Re-enable read access to the flash 221 | ldi r24, 0b00010001 222 | sts _SFR_MEM_ADDR(SPMCSR), r24 223 | spm 224 | 225 | 9: pop r1 226 | pop r0 227 | ret 228 | .endfunc 229 | -------------------------------------------------------------------------------- /binary_sketches/Blink2GPS.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osbock/avr_boot/4c3dee542fe9f65f9860ed5c87ef1047bfa010b1/binary_sketches/Blink2GPS.bin -------------------------------------------------------------------------------- /binary_sketches/PTU.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osbock/avr_boot/4c3dee542fe9f65f9860ed5c87ef1047bfa010b1/binary_sketches/PTU.bin -------------------------------------------------------------------------------- /binary_sketches/README.txt: -------------------------------------------------------------------------------- 1 | These sample sketches have been converted to binary format for bootloading from SD card. 2 | 3 | blink.bin -- BlinkWithoutDelay sketch on pin13 4 | fastblink.bin -- BlinkWIthoutDelay sketch on pin32 with faster blink 5 | Blink2GPS.bin -- alternate blinking 2 LED's on the i2GPS 6 | PTU.bin - Precision Timing Unit for i2GPS 7 | 8 | Copy one of these to the SD card and rename it app.bin 9 | -------------------------------------------------------------------------------- /binary_sketches/blink.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osbock/avr_boot/4c3dee542fe9f65f9860ed5c87ef1047bfa010b1/binary_sketches/blink.bin -------------------------------------------------------------------------------- /binary_sketches/fastblink.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osbock/avr_boot/4c3dee542fe9f65f9860ed5c87ef1047bfa010b1/binary_sketches/fastblink.bin -------------------------------------------------------------------------------- /burn.bat: -------------------------------------------------------------------------------- 1 | avrdude -c usbtiny -p m328p -e -u -U lock:w:0x3f:m -U efuse:w:0x05:m -U hfuse:w:0xD8:m -U lfuse:w:0xFF:m 2 | avrdude -c usbtiny -p m328p -U flash:w:avr_boot.hex -U lock:w:0x0f:m 3 | -------------------------------------------------------------------------------- /diskio.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------- 2 | / PFF - Low level disk interface modlue include file (C)ChaN, 2009 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO 6 | 7 | #include "integer.h" 8 | 9 | 10 | /* Status of Disk Functions */ 11 | typedef BYTE DSTATUS; 12 | 13 | 14 | /* Results of Disk Functions */ 15 | typedef enum { 16 | RES_OK = 0, /* 0: Function succeeded */ 17 | RES_ERROR, /* 1: Disk error */ 18 | RES_NOTRDY, /* 2: Not ready */ 19 | RES_PARERR /* 3: Invalid parameter */ 20 | } DRESULT; 21 | 22 | 23 | /*---------------------------------------*/ 24 | /* Prototypes for disk control functions */ 25 | 26 | DSTATUS disk_initialize (void); 27 | DRESULT disk_readp (BYTE*, DWORD, WORD, WORD); 28 | DRESULT disk_writep (const BYTE*, DWORD); 29 | 30 | #define STA_NOINIT 0x01 /* Drive not initialized */ 31 | #define STA_NODISK 0x02 /* No medium in the drive */ 32 | 33 | /* Card type flags (CardType) */ 34 | #define CT_MMC 0x01 /* MMC ver 3 */ 35 | #define CT_SD1 0x02 /* SD ver 1 */ 36 | #define CT_SD2 0x04 /* SD ver 2 */ 37 | #define CT_SDC (CT_SD1|CT_SD2) /* SD */ 38 | #define CT_BLOCK 0x08 /* Block addressing */ 39 | 40 | #define _DISKIO 41 | #endif 42 | -------------------------------------------------------------------------------- /integer.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------*/ 2 | /* Integer type definitions for FatFs module */ 3 | /*-------------------------------------------*/ 4 | 5 | #ifndef _INTEGER 6 | #define _INTEGER 7 | 8 | #ifdef _WIN32 /* FatFs development platform */ 9 | 10 | #include 11 | #include 12 | 13 | #else /* Embedded platform */ 14 | 15 | /* These types must be 16-bit, 32-bit or larger integer */ 16 | typedef int INT; 17 | typedef unsigned int UINT; 18 | 19 | /* These types must be 8-bit integer */ 20 | typedef char CHAR; 21 | typedef unsigned char UCHAR; 22 | typedef unsigned char BYTE; 23 | 24 | /* These types must be 16-bit integer */ 25 | typedef short SHORT; 26 | typedef unsigned short USHORT; 27 | typedef unsigned short WORD; 28 | typedef unsigned short WCHAR; 29 | 30 | /* These types must be 32-bit integer */ 31 | typedef long LONG; 32 | typedef unsigned long ULONG; 33 | typedef unsigned long DWORD; 34 | 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------/ 2 | / Stand-alone MMC boot loader R0.01 3 | /--------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2010, ChaN, all right reserved. 6 | / 7 | / * This software is a free software and there is NO WARRANTY. 8 | / * No restriction on use. You can use, modify and redistribute it for 9 | / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 10 | / * Redistributions of source code must retain the above copyright notice. 11 | / 12 | /--------------------------------------------------------------------------/ 13 | / Dec 6, 2010 R0.01 First release 14 | /--------------------------------------------------------------------------/ 15 | / This is a stand-alone MMC/SD boot loader for megaAVRs. It requires a 4KB 16 | / boot section for code, four GPIO pins for MMC/SD as shown in sch.jpg and 17 | / nothing else. To port the boot loader into your project, follow the 18 | / instruction sdescribed below. 19 | / 20 | / 1. Setup the hardware. Attach a memory card socket to the any GPIO port 21 | / where you like. Select boot size at least 4KB for the boot loader with 22 | / BOOTSZ fuses and enable boot loader with BOOTRST fuse. 23 | / 24 | / 2. Setup the software. Change the four port definitions in the asmfunc.S. 25 | / Change MCU_TARGET, BOOT_ADR and MCU_FREQ in the Makefile. The BOOT_ADR 26 | / is a BYTE address of boot section in the flash. Build the boot loader 27 | / and write it to the device with a programmer. 28 | / 29 | / 3. Build the application program and output it in binary form instead of 30 | / hex format. Rename the file "app.bin" and put it into the memory card. 31 | / 32 | / 4. Insert the card and turn the target power on. When the boot loader found 33 | / the application file, the file is written into the flash memory prior to 34 | / start the application program. On-board LED lights (if exist) during 35 | / the flash programming operation. 36 | / 37 | /-------------------------------------------------------------------------*/ 38 | 39 | 40 | 41 | #include 42 | #include 43 | #include 44 | #include "pff.h" 45 | 46 | 47 | void flash_erase (DWORD); /* Erase a flash page (asmfunc.S) */ 48 | void flash_write (DWORD, const BYTE*); /* Program a flash page (asmfunc.S) */ 49 | 50 | FATFS Fatfs; /* Petit-FatFs work area */ 51 | BYTE Buff[SPM_PAGESIZE]; /* Page data buffer */ 52 | 53 | 54 | static uint8_t 55 | pagecmp(uint16_t addr, uint8_t *data) 56 | { 57 | uint16_t i; 58 | 59 | for (i = 0; i < SPM_PAGESIZE; i++) { 60 | if (pgm_read_byte(addr++) != *data++) 61 | return 1; 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | 68 | int main (void) 69 | { 70 | 71 | DWORD fa; /* Flash address */ 72 | WORD br; /* Bytes read */ 73 | uint8_t i = 0; 74 | uint8_t ch = 0; 75 | 76 | pf_mount(&Fatfs); /* Initialize file system */ 77 | 78 | 79 | /* read board name from eeprom to Buff */ 80 | while(i<13) { //8+'.'+3 81 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) 82 | while(EECR & (1< 0? */ 112 | for (i = br; i < SPM_PAGESIZE; i++) /* Pad the remaining last page with 0xFF so that comparison goes OK */ 113 | Buff[i] = 0xFF; 114 | if (pagecmp(fa, Buff)) { /* Only flash if page is changed */ 115 | flash_erase(fa); /* Erase a page */ 116 | flash_write(fa, Buff); /* Write it if the data is available */ 117 | } 118 | } 119 | } 120 | } 121 | 122 | if (pgm_read_word(0) != 0xFFFF) /* Start application if exist */ 123 | ((void(*)(void))0)(); 124 | 125 | for (;;) ; /* No application, Halt. */ 126 | } 127 | 128 | -------------------------------------------------------------------------------- /mmc.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / PFF - Generic low level disk control module (C)ChaN, 2010 3 | /------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2010, ChaN, all right reserved. 6 | / 7 | / * This software is a free software and there is NO WARRANTY. 8 | / * No restriction on use. You can use, modify and redistribute it for 9 | / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 10 | / * Redistributions of source code must retain the above copyright notice. 11 | / 12 | /------------------------------------------------------------------------/ 13 | /* Dec 6, 2010 First release 14 | */ 15 | 16 | #include "pff.h" 17 | #include "diskio.h" 18 | 19 | void init_spi (void); /* Initialize SPI port (asmfunc.S) */ 20 | void deselect (void); /* Select MMC (asmfunc.S) */ 21 | void select (void); /* Deselect MMC (asmfunc.S) */ 22 | void xmit_spi (BYTE d); /* Send a byte to the MMC (asmfunc.S) */ 23 | BYTE rcv_spi (void); /* Send a 0xFF to the MMC and get the received byte (asmfunc.S) */ 24 | void dly_100us (void); /* Delay 100 microseconds (asmfunc.S) */ 25 | 26 | 27 | 28 | /*-------------------------------------------------------------------------- 29 | 30 | Module Private Functions 31 | 32 | ---------------------------------------------------------------------------*/ 33 | 34 | /* Definitions for MMC/SDC command */ 35 | #define CMD0 (0x40+0) /* GO_IDLE_STATE */ 36 | #define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */ 37 | #define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */ 38 | #define CMD8 (0x40+8) /* SEND_IF_COND */ 39 | #define CMD16 (0x40+16) /* SET_BLOCKLEN */ 40 | #define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */ 41 | #define CMD24 (0x40+24) /* WRITE_BLOCK */ 42 | #define CMD55 (0x40+55) /* APP_CMD */ 43 | #define CMD58 (0x40+58) /* READ_OCR */ 44 | 45 | 46 | /* Card type flags (CardType) */ 47 | #define CT_MMC 0x01 /* MMC ver 3 */ 48 | #define CT_SD1 0x02 /* SD ver 1 */ 49 | #define CT_SD2 0x04 /* SD ver 2 */ 50 | #define CT_BLOCK 0x08 /* Block addressing */ 51 | 52 | 53 | static 54 | BYTE CardType; 55 | 56 | 57 | /*-----------------------------------------------------------------------*/ 58 | /* Send a command packet to MMC */ 59 | /*-----------------------------------------------------------------------*/ 60 | 61 | static 62 | BYTE send_cmd ( 63 | BYTE cmd, /* 1st byte (Start + Index) */ 64 | DWORD arg /* Argument (32 bits) */ 65 | ) 66 | { 67 | BYTE n, res; 68 | 69 | 70 | if (cmd & 0x80) { /* ACMD is the command sequense of CMD55-CMD */ 71 | cmd &= 0x7F; 72 | res = send_cmd(CMD55, 0); 73 | if (res > 1) return res; 74 | } 75 | 76 | /* Select the card */ 77 | select(); 78 | 79 | /* Send a command packet */ 80 | xmit_spi(cmd); /* Start + Command index */ 81 | xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ 82 | xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ 83 | xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ 84 | xmit_spi((BYTE)arg); /* Argument[7..0] */ 85 | n = 0x01; /* Dummy CRC + Stop */ 86 | if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */ 87 | if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */ 88 | xmit_spi(n); 89 | 90 | /* Receive a command response */ 91 | n = 10; /* Wait for a valid response in timeout of 10 attempts */ 92 | do { 93 | res = rcv_spi(); 94 | } while ((res & 0x80) && --n); 95 | 96 | return res; /* Return with the response value */ 97 | } 98 | 99 | 100 | 101 | 102 | /*-------------------------------------------------------------------------- 103 | 104 | Public Functions 105 | 106 | ---------------------------------------------------------------------------*/ 107 | 108 | /*-----------------------------------------------------------------------*/ 109 | /* Initialize Disk Drive */ 110 | /*-----------------------------------------------------------------------*/ 111 | 112 | DSTATUS disk_initialize (void) 113 | { 114 | BYTE n, cmd, ty, ocr[4]; 115 | UINT tmr; 116 | 117 | 118 | init_spi(); /* Initialize ports to control MMC */ 119 | for (n = 100; n; n--) dly_100us(); /* 10ms delay */ 120 | for (n = 10; n; n--) deselect(); /* 80 Dummy clocks with CS=H */ 121 | 122 | ty = 0; 123 | if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */ 124 | if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2 */ 125 | for (n = 0; n < 4; n++) ocr[n] = rcv_spi(); /* Get trailing return value of R7 resp */ 126 | if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */ 127 | for (tmr = 10000; tmr && send_cmd(ACMD41, 1UL << 30); tmr--) dly_100us(); /* Wait for leaving idle state (ACMD41 with HCS bit) */ 128 | if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */ 129 | for (n = 0; n < 4; n++) ocr[n] = rcv_spi(); 130 | ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 (HC or SC) */ 131 | } 132 | } 133 | } else { /* SDv1 or MMCv3 */ 134 | if (send_cmd(ACMD41, 0) <= 1) { 135 | ty = CT_SD1; cmd = ACMD41; /* SDv1 */ 136 | } else { 137 | ty = CT_MMC; cmd = CMD1; /* MMCv3 */ 138 | } 139 | for (tmr = 10000; tmr && send_cmd(cmd, 0); tmr--) dly_100us(); /* Wait for leaving idle state */ 140 | if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */ 141 | ty = 0; 142 | } 143 | } 144 | CardType = ty; 145 | deselect(); 146 | 147 | return ty ? 0 : STA_NOINIT; 148 | } 149 | 150 | 151 | 152 | /*-----------------------------------------------------------------------*/ 153 | /* Read partial sector */ 154 | /*-----------------------------------------------------------------------*/ 155 | 156 | DRESULT disk_readp ( 157 | BYTE *buff, /* Pointer to the read buffer (NULL:Read bytes are forwarded to the stream) */ 158 | DWORD lba, /* Sector number (LBA) */ 159 | WORD ofs, /* Byte offset to read from (0..511) */ 160 | WORD cnt /* Number of bytes to read (ofs + cnt mus be <= 512) */ 161 | ) 162 | { 163 | DRESULT res; 164 | BYTE rc; 165 | WORD bc; 166 | 167 | 168 | if (!(CardType & CT_BLOCK)) lba *= 512; /* Convert to byte address if needed */ 169 | 170 | res = RES_ERROR; 171 | if (send_cmd(CMD17, lba) == 0) { /* READ_SINGLE_BLOCK */ 172 | 173 | bc = 40000; 174 | do { /* Wait for data packet */ 175 | rc = rcv_spi(); 176 | } while (rc == 0xFF && --bc); 177 | 178 | if (rc == 0xFE) { /* A data packet arrived */ 179 | bc = 514 - ofs - cnt; 180 | 181 | /* Skip leading bytes */ 182 | if (ofs) { 183 | do rcv_spi(); while (--ofs); 184 | } 185 | 186 | /* Receive a part of the sector */ 187 | do { 188 | *buff++ = rcv_spi(); 189 | } while (--cnt); 190 | 191 | /* Skip trailing bytes and CRC */ 192 | do rcv_spi(); while (--bc); 193 | 194 | res = RES_OK; 195 | } 196 | } 197 | 198 | deselect(); 199 | 200 | return res; 201 | } 202 | 203 | 204 | -------------------------------------------------------------------------------- /pff.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------/ 2 | / Petit FatFs - FAT file system module R0.02a (C)ChaN, 2010 3 | /-----------------------------------------------------------------------------/ 4 | / Petit FatFs module is an open source software to implement FAT file system to 5 | / small embedded systems. This is a free software and is opened for education, 6 | / research and commercial developments under license policy of following trems. 7 | / 8 | / Copyright (C) 2010, ChaN, all right reserved. 9 | / 10 | / * The Petit FatFs module is a free software and there is NO WARRANTY. 11 | / * No restriction on use. You can use, modify and redistribute it for 12 | / personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY. 13 | / * Redistributions of source code must retain the above copyright notice. 14 | / 15 | /-----------------------------------------------------------------------------/ 16 | / Jun 15,'09 R0.01a First release. (Branched from FatFs R0.07b.) 17 | / 18 | / Dec 14,'09 R0.02 Added multiple code page support. 19 | / Added write funciton. 20 | / Changed stream read mode interface. 21 | / Dec 07,'10 R0.02a Added some configuration options. 22 | / Fixed fails to open objects with DBCS character. 23 | /----------------------------------------------------------------------------*/ 24 | 25 | #include "pff.h" /* Petit FatFs configurations and declarations */ 26 | #include "diskio.h" /* Declarations of low level disk I/O functions */ 27 | 28 | 29 | 30 | /*-------------------------------------------------------------------------- 31 | 32 | Module Private Definitions 33 | 34 | ---------------------------------------------------------------------------*/ 35 | 36 | 37 | #if _FS_FAT32 38 | #define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO)) 39 | #else 40 | #define LD_CLUST(dir) LD_WORD(dir+DIR_FstClusLO) 41 | #endif 42 | 43 | 44 | /*--------------------------------------------------------*/ 45 | /* DBCS code ranges and SBCS extend char conversion table */ 46 | 47 | #if _CODE_PAGE == 932 /* Japanese Shift-JIS */ 48 | #define _DF1S 0x81 /* DBC 1st byte range 1 start */ 49 | #define _DF1E 0x9F /* DBC 1st byte range 1 end */ 50 | #define _DF2S 0xE0 /* DBC 1st byte range 2 start */ 51 | #define _DF2E 0xFC /* DBC 1st byte range 2 end */ 52 | #define _DS1S 0x40 /* DBC 2nd byte range 1 start */ 53 | #define _DS1E 0x7E /* DBC 2nd byte range 1 end */ 54 | #define _DS2S 0x80 /* DBC 2nd byte range 2 start */ 55 | #define _DS2E 0xFC /* DBC 2nd byte range 2 end */ 56 | 57 | #elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ 58 | #define _DF1S 0x81 59 | #define _DF1E 0xFE 60 | #define _DS1S 0x40 61 | #define _DS1E 0x7E 62 | #define _DS2S 0x80 63 | #define _DS2E 0xFE 64 | 65 | #elif _CODE_PAGE == 949 /* Korean */ 66 | #define _DF1S 0x81 67 | #define _DF1E 0xFE 68 | #define _DS1S 0x41 69 | #define _DS1E 0x5A 70 | #define _DS2S 0x61 71 | #define _DS2E 0x7A 72 | #define _DS3S 0x81 73 | #define _DS3E 0xFE 74 | 75 | #elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ 76 | #define _DF1S 0x81 77 | #define _DF1E 0xFE 78 | #define _DS1S 0x40 79 | #define _DS1E 0x7E 80 | #define _DS2S 0xA1 81 | #define _DS2E 0xFE 82 | 83 | #elif _CODE_PAGE == 437 /* U.S. (OEM) */ 84 | #define _DF1S 0 85 | #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, \ 86 | 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, \ 87 | 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, \ 88 | 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} 89 | 90 | #elif _CODE_PAGE == 720 /* Arabic (OEM) */ 91 | #define _DF1S 0 92 | #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, \ 93 | 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, \ 94 | 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, \ 95 | 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} 96 | 97 | #elif _CODE_PAGE == 737 /* Greek (OEM) */ 98 | #define _DF1S 0 99 | #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, \ 100 | 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, \ 101 | 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, \ 102 | 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} 103 | 104 | #elif _CODE_PAGE == 775 /* Baltic (OEM) */ 105 | #define _DF1S 0 106 | #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, \ 107 | 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, \ 108 | 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, \ 109 | 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} 110 | 111 | #elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ 112 | #define _DF1S 0 113 | #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, \ 114 | 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, \ 115 | 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, \ 116 | 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} 117 | 118 | #elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ 119 | #define _DF1S 0 120 | #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, \ 121 | 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, \ 122 | 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, \ 123 | 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} 124 | 125 | #elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ 126 | #define _DF1S 0 127 | #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, \ 128 | 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, \ 129 | 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, \ 130 | 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} 131 | 132 | #elif _CODE_PAGE == 857 /* Turkish (OEM) */ 133 | #define _DF1S 0 134 | #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, \ 135 | 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, \ 136 | 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, \ 137 | 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} 138 | 139 | #elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ 140 | #define _DF1S 0 141 | #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, \ 142 | 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, \ 143 | 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, \ 144 | 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} 145 | 146 | #elif _CODE_PAGE == 862 /* Hebrew (OEM) */ 147 | #define _DF1S 0 148 | #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, \ 149 | 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, \ 150 | 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, \ 151 | 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} 152 | 153 | #elif _CODE_PAGE == 866 /* Russian (OEM) */ 154 | #define _DF1S 0 155 | #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, \ 156 | 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, \ 157 | 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, \ 158 | 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} 159 | 160 | #elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ 161 | #define _DF1S 0 162 | #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, \ 163 | 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, \ 164 | 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, \ 165 | 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} 166 | 167 | #elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ 168 | #define _DF1S 0 169 | #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, \ 170 | 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, \ 171 | 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, \ 172 | 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} 173 | 174 | #elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ 175 | #define _DF1S 0 176 | #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, \ 177 | 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, \ 178 | 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, \ 179 | 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} 180 | 181 | #elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ 182 | #define _DF1S 0 183 | #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, \ 184 | 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, \ 185 | 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, \ 186 | 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} 187 | 188 | #elif _CODE_PAGE == 1253 /* Greek (Windows) */ 189 | #define _DF1S 0 190 | #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, \ 191 | 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, \ 192 | 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, \ 193 | 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} 194 | 195 | #elif _CODE_PAGE == 1254 /* Turkish (Windows) */ 196 | #define _DF1S 0 197 | #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, \ 198 | 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, \ 199 | 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, \ 200 | 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} 201 | 202 | #elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ 203 | #define _DF1S 0 204 | #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, \ 205 | 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, \ 206 | 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, \ 207 | 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} 208 | 209 | #elif _CODE_PAGE == 1256 /* Arabic (Windows) */ 210 | #define _DF1S 0 211 | #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, \ 212 | 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, \ 213 | 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, \ 214 | 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} 215 | 216 | #elif _CODE_PAGE == 1257 /* Baltic (Windows) */ 217 | #define _DF1S 0 218 | #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, \ 219 | 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, \ 220 | 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, \ 221 | 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} 222 | 223 | #elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ 224 | #define _DF1S 0 225 | #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, \ 226 | 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, \ 227 | 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, \ 228 | 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} 229 | 230 | #elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ 231 | #define _DF1S 0 232 | 233 | #else 234 | #error Unknown code page 235 | 236 | #endif 237 | 238 | 239 | 240 | /* Character code support macros */ 241 | 242 | #define IsUpper(c) (((c)>='A')&&((c)<='Z')) 243 | #define IsLower(c) (((c)>='a')&&((c)<='z')) 244 | 245 | #if _DF1S /* DBCS configuration */ 246 | 247 | #ifdef _DF2S /* Two 1st byte areas */ 248 | #define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) 249 | #else /* One 1st byte area */ 250 | #define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) 251 | #endif 252 | 253 | #ifdef _DS3S /* Three 2nd byte areas */ 254 | #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) 255 | #else /* Two 2nd byte areas */ 256 | #define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) 257 | #endif 258 | 259 | #else /* SBCS configuration */ 260 | 261 | #define IsDBCS1(c) 0 262 | #define IsDBCS2(c) 0 263 | 264 | #endif /* _DF1S */ 265 | 266 | 267 | /* FatFs refers the members in the FAT structures with byte offset instead 268 | / of structure member because there are incompatibility of the packing option 269 | / between various compilers. */ 270 | 271 | #define BS_jmpBoot 0 272 | #define BS_OEMName 3 273 | #define BPB_BytsPerSec 11 274 | #define BPB_SecPerClus 13 275 | #define BPB_RsvdSecCnt 14 276 | #define BPB_NumFATs 16 277 | #define BPB_RootEntCnt 17 278 | #define BPB_TotSec16 19 279 | #define BPB_Media 21 280 | #define BPB_FATSz16 22 281 | #define BPB_SecPerTrk 24 282 | #define BPB_NumHeads 26 283 | #define BPB_HiddSec 28 284 | #define BPB_TotSec32 32 285 | #define BS_55AA 510 286 | 287 | #define BS_DrvNum 36 288 | #define BS_BootSig 38 289 | #define BS_VolID 39 290 | #define BS_VolLab 43 291 | #define BS_FilSysType 54 292 | 293 | #define BPB_FATSz32 36 294 | #define BPB_ExtFlags 40 295 | #define BPB_FSVer 42 296 | #define BPB_RootClus 44 297 | #define BPB_FSInfo 48 298 | #define BPB_BkBootSec 50 299 | #define BS_DrvNum32 64 300 | #define BS_BootSig32 66 301 | #define BS_VolID32 67 302 | #define BS_VolLab32 71 303 | #define BS_FilSysType32 82 304 | 305 | #define MBR_Table 446 306 | 307 | #define DIR_Name 0 308 | #define DIR_Attr 11 309 | #define DIR_NTres 12 310 | #define DIR_CrtTime 14 311 | #define DIR_CrtDate 16 312 | #define DIR_FstClusHI 20 313 | #define DIR_WrtTime 22 314 | #define DIR_WrtDate 24 315 | #define DIR_FstClusLO 26 316 | #define DIR_FileSize 28 317 | 318 | 319 | 320 | /*-------------------------------------------------------------------------- 321 | 322 | Private Functions 323 | 324 | ---------------------------------------------------------------------------*/ 325 | 326 | 327 | static 328 | FATFS *FatFs; /* Pointer to the file system object (logical drive) */ 329 | 330 | 331 | /* Fill memory */ 332 | static 333 | void mem_set (void* dst, int val, int cnt) { 334 | char *d = (char*)dst; 335 | while (cnt--) *d++ = (char)val; 336 | } 337 | 338 | /* Compare memory to memory */ 339 | static 340 | int mem_cmp (const void* dst, const void* src, int cnt) { 341 | const char *d = (const char *)dst, *s = (const char *)src; 342 | int r = 0; 343 | while (cnt-- && (r = *d++ - *s++) == 0) ; 344 | return r; 345 | } 346 | 347 | 348 | 349 | /*-----------------------------------------------------------------------*/ 350 | /* FAT access - Read value of a FAT entry */ 351 | /*-----------------------------------------------------------------------*/ 352 | 353 | static 354 | CLUST get_fat ( /* 1:IO error, Else:Cluster status */ 355 | CLUST clst /* Cluster# to get the link information */ 356 | ) 357 | { 358 | WORD wc, bc, ofs; 359 | BYTE buf[4]; 360 | FATFS *fs = FatFs; 361 | 362 | 363 | if (clst < 2 || clst >= fs->n_fatent) /* Range check */ 364 | return 1; 365 | 366 | switch (fs->fs_type) { 367 | #if _FS_FAT12 368 | case FS_FAT12 : 369 | bc = (WORD)clst; bc += bc / 2; 370 | ofs = bc % 512; bc /= 512; 371 | if (ofs != 511) { 372 | if (disk_readp(buf, fs->fatbase + bc, ofs, 2)) break; 373 | } else { 374 | if (disk_readp(buf, fs->fatbase + bc, 511, 1)) break; 375 | if (disk_readp(buf+1, fs->fatbase + bc + 1, 0, 1)) break; 376 | } 377 | wc = LD_WORD(buf); 378 | return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); 379 | #endif 380 | case FS_FAT16 : 381 | if (disk_readp(buf, fs->fatbase + clst / 256, (WORD)(((WORD)clst % 256) * 2), 2)) break; 382 | return LD_WORD(buf); 383 | #if _FS_FAT32 384 | case FS_FAT32 : 385 | if (disk_readp(buf, fs->fatbase + clst / 128, (WORD)(((WORD)clst % 128) * 4), 4)) break; 386 | return LD_DWORD(buf) & 0x0FFFFFFF; 387 | #endif 388 | } 389 | 390 | return 1; /* An error occured at the disk I/O layer */ 391 | } 392 | 393 | 394 | 395 | 396 | /*-----------------------------------------------------------------------*/ 397 | /* Get sector# from cluster# */ 398 | /*-----------------------------------------------------------------------*/ 399 | 400 | static 401 | DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ 402 | CLUST clst /* Cluster# to be converted */ 403 | ) 404 | { 405 | FATFS *fs = FatFs; 406 | 407 | 408 | clst -= 2; 409 | if (clst >= (fs->n_fatent - 2)) return 0; /* Invalid cluster# */ 410 | return (DWORD)clst * fs->csize + fs->database; 411 | } 412 | 413 | 414 | 415 | 416 | /*-----------------------------------------------------------------------*/ 417 | /* Directory handling - Rewind directory index */ 418 | /*-----------------------------------------------------------------------*/ 419 | 420 | static 421 | FRESULT dir_rewind ( 422 | DIR *dj /* Pointer to directory object */ 423 | ) 424 | { 425 | CLUST clst; 426 | FATFS *fs = FatFs; 427 | 428 | 429 | dj->index = 0; 430 | clst = dj->sclust; 431 | if (clst == 1 || clst >= fs->n_fatent) /* Check start cluster range */ 432 | return FR_DISK_ERR; 433 | if (_FS_FAT32 && !clst && fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ 434 | clst = (CLUST)fs->dirbase; 435 | dj->clust = clst; /* Current cluster */ 436 | dj->sect = clst ? clust2sect(clst) : fs->dirbase; /* Current sector */ 437 | 438 | return FR_OK; /* Seek succeeded */ 439 | } 440 | 441 | 442 | 443 | 444 | /*-----------------------------------------------------------------------*/ 445 | /* Directory handling - Move directory index next */ 446 | /*-----------------------------------------------------------------------*/ 447 | 448 | static 449 | FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table */ 450 | DIR *dj /* Pointer to directory object */ 451 | ) 452 | { 453 | CLUST clst; 454 | WORD i; 455 | FATFS *fs = FatFs; 456 | 457 | 458 | i = dj->index + 1; 459 | if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ 460 | return FR_NO_FILE; 461 | 462 | if (!(i % 16)) { /* Sector changed? */ 463 | dj->sect++; /* Next sector */ 464 | 465 | if (dj->clust == 0) { /* Static table */ 466 | if (i >= fs->n_rootdir) /* Report EOT when end of table */ 467 | return FR_NO_FILE; 468 | } 469 | else { /* Dynamic table */ 470 | if (((i / 16) & (fs->csize-1)) == 0) { /* Cluster changed? */ 471 | clst = get_fat(dj->clust); /* Get next cluster */ 472 | if (clst <= 1) return FR_DISK_ERR; 473 | if (clst >= fs->n_fatent) /* When it reached end of dynamic table */ 474 | return FR_NO_FILE; /* Report EOT */ 475 | dj->clust = clst; /* Initialize data for new cluster */ 476 | dj->sect = clust2sect(clst); 477 | } 478 | } 479 | } 480 | 481 | dj->index = i; 482 | 483 | return FR_OK; 484 | } 485 | 486 | 487 | 488 | 489 | /*-----------------------------------------------------------------------*/ 490 | /* Directory handling - Find an object in the directory */ 491 | /*-----------------------------------------------------------------------*/ 492 | 493 | static 494 | FRESULT dir_find ( 495 | DIR *dj, /* Pointer to the directory object linked to the file name */ 496 | BYTE *dir /* 32-byte working buffer */ 497 | ) 498 | { 499 | FRESULT res; 500 | BYTE c; 501 | 502 | 503 | res = dir_rewind(dj); /* Rewind directory object */ 504 | if (res != FR_OK) return res; 505 | 506 | do { 507 | res = disk_readp(dir, dj->sect, (WORD)((dj->index % 16) * 32), 32) /* Read an entry */ 508 | ? FR_DISK_ERR : FR_OK; 509 | if (res != FR_OK) break; 510 | c = dir[DIR_Name]; /* First character */ 511 | if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ 512 | if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ 513 | break; 514 | res = dir_next(dj); /* Next entry */ 515 | } while (res == FR_OK); 516 | 517 | return res; 518 | } 519 | 520 | 521 | 522 | 523 | /*-----------------------------------------------------------------------*/ 524 | /* Read an object from the directory */ 525 | /*-----------------------------------------------------------------------*/ 526 | #if _USE_DIR 527 | static 528 | FRESULT dir_read ( 529 | DIR *dj, /* Pointer to the directory object to store read object name */ 530 | BYTE *dir /* 32-byte working buffer */ 531 | ) 532 | { 533 | FRESULT res; 534 | BYTE a, c; 535 | 536 | 537 | res = FR_NO_FILE; 538 | while (dj->sect) { 539 | res = disk_readp(dir, dj->sect, (WORD)((dj->index % 16) * 32), 32) /* Read an entry */ 540 | ? FR_DISK_ERR : FR_OK; 541 | if (res != FR_OK) break; 542 | c = dir[DIR_Name]; 543 | if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ 544 | a = dir[DIR_Attr] & AM_MASK; 545 | if (c != 0xE5 && c != '.' && !(a & AM_VOL)) /* Is it a valid entry? */ 546 | break; 547 | res = dir_next(dj); /* Next entry */ 548 | if (res != FR_OK) break; 549 | } 550 | 551 | if (res != FR_OK) dj->sect = 0; 552 | 553 | return res; 554 | } 555 | #endif 556 | 557 | 558 | 559 | /*-----------------------------------------------------------------------*/ 560 | /* Pick a segment and create the object name in directory form */ 561 | /*-----------------------------------------------------------------------*/ 562 | 563 | #ifdef _EXCVT 564 | static const BYTE cvt[] = _EXCVT; 565 | #endif 566 | 567 | static 568 | FRESULT create_name ( 569 | DIR *dj, /* Pointer to the directory object */ 570 | const char **path /* Pointer to pointer to the segment in the path string */ 571 | ) 572 | { 573 | BYTE c, d, ni, si, i, *sfn; 574 | const char *p; 575 | 576 | /* Create file name in directory form */ 577 | sfn = dj->fn; 578 | mem_set(sfn, ' ', 11); 579 | si = i = 0; ni = 8; 580 | p = *path; 581 | for (;;) { 582 | c = p[si++]; 583 | if (c <= ' ' || c == '/') break; /* Break on end of segment */ 584 | if (c == '.' || i >= ni) { 585 | if (ni != 8 || c != '.') break; 586 | i = 8; ni = 11; 587 | continue; 588 | } 589 | #ifdef _EXCVT 590 | if (c >= 0x80) /* To upper extended char (SBCS) */ 591 | c = cvt[c - 0x80]; 592 | #endif 593 | if (IsDBCS1(c) && i < ni - 1) { /* DBC 1st byte? */ 594 | d = p[si++]; /* Get 2nd byte */ 595 | sfn[i++] = c; 596 | sfn[i++] = d; 597 | } else { /* Single byte code */ 598 | if (IsLower(c)) c -= 0x20; /* toupper */ 599 | sfn[i++] = c; 600 | } 601 | } 602 | *path = &p[si]; /* Rerurn pointer to the next segment */ 603 | 604 | sfn[11] = (c <= ' ') ? 1 : 0; /* Set last segment flag if end of path */ 605 | 606 | return FR_OK; 607 | } 608 | 609 | 610 | 611 | 612 | /*-----------------------------------------------------------------------*/ 613 | /* Get file information from directory entry */ 614 | /*-----------------------------------------------------------------------*/ 615 | #if _USE_DIR 616 | static 617 | void get_fileinfo ( /* No return code */ 618 | DIR *dj, /* Pointer to the directory object */ 619 | BYTE *dir, /* 32-byte working buffer */ 620 | FILINFO *fno /* Pointer to store the file information */ 621 | ) 622 | { 623 | BYTE i, c; 624 | char *p; 625 | 626 | 627 | p = fno->fname; 628 | if (dj->sect) { 629 | for (i = 0; i < 8; i++) { /* Copy file name body */ 630 | c = dir[i]; 631 | if (c == ' ') break; 632 | if (c == 0x05) c = 0xE5; 633 | *p++ = c; 634 | } 635 | if (dir[8] != ' ') { /* Copy file name extension */ 636 | *p++ = '.'; 637 | for (i = 8; i < 11; i++) { 638 | c = dir[i]; 639 | if (c == ' ') break; 640 | *p++ = c; 641 | } 642 | } 643 | fno->fattrib = dir[DIR_Attr]; /* Attribute */ 644 | fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ 645 | fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ 646 | fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ 647 | } 648 | *p = 0; 649 | } 650 | #endif /* _USE_DIR */ 651 | 652 | 653 | 654 | /*-----------------------------------------------------------------------*/ 655 | /* Follow a file path */ 656 | /*-----------------------------------------------------------------------*/ 657 | 658 | static 659 | FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ 660 | DIR *dj, /* Directory object to return last directory and found object */ 661 | BYTE *dir, /* 32-byte working buffer */ 662 | const char *path /* Full-path string to find a file or directory */ 663 | ) 664 | { 665 | FRESULT res; 666 | 667 | 668 | while (*path == ' ') path++; /* Skip leading spaces */ 669 | if (*path == '/') path++; /* Strip heading separator */ 670 | dj->sclust = 0; /* Set start directory (always root dir) */ 671 | 672 | if ((BYTE)*path <= ' ') { /* Null path means the root directory */ 673 | res = dir_rewind(dj); 674 | dir[0] = 0; 675 | 676 | } else { /* Follow path */ 677 | for (;;) { 678 | res = create_name(dj, &path); /* Get a segment */ 679 | if (res != FR_OK) break; 680 | res = dir_find(dj, dir); /* Find it */ 681 | if (res != FR_OK) { /* Could not find the object */ 682 | if (res == FR_NO_FILE && !*(dj->fn+11)) 683 | res = FR_NO_PATH; 684 | break; 685 | } 686 | if (*(dj->fn+11)) break; /* Last segment match. Function completed. */ 687 | if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ 688 | res = FR_NO_PATH; break; 689 | } 690 | dj->sclust = LD_CLUST(dir); 691 | } 692 | } 693 | 694 | return res; 695 | } 696 | 697 | 698 | 699 | 700 | /*-----------------------------------------------------------------------*/ 701 | /* Check a sector if it is an FAT boot record */ 702 | /*-----------------------------------------------------------------------*/ 703 | 704 | static 705 | BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */ 706 | BYTE *buf, /* Working buffer */ 707 | DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ 708 | ) 709 | { 710 | if (disk_readp(buf, sect, 510, 2)) /* Read the boot sector */ 711 | return 3; 712 | if (LD_WORD(buf) != 0xAA55) /* Check record signature */ 713 | return 2; 714 | 715 | if (!disk_readp(buf, sect, BS_FilSysType, 2) && LD_WORD(buf) == 0x4146) /* Check FAT12/16 */ 716 | return 0; 717 | if (_FS_FAT32 && !disk_readp(buf, sect, BS_FilSysType32, 2) && LD_WORD(buf) == 0x4146) /* Check FAT32 */ 718 | return 0; 719 | return 1; 720 | } 721 | 722 | 723 | 724 | 725 | /*-------------------------------------------------------------------------- 726 | 727 | Public Functions 728 | 729 | --------------------------------------------------------------------------*/ 730 | 731 | 732 | 733 | /*-----------------------------------------------------------------------*/ 734 | /* Mount/Unmount a Locical Drive */ 735 | /*-----------------------------------------------------------------------*/ 736 | 737 | FRESULT pf_mount ( 738 | FATFS *fs /* Pointer to new file system object (NULL: Unmount) */ 739 | ) 740 | { 741 | BYTE fmt, buf[36]; 742 | DWORD bsect, fsize, tsect, mclst; 743 | 744 | 745 | FatFs = 0; 746 | if (!fs) return FR_OK; /* Unregister fs object */ 747 | 748 | if (disk_initialize() & STA_NOINIT) /* Check if the drive is ready or not */ 749 | return FR_NOT_READY; 750 | 751 | /* Search FAT partition on the drive */ 752 | bsect = 0; 753 | fmt = check_fs(buf, bsect); /* Check sector 0 as an SFD format */ 754 | if (fmt == 1) { /* Not an FAT boot record, it may be FDISK format */ 755 | /* Check a partition listed in top of the partition table */ 756 | if (disk_readp(buf, bsect, MBR_Table, 16)) { /* 1st partition entry */ 757 | fmt = 3; 758 | } else { 759 | if (buf[4]) { /* Is the partition existing? */ 760 | bsect = LD_DWORD(&buf[8]); /* Partition offset in LBA */ 761 | fmt = check_fs(buf, bsect); /* Check the partition */ 762 | } 763 | } 764 | } 765 | if (fmt == 3) return FR_DISK_ERR; 766 | if (fmt) return FR_NO_FILESYSTEM; /* No valid FAT patition is found */ 767 | 768 | /* Initialize the file system object */ 769 | if (disk_readp(buf, bsect, 13, sizeof(buf))) return FR_DISK_ERR; 770 | 771 | fsize = LD_WORD(buf+BPB_FATSz16-13); /* Number of sectors per FAT */ 772 | if (!fsize) fsize = LD_DWORD(buf+BPB_FATSz32-13); 773 | 774 | fsize *= buf[BPB_NumFATs-13]; /* Number of sectors in FAT area */ 775 | fs->fatbase = bsect + LD_WORD(buf+BPB_RsvdSecCnt-13); /* FAT start sector (lba) */ 776 | fs->csize = buf[BPB_SecPerClus-13]; /* Number of sectors per cluster */ 777 | fs->n_rootdir = LD_WORD(buf+BPB_RootEntCnt-13); /* Nmuber of root directory entries */ 778 | tsect = LD_WORD(buf+BPB_TotSec16-13); /* Number of sectors on the file system */ 779 | if (!tsect) tsect = LD_DWORD(buf+BPB_TotSec32-13); 780 | mclst = (tsect /* Last cluster# + 1 */ 781 | - LD_WORD(buf+BPB_RsvdSecCnt-13) - fsize - fs->n_rootdir / 16 782 | ) / fs->csize + 2; 783 | fs->n_fatent = (CLUST)mclst; 784 | 785 | fmt = FS_FAT16; /* Determine the FAT sub type */ 786 | if (mclst < 0xFF7) /* Number of clusters < 0xFF5 */ 787 | #if _FS_FAT12 788 | fmt = FS_FAT12; 789 | #else 790 | return FR_NO_FILESYSTEM; 791 | #endif 792 | if (mclst >= 0xFFF7) /* Number of clusters >= 0xFFF5 */ 793 | #if _FS_FAT32 794 | fmt = FS_FAT32; 795 | #else 796 | return FR_NO_FILESYSTEM; 797 | #endif 798 | 799 | fs->fs_type = fmt; /* FAT sub-type */ 800 | if (_FS_FAT32 && fmt == FS_FAT32) 801 | fs->dirbase = LD_DWORD(buf+(BPB_RootClus-13)); /* Root directory start cluster */ 802 | else 803 | fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */ 804 | fs->database = fs->fatbase + fsize + fs->n_rootdir / 16; /* Data start sector (lba) */ 805 | 806 | fs->flag = 0; 807 | FatFs = fs; 808 | 809 | return FR_OK; 810 | } 811 | 812 | 813 | 814 | 815 | /*-----------------------------------------------------------------------*/ 816 | /* Open or Create a File */ 817 | /*-----------------------------------------------------------------------*/ 818 | 819 | FRESULT pf_open ( 820 | const char *path /* Pointer to the file name */ 821 | ) 822 | { 823 | FRESULT res; 824 | DIR dj; 825 | BYTE sp[12], dir[32]; 826 | FATFS *fs = FatFs; 827 | 828 | 829 | if (!fs) /* Check file system */ 830 | return FR_NOT_ENABLED; 831 | 832 | fs->flag = 0; 833 | dj.fn = sp; 834 | res = follow_path(&dj, dir, path); /* Follow the file path */ 835 | if (res != FR_OK) return res; /* Follow failed */ 836 | if (!dir[0] || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ 837 | return FR_NO_FILE; 838 | 839 | fs->org_clust = LD_CLUST(dir); /* File start cluster */ 840 | fs->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ 841 | fs->fptr = 0; /* File pointer */ 842 | fs->flag = FA_OPENED; 843 | 844 | return FR_OK; 845 | } 846 | 847 | 848 | 849 | 850 | /*-----------------------------------------------------------------------*/ 851 | /* Read File */ 852 | /*-----------------------------------------------------------------------*/ 853 | #if _USE_READ 854 | 855 | FRESULT pf_read ( 856 | void* buff, /* Pointer to the read buffer (NULL:Forward data to the stream)*/ 857 | WORD btr, /* Number of bytes to read */ 858 | WORD* br /* Pointer to number of bytes read */ 859 | ) 860 | { 861 | DRESULT dr; 862 | CLUST clst; 863 | DWORD sect, remain; 864 | WORD rcnt; 865 | BYTE cs, *rbuff = buff; 866 | FATFS *fs = FatFs; 867 | 868 | 869 | *br = 0; 870 | if (!fs) return FR_NOT_ENABLED; /* Check file system */ 871 | if (!(fs->flag & FA_OPENED)) /* Check if opened */ 872 | return FR_NOT_OPENED; 873 | 874 | remain = fs->fsize - fs->fptr; 875 | if (btr > remain) btr = (WORD)remain; /* Truncate btr by remaining bytes */ 876 | 877 | while (btr) { /* Repeat until all data transferred */ 878 | if ((fs->fptr % 512) == 0) { /* On the sector boundary? */ 879 | cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */ 880 | if (!cs) { /* On the cluster boundary? */ 881 | clst = (fs->fptr == 0) ? /* On the top of the file? */ 882 | fs->org_clust : get_fat(fs->curr_clust); 883 | if (clst <= 1) goto fr_abort; 884 | fs->curr_clust = clst; /* Update current cluster */ 885 | } 886 | sect = clust2sect(fs->curr_clust); /* Get current sector */ 887 | if (!sect) goto fr_abort; 888 | fs->dsect = sect + cs; 889 | } 890 | rcnt = (WORD)(512 - (fs->fptr % 512)); /* Get partial sector data from sector buffer */ 891 | if (rcnt > btr) rcnt = btr; 892 | dr = disk_readp(!buff ? 0 : rbuff, fs->dsect, (WORD)(fs->fptr % 512), rcnt); 893 | if (dr) goto fr_abort; 894 | fs->fptr += rcnt; rbuff += rcnt; /* Update pointers and counters */ 895 | btr -= rcnt; *br += rcnt; 896 | } 897 | 898 | return FR_OK; 899 | 900 | fr_abort: 901 | fs->flag = 0; 902 | return FR_DISK_ERR; 903 | } 904 | #endif 905 | 906 | 907 | 908 | /*-----------------------------------------------------------------------*/ 909 | /* Write File */ 910 | /*-----------------------------------------------------------------------*/ 911 | #if _USE_WRITE 912 | 913 | FRESULT pf_write ( 914 | const void* buff, /* Pointer to the data to be written */ 915 | WORD btw, /* Number of bytes to write (0:Finalize the current write operation) */ 916 | WORD* bw /* Pointer to number of bytes written */ 917 | ) 918 | { 919 | CLUST clst; 920 | DWORD sect, remain; 921 | const BYTE *p = buff; 922 | BYTE cs; 923 | WORD wcnt; 924 | FATFS *fs = FatFs; 925 | 926 | 927 | *bw = 0; 928 | if (!fs) return FR_NOT_ENABLED; /* Check file system */ 929 | if (!(fs->flag & FA_OPENED)) /* Check if opened */ 930 | return FR_NOT_OPENED; 931 | 932 | if (!btw) { /* Finalize request */ 933 | if ((fs->flag & FA__WIP) && disk_writep(0, 0)) goto fw_abort; 934 | fs->flag &= ~FA__WIP; 935 | return FR_OK; 936 | } else { /* Write data request */ 937 | if (!(fs->flag & FA__WIP)) /* Round-down fptr to the sector boundary */ 938 | fs->fptr &= 0xFFFFFE00; 939 | } 940 | remain = fs->fsize - fs->fptr; 941 | if (btw > remain) btw = (WORD)remain; /* Truncate btw by remaining bytes */ 942 | 943 | while (btw) { /* Repeat until all data transferred */ 944 | if (((WORD)fs->fptr % 512) == 0) { /* On the sector boundary? */ 945 | cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */ 946 | if (!cs) { /* On the cluster boundary? */ 947 | clst = (fs->fptr == 0) ? /* On the top of the file? */ 948 | fs->org_clust : get_fat(fs->curr_clust); 949 | if (clst <= 1) goto fw_abort; 950 | fs->curr_clust = clst; /* Update current cluster */ 951 | } 952 | sect = clust2sect(fs->curr_clust); /* Get current sector */ 953 | if (!sect) goto fw_abort; 954 | fs->dsect = sect + cs; 955 | if (disk_writep(0, fs->dsect)) goto fw_abort; /* Initiate a sector write operation */ 956 | fs->flag |= FA__WIP; 957 | } 958 | wcnt = 512 - ((WORD)fs->fptr % 512); /* Number of bytes to write to the sector */ 959 | if (wcnt > btw) wcnt = btw; 960 | if (disk_writep(p, wcnt)) goto fw_abort; /* Send data to the sector */ 961 | fs->fptr += wcnt; p += wcnt; /* Update pointers and counters */ 962 | btw -= wcnt; *bw += wcnt; 963 | if (((WORD)fs->fptr % 512) == 0) { 964 | if (disk_writep(0, 0)) goto fw_abort; /* Finalize the currtent secter write operation */ 965 | fs->flag &= ~FA__WIP; 966 | } 967 | } 968 | 969 | return FR_OK; 970 | 971 | fw_abort: 972 | fs->flag = 0; 973 | return FR_DISK_ERR; 974 | } 975 | #endif 976 | 977 | 978 | 979 | /*-----------------------------------------------------------------------*/ 980 | /* Seek File R/W Pointer */ 981 | /*-----------------------------------------------------------------------*/ 982 | #if _USE_LSEEK 983 | 984 | FRESULT pf_lseek ( 985 | DWORD ofs /* File pointer from top of file */ 986 | ) 987 | { 988 | CLUST clst; 989 | DWORD bcs, sect, ifptr; 990 | FATFS *fs = FatFs; 991 | 992 | 993 | if (!fs) return FR_NOT_ENABLED; /* Check file system */ 994 | if (!(fs->flag & FA_OPENED)) /* Check if opened */ 995 | return FR_NOT_OPENED; 996 | 997 | if (ofs > fs->fsize) ofs = fs->fsize; /* Clip offset with the file size */ 998 | ifptr = fs->fptr; 999 | fs->fptr = 0; 1000 | if (ofs > 0) { 1001 | bcs = (DWORD)fs->csize * 512; /* Cluster size (byte) */ 1002 | if (ifptr > 0 && 1003 | (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ 1004 | fs->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ 1005 | ofs -= fs->fptr; 1006 | clst = fs->curr_clust; 1007 | } else { /* When seek to back cluster, */ 1008 | clst = fs->org_clust; /* start from the first cluster */ 1009 | fs->curr_clust = clst; 1010 | } 1011 | while (ofs > bcs) { /* Cluster following loop */ 1012 | clst = get_fat(clst); /* Follow cluster chain */ 1013 | if (clst <= 1 || clst >= fs->n_fatent) goto fe_abort; 1014 | fs->curr_clust = clst; 1015 | fs->fptr += bcs; 1016 | ofs -= bcs; 1017 | } 1018 | fs->fptr += ofs; 1019 | sect = clust2sect(clst); /* Current sector */ 1020 | if (!sect) goto fe_abort; 1021 | fs->dsect = sect + (fs->fptr / 512 & (fs->csize - 1)); 1022 | } 1023 | 1024 | return FR_OK; 1025 | 1026 | fe_abort: 1027 | fs->flag = 0; 1028 | return FR_DISK_ERR; 1029 | } 1030 | #endif 1031 | 1032 | 1033 | 1034 | /*-----------------------------------------------------------------------*/ 1035 | /* Create a Directroy Object */ 1036 | /*-----------------------------------------------------------------------*/ 1037 | #if _USE_DIR 1038 | 1039 | FRESULT pf_opendir ( 1040 | DIR *dj, /* Pointer to directory object to create */ 1041 | const char *path /* Pointer to the directory path */ 1042 | ) 1043 | { 1044 | FRESULT res; 1045 | BYTE sp[12], dir[32]; 1046 | FATFS *fs = FatFs; 1047 | 1048 | 1049 | if (!fs) { /* Check file system */ 1050 | res = FR_NOT_ENABLED; 1051 | } else { 1052 | dj->fn = sp; 1053 | res = follow_path(dj, dir, path); /* Follow the path to the directory */ 1054 | if (res == FR_OK) { /* Follow completed */ 1055 | if (dir[0]) { /* It is not the root dir */ 1056 | if (dir[DIR_Attr] & AM_DIR) /* The object is a directory */ 1057 | dj->sclust = LD_CLUST(dir); 1058 | else /* The object is not a directory */ 1059 | res = FR_NO_PATH; 1060 | } 1061 | if (res == FR_OK) 1062 | res = dir_rewind(dj); /* Rewind dir */ 1063 | } 1064 | if (res == FR_NO_FILE) res = FR_NO_PATH; 1065 | } 1066 | 1067 | return res; 1068 | } 1069 | 1070 | 1071 | 1072 | 1073 | /*-----------------------------------------------------------------------*/ 1074 | /* Read Directory Entry in Sequense */ 1075 | /*-----------------------------------------------------------------------*/ 1076 | 1077 | FRESULT pf_readdir ( 1078 | DIR *dj, /* Pointer to the open directory object */ 1079 | FILINFO *fno /* Pointer to file information to return */ 1080 | ) 1081 | { 1082 | FRESULT res; 1083 | BYTE sp[12], dir[32]; 1084 | FATFS *fs = FatFs; 1085 | 1086 | 1087 | if (!fs) { /* Check file system */ 1088 | res = FR_NOT_ENABLED; 1089 | } else { 1090 | dj->fn = sp; 1091 | if (!fno) { 1092 | res = dir_rewind(dj); 1093 | } else { 1094 | res = dir_read(dj, dir); 1095 | if (res == FR_NO_FILE) { 1096 | dj->sect = 0; 1097 | res = FR_OK; 1098 | } 1099 | if (res == FR_OK) { /* A valid entry is found */ 1100 | get_fileinfo(dj, dir, fno); /* Get the object information */ 1101 | res = dir_next(dj); /* Increment index for next */ 1102 | if (res == FR_NO_FILE) { 1103 | dj->sect = 0; 1104 | res = FR_OK; 1105 | } 1106 | } 1107 | } 1108 | } 1109 | 1110 | return res; 1111 | } 1112 | 1113 | #endif /* _USE_DIR */ 1114 | 1115 | -------------------------------------------------------------------------------- /pff.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / Petit FatFs - FAT file system module include file R0.02a (C)ChaN, 2010 3 | /----------------------------------------------------------------------------/ 4 | / Petit FatFs module is an open source software to implement FAT file system to 5 | / small embedded systems. This is a free software and is opened for education, 6 | / research and commercial developments under license policy of following trems. 7 | / 8 | / Copyright (C) 2010, ChaN, all right reserved. 9 | / 10 | / * The Petit FatFs module is a free software and there is NO WARRANTY. 11 | / * No restriction on use. You can use, modify and redistribute it for 12 | / personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY. 13 | / * Redistributions of source code must retain the above copyright notice. 14 | / 15 | /----------------------------------------------------------------------------*/ 16 | 17 | #include "integer.h" 18 | 19 | /*---------------------------------------------------------------------------/ 20 | / Petit FatFs Configuration Options 21 | / 22 | / CAUTION! Do not forget to make clean the project after any changes to 23 | / the configuration options. 24 | / 25 | /----------------------------------------------------------------------------*/ 26 | #ifndef _FATFS 27 | #define _FATFS 28 | 29 | #define _USE_READ 1 /* 1:Enable pf_read() */ 30 | 31 | #define _USE_DIR 0 /* 1:Enable pf_opendir() and pf_readdir() */ 32 | 33 | #define _USE_LSEEK 0 /* 1:Enable pf_lseek() */ 34 | 35 | #define _USE_WRITE 0 /* 1:Enable pf_write() */ 36 | 37 | #define _FS_FAT12 0 /* 1:Enable FAT12 support */ 38 | #define _FS_FAT32 1 /* 1:Enable FAT32 support */ 39 | 40 | 41 | #define _CODE_PAGE 1 42 | /* Defines which code page is used for path name. Supported code pages are: 43 | / 932, 936, 949, 950, 437, 720, 737, 775, 850, 852, 855, 857, 858, 862, 866, 44 | / 874, 1250, 1251, 1252, 1253, 1254, 1255, 1257, 1258 and 1 (ASCII only). 45 | / SBCS code pages except for 1 requiers a case conversion table. This 46 | / might occupy 128 bytes on the RAM on some platforms, e.g. avr-gcc. */ 47 | 48 | 49 | #define _WORD_ACCESS 1 50 | /* The _WORD_ACCESS option defines which access method is used to the word 51 | / data in the FAT structure. 52 | / 53 | / 0: Byte-by-byte access. Always compatible with all platforms. 54 | / 1: Word access. Do not choose this unless following condition is met. 55 | / 56 | / When the byte order on the memory is big-endian or address miss-aligned 57 | / word access results incorrect behavior, the _WORD_ACCESS must be set to 0. 58 | / If it is not the case, the value can also be set to 1 to improve the 59 | / performance and code efficiency. */ 60 | 61 | 62 | /* End of configuration options. Do not change followings without care. */ 63 | /*--------------------------------------------------------------------------*/ 64 | 65 | 66 | 67 | #if _FS_FAT32 68 | #define CLUST DWORD 69 | #else 70 | #define CLUST WORD 71 | #endif 72 | 73 | 74 | /* File system object structure */ 75 | 76 | typedef struct { 77 | BYTE fs_type; /* FAT sub type */ 78 | BYTE flag; /* File status flags */ 79 | BYTE csize; /* Number of sectors per cluster */ 80 | BYTE pad1; 81 | WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ 82 | CLUST n_fatent; /* Number of FAT entries (= number of clusters + 2) */ 83 | DWORD fatbase; /* FAT start sector */ 84 | DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */ 85 | DWORD database; /* Data start sector */ 86 | DWORD fptr; /* File R/W pointer */ 87 | DWORD fsize; /* File size */ 88 | CLUST org_clust; /* File start cluster */ 89 | CLUST curr_clust; /* File current cluster */ 90 | DWORD dsect; /* File current data sector */ 91 | } FATFS; 92 | 93 | 94 | 95 | /* Directory object structure */ 96 | 97 | typedef struct { 98 | WORD index; /* Current read/write index number */ 99 | BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ 100 | CLUST sclust; /* Table start cluster (0:Static table) */ 101 | CLUST clust; /* Current cluster */ 102 | DWORD sect; /* Current sector */ 103 | } DIR; 104 | 105 | 106 | 107 | /* File status structure */ 108 | 109 | typedef struct { 110 | DWORD fsize; /* File size */ 111 | WORD fdate; /* Last modified date */ 112 | WORD ftime; /* Last modified time */ 113 | BYTE fattrib; /* Attribute */ 114 | char fname[13]; /* File name */ 115 | } FILINFO; 116 | 117 | 118 | 119 | /* File function return code (FRESULT) */ 120 | 121 | typedef enum { 122 | FR_OK = 0, /* 0 */ 123 | FR_DISK_ERR, /* 1 */ 124 | FR_NOT_READY, /* 2 */ 125 | FR_NO_FILE, /* 3 */ 126 | FR_NO_PATH, /* 4 */ 127 | FR_NOT_OPENED, /* 5 */ 128 | FR_NOT_ENABLED, /* 6 */ 129 | FR_NO_FILESYSTEM /* 7 */ 130 | } FRESULT; 131 | 132 | 133 | 134 | /*--------------------------------------------------------------*/ 135 | /* Petit FatFs module application interface */ 136 | 137 | FRESULT pf_mount (FATFS*); /* Mount/Unmount a logical drive */ 138 | FRESULT pf_open (const char*); /* Open a file */ 139 | FRESULT pf_read (void*, WORD, WORD*); /* Read data from the open file */ 140 | FRESULT pf_write (const void*, WORD, WORD*); /* Write data to the open file */ 141 | FRESULT pf_lseek (DWORD); /* Move file pointer of the open file */ 142 | FRESULT pf_opendir (DIR*, const char*); /* Open a directory */ 143 | FRESULT pf_readdir (DIR*, FILINFO*); /* Read a directory item from the open directory */ 144 | 145 | 146 | 147 | /*--------------------------------------------------------------*/ 148 | /* Flags and offset address */ 149 | 150 | /* File status flag (FATFS.flag) */ 151 | 152 | #define FA_OPENED 0x01 153 | #define FA_WPRT 0x02 154 | #define FA__WIP 0x40 155 | 156 | 157 | /* FAT sub type (FATFS.fs_type) */ 158 | 159 | #define FS_FAT12 1 160 | #define FS_FAT16 2 161 | #define FS_FAT32 3 162 | 163 | 164 | /* File attribute bits for directory entry */ 165 | 166 | #define AM_RDO 0x01 /* Read only */ 167 | #define AM_HID 0x02 /* Hidden */ 168 | #define AM_SYS 0x04 /* System */ 169 | #define AM_VOL 0x08 /* Volume label */ 170 | #define AM_LFN 0x0F /* LFN entry */ 171 | #define AM_DIR 0x10 /* Directory */ 172 | #define AM_ARC 0x20 /* Archive */ 173 | #define AM_MASK 0x3F /* Mask of defined bits */ 174 | 175 | 176 | /*--------------------------------*/ 177 | /* Multi-byte word access macros */ 178 | 179 | #if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ 180 | #define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) 181 | #define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) 182 | #define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) 183 | #define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) 184 | #else /* Use byte-by-byte access to the FAT structure */ 185 | #define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) 186 | #define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr)) 187 | #define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8) 188 | #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) 189 | #endif 190 | 191 | 192 | #endif /* _FATFS */ 193 | -------------------------------------------------------------------------------- /sch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osbock/avr_boot/4c3dee542fe9f65f9860ed5c87ef1047bfa010b1/sch.jpg --------------------------------------------------------------------------------