diff --git a/CakeBrah b/CakeBrah index 42ebe0d..9f7cea7 160000 --- a/CakeBrah +++ b/CakeBrah @@ -1 +1 @@ -Subproject commit 42ebe0d0bc075ba98fa631441590de9bd2733d2b +Subproject commit 9f7cea77d4db4d743e45b2e5193df76ffed0a571 diff --git a/CakeHax b/CakeHax index 6b8fca0..5245c7b 160000 --- a/CakeHax +++ b/CakeHax @@ -1 +1 @@ -Subproject commit 6b8fca0b37a370a605f76b34b133da91a0b40f5e +Subproject commit 5245c7b9dc232956a8578a36468f9024d8cf7001 diff --git a/Makefile b/Makefile index 2d2402e..4460010 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ $(dir_build)/main.bin: $(dir_build)/main.elf $(dir_build)/main.elf: $(objects) $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ -$(dir_build)/memory.o: CFLAGS += -O3 +$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3 $(dir_build)/installer.o: CFLAGS += -DTITLE="\"$(name) $(revision)\"" $(dir_build)/%.o: $(dir_source)/%.c diff --git a/source/cache.h b/source/cache.h new file mode 100644 index 0000000..6a064d5 --- /dev/null +++ b/source/cache.h @@ -0,0 +1,29 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +#pragma once + +#include "types.h" + +void flushEntireDCache(void); //actually: "clean and flush" +void flushDCacheRange(void *startAddress, u32 size); +void flushEntireICache(void); \ No newline at end of file diff --git a/source/cache.s b/source/cache.s new file mode 100644 index 0000000..d1857ac --- /dev/null +++ b/source/cache.s @@ -0,0 +1,74 @@ +@ This file is part of Luma3DS +@ Copyright (C) 2016 Aurora Wright, TuxSH +@ +@ This program is free software: you can redistribute it and/or modify +@ it under the terms of the GNU General Public License as published by +@ the Free Software Foundation, either version 3 of the License, or +@ (at your option) any later version. +@ +@ 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. +@ +@ You should have received a copy of the GNU General Public License +@ along with this program. If not, see . +@ +@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +@ reasonable legal notices or author attributions in that material or in the Appropriate Legal +@ Notices displayed by works containing it. + +.text +.arm +.align 4 + +.global flushEntireDCache +.type flushEntireDCache, %function +flushEntireDCache: + @ Adapted from http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0155a/ch03s03s05.html, + @ and https://github.com/gemarcano/libctr9_io/blob/master/src/ctr_system_ARM.c#L39 as well + @ Note: ARM's example is actually for a 8KB DCache (which is what the 3DS has) + + @ Implemented in bootROM at address 0xffff0830 + mov r1, #0 @ segment counter + outer_loop: + mov r0, #0 @ line counter + + inner_loop: + orr r2, r1, r0 @ generate segment and line address + mcr p15, 0, r2, c7, c14, 2 @ clean and flush the line + add r0, #0x20 @ increment to next line + cmp r0, #0x400 + bne inner_loop + + add r1, #0x40000000 + cmp r1, #0 + bne outer_loop + + mcr p15, 0, r1, c7, c10, 4 @ drain write buffer + bx lr + +.global flushDCacheRange +.type flushDCacheRange, %function +flushDCacheRange: + @ Implemented in bootROM at address 0xffff08a0 + add r1, r0, r1 @ end address + bic r0, #0x1f @ align source address to cache line size (32 bytes) + + flush_dcache_range_loop: + mcr p15, 0, r0, c7, c14, 1 @ clean and flush the line corresponding to the address r0 is holding + add r0, #0x20 + cmp r0, r1 + blo flush_dcache_range_loop + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + bx lr + +.global flushEntireICache +.type flushEntireICache, %function +flushEntireICache: + @ Implemented in bootROM at address 0xffff0ab4 + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + bx lr diff --git a/source/crypto.c b/source/crypto.c index b42730f..434eec5 100755 --- a/source/crypto.c +++ b/source/crypto.c @@ -9,301 +9,297 @@ #include "fatfs/sdmmc/sdmmc.h" /**************************************************************** -* Crypto Libs +* Crypto libs ****************************************************************/ /* original version by megazig */ #ifndef __thumb__ #define BSWAP32(x) {\ - __asm__\ - (\ - "eor r1, %1, %1, ror #16\n\t"\ - "bic r1, r1, #0xFF0000\n\t"\ - "mov %0, %1, ror #8\n\t"\ - "eor %0, %0, r1, lsr #8\n\t"\ - :"=r"(x)\ - :"0"(x)\ - :"r1"\ - );\ + __asm__\ + (\ + "eor r1, %1, %1, ror #16\n\t"\ + "bic r1, r1, #0xFF0000\n\t"\ + "mov %0, %1, ror #8\n\t"\ + "eor %0, %0, r1, lsr #8\n\t"\ + :"=r"(x)\ + :"0"(x)\ + :"r1"\ + );\ }; #define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\ __asm__\ - (\ - "adds %0, %4\n\t"\ - "addcss %1, %1, #1\n\t"\ - "addcss %2, %2, #1\n\t"\ - "addcs %3, %3, #1\n\t"\ - : "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\ - : "r"(u32_0)\ - : "cc"\ - );\ + (\ + "adds %0, %4\n\t"\ + "addcss %1, %1, #1\n\t"\ + "addcss %2, %2, #1\n\t"\ + "addcs %3, %3, #1\n\t"\ + : "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\ + : "r"(u32_0)\ + : "cc"\ + );\ } #else #define BSWAP32(x) {x = __builtin_bswap32(x);} #define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\ __asm__\ - (\ - "mov r4, #0\n\t"\ - "add %0, %0, %4\n\t"\ - "adc %1, %1, r4\n\t"\ - "adc %2, %2, r4\n\t"\ - "adc %3, %3, r4\n\t"\ - : "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\ - : "r"(u32_0)\ - : "cc", "r4"\ - );\ + (\ + "mov r4, #0\n\t"\ + "add %0, %0, %4\n\t"\ + "adc %1, %1, r4\n\t"\ + "adc %2, %2, r4\n\t"\ + "adc %3, %3, r4\n\t"\ + : "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\ + : "r"(u32_0)\ + : "cc", "r4"\ + );\ } #endif /*__thumb__*/ static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode) { - if(keyslot <= 0x03) return; // Ignore TWL keys for now - u32 *key32 = (u32 *)key; - *REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; - *REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE; + if(keyslot <= 0x03) return; // Ignore TWL keys for now + u32 *key32 = (u32 *)key; + *REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; + *REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE; - REG_AESKEYFIFO[keyType] = key32[0]; - REG_AESKEYFIFO[keyType] = key32[1]; - REG_AESKEYFIFO[keyType] = key32[2]; - REG_AESKEYFIFO[keyType] = key32[3]; + REG_AESKEYFIFO[keyType] = key32[0]; + REG_AESKEYFIFO[keyType] = key32[1]; + REG_AESKEYFIFO[keyType] = key32[2]; + REG_AESKEYFIFO[keyType] = key32[3]; } static void aes_use_keyslot(u8 keyslot) { - if(keyslot > 0x3F) - return; + if(keyslot > 0x3F) + return; - *REG_AESKEYSEL = keyslot; - *REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */ + *REG_AESKEYSEL = keyslot; + *REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */ } static void aes_setiv(const void *iv, u32 mode) { - const u32 *iv32 = (const u32 *)iv; - *REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; + const u32 *iv32 = (const u32 *)iv; + *REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode; - // Word order for IV can't be changed in REG_AESCNT and always default to reversed - if(mode & AES_INPUT_NORMAL) - { - REG_AESCTR[0] = iv32[3]; - REG_AESCTR[1] = iv32[2]; - REG_AESCTR[2] = iv32[1]; - REG_AESCTR[3] = iv32[0]; - } - else - { - REG_AESCTR[0] = iv32[0]; - REG_AESCTR[1] = iv32[1]; - REG_AESCTR[2] = iv32[2]; - REG_AESCTR[3] = iv32[3]; - } + // Word order for IV can't be changed in REG_AESCNT and always default to reversed + if(mode & AES_INPUT_NORMAL) + { + REG_AESCTR[0] = iv32[3]; + REG_AESCTR[1] = iv32[2]; + REG_AESCTR[2] = iv32[1]; + REG_AESCTR[3] = iv32[0]; + } + else + { + REG_AESCTR[0] = iv32[0]; + REG_AESCTR[1] = iv32[1]; + REG_AESCTR[2] = iv32[2]; + REG_AESCTR[3] = iv32[3]; + } } static void aes_advctr(void *ctr, u32 val, u32 mode) { - u32 *ctr32 = (u32 *)ctr; - - int i; - if(mode & AES_INPUT_BE) - { - for(i = 0; i < 4; ++i) // Endian swap - BSWAP32(ctr32[i]); - } - - if(mode & AES_INPUT_NORMAL) - { - ADD_u128_u32(ctr32[3], ctr32[2], ctr32[1], ctr32[0], val); - } - else - { - ADD_u128_u32(ctr32[0], ctr32[1], ctr32[2], ctr32[3], val); - } - - if(mode & AES_INPUT_BE) - { - for(i = 0; i < 4; ++i) // Endian swap - BSWAP32(ctr32[i]); - } + u32 *ctr32 = (u32 *)ctr; + + int i; + if(mode & AES_INPUT_BE) + { + for(i = 0; i < 4; ++i) // Endian swap + BSWAP32(ctr32[i]); + } + + if(mode & AES_INPUT_NORMAL) + { + ADD_u128_u32(ctr32[3], ctr32[2], ctr32[1], ctr32[0], val); + } + else + { + ADD_u128_u32(ctr32[0], ctr32[1], ctr32[2], ctr32[3], val); + } + + if(mode & AES_INPUT_BE) + { + for(i = 0; i < 4; ++i) // Endian swap + BSWAP32(ctr32[i]); + } } static void aes_change_ctrmode(void *ctr, u32 fromMode, u32 toMode) { - u32 *ctr32 = (u32 *)ctr; - int i; - if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN) - { - for(i = 0; i < 4; ++i) - BSWAP32(ctr32[i]); - } + u32 *ctr32 = (u32 *)ctr; + int i; + if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN) + { + for(i = 0; i < 4; ++i) + BSWAP32(ctr32[i]); + } - if((fromMode ^ toMode) & AES_CNT_INPUT_ORDER) - { - u32 temp = ctr32[0]; - ctr32[0] = ctr32[3]; - ctr32[3] = temp; + if((fromMode ^ toMode) & AES_CNT_INPUT_ORDER) + { + u32 temp = ctr32[0]; + ctr32[0] = ctr32[3]; + ctr32[3] = temp; - temp = ctr32[1]; - ctr32[1] = ctr32[2]; - ctr32[2] = temp; - } + temp = ctr32[1]; + ctr32[1] = ctr32[2]; + ctr32[2] = temp; + } } static void aes_batch(void *dst, const void *src, u32 blockCount) { - *REG_AESBLKCNT = blockCount << 16; - *REG_AESCNT |= AES_CNT_START; - - const u32 *src32 = (const u32 *)src; - u32 *dst32 = (u32 *)dst; - - u32 wbc = blockCount; - u32 rbc = blockCount; - - while(rbc) - { - if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) // There's space for at least 4 ints - { - *REG_AESWRFIFO = *src32++; - *REG_AESWRFIFO = *src32++; - *REG_AESWRFIFO = *src32++; - *REG_AESWRFIFO = *src32++; - wbc--; - } - - if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) // At least 4 ints available for read - { - *dst32++ = *REG_AESRDFIFO; - *dst32++ = *REG_AESRDFIFO; - *dst32++ = *REG_AESRDFIFO; - *dst32++ = *REG_AESRDFIFO; - rbc--; - } - } + *REG_AESBLKCNT = blockCount << 16; + *REG_AESCNT |= AES_CNT_START; + + const u32 *src32 = (const u32 *)src; + u32 *dst32 = (u32 *)dst; + + u32 wbc = blockCount; + u32 rbc = blockCount; + + while(rbc) + { + if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) // There's space for at least 4 ints + { + *REG_AESWRFIFO = *src32++; + *REG_AESWRFIFO = *src32++; + *REG_AESWRFIFO = *src32++; + *REG_AESWRFIFO = *src32++; + wbc--; + } + + if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) // At least 4 ints available for read + { + *dst32++ = *REG_AESRDFIFO; + *dst32++ = *REG_AESRDFIFO; + *dst32++ = *REG_AESRDFIFO; + *dst32++ = *REG_AESRDFIFO; + rbc--; + } + } } static void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode) { - *REG_AESCNT = mode | - AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | - AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN | - AES_CNT_FLUSH_READ | AES_CNT_FLUSH_WRITE; + *REG_AESCNT = mode | + AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | + AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN | + AES_CNT_FLUSH_READ | AES_CNT_FLUSH_WRITE; - u32 blocks; - while(blockCount != 0) - { - if((mode & AES_ALL_MODES) != AES_ECB_ENCRYPT_MODE - && (mode & AES_ALL_MODES) != AES_ECB_DECRYPT_MODE) - aes_setiv(iv, ivMode); + u32 blocks; + while(blockCount != 0) + { + if((mode & AES_ALL_MODES) != AES_ECB_ENCRYPT_MODE + && (mode & AES_ALL_MODES) != AES_ECB_DECRYPT_MODE) + aes_setiv(iv, ivMode); - blocks = (blockCount >= 0xFFFF) ? 0xFFFF : blockCount; + blocks = (blockCount >= 0xFFFF) ? 0xFFFF : blockCount; - // Save the last block for the next decryption CBC batch's iv - if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE) - { - memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); - aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); - } + // Save the last block for the next decryption CBC batch's iv + if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE) + { + memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); + aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); + } - // Process the current batch - aes_batch(dst, src, blocks); + // Process the current batch + aes_batch(dst, src, blocks); - // Save the last block for the next encryption CBC batch's iv - if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE) - { - memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); - aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); - } - - // Advance counter for CTR mode - else if((mode & AES_ALL_MODES) == AES_CTR_MODE) - aes_advctr(iv, blocks, ivMode); + // Save the last block for the next encryption CBC batch's iv + if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE) + { + memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE); + aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode); + } - src += blocks * AES_BLOCK_SIZE; - dst += blocks * AES_BLOCK_SIZE; - blockCount -= blocks; - } + // Advance counter for CTR mode + else if((mode & AES_ALL_MODES) == AES_CTR_MODE) + aes_advctr(iv, blocks, ivMode); + + src += blocks * AES_BLOCK_SIZE; + dst += blocks * AES_BLOCK_SIZE; + blockCount -= blocks; + } } -void sha_wait_idle() +static void sha_wait_idle() { - while(*REG_SHA_CNT & 1); + while(*REG_SHA_CNT & 1); } -void sha(void *res, const void *src, u32 size, u32 mode) +static void sha(void *res, const void *src, u32 size, u32 mode) { - sha_wait_idle(); - *REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND; - - const u32 *src32 = (const u32 *)src; - int i; - while(size >= 0x40) - { - sha_wait_idle(); - for(i = 0; i < 4; ++i) - { - *REG_SHA_INFIFO = *src32++; - *REG_SHA_INFIFO = *src32++; - *REG_SHA_INFIFO = *src32++; - *REG_SHA_INFIFO = *src32++; - } + sha_wait_idle(); + *REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND; - size -= 0x40; - } - - sha_wait_idle(); - memcpy((void *)REG_SHA_INFIFO, src32, size); - - *REG_SHA_CNT = (*REG_SHA_CNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND; - - while(*REG_SHA_CNT & SHA_FINAL_ROUND); - sha_wait_idle(); - - u32 hashSize = SHA_256_HASH_SIZE; - if(mode == SHA_224_MODE) - hashSize = SHA_224_HASH_SIZE; - else if(mode == SHA_1_MODE) - hashSize = SHA_1_HASH_SIZE; + const u32 *src32 = (const u32 *)src; + int i; + while(size >= 0x40) + { + sha_wait_idle(); + for(i = 0; i < 4; ++i) + { + *REG_SHA_INFIFO = *src32++; + *REG_SHA_INFIFO = *src32++; + *REG_SHA_INFIFO = *src32++; + *REG_SHA_INFIFO = *src32++; + } - memcpy(res, (void *)REG_SHA_HASH, hashSize); + size -= 0x40; + } + + sha_wait_idle(); + memcpy((void *)REG_SHA_INFIFO, src32, size); + + *REG_SHA_CNT = (*REG_SHA_CNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND; + + while(*REG_SHA_CNT & SHA_FINAL_ROUND); + sha_wait_idle(); + + u32 hashSize = SHA_256_HASH_SIZE; + if(mode == SHA_224_MODE) + hashSize = SHA_224_HASH_SIZE; + else if(mode == SHA_1_MODE) + hashSize = SHA_1_HASH_SIZE; + + memcpy(res, (void *)REG_SHA_HASH, hashSize); } -/**************************************************************** -* Nand/FIRM Crypto stuff -****************************************************************/ - -static u8 nandSlot, nandCTR[0x10]; +/*****************************************************************/ +static u8 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE]; +static u8 nandSlot; static u32 fatStart; - -const u8 key2s[3][0x10] = { +const u8 __attribute__((aligned(4))) key2s[3][AES_BLOCK_SIZE] = { {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xF5, 0xF6}, {0x65, 0x29, 0x3E, 0x12, 0x56, 0x0C, 0x0B, 0xD1, 0xDD, 0xB5, 0x63, 0x1C, 0xB6, 0xD9, 0x52, 0x75} }; -//Get Nand CTR key void getNandCTR(void) { - u8 cid[0x10]; - u8 shaSum[0x20]; + u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE]; + u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE]; sdmmc_get_cid(1, (u32 *)cid); - sha(shaSum, cid, 0x10, SHA_256_MODE); - memcpy(nandCTR, shaSum, 0x10); + sha(shaSum, cid, sizeof(cid), SHA_256_MODE); + memcpy(nandCtr, shaSum, sizeof(nandCtr)); } -//Initialize the CTRNAND crypto void ctrNandInit(void) { getNandCTR(); - if(console) + if(isN3DS) { - u8 keyY0x5[0x10] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE}; + u8 __attribute__((aligned(4))) keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE}; aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + nandSlot = 0x05; fatStart = 0x5CAD7; } @@ -314,86 +310,81 @@ void ctrNandInit(void) } } -//Read and decrypt from CTRNAND u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf) { - u8 tmpCTR[0x10]; - memcpy(tmpCTR, nandCTR, 0x10); - aes_advctr(tmpCTR, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); + u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)]; + memcpy(tmpCtr, nandCtr, sizeof(nandCtr)); + aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); //Read u32 result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf); //Decrypt aes_use_keyslot(nandSlot); - aes(outbuf, outbuf, sectorCount * 0x200 / AES_BLOCK_SIZE, tmpCTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + aes(outbuf, outbuf, sectorCount * 0x200 / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); return result; } -//Read and decrypt from the FIRM0 partition on NAND void readFirm0(u8 *outbuf, u32 size) { - u8 CTRtmp[0x10]; - memcpy(CTRtmp, nandCTR, 0x10); + u8 __attribute__((aligned(4))) ctrTmp[sizeof(nandCtr)]; + memcpy(ctrTmp, nandCtr, sizeof(nandCtr)); //Read FIRM0 data sdmmc_nand_readsectors(0x0B130000 / 0x200, size / 0x200, outbuf); //Decrypt - aes_advctr(CTRtmp, 0x0B130000 / 0x10, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_advctr(ctrTmp, 0x0B130000 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); aes_use_keyslot(0x06); - aes(outbuf, outbuf, size / AES_BLOCK_SIZE, CTRtmp, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + aes(outbuf, outbuf, size / AES_BLOCK_SIZE, ctrTmp, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); } -//Encrypt and write a FIRM partition to NAND -void writeFirm(u8 *inbuf, u32 firm, u32 size) +void writeFirm(u8 *inbuf, bool isFirm1, u32 size) { - u32 offset = firm ? 0x0B530000 : 0x0B130000; - u8 CTRtmp[0x10]; - memcpy(CTRtmp, nandCTR, 0x10); + u32 offset = isFirm1 ? 0x0B530000 : 0x0B130000; + u8 __attribute__((aligned(4))) ctrTmp[sizeof(nandCtr)]; + memcpy(ctrTmp, nandCtr, sizeof(nandCtr)); //Encrypt FIRM data - aes_advctr(CTRtmp, offset / 0x10, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_advctr(ctrTmp, offset / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL); aes_use_keyslot(0x06); - aes(inbuf, inbuf, size / AES_BLOCK_SIZE, CTRtmp, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + aes(inbuf, inbuf, size / AES_BLOCK_SIZE, ctrTmp, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); //Write to NAND sdmmc_nand_writesectors(offset / 0x200, size / 0x200, inbuf); } -//Setup keyslot 0x11 for key sector de/encryption -void setupKeyslot0x11(u32 a9lhBoot, const void *otp) +void setupKeyslot0x11(bool isA9lh, const void *otp) { - u8 shasum[0x20]; - u8 keyX[0x10]; - u8 keyY[0x10]; + u8 __attribute__((aligned(4))) shasum[SHA_256_HASH_SIZE]; + u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE]; + u8 __attribute__((aligned(4))) keyY[AES_BLOCK_SIZE]; //If booting via A9LH, use the leftover contents of the SHA register - if(a9lhBoot) memcpy((void *)shasum, (void *)REG_SHA_HASH, 0x20); + if(isA9lh) memcpy(shasum, (void *)REG_SHA_HASH, sizeof(shasum)); + //Else calculate the otp.bin hash else sha(shasum, otp, 0x90, SHA_256_MODE); //Set keyX and keyY - memcpy(keyX, shasum, 0x10); - memcpy(keyY, shasum + 0x10, 0x10); + memcpy(keyX, shasum, sizeof(keyX)); + memcpy(keyY, shasum + sizeof(keyX), sizeof(keyY)); aes_setkey(0x11, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); aes_setkey(0x11, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); } -//Generate and encrypt an A9LH key sector void generateSector(u8 *keySector, u32 mode) { //Inject key2 - memcpy(keySector + 0x10, mode ? key2s[0] : key2s[2], 0x10); + memcpy(keySector + AES_BLOCK_SIZE, mode ? key2s[0] : key2s[2], AES_BLOCK_SIZE); //Encrypt key sector aes_use_keyslot(0x11); for(u32 i = 0; i < 32; i++) - aes(keySector + (0x10 * i), keySector + (0x10 * i), 1, NULL, AES_ECB_ENCRYPT_MODE, 0); + aes(keySector + (AES_BLOCK_SIZE * i), keySector + (AES_BLOCK_SIZE * i), 1, NULL, AES_ECB_ENCRYPT_MODE, 0); } -//Read and decrypt the NAND key sector void getSector(u8 *keySector) { //Read keysector from NAND @@ -402,33 +393,30 @@ void getSector(u8 *keySector) //Decrypt key sector aes_use_keyslot(0x11); for(u32 i = 0; i < 32; i++) - aes(keySector + (0x10 * i), keySector + (0x10 * i), 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes(keySector + (AES_BLOCK_SIZE * i), keySector + (AES_BLOCK_SIZE * i), 1, NULL, AES_ECB_DECRYPT_MODE, 0); } -//Check SHA256 hash u32 verifyHash(const void *data, u32 size, const u8 *hash) { - u8 shasum[0x20]; + u8 __attribute__((aligned(4))) shasum[SHA_256_HASH_SIZE]; sha(shasum, data, size, SHA_256_MODE); - return memcmp(shasum, hash, 0x20) == 0; + return memcmp(shasum, hash, sizeof(shasum)) == 0; } -//Decrypt a FIRM ExeFS u32 decryptExeFs(u8 *inbuf) { u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200; u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200; - u8 ncchCTR[0x10] = {0}; + u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0}; for(u32 i = 0; i < 8; i++) - ncchCTR[7 - i] = *(inbuf + 0x108 + i); - ncchCTR[8] = 2; + ncchCtr[7 - i] = *(inbuf + 0x108 + i); + ncchCtr[8] = 2; aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); - aes_setiv(ncchCTR, AES_INPUT_BE | AES_INPUT_NORMAL); aes_use_keyslot(0x2C); - aes(inbuf - 0x200, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); + aes(inbuf - 0x200, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); return exeFsSize - 0x200; } \ No newline at end of file diff --git a/source/crypto.h b/source/crypto.h index 8602734..c0a8b9b 100755 --- a/source/crypto.h +++ b/source/crypto.h @@ -79,16 +79,15 @@ #define SHA_224_HASH_SIZE (224 / 8) #define SHA_1_HASH_SIZE (160 / 8) -//NAND/FIRM stuff -extern u32 console; +extern bool isN3DS; const u8 key2s[3][0x10]; void getNandCTR(void); void ctrNandInit(void); u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf); void readFirm0(u8 *outbuf, u32 size); -void writeFirm(u8 *inbuf, u32 offset, u32 size); -void setupKeyslot0x11(u32 a9lhBoot, const void *otp); +void writeFirm(u8 *inbuf, bool isFirm1, u32 size); +void setupKeyslot0x11(bool isA9lh, const void *otp); void generateSector(u8 *keySector, u32 mode); void getSector(u8 *keySector); u32 verifyHash(const void *data, u32 size, const u8 *hash); diff --git a/source/draw.c b/source/draw.c index 8092c46..1ab2027 100644 --- a/source/draw.c +++ b/source/draw.c @@ -1,47 +1,47 @@ /* -* draw.c +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH * +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* * Code to print to the screen by mid-kid @CakesFW +* https://github.com/mid-kid/CakesForeveryWan */ #include "draw.h" -#include "memory.h" +#include "screen.h" +#include "strings.h" #include "font.h" -static const struct fb { - u8 *top_left; - u8 *top_right; - u8 *bottom; -} *const fb = (struct fb *)0x23FFFE00; - -static inline int strlen(const char *string) +static void drawCharacter(char character, u32 posX, u32 posY, u32 color) { - char *stringEnd = (char *)string; + u8 *select = fb->top_left; - while(*stringEnd) stringEnd++; - - return stringEnd - string; -} - -void clearScreens(void) -{ - memset32(fb->top_left, 0, 0x46500); - memset32(fb->top_right, 0, 0x46500); - memset32(fb->bottom, 0, 0x38400); -} - -void drawCharacter(char character, int posX, int posY, u32 color) -{ - u8 *const select = fb->top_left; - - for(int y = 0; y < 8; y++) + for(u32 y = 0; y < 8; y++) { char charPos = font[character * 8 + y]; - for(int x = 7; x >= 0; x--) - if ((charPos >> x) & 1) + for(u32 x = 0; x < 8; x++) + if(((charPos >> (7 - x)) & 1) == 1) { - int screenPos = (posX * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - posY - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT; + u32 screenPos = (posX * SCREEN_HEIGHT * 3 + (SCREEN_HEIGHT - y - posY - 1) * 3) + x * 3 * SCREEN_HEIGHT; select[screenPos] = color >> 16; select[screenPos + 1] = color >> 8; @@ -50,26 +50,34 @@ void drawCharacter(char character, int posX, int posY, u32 color) } } -int drawString(const char *string, int posX, int posY, u32 color) +u32 drawString(const char *string, u32 posX, u32 posY, u32 color) { - for(int i = 0, line_i = 0; i < strlen(string); i++, line_i++) - { - if(string[i] == '\n') + for(u32 i = 0, line_i = 0; i < strlen(string); i++) + switch(string[i]) { - posY += SPACING_Y; - line_i = 0; - i++; - } - else if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X) - { - // Make sure we never get out of the screen. - posY += SPACING_Y; - line_i = 2; //Little offset so we know the same string continues. - if(string[i] == ' ') i++; //Spaces at the start look weird - } + case '\n': + posY += SPACING_Y; + line_i = 0; + break; - drawCharacter(string[i], posX + line_i * SPACING_X, posY, color); - } + case '\t': + line_i += 2; + break; + + default: + //Make sure we never get out of the screen + if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X) + { + posY += SPACING_Y; + line_i = 1; //Little offset so we know the same string continues + if(string[i] == ' ') break; //Spaces at the start look weird + } + + drawCharacter(string[i], posX + line_i * SPACING_X, posY, color); + + line_i++; + break; + } return posY + SPACING_Y; } \ No newline at end of file diff --git a/source/draw.h b/source/draw.h index 892e422..b235600 100644 --- a/source/draw.h +++ b/source/draw.h @@ -1,19 +1,35 @@ /* -* draw.h +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH * +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* * Code to print to the screen by mid-kid @CakesFW +* https://github.com/mid-kid/CakesForeveryWan */ #pragma once #include "types.h" -#define SCREEN_TOP_WIDTH 400 -#define SCREEN_TOP_HEIGHT 240 - #define SPACING_Y 10 #define SPACING_X 8 -void clearScreens(void); -void drawCharacter(char character, int pos_x, int pos_y, u32 color); -int drawString(const char *string, int pos_x, int pos_y, u32 color); \ No newline at end of file +u32 drawString(const char *string, u32 posX, u32 posY, u32 color); \ No newline at end of file diff --git a/source/fatfs/00history.txt b/source/fatfs/00history.txt old mode 100644 new mode 100755 index 4d76198..49aac28 --- a/source/fatfs/00history.txt +++ b/source/fatfs/00history.txt @@ -10,7 +10,7 @@ R0.00 (February 26, 2006) R0.01 (April 29, 2006) - First stable version. + The first release. @@ -246,9 +246,34 @@ R0.11a (September 05, 2015) R0.12 (April 12, 2016) - Added support of exFAT file system. (_FS_EXFAT) + Added support for exFAT file system. (_FS_EXFAT) Added f_expand(). (_USE_EXPAND) Changed some members in FINFO structure and behavior of f_readdir(). - Added an option _USE_CHMOD and removed an option _WORD_ACCESS. - Fixed errors in the case conversion teble of Unicode (cc*.c). + Added an option _USE_CHMOD. + Removed an option _WORD_ACCESS. + Fixed errors in the case conversion table of Unicode (cc*.c). + + + +R0.12a (July 10, 2016) + + Added support for creating exFAT volume with some changes of f_mkfs(). + Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed. + f_forward() is available regardless of _FS_TINY. + Fixed f_mkfs() creates wrong volume. (appeared at R0.12) + Fixed wrong memory read in create_name(). (appeared at R0.12) + Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD. + + + +R0.12b (September 04, 2016) + + Improved f_rename() to be able to rename objects with the same name but case. + Fixed an error in the case conversion teble of code page 866. (ff.c) + Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12) + Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12) + Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12) + Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12) + Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12) + Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12) diff --git a/source/fatfs/00readme.txt b/source/fatfs/00readme.txt old mode 100644 new mode 100755 index 35536f7..42426a4 --- a/source/fatfs/00readme.txt +++ b/source/fatfs/00readme.txt @@ -1,4 +1,4 @@ -FatFs Module Source Files R0.12 +FatFs Module Source Files R0.12a FILES diff --git a/source/fatfs/diskio.c b/source/fatfs/diskio.c index 0bae01c..e6f366e 100644 --- a/source/fatfs/diskio.c +++ b/source/fatfs/diskio.c @@ -33,6 +33,7 @@ DSTATUS disk_status ( /* Inidialize a Drive */ /*-----------------------------------------------------------------------*/ + DSTATUS disk_initialize ( BYTE pdrv /* Physical drive nmuber to identify the drive */ ) diff --git a/source/fatfs/ff.c b/source/fatfs/ff.c old mode 100644 new mode 100755 index 9656965..901697b --- a/source/fatfs/ff.c +++ b/source/fatfs/ff.c @@ -1,11 +1,13 @@ /*----------------------------------------------------------------------------/ -/ FatFs - FAT file system module R0.12 (C)ChaN, 2016 / +/ FatFs - Generic FAT file system module R0.12b / /-----------------------------------------------------------------------------/ -/ FatFs module is a free software that opened under license policy of -/ following conditions. / / Copyright (C) 2016, ChaN, all right reserved. / +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. / @@ -17,7 +19,7 @@ #include "ff.h" /* Declarations of FatFs API */ -#include "diskio.h" /* Declarations of disk I/O functions */ +#include "diskio.h" /* Declarations of device I/O functions */ /*-------------------------------------------------------------------------- @@ -26,11 +28,14 @@ ---------------------------------------------------------------------------*/ -#if _FATFS != 88100 /* Revision ID */ +#if _FATFS != 68020 /* Revision ID */ #error Wrong include file (ff.h). #endif +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + /* Reentrancy related */ #if _FS_REENTRANT #if _USE_LFN == 1 @@ -43,7 +48,6 @@ #define LEAVE_FF(fs, res) return res #endif -#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } /* Definitions of sector size */ @@ -294,7 +298,7 @@ typedef struct { 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, \ + 0x90,0x91,0x92,0x93,0x94,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 == 869 /* Greek 2 */ @@ -347,6 +351,18 @@ typedef struct { #endif /* _DF1S */ +/* File attribute bits (internal use) */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* File access control and file status flags (internal use) */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + /* Name status flags */ #define NSFLAG 11 /* Index of name status byte in fn[] */ #define NS_LOSS 0x01 /* Out of 8.3 format */ @@ -355,12 +371,15 @@ typedef struct { #define NS_BODY 0x08 /* Lower case flag (body) */ #define NS_EXT 0x10 /* Lower case flag (ext) */ #define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ #define NS_NONAME 0x80 /* Not followed */ -/* Limits and Boundaries (Differ from specs but correct for real DOS/Windows) */ -#define MIN_FAT16 4086U /* Minimum number of clusters of FAT16 */ -#define MIN_FAT32 65526U /* Minimum number of clusters of FAT32 */ +/* Limits and boundaries (differ from specs but correct for real DOS/Windows) */ +#define MAX_FAT12 0xFF5 /* Maximum number of FAT12 clusters */ +#define MAX_FAT16 0xFFF5 /* Maximum number of FAT16 clusters */ +#define MAX_FAT32 0xFFFFFF5 /* Maximum number of FAT32 clusters */ +#define MAX_EXFAT 0x7FFFFFFD /* Maximum number of exFAT clusters (limited by implementation) */ #define MAX_DIR 0x200000 /* Maximum size of FAT directory */ #define MAX_DIR_EX 0x10000000 /* Maximum size of exFAT directory */ @@ -369,7 +388,7 @@ typedef struct { / structure members because the structure is not binary compatible between / different platforms */ -#define BS_jmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ #define BS_OEMName 3 /* OEM name (8-byte) */ #define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ @@ -377,7 +396,7 @@ typedef struct { #define BPB_NumFATs 16 /* Number of FATs (BYTE) */ #define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */ #define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ -#define BPB_Media 21 /* Media descriptor (BYTE) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ #define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ #define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */ #define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ @@ -389,9 +408,11 @@ typedef struct { #define BS_VolID 39 /* Volume serial number (DWORD) */ #define BS_VolLab 43 /* Volume label string (8-byte) */ #define BS_FilSysType 54 /* File system type string (8-byte) */ -#define BPB_FATSz32 36 /* FAT size (32-bit) [sector] (DWORD) */ -#define BPB_ExtFlags 40 /* Extended flags (WORD) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ #define BPB_FSVer32 42 /* FAT32: File system version (WORD) */ #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ #define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ @@ -402,8 +423,9 @@ typedef struct { #define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ #define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ #define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ -#define BPB_ZeroedEx 11 /* exFAT: Must be zero (35-byte) */ +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ #define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ #define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ #define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ @@ -420,33 +442,41 @@ typedef struct { #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ #define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ #define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ #define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ #define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ #define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ #define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ -#define MBR_Table 446 /* MBR: Partition table offset */ +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ #define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ -#define BS_55AA 510 /* Signature word (WORD) */ - -#define DIR_Name 0 /* Short file name (11) */ -#define DIR_Attr 11 /* Attribute (1) */ -#define DIR_NTres 12 /* Lower case flag (1) */ -#define DIR_CrtTime10 13 /* Created time sub-second (1) */ -#define DIR_CrtTime 14 /* Created time (2) */ -#define DIR_CrtDate 16 /* Created date (2) */ -#define DIR_LstAccDate 18 /* Last accessed date (2) */ +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ #define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ -#define DIR_WrtTime 22 /* Modified time (2) */ -#define DIR_WrtDate 24 /* Modified date (2) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ #define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ #define DIR_FileSize 28 /* File size (DWORD) */ -#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ -#define LDIR_Attr 11 /* LFN attribute (1) */ -#define LDIR_Type 12 /* LFN type (1) */ -#define LDIR_Chksum 13 /* Checksum of the SFN entry */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN type (BYTE) */ +#define LDIR_Chksum 13 /* Checksum of the SFN entry (BYTE) */ #define LDIR_FstClusLO 26 /* Must be zero (WORD) */ #define XDIR_Type 0 /* Type of exFAT directory entry (BYTE) */ #define XDIR_NumLabel 1 /* Number of volume label characters (BYTE) */ @@ -455,24 +485,24 @@ typedef struct { #define XDIR_NumSec 1 /* Number of secondary entries (BYTE) */ #define XDIR_SetSum 2 /* Sum of the set of directory entries (WORD) */ #define XDIR_Attr 4 /* File attribute (WORD) */ -#define XDIR_CrtTime 8 /* Created time (4) */ -#define XDIR_ModTime 12 /* Modified time (4) */ -#define XDIR_AccTime 16 /* Last accessed time (4) */ -#define XDIR_CrtTime10 20 /* Created time subsecond (1) */ -#define XDIR_ModTime10 21 /* Modified time subsecond (1) */ -#define XDIR_CrtTZ 22 /* Created timezone (1) */ -#define XDIR_ModTZ 23 /* Modified timezone (1) */ -#define XDIR_AccTZ 24 /* Last accessed timezone (1) */ -#define XDIR_GenFlags 33 /* Gneral flags (1) */ +#define XDIR_CrtTime 8 /* Created time (DWORD) */ +#define XDIR_ModTime 12 /* Modified time (DWORD) */ +#define XDIR_AccTime 16 /* Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* Gneral secondary flags (WORD) */ #define XDIR_NumName 35 /* Number of file name characters (BYTE) */ #define XDIR_NameHash 36 /* Hash of file name (WORD) */ #define XDIR_ValidFileSize 40 /* Valid file size (QWORD) */ -#define XDIR_FstClus 52 /* First cluster of the File/Directory (DWORD) */ +#define XDIR_FstClus 52 /* First cluster of the file data (DWORD) */ #define XDIR_FileSize 56 /* File/Directory size (QWORD) */ #define SZDIRE 32 /* Size of a directory entry */ #define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ -#define DDEM 0xE5 /* Deleted directory entry mark at DIR_Name[0] */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ #define RDDEM 0x05 /* Replacement of the character collides with DDEM */ @@ -504,12 +534,9 @@ static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ #endif #if _USE_LFN == 0 /* Non-LFN configuration */ -#define DEF_NAMBUF BYTE sfn[12] -#define INIT_NAMBUF(dobj) (dobj).fn = sfn +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) #define FREE_NAMBUF() -#define DEF_DIRBUF -#define INIT_DIRBUF(fs) -#define FREE_DIRBUF() #else #if _MAX_LFN < 12 || _MAX_LFN > 255 #error Wrong _MAX_LFN setting @@ -520,45 +547,30 @@ static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ static BYTE DirBuf[SZDIRE*19]; /* Directory entry block scratchpad buffer (19 entries in size) */ #endif static WCHAR LfnBuf[_MAX_LFN+1]; /* LFN enabled with static working buffer */ -#define DEF_NAMBUF BYTE sfn[12] -#define INIT_NAMBUF(dj) { (dj).fn = sfn; (dj).lfn = LfnBuf; } +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) #define FREE_NAMBUF() -#define DEF_DIRBUF -#define INIT_DIRBUF(fs) -#define FREE_DIRBUF() #elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ #if _FS_EXFAT -#define DEF_NAMBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[SZDIRE*19] -#define INIT_NAMBUF(dj) { (dj).fn = sfn; (dj).lfn = lbuf; (dj).obj.fs->dirbuf = dbuf; } +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[SZDIRE*19]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } #define FREE_NAMBUF() -#define DEF_DIRBUF BYTE dbuf[SZDIRE*19] -#define INIT_DIRBUF(fs) fs->dirbuf = dbuf -#define FREE_DIRBUF() #else -#define DEF_NAMBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] -#define INIT_NAMBUF(dj) { (dj).fn = sfn; (dj).lfn = lbuf; } +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } #define FREE_NAMBUF() -#define DEF_DIRBUF -#define INIT_DIRBUF(fs) -#define FREE_DIRBUF() #endif #elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ #if _FS_EXFAT -#define DEF_NAMBUF BYTE sfn[12]; WCHAR *lfn -#define INIT_NAMBUF(dj) { lfn = ff_memalloc((_MAX_LFN+1)*2 + SZDIRE*19); if (!lfn) LEAVE_FF((dj).obj.fs, FR_NOT_ENOUGH_CORE); (dj).fn = sfn; (dj).lfn = lfn; (dj).obj.fs->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2 + SZDIRE*19); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } #define FREE_NAMBUF() ff_memfree(lfn) -#define DEF_DIRBUF BYTE *dirb -#define INIT_DIRBUF(fs) { dirb = ff_memalloc(SZDIRE*19); if (!dirb) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); fs->dirbuf = dirb; } -#define FREE_DIRBUF() ff_memfree(dirb) #else -#define DEF_NAMBUF BYTE sfn[12]; WCHAR *lfn -#define INIT_NAMBUF(dj) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF((dj).obj.fs, FR_NOT_ENOUGH_CORE); (dj).fn = sfn; (dj).lfn = lfn; } +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } #define FREE_NAMBUF() ff_memfree(lfn) -#define DEF_DIRBUF -#define INIT_DIRBUF(fs) -#define FREE_DIRBUF() #endif #else @@ -707,10 +719,10 @@ int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */ +#if _FS_REENTRANT /*-----------------------------------------------------------------------*/ /* Request/Release grant to access the volume */ /*-----------------------------------------------------------------------*/ -#if _FS_REENTRANT static int lock_fs ( FATFS* fs /* File system object */ @@ -730,15 +742,15 @@ void unlock_fs ( ff_rel_grant(fs->sobj); } } + #endif - +#if _FS_LOCK != 0 /*-----------------------------------------------------------------------*/ /* File lock control functions */ /*-----------------------------------------------------------------------*/ -#if _FS_LOCK != 0 static FRESULT chk_lock ( /* Check if the file can be accessed */ @@ -843,8 +855,8 @@ void clear_lock ( /* Clear lock entries of the volume */ if (Files[i].fs == fs) Files[i].fs = 0; } } -#endif +#endif /* _FS_LOCK != 0 */ @@ -908,10 +920,11 @@ FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */ +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Synchronize file system and strage device */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY + static FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ FATFS* fs /* File system object */ @@ -942,8 +955,8 @@ FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ return res; } -#endif +#endif @@ -993,17 +1006,17 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluste wc = fs->win[bc++ % SS(fs)]; if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; wc |= fs->win[bc % SS(fs)] << 8; - val = clst & 1 ? wc >> 4 : (wc & 0xFFF); + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); break; case FS_FAT16 : if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; - val = ld_word(&fs->win[clst * 2 % SS(fs)]); + val = ld_word(fs->win + clst * 2 % SS(fs)); break; case FS_FAT32 : if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; - val = ld_dword(&fs->win[clst * 4 % SS(fs)]) & 0x0FFFFFFF; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; break; #if _FS_EXFAT case FS_EXFAT : @@ -1023,11 +1036,11 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluste } if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; - val = ld_dword(&fs->win[clst * 4 % SS(fs)]) & 0x7FFFFFFF; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; break; } } - /* Go default */ + /* go next */ #endif default: val = 1; /* Internal error */ @@ -1040,14 +1053,14 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluste +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* FAT access - Change value of a FAT entry */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ - FATFS* fs, /* Corresponding object */ + FATFS* fs, /* Corresponding file system object */ DWORD clst, /* FAT index number (cluster number) to be changed */ DWORD val /* New value to be set to the entry */ ) @@ -1063,12 +1076,12 @@ FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ bc = (UINT)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 = fs->win + bc++ % SS(fs); *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; fs->wflag = 1; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; - p = &fs->win[bc % SS(fs)]; + p = fs->win + bc % SS(fs); *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); fs->wflag = 1; break; @@ -1076,7 +1089,7 @@ FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ case FS_FAT16 : /* WORD aligned items */ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); if (res != FR_OK) break; - st_word(&fs->win[clst * 2 % SS(fs)], (WORD)val); + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); fs->wflag = 1; break; @@ -1087,15 +1100,16 @@ FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); if (res != FR_OK) break; if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { - val = (val & 0x0FFFFFFF) | (ld_dword(&fs->win[clst * 4 % SS(fs)]) & 0xF0000000); + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); } - st_dword(&fs->win[clst * 4 % SS(fs)], val); + st_dword(fs->win + clst * 4 % SS(fs), val); fs->wflag = 1; break; } } return res; } + #endif /* !_FS_READONLY */ @@ -1109,6 +1123,7 @@ FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ /*---------------------------------------------*/ /* exFAT: Find a contiguous free cluster block */ /*---------------------------------------------*/ + static DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Disk error */ FATFS* fs, /* File system object */ @@ -1125,8 +1140,8 @@ DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Dis if (clst >= fs->n_fatent - 2) clst = 0; scl = val = clst; ctr = 0; for (;;) { - if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; - i = val / 8 & (SS(fs) - 1); bm = 1 << (val % 8); + if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */ + i = val / 8 % SS(fs); bm = 1 << (val % 8); do { do { bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ @@ -1145,9 +1160,11 @@ DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Dis } } + /*------------------------------------*/ /* exFAT: Set/Clear a block of bitmap */ /*------------------------------------*/ + static FRESULT change_bitmap ( FATFS* fs, /* File system object */ @@ -1162,8 +1179,8 @@ FRESULT change_bitmap ( clst -= 2; /* The first bit corresponds to cluster #2 */ - sect = fs->database + clst / 8 / SS(fs); /* Sector address */ - i = clst / 8 & (SS(fs) - 1); /* Byte offset in the sector */ + sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ bm = 1 << (clst % 8); /* Bit mask in the byte */ for (;;) { if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; @@ -1176,6 +1193,7 @@ FRESULT change_bitmap ( } while (bm <<= 1); /* Next bit */ bm = 1; } while (++i < SS(fs)); /* Next byte */ + i = 0; } } @@ -1183,6 +1201,7 @@ FRESULT change_bitmap ( /*---------------------------------------------*/ /* Complement contiguous part of the FAT chain */ /*---------------------------------------------*/ + static FRESULT fill_fat_chain ( _FDID* obj /* Pointer to the corresponding object */ @@ -1191,7 +1210,7 @@ FRESULT fill_fat_chain ( FRESULT res; DWORD cl, n; - if (obj->stat == 3) { /* Has the object got fragmented? */ + if (obj->stat == 3) { /* Has the object been changed 'fragmented'? */ for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ res = put_fat(obj->fs, cl, cl + 1); if (res != FR_OK) return res; @@ -1201,14 +1220,14 @@ FRESULT fill_fat_chain ( return FR_OK; } -#endif +#endif /* _FS_EXFAT && !_FS_READONLY */ +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* FAT handling - Remove a cluster chain */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ _FDID* obj, /* Corresponding object */ @@ -1244,7 +1263,7 @@ FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ if (res != FR_OK) return res; } - if (fs->free_clst != 0xFFFFFFFF) { /* Update FSINFO */ + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ fs->free_clst++; fs->fsi_flag |= 1; } @@ -1282,7 +1301,6 @@ FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ #endif return FR_OK; } -#endif @@ -1290,7 +1308,6 @@ FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ /*-----------------------------------------------------------------------*/ /* FAT handling - Stretch a chain or Create a new chain */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ _FDID* obj, /* Corresponding object */ @@ -1303,7 +1320,7 @@ DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk err if (clst == 0) { /* Create a new chain */ - scl = fs->last_clst; /* Get suggested cluster to start at */ + scl = fs->last_clst; /* Get suggested cluster to start from */ if (scl == 0 || scl >= fs->n_fatent) scl = 1; } else { /* Stretch current chain */ @@ -1315,7 +1332,7 @@ DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk err } #if _FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ @@ -1331,7 +1348,7 @@ DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk err } } else #endif - { /* At the FAT12/16/32 */ + { /* On the FAT12/16/32 volume */ ncl = scl; /* Start cluster */ for (;;) { ncl++; /* Next cluster */ @@ -1365,16 +1382,17 @@ DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk err return ncl; /* Return new cluster number or error status */ } + #endif /* !_FS_READONLY */ +#if _USE_FASTSEEK /*-----------------------------------------------------------------------*/ /* FAT handling - Convert offset into cluster with link map table */ /*-----------------------------------------------------------------------*/ -#if _USE_FASTSEEK static DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ FIL* fp, /* Pointer to the file object */ @@ -1395,10 +1413,12 @@ DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ } return cl + *tbl; /* Return the cluster number */ } + #endif /* _USE_FASTSEEK */ + /*-----------------------------------------------------------------------*/ /* Directory handling - Set directory index */ /*-----------------------------------------------------------------------*/ @@ -1509,7 +1529,7 @@ FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Cou } } dp->dptr = ofs; /* Current entry */ - dp->dir = &fs->win[ofs % SS(fs)]; /* Pointer to the entry in the win[] */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ return FR_OK; } @@ -1517,11 +1537,11 @@ FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Cou +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Directory handling - Reserve a block of directory entries */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to the directory object */ @@ -1540,7 +1560,7 @@ FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ res = move_window(fs, dp->sect); if (res != FR_OK) break; #if _FS_EXFAT - if (fs->fs_type == FS_EXFAT ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { #else if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { #endif @@ -1555,7 +1575,8 @@ FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ return res; } -#endif + +#endif /* !_FS_READONLY */ @@ -1598,14 +1619,14 @@ void st_clust ( - +#if _USE_LFN != 0 /*------------------------------------------------------------------------*/ /* FAT-LFN: LFN handling */ /*------------------------------------------------------------------------*/ -#if _USE_LFN != 0 static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ + /*--------------------------------------------------------*/ /* FAT-LFN: Compare a part of file name with an LFN entry */ /*--------------------------------------------------------*/ @@ -1641,8 +1662,7 @@ int cmp_lfn ( /* 1:matched, 0:not matched */ } - -#if _FS_MINIMIZE <= 1 || _FS_EXFAT +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT /*-----------------------------------------------------*/ /* FAT-LFN: Pick a part of file name from an LFN entry */ /*-----------------------------------------------------*/ @@ -1679,6 +1699,7 @@ int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ } #endif + #if !_FS_READONLY /*-----------------------------------------*/ /* FAT-LFN: Create an entry of LFN entries */ @@ -1711,15 +1732,16 @@ void put_lfn ( dir[LDIR_Ord] = ord; /* Set the LFN order */ } -#endif -#endif +#endif /* !_FS_READONLY */ +#endif /* _USE_LFN != 0 */ +#if _USE_LFN != 0 && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* FAT-LFN: Create a Numbered SFN */ /*-----------------------------------------------------------------------*/ -#if _USE_LFN != 0 && !_FS_READONLY + static void gen_numname ( BYTE* dst, /* Pointer to the buffer to store numbered SFN */ @@ -1752,7 +1774,7 @@ void gen_numname ( /* itoa (hexdecimal) */ i = 7; do { - c = (seq % 16) + '0'; + c = (BYTE)((seq % 16) + '0'); if (c > '9') c += 7; ns[i--] = c; seq /= 16; @@ -1770,14 +1792,15 @@ void gen_numname ( dst[j++] = (i < 8) ? ns[i++] : ' '; } while (j < 8); } -#endif +#endif /* _USE_LFN != 0 && !_FS_READONLY */ +#if _USE_LFN != 0 /*-----------------------------------------------------------------------*/ /* FAT-LFN: Calculate checksum of an SFN entry */ /*-----------------------------------------------------------------------*/ -#if _USE_LFN != 0 + static BYTE sum_sfn ( const BYTE* dir /* Pointer to the SFN entry */ @@ -1789,14 +1812,14 @@ BYTE sum_sfn ( do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); return sum; } -#endif +#endif /* _USE_LFN != 0 */ #if _FS_EXFAT /*-----------------------------------------------------------------------*/ -/* exFAT: Directory handling - Load/Store a block of directory entries */ +/* exFAT: Checksum */ /*-----------------------------------------------------------------------*/ static @@ -1839,17 +1862,35 @@ WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ } +#if !_FS_READONLY && _USE_MKFS +static +DWORD xsum32 ( + BYTE dat, /* Data to be sumed */ + DWORD sum /* Previous value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 /*------------------------------------------------------*/ /* exFAT: Get object information from a directory block */ /*------------------------------------------------------*/ + static void get_xdir_info ( BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ FILINFO* fno /* Buffer to store the extracted file information */ ) { - UINT di, si, nc; + UINT di, si; WCHAR w; +#if !_LFN_UNICODE + UINT nc; +#endif /* Get file name */ #if _LFN_UNICODE @@ -1885,10 +1926,13 @@ void get_xdir_info ( fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ } +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + /*-----------------------------------*/ /* exFAT: Get a directry entry block */ /*-----------------------------------*/ + static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ @@ -1903,7 +1947,7 @@ FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; - mem_cpy(&dirb[0], dp->dir, SZDIRE); + mem_cpy(dirb, dp->dir, SZDIRE); nent = dirb[XDIR_NumSec] + 1; /* Load C0 entry */ @@ -1974,16 +2018,17 @@ FRESULT store_xdir ( { FRESULT res; UINT nent; - WORD sum; BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ /* Create set sum */ - sum = xdir_sum(dirb); - st_word(dirb + XDIR_SetSum, sum); + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); nent = dirb[XDIR_NumSec] + 1; + /* Store the set of directory to the volume */ res = dir_sdi(dp, dp->blk_ofs); - while (res == FR_OK && (res = move_window(dp->obj.fs, dp->sect)) == FR_OK) { + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; mem_cpy(dp->dir, dirb, SZDIRE); dp->obj.fs->wflag = 1; if (--nent == 0) break; @@ -1994,9 +2039,11 @@ FRESULT store_xdir ( } + /*-------------------------------------------*/ /* exFAT: Create a new directory enrty block */ /*-------------------------------------------*/ + static void create_xdir ( BYTE* dirb, /* Pointer to the direcotry entry block buffer */ @@ -2006,14 +2053,12 @@ void create_xdir ( UINT i; BYTE nb, nc; WCHAR chr; - WORD hash; mem_set(dirb, 0, 2 * SZDIRE); /* Initialize 85+C0 entry */ dirb[XDIR_Type] = 0x85; dirb[XDIR_Type + SZDIRE] = 0xC0; - hash = xname_sum(lfn); - st_word(dirb + XDIR_NameHash, hash); /* Set name hash */ + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ i = SZDIRE * 2; /* C1 offset */ nc = 0; nb = 1; chr = 1; @@ -2029,15 +2074,17 @@ void create_xdir ( dirb[XDIR_NumName] = nc; /* Set name length */ dirb[XDIR_NumSec] = nb; /* Set number of C0+C1s */ } -#endif -#endif + +#endif /* !_FS_READONLY */ +#endif /* _FS_EXFAT */ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT /*-----------------------------------------------------------------------*/ /* Read an object from the directory */ /*-----------------------------------------------------------------------*/ -#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT + static FRESULT dir_read ( DIR* dp, /* Pointer to the directory object */ @@ -2057,12 +2104,12 @@ FRESULT dir_read ( c = dp->dir[DIR_Name]; /* Test for the entry type */ if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of the directory */ #if _FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ if (_USE_LABEL && vol) { if (c == 0x83) break; /* Volume label entry? */ } else { - if (c == 0x85) { /* Start of the entry block? */ - dp->blk_ofs = dp->dptr; /* Set location of block */ + if (c == 0x85) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ res = load_xdir(dp); /* Load the entry block */ if (res == FR_OK) { dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ @@ -2072,7 +2119,7 @@ FRESULT dir_read ( } } else #endif - { /* At the FAT12/16/32 */ + { /* On the FAT12/16/32 volume */ dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ #if _USE_LFN != 0 /* LFN configuration */ if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ @@ -2081,11 +2128,11 @@ FRESULT dir_read ( if (a == AM_LFN) { /* An LFN entry is found */ if (c & LLEF) { /* Is it start of an LFN sequence? */ sum = dp->dir[LDIR_Chksum]; - c &= ~LLEF; ord = c; + c &= (BYTE)~LLEF; ord = c; dp->blk_ofs = dp->dptr; } /* Check LFN validity and capture it */ - ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(dp->lfn, dp->dir)) ? ord - 1 : 0xFF; + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } else { /* An SFN entry is found */ if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ @@ -2106,6 +2153,7 @@ FRESULT dir_read ( if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ return res; } + #endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ @@ -2129,23 +2177,23 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ res = dir_sdi(dp, 0); /* Rewind directory object */ if (res != FR_OK) return res; #if _FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ BYTE nc; UINT di, ni; - WORD hash = xname_sum(dp->lfn); /* Hash value of the name to find */ + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip the comparison if hash value mismatched */ for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ if ((di % SZDIRE) == 0) di += 2; - if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(dp->lfn[ni])) break; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; } - if (nc == 0 && !dp->lfn[ni]) break; /* Name matched? */ + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ } return res; } #endif - /* At the FAT12/16/32 */ + /* On the FAT12/16/32 volume */ #if _USE_LFN != 0 ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ #endif @@ -2160,14 +2208,14 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } else { if (a == AM_LFN) { /* An LFN entry is found */ - if (dp->lfn) { + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { if (c & LLEF) { /* Is it start of LFN sequence? */ sum = dp->dir[LDIR_Chksum]; - c &= ~LLEF; ord = c; /* LFN start order */ + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ dp->blk_ofs = dp->dptr; /* Start offset of LFN */ } /* Check validity of the LFN entry and compare it with given name */ - ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dp->dir)) ? ord - 1 : 0xFF; + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; } } else { /* An SFN entry is found */ if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ @@ -2188,10 +2236,11 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ +#if !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Register an object to the directory */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY + static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ DIR* dp /* Target directory with object name to be created */ @@ -2201,16 +2250,14 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S FATFS *fs = dp->obj.fs; #if _USE_LFN != 0 /* LFN configuration */ UINT n, nlen, nent; - BYTE sn[12], *fn, sum; - WCHAR *lfn; + BYTE sn[12], sum; - fn = dp->fn; lfn = dp->lfn; - if (fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ - for (nlen = 0; lfn[nlen]; nlen++) ; /* Get lfn length */ + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ #if _FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ DIR dj; nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ @@ -2218,7 +2265,7 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S if (res != FR_OK) return res; dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */ - if (dp->obj.stat & 4) { /* Has the sub-directory been stretched? */ + if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */ dp->obj.stat &= 3; dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */ res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */ @@ -2232,22 +2279,22 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S if (res != FR_OK) return res; } - create_xdir(fs->dirbuf, lfn); /* Create on-memory directory block to be written later */ + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ return FR_OK; } #endif - /* At the FAT12/16/32 */ - mem_cpy(sn, fn, 12); + /* On the FAT12/16/32 volume */ + mem_cpy(sn, dp->fn, 12); if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ - fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ for (n = 1; n < 100; n++) { - gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ res = dir_find(dp); /* 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[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn; + dp->fn[NSFLAG] = sn[NSFLAG]; } /* Create an SFN with/without LFNs. */ @@ -2260,7 +2307,7 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S do { /* Store LFN entries in bottom first */ res = move_window(fs, dp->sect); if (res != FR_OK) break; - put_lfn(dp->lfn, dp->dir, (BYTE)nent, sum); + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); fs->wflag = 1; res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK && --nent); @@ -2287,15 +2334,16 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S return res; } + #endif /* !_FS_READONLY */ - +#if !_FS_READONLY && _FS_MINIMIZE == 0 /*-----------------------------------------------------------------------*/ /* Remove an object from the directory */ /*-----------------------------------------------------------------------*/ -#if !_FS_READONLY && !_FS_MINIMIZE + static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ DIR* dp /* Directory object pointing the entry to be removed */ @@ -2306,15 +2354,15 @@ FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ #if _USE_LFN != 0 /* LFN configuration */ DWORD last = dp->dptr; - res = dp->blk_ofs == 0xFFFFFFFF ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ if (res == FR_OK) { do { res = move_window(fs, dp->sect); if (res != FR_OK) break; /* Mark an entry 'deleted' */ - if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* At the exFAT */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ dp->dir[XDIR_Type] &= 0x7F; - } else { /* At the FAT12/16/32 */ + } else { /* On the FAT12/16/32 volume */ dp->dir[DIR_Name] = DDEM; } fs->wflag = 1; @@ -2334,15 +2382,16 @@ FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ return res; } -#endif /* !_FS_READONLY */ - + +#endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 /*-----------------------------------------------------------------------*/ /* Get file information from directory entry */ /*-----------------------------------------------------------------------*/ -#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 + static void get_fileinfo ( /* No return code */ DIR* dp, /* Pointer to the directory object */ @@ -2351,9 +2400,10 @@ void get_fileinfo ( /* No return code */ { UINT i, j; TCHAR c; + DWORD tm; #if _USE_LFN != 0 - WCHAR w, *lfn; - WCHAR lfv; + WCHAR w, lfv; + FATFS *fs = dp->obj.fs; #endif @@ -2362,15 +2412,15 @@ void get_fileinfo ( /* No return code */ #if _USE_LFN != 0 /* LFN configuration */ #if _FS_EXFAT - if (dp->obj.fs->fs_type == FS_EXFAT) { /* At the exFAT */ - get_xdir_info(dp->obj.fs->dirbuf, fno); + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xdir_info(fs->dirbuf, fno); return; } else #endif - { /* At the FAT12/16/32 */ + { /* On the FAT12/16/32 volume */ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ - i = 0; lfn = dp->lfn; - while ((w = *lfn++) != 0) { /* Get an LFN character */ + i = j = 0; + while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ #if !_LFN_UNICODE w = ff_convert(w, 0); /* Unicode -> OEM */ if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */ @@ -2379,7 +2429,7 @@ void get_fileinfo ( /* No return code */ } #endif if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ - fno->fname[i++] = (char)w; + fno->fname[i++] = (TCHAR)w; } fno->fname[i] = 0; /* Terminate the LFN */ } @@ -2431,18 +2481,19 @@ void get_fileinfo ( /* No return code */ fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ - fno->fdate = ld_word(dp->dir + DIR_WrtDate); /* Date */ - fno->ftime = ld_word(dp->dir + DIR_WrtTime); /* Time */ + tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ + fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); } + #endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ - +#if _USE_FIND && _FS_MINIMIZE <= 1 /*-----------------------------------------------------------------------*/ /* Pattern matching */ /*-----------------------------------------------------------------------*/ -#if _USE_FIND && _FS_MINIMIZE <= 1 + static WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ @@ -2493,7 +2544,7 @@ int pattern_matching ( /* 0:not matched, 1:matched */ do { /* Analyze the wildcard chars */ if (*pp++ == '?') nm++; else nx = 1; } while (*pp == '?' || *pp == '*'); - if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recursions upto number of wildcard blocks in the pattern) */ + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ nc = *np; break; /* Branch mismatched */ } pc = get_achar(&pp); /* Get a pattern char */ @@ -2506,8 +2557,8 @@ int pattern_matching ( /* 0:not matched, 1:matched */ return 0; } -#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ @@ -2528,11 +2579,12 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ const TCHAR *p; /* Create LFN in Unicode */ - p = *path; lfn = dp->lfn; si = di = 0; + p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; for (;;) { w = p[si++]; /* Get a character */ - if (w < ' ' || w == '/' || w == '\\') { /* Break on end of segment */ - while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator */ + if (w < ' ') break; /* Break if end of the path name */ + if (w == '/' || w == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ break; } if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ @@ -2550,7 +2602,7 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ lfn[di++] = w; /* Store the Unicode character */ } *path = &p[si]; /* Return pointer to the next segment */ - cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ #if _FS_RPATH != 0 if ((di == 1 && lfn[di - 1] == '.') || (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ @@ -2575,7 +2627,7 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ if (si) cf |= NS_LOSS | NS_LFN; while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ - b = i = 0; ni = 8; + i = b = 0; ni = 8; for (;;) { w = lfn[si++]; /* Get an LFN character */ if (!w) break; /* Break on end of the LFN */ @@ -2638,15 +2690,15 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ return FR_OK; -#else /* Non-LFN configuration */ - BYTE b, c, d, *sfn; +#else /* _USE_LFN != 0 : Non-LFN configuration */ + BYTE c, d, *sfn; UINT ni, si, i; const char *p; /* Create file name in directory form */ p = *path; sfn = dp->fn; mem_set(sfn, ' ', 11); - si = i = b = 0; ni = 8; + si = i = 0; ni = 8; #if _FS_RPATH != 0 if (p[si] == '.') { /* Is this a dot entry? */ for (;;) { @@ -2655,29 +2707,29 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ sfn[i++] = c; } if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; - *path = &p[si]; /* Return pointer to the next segment */ - sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ return FR_OK; } #endif for (;;) { c = (BYTE)p[si++]; - if (c <= ' ' || c == '/' || c == '\\') { /* Break on end of segment */ - while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator */ + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ break; } - if (c == '.' || i >= ni) { - if (ni != 8 || c != '.') return FR_INVALID_NAME; - i = 8; ni = 11; - b <<= 2; continue; + if (c == '.' || i >= ni) { /* End of body or over size? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ + i = 8; ni = 11; /* Goto extension */ + continue; } if (c >= 0x80) { /* Extended character? */ - b |= 3; /* Eliminate NT flag */ #ifdef _EXCVT c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ #else #if !_DF1S - return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */ + return FR_INVALID_NAME; /* Reject extended characters (ASCII only cfg) */ #endif #endif } @@ -2688,30 +2740,18 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ sfn[i++] = d; } else { /* SBC */ if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ - if (IsUpper(c)) { /* ASCII large capital? */ - b |= 2; - } else { - if (IsLower(c)) { /* ASCII small capital? */ - b |= 1; c -= 0x20; - } - } + if (IsLower(c)) c -= 0x20; /* To upper */ 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 */ - + *path = p + si; /* Return pointer to the next segment */ if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ - if (sfn[0] == DDEM) sfn[0] = RDDEM; /* When first character collides with DDEM, replace it with RDDEM */ - 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[NSFLAG] = c; /* Store NT flag, File name is created */ + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ return FR_OK; -#endif +#endif /* _USE_LFN != 0 */ } @@ -2794,7 +2834,7 @@ FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ } else #endif { - obj->sclust = ld_clust(fs, &fs->win[dp->dptr % SS(fs)]); /* Open next directory */ + obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ } } } @@ -2873,18 +2913,20 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ static BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ FATFS* fs, /* File system object */ - DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ + DWORD sect /* Sector# (lba) to check if it is an FAT-VBR or not */ ) { fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ - if (ld_word(&fs->win[BS_55AA]) != 0xAA55) return 3; /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ - if ((ld_dword(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ - if ((ld_dword(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ + if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { + if ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ + if (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0; /* Check "FAT3" string */ + } #if _FS_EXFAT - if (!mem_cmp(&fs->win[BS_OEMName], "EXFAT ", 8)) return 1; + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; #endif return 2; } @@ -2912,7 +2954,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ UINT i; - /* Get logical drive number from the path name */ + /* Get logical drive number */ *rfs = 0; vol = get_ldnumber(path); if (vol < 0) return FR_INVALID_DRIVE; @@ -2924,7 +2966,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ ENTER_FF(fs); /* Lock the volume */ *rfs = fs; /* Return pointer to the file system object */ - mode &= ~FA_READ; /* Desired access mode, write access or not */ + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ if (fs->fs_type) { /* If the volume has been mounted */ stat = disk_status(fs->drv); if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ @@ -2948,16 +2990,16 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ return FR_WRITE_PROTECTED; } #if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ - if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK - || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR; + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; #endif /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ bsect = 0; - fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */ - if (fmt == 2 || (fmt < 2 && LD2PT(vol))) { /* Not an FAT boot sector or forced partition number */ + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ for (i = 0; i < 4; i++) { /* Get partition offset */ - pt = fs->win + MBR_Table + i * SZ_PTE; - br[i] = pt[4] ? ld_dword(&pt[8]) : 0; + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; } i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ if (i) i--; @@ -2975,7 +3017,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ if (fmt == 1) { QWORD maxlba; - for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && !fs->win[i]; i++) ; /* Check zero filler */ + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */ @@ -2983,26 +3025,26 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ return FR_NO_FILESYSTEM; - maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Number of sectors on the volume */ + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ - if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Must be 1) */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ fs->n_fatent = nclst + 2; - if (fs->n_fatent >= 0x80000000) return FR_NO_FILESYSTEM; /* (Must be <= 0x7FFFFFFF) */ /* Boundaries and Limits */ fs->volbase = bsect; fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); - if (maxlba < fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); /* Check if bitmap location is in assumption (at the first cluster) */ @@ -3013,13 +3055,10 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ if (i == SS(fs)) return FR_NO_FILESYSTEM; #if !_FS_READONLY fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ -#endif -#if _USE_LFN == 1 - fs->dirbuf = DirBuf; /* Static directory block working buuffer */ #endif fmt = FS_EXFAT; /* FAT sub-type */ } else -#endif +#endif /* _FS_EXFAT */ { if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ @@ -3048,9 +3087,9 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ - fmt = FS_FAT12; - if (nclst >= MIN_FAT16) fmt = FS_FAT16; - if (nclst >= MIN_FAT32) fmt = FS_FAT32; + fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; /* Boundaries and Limits */ fs->n_fatent = nclst + 2; /* Number of FAT entries */ @@ -3071,10 +3110,8 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ #if !_FS_READONLY - /* Initialize cluster allocation information */ - fs->last_clst = fs->free_clst = 0xFFFFFFFF; - /* Get FSINFO if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ fs->fsi_flag = 0x80; #if (_FS_NOFSINFO & 3) != 3 if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */ @@ -3094,12 +3131,18 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ #endif } } -#endif -#endif +#endif /* (_FS_NOFSINFO & 3) != 3 */ +#endif /* !_FS_READONLY */ } fs->fs_type = fmt; /* FAT sub-type */ fs->id = ++Fsid; /* File system mount ID */ +#if _USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if _FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block working buuffer */ +#endif +#endif #if _FS_RPATH != 0 fs->cdir = 0; /* Initialize current directory */ #endif @@ -3118,15 +3161,14 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ - void* dfp, /* Pointer to the FIL/DIR object to check validity */ + _FDID* obj, /* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */ FATFS** fs /* Pointer to pointer to the owner file system object to return */ ) { - _FDID *obj = (_FDID*)dfp; /* Assuming .obj in the FIL/DIR is the first member */ FRESULT res; - if (!dfp || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) { + if (!obj || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) { *fs = 0; /* The object is invalid */ res = FR_INVALID_OBJECT; } else { @@ -3164,6 +3206,7 @@ FRESULT f_mount ( const TCHAR *rp = path; + /* Get logical drive number */ vol = get_ldnumber(&rp); if (vol < 0) return FR_INVALID_DRIVE; cfs = FatFs[vol]; /* Pointer to fs object */ @@ -3209,20 +3252,20 @@ FRESULT f_open ( DIR dj; FATFS *fs; #if !_FS_READONLY - DWORD dw, cl; + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; #endif - DEF_NAMBUF; + DEF_NAMBUF if (!fp) return FR_INVALID_OBJECT; - fp->obj.fs = 0; /* Clear file object */ - /* Get logical drive number */ - mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + /* Get logical drive */ + mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; res = find_volume(&path, &fs, mode); if (res == FR_OK) { dj.obj.fs = fs; - INIT_NAMBUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ #if !_FS_READONLY /* R/W configuration */ if (res == FR_OK) { @@ -3282,7 +3325,7 @@ FRESULT f_open ( { /* Clean directory info */ st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */ - st_dword(dj.dir + DIR_WrtTime, dw); /* Set modified time */ + st_dword(dj.dir + DIR_ModTime, dw); /* Set modified time */ dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ cl = ld_clust(fs, dj.dir); /* Get cluster chain */ st_clust(fs, dj.dir, 0); /* Reset file allocation info */ @@ -3313,7 +3356,7 @@ FRESULT f_open ( } if (res == FR_OK) { if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ - mode |= _FA_MODIFIED; + mode |= FA_MODIFIED; fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ fp->dir_ptr = dj.dir; #if _FS_LOCK != 0 @@ -3349,20 +3392,48 @@ FRESULT f_open ( fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); } #if _USE_FASTSEEK - fp->cltbl = 0; /* Normal seek mode */ + fp->cltbl = 0; /* Disable fast seek mode */ #endif - fp->err = 0; /* Clear error flag */ - fp->fptr = 0; /* Set file pointer */ - fp->sect = 0; /* Invalidate current data sector */ - fp->flag = mode; /* File access mode */ fp->obj.fs = fs; /* Validate the file object */ fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !_FS_READONLY +#if !_FS_TINY + mem_set(fp->buf, 0, _MAX_SS); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clust2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !_FS_TINY + if (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } +#endif } FREE_NAMBUF(); } - LEAVE_FF(dj.obj.fs, res); + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); } @@ -3388,7 +3459,7 @@ FRESULT f_read ( *br = 0; /* Clear read byte counter */ - res = validate(fp, &fs); + res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ remain = fp->obj.objsize - fp->fptr; @@ -3396,7 +3467,7 @@ FRESULT f_read ( for ( ; btr; /* Repeat until all data read */ rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { - if ((fp->fptr % SS(fs)) == 0) { /* On the sector boundary? */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ @@ -3423,16 +3494,14 @@ FRESULT f_read ( if (csect + cc > fs->csize) { /* Clip at cluster boundary */ cc = fs->csize - csect; } - if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) { - ABORT(fs, FR_DISK_ERR); - } + if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(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 (fs->wflag && fs->winsect - sect < cc) { mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); } #else - if ((fp->flag & _FA_DIRTY) && fp->sect - sect < cc) { + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); } #endif @@ -3443,29 +3512,23 @@ FRESULT f_read ( #if !_FS_TINY if (fp->sect != sect) { /* Load data sector if not in cache */ #if !_FS_READONLY - if (fp->flag & _FA_DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { - ABORT(fs, FR_DISK_ERR); - } - fp->flag &= ~_FA_DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif - if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { /* Fill sector cache */ - ABORT(fs, FR_DISK_ERR); - } + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ } #endif fp->sect = sect; } - rcnt = SS(fs) - ((UINT)fp->fptr % SS(fs)); /* Get partial sector data from sector buffer */ - if (rcnt > btr) rcnt = btr; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ #if _FS_TINY - if (move_window(fs, fp->sect) != FR_OK) { /* Move sector window */ - ABORT(fs, FR_DISK_ERR); - } - mem_cpy(rbuff, &fs->win[fp->fptr % SS(fs)], rcnt); /* Pick partial sector */ + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #else - mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fs)], rcnt); /* Pick partial sector */ + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #endif } @@ -3495,19 +3558,18 @@ FRESULT f_write ( *bw = 0; /* Clear write byte counter */ - res = validate(fp, &fs); + res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ - /* Check fptr wrap-around (file size cannot exceed the limit on each FAT specs) */ - if ((_FS_EXFAT && fs->fs_type == FS_EXFAT && fp->fptr + btw < fp->fptr) - || (DWORD)fp->fptr + btw < (DWORD)fp->fptr) { + /* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */ + if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); } for ( ; btw; /* Repeat until all data written */ wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) { - if ((fp->fptr % SS(fs)) == 0) { /* On the sector boundary? */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ @@ -3532,15 +3594,11 @@ FRESULT f_write ( if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ } #if _FS_TINY - if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) { /* Write-back sector cache */ - ABORT(fs, FR_DISK_ERR); - } + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ #else - if (fp->flag & _FA_DIRTY) { /* Write-back sector cache */ - if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { - ABORT(fs, FR_DISK_ERR); - } - fp->flag &= ~_FA_DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif sect = clust2sect(fs, fp->clust); /* Get current sector */ @@ -3551,9 +3609,7 @@ FRESULT f_write ( if (csect + cc > fs->csize) { /* Clip at cluster boundary */ cc = fs->csize - csect; } - if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) { - ABORT(fs, FR_DISK_ERR); - } + if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); #if _FS_MINIMIZE <= 2 #if _FS_TINY if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ @@ -3563,7 +3619,7 @@ FRESULT f_write ( #else if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); - fp->flag &= ~_FA_DIRTY; + fp->flag &= (BYTE)~FA_DIRTY; } #endif #endif @@ -3571,35 +3627,32 @@ FRESULT f_write ( continue; } #if _FS_TINY - if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling at growing edge */ + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); fs->winsect = sect; } #else - if (fp->sect != sect) { /* Fill sector cache with file data */ - if (fp->fptr < fp->obj.objsize && - disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { - ABORT(fs, FR_DISK_ERR); - } + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); } #endif fp->sect = sect; } - wcnt = SS(fs) - ((UINT)fp->fptr % SS(fs)); /* Put partial sector into file I/O buffer */ - if (wcnt > btw) wcnt = btw; + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ #if _FS_TINY - if (move_window(fs, fp->sect) != FR_OK) { /* Move sector window */ - ABORT(fs, FR_DISK_ERR); - } - mem_cpy(&fs->win[fp->fptr % SS(fs)], wbuff, wcnt); /* Fit partial sector */ + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fs->wflag = 1; #else - mem_cpy(&fp->buf[fp->fptr % SS(fs)], wbuff, wcnt); /* Fit partial sector */ - fp->flag |= _FA_DIRTY; + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; #endif } - fp->flag |= _FA_MODIFIED; /* Set file change flag */ + fp->flag |= FA_MODIFIED; /* Set file change flag */ LEAVE_FF(fs, FR_OK); } @@ -3619,18 +3672,18 @@ FRESULT f_sync ( FATFS *fs; DWORD tm; BYTE *dir; - DEF_DIRBUF; +#if _FS_EXFAT + DEF_NAMBUF +#endif - res = validate(fp, &fs); /* Check validity of the object */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res == FR_OK) { - if (fp->flag & _FA_MODIFIED) { /* Is there any change to the file? */ + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ #if !_FS_TINY - if (fp->flag & _FA_DIRTY) { /* Write-back cached data if needed */ - if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { - LEAVE_FF(fs, FR_DISK_ERR); - } - fp->flag &= ~_FA_DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif /* Update the directory entry */ @@ -3641,24 +3694,24 @@ FRESULT f_sync ( if (res == FR_OK) { DIR dj; - INIT_DIRBUF(fs); + INIT_NAMBUF(fs); res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */ if (res == FR_OK) { - fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ - fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); - st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ fs->dirbuf[XDIR_ModTime10] = 0; st_dword(fs->dirbuf + XDIR_AccTime, 0); res = store_xdir(&dj); /* Restore it to the directory */ if (res == FR_OK) { res = sync_fs(fs); - fp->flag &= ~_FA_MODIFIED; + fp->flag &= (BYTE)~FA_MODIFIED; } } - FREE_DIRBUF(); + FREE_NAMBUF(); } } else #endif @@ -3669,11 +3722,11 @@ FRESULT f_sync ( dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */ st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ - st_dword(dir + DIR_WrtTime, tm); /* Update modified time */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ st_word(dir + DIR_LstAccDate, 0); fs->wflag = 1; res = sync_fs(fs); /* Restore it to the directory */ - fp->flag &= ~_FA_MODIFIED; + fp->flag &= (BYTE)~FA_MODIFIED; } } } @@ -3703,7 +3756,7 @@ FRESULT f_close ( if (res == FR_OK) #endif { - res = validate(fp, &fs); /* Lock volume */ + res = validate(&fp->obj, &fs); /* Lock volume */ if (res == FR_OK) { #if _FS_LOCK != 0 res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ @@ -3723,11 +3776,11 @@ FRESULT f_close ( +#if _FS_RPATH >= 1 /*-----------------------------------------------------------------------*/ /* Change Current Directory or Current Drive, Get Current Directory */ /*-----------------------------------------------------------------------*/ -#if _FS_RPATH >= 1 #if _VOLUMES >= 2 FRESULT f_chdrive ( const TCHAR* path /* Drive number */ @@ -3736,10 +3789,11 @@ FRESULT f_chdrive ( int vol; + /* Get logical drive number */ vol = get_ldnumber(&path); if (vol < 0) return FR_INVALID_DRIVE; - CurrVol = (BYTE)vol; + CurrVol = (BYTE)vol; /* Set it as current volume */ return FR_OK; } @@ -3753,13 +3807,13 @@ FRESULT f_chdir ( FRESULT res; DIR dj; FATFS *fs; - DEF_NAMBUF; + DEF_NAMBUF - /* Get logical drive number */ + /* Get logical drive */ res = find_volume(&path, &fs, 0); if (res == FR_OK) { dj.obj.fs = fs; - INIT_NAMBUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the path */ if (res == FR_OK) { /* Follow completed */ if (dj.fn[NSFLAG] & NS_NONAME) { @@ -3810,15 +3864,15 @@ FRESULT f_getcwd ( DWORD ccl; TCHAR *tp; FILINFO fno; - DEF_NAMBUF; + DEF_NAMBUF *buff = 0; - /* Get logical drive number */ + /* Get logical drive */ res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ if (res == FR_OK) { dj.obj.fs = fs; - INIT_NAMBUF(dj); + INIT_NAMBUF(fs); i = len; /* Bottom of buffer (directory stack base) */ if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ @@ -3867,6 +3921,7 @@ FRESULT f_getcwd ( LEAVE_FF(fs, res); } + #endif /* _FS_RPATH >= 2 */ #endif /* _FS_RPATH >= 1 */ @@ -3890,14 +3945,14 @@ FRESULT f_lseek ( DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; #endif - res = validate(fp, &fs); /* Check validity of the object */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ #if _USE_FASTSEEK if (fp->cltbl) { /* Fast seek */ if (ofs == CREATE_LINKMAP) { /* Create CLMT */ tbl = fp->cltbl; tlen = *tbl++; ulen = 2; /* Given table size and required table size */ - cl = fp->obj.sclust; /* Top of the chain */ + cl = fp->obj.sclust; /* Origin of the chain */ if (cl) { do { /* Get a fragment */ @@ -3926,20 +3981,16 @@ FRESULT f_lseek ( fp->clust = clmt_clust(fp, ofs - 1); dsc = clust2sect(fs, fp->clust); if (!dsc) ABORT(fs, FR_INT_ERR); - dsc += (ofs - 1) / SS(fs) & (fs->csize - 1); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ #if !_FS_TINY #if !_FS_READONLY - if (fp->flag & _FA_DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { - ABORT(fp, FR_DISK_ERR); - } - fp->flag &= ~_FA_DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif - if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) { /* Load current sector */ - ABORT(fs, FR_DISK_ERR); - } + if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ #endif fp->sect = dsc; } @@ -3950,19 +4001,19 @@ FRESULT f_lseek ( /* Normal Seek */ { - if (ofs > fp->obj.objsize /* In read-only mode, clip offset with the file size */ -#if !_FS_READONLY - && !(fp->flag & FA_WRITE) +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */ #endif - ) ofs = fp->obj.objsize; - + if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } ifptr = fp->fptr; fp->fptr = nsect = 0; if (ofs) { bcs = (DWORD)fs->csize * SS(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 */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ ofs -= fp->fptr; clst = fp->clust; } else { /* When seek to back cluster, */ @@ -3979,20 +4030,25 @@ FRESULT f_lseek ( } if (clst != 0) { while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; #if !_FS_READONLY if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ - clst = create_chain(&fp->obj, clst); /* Force stretch if in write mode */ - if (clst == 0) { /* When disk gets full, clip file size */ - ofs = bcs; break; + if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; break; } } else #endif + { clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); fp->clust = clst; - fp->fptr += bcs; - ofs -= bcs; } fp->fptr += ofs; if (ofs % SS(fs)) { @@ -4002,28 +4058,22 @@ FRESULT f_lseek ( } } } + if (!_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ #if !_FS_TINY #if !_FS_READONLY - if (fp->flag & _FA_DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { - ABORT(fs, FR_DISK_ERR); - } - fp->flag &= ~_FA_DIRTY; + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; } #endif - if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) { /* Fill sector cache */ - ABORT(fs, FR_DISK_ERR); - } + if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ #endif fp->sect = nsect; } -#if !_FS_READONLY - if (fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ - fp->obj.objsize = fp->fptr; - fp->flag |= _FA_MODIFIED; - } -#endif } LEAVE_FF(fs, res); @@ -4044,17 +4094,17 @@ FRESULT f_opendir ( FRESULT res; FATFS *fs; _FDID *obj; - DEF_NAMBUF; + DEF_NAMBUF if (!dp) return FR_INVALID_OBJECT; - /* Get logical drive number */ + /* Get logical drive */ obj = &dp->obj; res = find_volume(&path, &fs, 0); if (res == FR_OK) { obj->fs = fs; - INIT_NAMBUF(*dp); + INIT_NAMBUF(fs); res = follow_path(dp, path); /* Follow the path to the directory */ if (res == FR_OK) { /* Follow completed */ if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ @@ -4114,7 +4164,7 @@ FRESULT f_closedir ( FATFS *fs; - res = validate(dp, &fs); + res = validate(&dp->obj, &fs); /* Check validity of the file object */ if (res == FR_OK) { #if _FS_LOCK != 0 if (dp->obj.lockid) { /* Decrement sub-directory open counter */ @@ -4146,15 +4196,15 @@ FRESULT f_readdir ( { FRESULT res; FATFS *fs; - DEF_NAMBUF; + DEF_NAMBUF - res = validate(dp, &fs); /* Check validity of the object */ + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ if (res == FR_OK) { if (!fno) { res = dir_sdi(dp, 0); /* Rewind the directory object */ } else { - INIT_NAMBUF(*dp); + INIT_NAMBUF(fs); res = dir_read(dp, 0); /* Read an item */ if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ if (res == FR_OK) { /* A valid entry is found */ @@ -4234,13 +4284,13 @@ FRESULT f_stat ( { FRESULT res; DIR dj; - DEF_NAMBUF; + DEF_NAMBUF - /* Get logical drive number */ + /* Get logical drive */ res = find_volume(&path, &dj.obj.fs, 0); if (res == FR_OK) { - INIT_NAMBUF(dj); + INIT_NAMBUF(dj.obj.fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK) { /* Follow completed */ if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ @@ -4276,7 +4326,7 @@ FRESULT f_getfree ( _FDID obj; - /* Get logical drive number */ + /* Get logical drive */ res = find_volume(&path, &fs, 0); if (res == FR_OK) { *fatfs = fs; /* Return ptr to the fs object */ @@ -4309,7 +4359,7 @@ FRESULT f_getfree ( if (!(bm & 1)) nfree++; bm >>= 1; } - i = (i + 1) & (SS(fs) - 1); + i = (i + 1) % SS(fs); } while (clst); } else #endif @@ -4358,8 +4408,8 @@ FRESULT f_truncate ( DWORD ncl; - res = validate(fp, &fs); /* Check validity of the object */ - if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ if (fp->obj.objsize > fp->fptr) { @@ -4376,13 +4426,13 @@ FRESULT f_truncate ( } } fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */ - fp->flag |= _FA_MODIFIED; + fp->flag |= FA_MODIFIED; #if !_FS_TINY - if (res == FR_OK && (fp->flag & _FA_DIRTY)) { + if (res == FR_OK && (fp->flag & FA_DIRTY)) { if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { res = FR_DISK_ERR; } else { - fp->flag &= ~_FA_DIRTY; + fp->flag &= (BYTE)~FA_DIRTY; } } #endif @@ -4410,14 +4460,14 @@ FRESULT f_unlink ( #if _FS_EXFAT _FDID obj; #endif - DEF_NAMBUF; + DEF_NAMBUF - /* Get logical drive number */ + /* Get logical drive */ res = find_volume(&path, &fs, FA_WRITE); dj.obj.fs = fs; if (res == FR_OK) { - INIT_NAMBUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { res = FR_INVALID_NAME; /* Cannot remove dot entry */ @@ -4504,14 +4554,14 @@ FRESULT f_mkdir ( BYTE *dir; UINT n; DWORD dsc, dcl, pcl, tm; - DEF_NAMBUF; + DEF_NAMBUF - /* Get logical drive number */ + /* Get logical drive */ res = find_volume(&path, &fs, FA_WRITE); dj.obj.fs = fs; if (res == FR_OK) { - INIT_NAMBUF(dj); + INIT_NAMBUF(fs); 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[NSFLAG] & NS_DOT)) { @@ -4534,12 +4584,11 @@ FRESULT f_mkdir ( mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ dir[DIR_Name] = '.'; dir[DIR_Attr] = AM_DIR; - st_dword(dir + DIR_WrtTime, tm); + st_dword(dir + DIR_ModTime, tm); st_clust(fs, dir, dcl); mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; - if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) - pcl = 0; + if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0; st_clust(fs, dir + SZDIRE, pcl); } for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */ @@ -4553,24 +4602,24 @@ FRESULT f_mkdir ( if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ if (res == FR_OK) { #if _FS_EXFAT - if (fs->fs_type == FS_EXFAT) { - st_dword(fs->dirbuf + XDIR_ModTime, tm); - st_dword(fs->dirbuf + XDIR_FstClus, dcl); - st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); - fs->dirbuf[XDIR_GenFlags] = 3; - fs->dirbuf[XDIR_Attr] = AM_DIR; + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag (contiguous) */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ res = store_xdir(&dj); } else #endif { dir = dj.dir; - st_dword(dir + DIR_WrtTime, tm); /* Created time */ + st_dword(dir + DIR_ModTime, tm); /* Created time */ st_clust(fs, dir, dcl); /* Table start cluster */ dir[DIR_Attr] = AM_DIR; /* Attribute */ fs->wflag = 1; } - res = sync_fs(fs); + if (res == FR_OK) res = sync_fs(fs); } else { remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ } @@ -4598,14 +4647,14 @@ FRESULT f_rename ( FATFS *fs; BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir; DWORD dw; - DEF_NAMBUF; + DEF_NAMBUF get_ldnumber(&path_new); /* Ignore drive number of new name */ - res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive number of the old object */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ if (res == FR_OK) { djo.obj.fs = fs; - INIT_NAMBUF(djo); + INIT_NAMBUF(fs); res = follow_path(&djo, path_old); /* Check old object */ if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ #if _FS_LOCK != 0 @@ -4619,10 +4668,12 @@ FRESULT f_rename ( mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ mem_cpy(&djn, &djo, sizeof djo); - res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ - if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */ - if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ - res = dir_register(&djn); /* Register the new entry */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; nh = ld_word(fs->dirbuf + XDIR_NameHash); @@ -4639,7 +4690,9 @@ FRESULT f_rename ( mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */ mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ - if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { @@ -4678,8 +4731,6 @@ FRESULT f_rename ( LEAVE_FF(fs, res); } - - #endif /* !_FS_READONLY */ #endif /* _FS_MINIMIZE == 0 */ #endif /* _FS_MINIMIZE <= 1 */ @@ -4701,13 +4752,13 @@ FRESULT f_chmod ( FRESULT res; DIR dj; FATFS *fs; - DEF_NAMBUF; + DEF_NAMBUF - res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */ + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ dj.obj.fs = fs; if (res == FR_OK) { - INIT_NAMBUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ if (res == FR_OK) { @@ -4722,7 +4773,7 @@ FRESULT f_chmod ( dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ fs->wflag = 1; } - res = sync_fs(fs); + if (res == FR_OK) res = sync_fs(fs); } FREE_NAMBUF(); } @@ -4745,26 +4796,24 @@ FRESULT f_utime ( FRESULT res; DIR dj; FATFS *fs; - DEF_NAMBUF; + DEF_NAMBUF - res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */ + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ dj.obj.fs = fs; if (res == FR_OK) { - INIT_NAMBUF(dj); + INIT_NAMBUF(fs); res = follow_path(&dj, path); /* Follow the file path */ if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ if (res == FR_OK) { #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { - st_word(fs->dirbuf + XDIR_ModTime, fno->ftime); - st_word(fs->dirbuf + XDIR_ModTime + 2, fno->fdate); + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); res = store_xdir(&dj); } else #endif { - st_word(dj.dir + DIR_WrtTime, fno->ftime); - st_word(dj.dir + DIR_WrtDate, fno->fdate); + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); fs->wflag = 1; } if (res == FR_OK) res = sync_fs(fs); @@ -4798,7 +4847,7 @@ FRESULT f_getlabel ( WCHAR w; #endif - /* Get logical drive number */ + /* Get logical drive */ res = find_volume(&path, &fs, 0); /* Get volume label */ @@ -4859,7 +4908,7 @@ FRESULT f_getlabel ( case FS_FAT32: di = BS_VolID32; break; default: di = BS_VolID; } - *vsn = ld_dword(&fs->win[di]); + *vsn = ld_dword(fs->win + di); } } @@ -4886,7 +4935,7 @@ FRESULT f_setlabel ( static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; - /* Get logical drive number */ + /* Get logical drive */ res = find_volume(&label, &fs, FA_WRITE); if (res != FR_OK) LEAVE_FF(fs, res); dj.obj.fs = fs; @@ -4895,7 +4944,7 @@ FRESULT f_setlabel ( for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */ #if _FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* At the exFAT */ + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ w = label[i++]; #if !_LFN_UNICODE @@ -4912,7 +4961,7 @@ FRESULT f_setlabel ( slen = j; } else #endif - { /* At the FAT12/16/32 */ + { /* On the FAT12/16/32 volume */ for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ if (slen) { /* Is there a volume label to be set? */ dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ @@ -4953,7 +5002,7 @@ FRESULT f_setlabel ( res = dir_read(&dj, 1); /* Get volume label entry */ if (res == FR_OK) { if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { - dj.dir[XDIR_NumLabel] = slen / 2; /* Change the volume label */ + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */ mem_cpy(dj.dir + XDIR_Label, dirvn, slen); } else { if (slen) { @@ -4973,7 +5022,7 @@ FRESULT f_setlabel ( mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ - dj.dir[XDIR_NumLabel] = slen / 2; + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); mem_cpy(dj.dir + XDIR_Label, dirvn, slen); } else { dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ @@ -5008,32 +5057,31 @@ FRESULT f_expand ( { FRESULT res; FATFS *fs; - DWORD val, clst, csz, stcl, scl, ncl, tcl; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; - res = validate(fp, &fs); /* Check validity of the object */ - if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); #if _FS_EXFAT if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ #endif - csz = (DWORD)fs->csize * SS(fs); /* Cluster size */ - tcl = (DWORD)(fsz / csz) + ((fsz & (csz - 1)) ? 1 : 0); /* Number of clusters required */ - stcl = fs->last_clst; + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; #if _FS_EXFAT if (fs->fs_type == FS_EXFAT) { scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ - if (scl == 1) res = FR_INT_ERR; if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; if (res == FR_OK) { if (opt) { res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ - fs->last_clst = scl + tcl - 1; + lclst = scl + tcl - 1; } else { - fs->last_clst = scl - 1; /* Set suggested cluster to start next */ + lclst = scl - 1; } } } else @@ -5041,36 +5089,42 @@ FRESULT f_expand ( { scl = clst = stcl; ncl = 0; for (;;) { /* Find a contiguous cluster block */ - val = get_fat(&fp->obj, clst); + n = get_fat(&fp->obj, clst); if (++clst >= fs->n_fatent) clst = 2; - if (val == 1) { res = FR_INT_ERR; break; } - if (val == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } - if (val == 0) { /* Is it a free cluster? */ - if (++ncl == tcl) break; /* Break if a contiguous cluster block was found */ + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ } else { scl = clst; ncl = 0; /* Not a free cluster */ } - if (clst == stcl) { res = FR_DENIED; break; } /* All cluster scanned? */ + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ } if (res == FR_OK) { if (opt) { - for (clst = scl; tcl; clst++, tcl--) { /* Create a cluster chain on the FAT */ - val = (tcl == 1) ? 0xFFFFFFFF : clst + 1; - res = put_fat(fs, clst, val); + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); if (res != FR_OK) break; - fs->last_clst = clst; + lclst = clst; } } else { - fs->last_clst = scl - 1; /* Set suggested cluster to start next */ + lclst = scl - 1; } } } - if (opt && res == FR_OK) { - fp->obj.sclust = scl; /* Update allocation information */ - fp->obj.objsize = fsz; - if (_FS_EXFAT) fp->obj.stat = 2; - fp->flag |= _FA_MODIFIED; + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } } LEAVE_FF(fs, res); @@ -5080,10 +5134,10 @@ FRESULT f_expand ( +#if _USE_FORWARD /*-----------------------------------------------------------------------*/ /* Forward data to the stream directly */ /*-----------------------------------------------------------------------*/ -#if _USE_FORWARD FRESULT f_forward ( FIL* fp, /* Pointer to the file object */ @@ -5097,12 +5151,12 @@ FRESULT f_forward ( DWORD clst, sect; FSIZE_t remain; UINT rcnt, csect; - const BYTE *dbuf; + BYTE *dbuf; *bf = 0; /* Clear transfer byte counter */ - res = validate(fp, &fs); /* Check validity of the object */ - if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ remain = fp->obj.objsize - fp->fptr; @@ -5129,9 +5183,9 @@ FRESULT f_forward ( #else if (fp->sect != sect) { /* Fill sector cache with file data */ #if !_FS_READONLY - if (fp->flag & _FA_DIRTY) { /* Write-back dirty sector cache */ + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); - fp->flag &= ~_FA_DIRTY; + fp->flag &= (BYTE)~FA_DIRTY; } #endif if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); @@ -5153,259 +5207,456 @@ FRESULT f_forward ( #if _USE_MKFS && !_FS_READONLY /*-----------------------------------------------------------------------*/ -/* Create file system on the logical drive */ +/* Create FAT file system on the logical drive */ /*-----------------------------------------------------------------------*/ -#define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */ -#define N_FATS 1 /* Number of FATs (1 or 2) */ - FRESULT f_mkfs ( const TCHAR* path, /* Logical drive number */ - BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ - UINT au /* Size of allocation unit in unit of byte or sector */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit [byte] */ + void* work, /* Pointer to working buffer */ + UINT len /* Size of working buffer */ ) { - 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}; - int vol; - BYTE fmt, md, sys, *tbl, pdrv, part; - DWORD n_clst, vs, n, wsect; + const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; + DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ UINT i; - DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ - DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ - FATFS *fs; + int vol; DSTATUS stat; -#if _USE_TRIM - DWORD eb[2]; +#if _USE_TRIM || _FS_EXFAT + DWORD tbl[3]; #endif /* Check mounted drive and clear work area */ - if (sfd > 1) return FR_INVALID_PARAMETER; - vol = get_ldnumber(&path); /* Get target volume */ + vol = get_ldnumber(&path); /* Get target logical drive */ if (vol < 0) return FR_INVALID_DRIVE; - fs = FatFs[vol]; /* Check if the volume has work area */ - if (!fs) return FR_NOT_ENABLED; - fs->fs_type = 0; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear mounted volume */ pdrv = LD2PD(vol); /* Physical drive */ - part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ - /* Get disk statics */ + /* Check physical drive status */ stat = disk_initialize(pdrv); if (stat & STA_NOINIT) return FR_NOT_READY; if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; -#if _MAX_SS != _MIN_SS /* Get disk sector size */ - if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS) { - return FR_DISK_ERR; - } + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ +#if _MAX_SS != _MIN_SS /* Get sector size of the medium */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = _MAX_SS; #endif - if (_MULTI_PARTITION && part) { + if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in unit of sector */ + + /* Get working buffer */ + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + if (!szb_buf) return FR_MKFS_ABORTED; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (_MULTI_PARTITION && part != 0) { /* Get partition information from partition table in the MBR */ - if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; - if (ld_word(fs->win + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; - tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; - if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ - b_vol = ld_dword(tbl + 8); /* Volume start sector */ - n_vol = ld_dword(tbl + 12); /* Volume size */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ } else { /* Create a single-partition in this function */ - if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) { - return FR_DISK_ERR; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) return FR_MKFS_ABORTED; + sz_vol -= b_vol; /* Volume size */ + } + if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */ + + /* Pre-determine the FAT type */ + do { + if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + fmt = FS_EXFAT; break; + } } - b_vol = (sfd) ? 0 : 63; /* Volume start sector */ - n_vol -= b_vol; /* Volume size */ - } + if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ + fmt = FS_FAT16; + } while (0); - if (au & (au - 1)) au = 0; - if (!au) { /* AU auto selection */ - vs = n_vol / (2000 / (SS(fs) / 512)); - for (i = 0; vs < vst[i]; i++) ; - au = cst[i]; - } - if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */ - if (!au) au = 1; - if (au > 128) au = 128; +#if _FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nb, cl; + WCHAR ch, si; + UINT j, st; + BYTE b; - /* Pre-compute number of clusters and FAT sub-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; + if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (!au) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ + if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ + if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ - /* 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 = (DWORD)N_ROOTDIR * SZDIRE / 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 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ - /* Align data start sector to erase block boundary (for flash memory media) */ - if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &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; - } + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = si = i = j = szb_case = 0; + do { + switch (st) { + case 0: + ch = ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* continue */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + default: + ch = (WCHAR)j; si += j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ + tbl[2] = 1; /* Number of root dir clusters */ - /* Determine number of clusters 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; + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (!nb && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb && i < szb_buf); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, szb_buf); + buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ + buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ + st_dword(buf + SZDIRE * 1 + 20, 2); + st_dword(buf + SZDIRE * 1 + 24, szb_bit); + buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ + st_dword(buf + SZDIRE * 2 + 4, sum); + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); + st_dword(buf + SZDIRE * 2 + 24, szb_case); + sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* File system version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + + } else +#endif /* _FS_EXFAT */ + { /* Create an FAT12/16/32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; + } else { /* FAT12/16 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (!au && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, (UINT)szb_buf); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + } + + /* Initialize root directory (fill with zero) */ + nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); } /* Determine system ID in the partition table */ - if (fmt == FS_FAT32) { - sys = 0x0C; /* FAT32X */ + if (_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ } else { - if (fmt == FS_FAT12 && n_vol < 0x10000) { - sys = 0x01; /* FAT12(<65536) */ + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ } else { - sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */ + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (>=64KS) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */ + } } } - if (_MULTI_PARTITION && part) { + if (_MULTI_PARTITION && part != 0) { /* Update system ID in the partition table */ - tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; - tbl[4] = sys; - if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) { /* Write it to teh MBR */ - return FR_DISK_ERR; - } - md = 0xF8; + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system type */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ } else { - if (sfd) { /* No partition table (SFD) */ - md = 0xF0; - } else { /* Create partition table (FDISK) */ - mem_set(fs->win, 0, SS(fs)); - tbl = fs->win + MBR_Table; /* Create partition table for single partition in the drive */ - tbl[1] = 1; /* Partition start head */ - tbl[2] = 1; /* Partition start sector */ - tbl[3] = 0; /* Partition start cylinder */ - tbl[4] = sys; /* System type */ - tbl[5] = 254; /* Partition end head */ - n = (b_vol + n_vol) / 63 / 255; - tbl[6] = (BYTE)(n >> 2 | 63); /* Partition end sector */ - tbl[7] = (BYTE)n; /* End cylinder */ - st_dword(tbl + 8, 63); /* Partition start in LBA */ - st_dword(tbl + 12, n_vol); /* Partition size in LBA */ - st_word(fs->win + BS_55AA, 0xAA55); /* MBR signature */ - if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) { /* Write it to the MBR */ - return FR_DISK_ERR; - } - md = 0xF8; + if (!(opt & FM_SFD)) { + /* Create partition table in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS is incorrect) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ } } - /* Create BPB in the VBR */ - tbl = fs->win; /* Clear sector */ - mem_set(tbl, 0, SS(fs)); - mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ - i = SS(fs); /* Sector size */ - st_word(tbl + BPB_BytsPerSec, (WORD)i); - tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ - st_word(tbl + BPB_RsvdSecCnt, (WORD)n_rsv); /* Reserved sectors */ - tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ - i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of root directory entries */ - st_word(tbl + BPB_RootEntCnt, (WORD)i); - if (n_vol < 0x10000) { /* Number of total sectors */ - st_word(tbl + BPB_TotSec16, (WORD)n_vol); - } else { - st_dword(tbl + BPB_TotSec32, (WORD)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); /* Volume offset */ - n = 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_RootClus32, 2); /* Root directory start cluster (2) */ - st_word(tbl + BPB_FSInfo32, 1); /* FSINFO record offset (VBR + 1) */ - st_word(tbl + BPB_BkBootSec32, 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, (WORD)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(pdrv, tbl, b_vol, 1) != RES_OK) { /* Write it to the VBR sector */ - return FR_DISK_ERR; - } - if (fmt == FS_FAT32) { /* Write it to the backup VBR if needed (VBR + 6) */ - disk_write(pdrv, tbl, b_vol + 6, 1); - } + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; - /* Initialize FAT area */ - wsect = b_fat; - for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ - 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 directory */ - } - if (disk_write(pdrv, 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 writes */ - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) { - return FR_DISK_ERR; - } - } - } - - /* Initialize root directory */ - i = (fmt == FS_FAT32) ? au : (UINT)n_dir; - do { - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) { - return FR_DISK_ERR; - } - } while (--i); - -#if _USE_TRIM /* Erase data area if needed */ - { - eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; - disk_ioctl(pdrv, CTRL_TRIM, eb); - } -#endif - - /* Create FSINFO if needed */ - if (fmt == FS_FAT32) { - st_dword(tbl + FSI_LeadSig, 0x41615252); - st_dword(tbl + FSI_StrucSig, 0x61417272); - st_dword(tbl + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ - st_dword(tbl + FSI_Nxt_Free, 2); /* Last allocated cluster# */ - st_word(tbl + BS_55AA, 0xAA55); - disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR + 1) */ - disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR + 7) */ - } - - return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; + return FR_OK; } @@ -5417,7 +5668,7 @@ FRESULT f_mkfs ( FRESULT f_fdisk ( BYTE pdrv, /* Physical drive number */ - const DWORD szt[], /* Pointer to the size table for each partitions */ + const DWORD* szt, /* Pointer to the size table for each partitions */ void* work /* Pointer to the working buffer */ ) { @@ -5432,7 +5683,7 @@ FRESULT f_fdisk ( if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; - /* Determine CHS in the table regardless of the drive geometry */ + /* Determine the CHS without any care of the drive geometry */ for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; if (n == 256) n--; e_hd = n - 1; @@ -5476,7 +5727,6 @@ FRESULT f_fdisk ( return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; } - #endif /* _MULTI_PARTITION */ #endif /* _USE_MKFS && !_FS_READONLY */ @@ -5568,14 +5818,14 @@ TCHAR* f_gets ( /*-----------------------------------------------------------------------*/ typedef struct { - FIL* fp; - int idx, nchr; - BYTE buf[64]; + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of chars written */ + BYTE buf[64]; /* Write buffer */ } putbuff; static -void putc_bfd ( +void putc_bfd ( /* Buffered write with code conversion */ putbuff* pb, TCHAR c ) @@ -5588,7 +5838,7 @@ void putc_bfd ( putc_bfd(pb, '\r'); } - i = pb->idx; /* Buffer write index (-1:error) */ + i = pb->idx; /* Write index of pb->buf[] */ if (i < 0) return; #if _LFN_UNICODE @@ -5630,6 +5880,31 @@ void putc_bfd ( } +static +int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return EOF; +} + + +static +void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + pb->fp = fp; + pb->nchr = pb->idx = 0; +} + + int f_putc ( TCHAR c, /* A character to be output */ @@ -5637,18 +5912,11 @@ int f_putc ( ) { putbuff pb; - UINT nw; - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; - - putc_bfd(&pb, c); /* Put a character */ - - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); } @@ -5664,19 +5932,11 @@ int f_puts ( ) { putbuff pb; - UINT nw; - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; - - while (*str) /* Put the string */ - putc_bfd(&pb, *str++); - - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); } @@ -5693,15 +5953,14 @@ int f_printf ( ) { va_list arp; + putbuff pb; BYTE f, r; - UINT nw, i, j, w; + UINT i, j, w; DWORD v; TCHAR c, d, str[32], *p; - putbuff pb; - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; + putc_init(&pb, fp); va_start(arp, fmt); @@ -5777,10 +6036,7 @@ int f_printf ( va_end(arp); - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; + return putc_flush(&pb); } #endif /* !_FS_READONLY */ diff --git a/source/fatfs/ff.h b/source/fatfs/ff.h old mode 100644 new mode 100755 index 437093f..981a886 --- a/source/fatfs/ff.h +++ b/source/fatfs/ff.h @@ -1,11 +1,13 @@ -/*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module include R0.12 (C)ChaN, 2016 -/----------------------------------------------------------------------------/ -/ FatFs module is a free software that opened under license policy of -/ following conditions. +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12b / +/-----------------------------------------------------------------------------/ / / Copyright (C) 2016, ChaN, all right reserved. / +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + / 1. Redistributions of source code must retain the above copyright notice, / this condition and the following disclaimer. / @@ -13,11 +15,11 @@ / and any warranties related to this software are DISCLAIMED. / The copyright owner or contributors be NOT LIABLE for any damages caused / by use of this software. -/---------------------------------------------------------------------------*/ +/----------------------------------------------------------------------------*/ #ifndef _FATFS -#define _FATFS 88100 /* Revision ID */ +#define _FATFS 68020 /* Revision ID */ #ifdef __cplusplus extern "C" { @@ -25,6 +27,7 @@ extern "C" { #include "integer.h" /* Basic integer types */ #include "ffconf.h" /* FatFs configuration options */ + #if _FATFS != _FFCONF #error Wrong configuration file (ffconf.h). #endif @@ -52,7 +55,7 @@ extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ /* Type of path name strings on FatFs API */ -#if _LFN_UNICODE /* Unicode string */ +#if _LFN_UNICODE /* Unicode (UTF-16) string */ #if _USE_LFN == 0 #error _LFN_UNICODE must be 0 at non-LFN cfg. #endif @@ -61,14 +64,25 @@ 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 + + +/* Type of file size variables */ + +#if _FS_EXFAT +#if _USE_LFN == 0 +#error LFN must be enabled when enable exFAT +#endif +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; #endif @@ -87,6 +101,9 @@ typedef struct { #if _MAX_SS != _MIN_SS WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ #endif +#if _USE_LFN != 0 + WCHAR* lfnbuf; /* LFN working buffer */ +#endif #if _FS_EXFAT BYTE* dirbuf; /* Directory entry block scratchpad buffer */ #endif @@ -117,19 +134,6 @@ typedef struct { -/* Type of file size variables and object identifier */ - -#if _FS_EXFAT -#if _USE_LFN == 0 -#error LFN must be enabled when enable exFAT -#endif -typedef QWORD FSIZE_t; -#else -typedef DWORD FSIZE_t; -#endif - - - /* Object ID and allocation information (_FDID) */ typedef struct { @@ -155,18 +159,18 @@ typedef struct { /* File object structure (FIL) */ typedef struct { - _FDID obj; /* Object identifier */ + _FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ BYTE flag; /* File status flags */ BYTE err; /* Abort flag (error code) */ FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ - DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */ + DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */ DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ #if !_FS_READONLY DWORD dir_sect; /* Sector number containing the directory entry */ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ #endif #if _USE_FASTSEEK - DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */ + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ #endif #if !_FS_TINY BYTE buf[_MAX_SS]; /* File private data read/write window */ @@ -183,10 +187,9 @@ typedef struct { DWORD clust; /* Current cluster */ DWORD sect; /* Current sector */ BYTE* dir; /* Pointer to the directory item in the win[] */ - BYTE* fn; /* Pointer to the SFN (in/out) {body[8],ext[3],status[1]} */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ #if _USE_LFN != 0 DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ - WCHAR* lfn; /* Pointer to the LFN working buffer */ #endif #if _USE_FIND const TCHAR* pat; /* Pointer to the name matching pattern */ @@ -229,7 +232,7 @@ typedef enum { 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 */ - FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ 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 sharing policy */ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ @@ -244,11 +247,11 @@ typedef enum { FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ FRESULT f_close (FIL* fp); /* Close an open file object */ -FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */ -FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */ -FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of a file object */ -FRESULT f_truncate (FIL* fp); /* Truncate file */ -FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ FRESULT f_closedir (DIR* dp); /* Close an open directory */ FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ @@ -258,8 +261,8 @@ FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ -FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */ -FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of the file/dir */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ FRESULT f_chdir (const TCHAR* path); /* Change current directory */ FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ @@ -269,8 +272,8 @@ FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ -FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */ -FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ @@ -323,40 +326,37 @@ int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ /* Flags and offset address */ -/* File access control and file status flags (FIL.flag) */ - +/* File access mode and open method flags (3rd argument of f_open) */ #define FA_READ 0x01 #define FA_WRITE 0x02 #define FA_OPEN_EXISTING 0x00 #define FA_CREATE_NEW 0x04 #define FA_CREATE_ALWAYS 0x08 #define FA_OPEN_ALWAYS 0x10 -#define _FA_MODIFIED 0x20 -#define _FA_DIRTY 0x40 +#define FA_OPEN_APPEND 0x30 +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) -/* FAT sub type (FATFS.fs_type) */ +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 +/* Filesystem type (FATFS.fs_type) */ #define FS_FAT12 1 #define FS_FAT16 2 #define FS_FAT32 3 #define FS_EXFAT 4 - -/* File attribute bits for directory entry */ - +/* File attribute bits for directory entry (FILINFO.fattrib) */ #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 controls */ -#define CREATE_LINKMAP ((FSIZE_t)0 - 1) #ifdef __cplusplus diff --git a/source/fatfs/ffconf.h b/source/fatfs/ffconf.h old mode 100644 new mode 100755 index 661fa57..75cfef4 --- a/source/fatfs/ffconf.h +++ b/source/fatfs/ffconf.h @@ -1,8 +1,8 @@ /*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module configuration file R0.12 (C)ChaN, 2016 +/ FatFs - FAT file system module configuration file /---------------------------------------------------------------------------*/ -#define _FFCONF 88100 /* Revision ID */ +#define _FFCONF 68020 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations @@ -15,7 +15,7 @@ / and optional writing functions as well. */ -#define _FS_MINIMIZE 1 +#define _FS_MINIMIZE 0 /* This option defines minimization level to remove some basic API functions. / / 0: All basic functions are enabled. @@ -62,8 +62,7 @@ #define _USE_FORWARD 0 -/* This option switches f_forward() function. (0:Disable or 1:Enable) -/ To enable it, also _FS_TINY need to be 1. */ +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ /*---------------------------------------------------------------------------/ @@ -118,13 +117,13 @@ #define _LFN_UNICODE 0 -/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) / To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. / This option also affects behavior of string I/O functions. */ #define _STRF_ENCODE 3 -/* When _LFN_UNICODE == 1, this option selects the character encoding on the file to +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). / / 0: ANSI/OEM @@ -153,7 +152,7 @@ #define _STR_VOLUME_ID 0 -#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" +#define _VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" /* _STR_VOLUME_ID switches string support of volume ID. / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / number in the path name. _VOLUME_STRS defines the drive ID strings for each @@ -205,19 +204,19 @@ #define _FS_TINY 0 /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) -/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS bytes. +/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. / Instead of private sector buffer eliminated from the file object, common sector / buffer in the file system object (FATFS) is used for the file data transfer. */ #define _FS_EXFAT 0 -/* This option switches support of exFAT file system in addition to the traditional -/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled. +/* This option switches support of exFAT file system. (0:Disable or 1:Enable) +/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) / Note that enabling exFAT discards C89 compatibility. */ #define _FS_NORTC 1 -#define _NORTC_MON 3 +#define _NORTC_MON 1 #define _NORTC_MDAY 1 #define _NORTC_YEAR 2016 /* The option _FS_NORTC switches timestamp functiton. If the system does not have @@ -260,7 +259,9 @@ / The _FS_TIMEOUT defines timeout period in unit of time tick. / The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, / SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be -/ included somewhere in the scope of ff.c. */ +/ included somewhere in the scope of ff.h. */ + +/* #include // O/S definitions */ /*--- End of configuration options ---*/ diff --git a/source/fatfs/integer.h b/source/fatfs/integer.h old mode 100644 new mode 100755 diff --git a/source/fatfs/option/ccsbcs.c b/source/fatfs/option/ccsbcs.c old mode 100644 new mode 100755 diff --git a/source/fatfs/sdmmc/common.h b/source/fatfs/sdmmc/common.h deleted file mode 100644 index 90a327e..0000000 --- a/source/fatfs/sdmmc/common.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include -#include "../../types.h" \ No newline at end of file diff --git a/source/fatfs/sdmmc/delay.h b/source/fatfs/sdmmc/delay.h index afad438..543794a 100644 --- a/source/fatfs/sdmmc/delay.h +++ b/source/fatfs/sdmmc/delay.h @@ -1,9 +1,5 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - #pragma once -#include "common.h" +#include "../../types.h" -void ioDelay(u32 us); +void waitcycles(u32 us); diff --git a/source/fatfs/sdmmc/delay.s b/source/fatfs/sdmmc/delay.s index b3baccd..0bf19c2 100644 --- a/source/fatfs/sdmmc/delay.s +++ b/source/fatfs/sdmmc/delay.s @@ -1,17 +1,16 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - +.text .arm -.global ioDelay -.type ioDelay STT_FUNC +.align 4 -@ioDelay ( u32 us ) -ioDelay: - ldr r1, =0x18000000 @ VRAM -1: - @ Loop doing uncached reads from VRAM to make loop timing more reliable - ldr r2, [r1] - subs r0, #1 - bgt 1b - bx lr +.global waitcycles +.type waitcycles, %function +waitcycles: + push {r0-r2, lr} + str r0, [sp, #4] + waitcycles_loop: + ldr r3, [sp, #4] + subs r2, r3, #1 + str r2, [sp, #4] + cmp r3, #0 + bne waitcycles_loop + pop {r0-r2, pc} diff --git a/source/fatfs/sdmmc/sdmmc.c b/source/fatfs/sdmmc/sdmmc.c index bee13d7..150fac3 100644 --- a/source/fatfs/sdmmc/sdmmc.c +++ b/source/fatfs/sdmmc/sdmmc.c @@ -1,30 +1,55 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2014-2015, Normmatt + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License Version 2, as described below: + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ #include "sdmmc.h" #include "delay.h" -struct mmcdevice handleNAND; -struct mmcdevice handleSD; +static struct mmcdevice handleNAND; +static struct mmcdevice handleSD; -static inline u16 sdmmc_read16(u16 reg) { - return *(vu16*)(SDMMC_BASE + reg); +static inline u16 sdmmc_read16(u16 reg) +{ + return *(vu16 *)(SDMMC_BASE + reg); } -static inline void sdmmc_write16(u16 reg, u16 val) { - *(vu16*)(SDMMC_BASE + reg) = val; +static inline void sdmmc_write16(u16 reg, u16 val) +{ + *(vu16 *)(SDMMC_BASE + reg) = val; } -static inline u32 sdmmc_read32(u16 reg) { - return *(vu32*)(SDMMC_BASE + reg); +static inline u32 sdmmc_read32(u16 reg) +{ + return *(vu32 *)(SDMMC_BASE + reg); } -static inline void sdmmc_write32(u16 reg, u32 val) { - *(vu32*)(SDMMC_BASE + reg) = val; +static inline void sdmmc_write32(u16 reg, u32 val) +{ + *(vu32 *)(SDMMC_BASE + reg) = val; } -static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) { +static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) +{ u16 val = sdmmc_read16(reg); val &= ~clear; val |= set; @@ -38,188 +63,213 @@ static inline void setckl(u32 data) sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100); } - mmcdevice *getMMCDevice(int drive) { - if(drive==0) return &handleNAND; + if(drive == 0) return &handleNAND; return &handleSD; } -static u32 __attribute__((noinline)) geterror(struct mmcdevice *ctx) +static int geterror(struct mmcdevice *ctx) { - return (ctx->error << 29) >> 31; + return (int)((ctx->error << 29) >> 31); } -static void __attribute__((noinline)) inittarget(struct mmcdevice *ctx) +static void inittarget(struct mmcdevice *ctx) { - sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber); + sdmmc_mask16(REG_SDPORTSEL, 0x3, (u16)ctx->devicenumber); setckl(ctx->clk); - if (ctx->SDOPT == 0) { - sdmmc_mask16(REG_SDOPT, 0, 0x8000); - } else { - sdmmc_mask16(REG_SDOPT, 0x8000, 0); - } - + if(ctx->SDOPT == 0) sdmmc_mask16(REG_SDOPT, 0, 0x8000); + else sdmmc_mask16(REG_SDOPT, 0x8000, 0); } static void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args) { - bool getSDRESP = (cmd << 15) >> 31; + u32 getSDRESP = (cmd << 15) >> 31; u16 flags = (cmd << 15) >> 31; - const bool readdata = cmd & 0x20000; - const bool writedata = cmd & 0x40000; + const int readdata = cmd & 0x20000; + const int writedata = cmd & 0x40000; - if (readdata || writedata) + if(readdata || writedata) flags |= TMIO_STAT0_DATAEND; ctx->error = 0; - while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working? - sdmmc_write16(REG_SDIRMASK0,0); - sdmmc_write16(REG_SDIRMASK1,0); - sdmmc_write16(REG_SDSTATUS0,0); - sdmmc_write16(REG_SDSTATUS1,0); - sdmmc_mask16(REG_SDDATACTL32,0x1800,0); - - sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); - sdmmc_write16(REG_SDCMDARG1,args >> 16); - sdmmc_write16(REG_SDCMD,cmd &0xFFFF); + while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working? + sdmmc_write16(REG_SDIRMASK0, 0); + sdmmc_write16(REG_SDIRMASK1, 0); + sdmmc_write16(REG_SDSTATUS0, 0); + sdmmc_write16(REG_SDSTATUS1, 0); + sdmmc_mask16(REG_DATACTL32, 0x1800, 0); + sdmmc_write16(REG_SDCMDARG0, args & 0xFFFF); + sdmmc_write16(REG_SDCMDARG1, args >> 16); + sdmmc_write16(REG_SDCMD, cmd & 0xFFFF); u32 size = ctx->size; - vu8 *dataPtr = ctx->data; + u8 *rDataPtr = ctx->rData; + const u8 *tDataPtr = ctx->tData; - bool useBuf = ( NULL != dataPtr ); + bool rUseBuf = rDataPtr != NULL; + bool tUseBuf = tDataPtr != NULL; u16 status0 = 0; - while(true) { - u16 status1 = sdmmc_read16(REG_SDSTATUS1); - if (status1 & TMIO_STAT1_RXRDY) { - if (readdata && useBuf) { - sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); - //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY); - if (size > 0x1FF) { - for(int i = 0; i<0x200; i+=2) { - u16 data = sdmmc_read16(REG_SDFIFO); - *dataPtr++ = data & 0xFF; - *dataPtr++ = data >> 8; + while(true) + { + vu16 status1 = sdmmc_read16(REG_SDSTATUS1); + vu16 ctl32 = sdmmc_read16(REG_DATACTL32); + if((ctl32 & 0x100)) + { + if(readdata) + { + if(rUseBuf) + { + sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); + if(size > 0x1FF) + { + //Gabriel Marcano: This implementation doesn't assume alignment. + //I've removed the alignment check doen with former rUseBuf32 as a result + for(int i = 0; i < 0x200; i += 4) + { + u32 data = sdmmc_read32(REG_SDFIFO32); + *rDataPtr++ = data; + *rDataPtr++ = data >> 8; + *rDataPtr++ = data >> 16; + *rDataPtr++ = data >> 24; + } + size -= 0x200; } - size -= 0x200; } - } - } - if (status1 & TMIO_STAT1_TXRQ) { - if (writedata && useBuf) { - sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0); - //sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ); - if (size > 0x1FF) { - for (int i = 0; i<0x200; i+=2) { - u16 data = *dataPtr++; - data |= *dataPtr++ << 8; - sdmmc_write16(REG_SDFIFO, data); - } - size -= 0x200; - } + sdmmc_mask16(REG_DATACTL32, 0x800, 0); } } - if (status1 & TMIO_MASK_GW) { + if(!(ctl32 & 0x200)) + { + if(writedata) + { + if(tUseBuf) + { + sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0); + if(size > 0x1FF) + { + for(int i = 0; i < 0x200; i += 4) + { + u32 data = *tDataPtr++; + data |= (u32)*tDataPtr++ << 8; + data |= (u32)*tDataPtr++ << 16; + data |= (u32)*tDataPtr++ << 24; + sdmmc_write32(REG_SDFIFO32, data); + } + size -= 0x200; + } + } + + sdmmc_mask16(REG_DATACTL32, 0x1000, 0); + } + } + if(status1 & TMIO_MASK_GW) + { ctx->error |= 4; break; } - if (!(status1 & TMIO_STAT1_CMD_BUSY)) { + if(!(status1 & TMIO_STAT1_CMD_BUSY)) + { status0 = sdmmc_read16(REG_SDSTATUS0); - if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) + if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) + { ctx->error |= 0x1; - if (status0 & TMIO_STAT0_DATAEND) + } + if(status0 & TMIO_STAT0_DATAEND) + { ctx->error |= 0x2; + } - if ((status0 & flags) == flags) + if((status0 & flags) == flags) break; } } ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); - sdmmc_write16(REG_SDSTATUS0,0); - sdmmc_write16(REG_SDSTATUS1,0); + sdmmc_write16(REG_SDSTATUS0, 0); + sdmmc_write16(REG_SDSTATUS1, 0); - if (getSDRESP != 0) { - ctx->ret[0] = (u32)sdmmc_read16(REG_SDRESP0) | (u32)(sdmmc_read16(REG_SDRESP1) << 16); - ctx->ret[1] = (u32)sdmmc_read16(REG_SDRESP2) | (u32)(sdmmc_read16(REG_SDRESP3) << 16); - ctx->ret[2] = (u32)sdmmc_read16(REG_SDRESP4) | (u32)(sdmmc_read16(REG_SDRESP5) << 16); - ctx->ret[3] = (u32)sdmmc_read16(REG_SDRESP6) | (u32)(sdmmc_read16(REG_SDRESP7) << 16); + if(getSDRESP != 0) + { + ctx->ret[0] = (u32)(sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16)); + ctx->ret[1] = (u32)(sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16)); + ctx->ret[2] = (u32)(sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16)); + ctx->ret[3] = (u32)(sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16)); } } -u32 __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in) +int __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in) { - if (handleSD.isSDHC == 0) - sector_no <<= 9; + if(handleSD.isSDHC == 0) sector_no <<= 9; inittarget(&handleSD); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - handleSD.data = in; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleSD.tData = in; handleSD.size = numsectors << 9; - sdmmc_send_command(&handleSD,0x52C19,sector_no); + sdmmc_send_command(&handleSD, 0x52C19, sector_no); return geterror(&handleSD); } -u32 __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out) +int __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out) { - if (handleSD.isSDHC == 0) - sector_no <<= 9; + if(handleSD.isSDHC == 0) sector_no <<= 9; inittarget(&handleSD); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - handleSD.data = out; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleSD.rData = out; handleSD.size = numsectors << 9; - sdmmc_send_command(&handleSD,0x33C12,sector_no); + sdmmc_send_command(&handleSD, 0x33C12, sector_no); return geterror(&handleSD); } -u32 __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out) +int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out) { - if (handleNAND.isSDHC == 0) - sector_no <<= 9; + if(handleNAND.isSDHC == 0) sector_no <<= 9; inittarget(&handleNAND); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - - handleNAND.data = out; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleNAND.rData = out; handleNAND.size = numsectors << 9; - sdmmc_send_command(&handleNAND,0x33C12,sector_no); + sdmmc_send_command(&handleNAND, 0x33C12, sector_no); inittarget(&handleSD); return geterror(&handleNAND); } -u32 __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, vu8 *in) //experimental +int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in) //experimental { - if (handleNAND.isSDHC == 0) - sector_no <<= 9; + if(handleNAND.isSDHC == 0) sector_no <<= 9; inittarget(&handleNAND); - sdmmc_write16(REG_SDSTOP,0x100); - - sdmmc_write16(REG_SDBLKCOUNT,numsectors); - - handleNAND.data = in; + sdmmc_write16(REG_SDSTOP, 0x100); + sdmmc_write16(REG_SDBLKCOUNT32, numsectors); + sdmmc_write16(REG_SDBLKLEN32, 0x200); + sdmmc_write16(REG_SDBLKCOUNT, numsectors); + handleNAND.tData = in; handleNAND.size = numsectors << 9; - sdmmc_send_command(&handleNAND,0x52C19,sector_no); + sdmmc_send_command(&handleNAND, 0x52C19, sector_no); inittarget(&handleSD); return geterror(&handleNAND); } -static u32 calcSDSize(u8* csd, int type) +static u32 calcSDSize(u8 *csd, int type) { u32 result = 0; - if (type == -1) type = csd[14] >> 6; - switch (type) { + if(type == -1) type = csd[14] >> 6; + switch(type) + { case 0: { - u32 block_len = csd[9] & 0xf; + u32 block_len = csd[9] & 0xF; block_len = 1u << block_len; - u32 mult = (u32)(csd[4] >> 7) | (u32)((csd[5] & 3) << 1); + u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1)); mult = 1u << (mult + 2); result = csd[8] & 3; result = (result << 8) | csd[7]; @@ -228,18 +278,42 @@ static u32 calcSDSize(u8* csd, int type) } break; case 1: - result = csd[7] & 0x3f; + result = csd[7] & 0x3F; result = (result << 8) | csd[6]; result = (result << 8) | csd[5]; result = (result + 1) * 1024; break; - default: - break; //Do nothing otherwise + default: + break; //Do nothing otherwise FIXME perhaps return some error? } return result; } static void InitSD() +{ + *(vu16 *)0x10006100 &= 0xF7FFu; //SDDATACTL32 + *(vu16 *)0x10006100 &= 0xEFFFu; //SDDATACTL32 + *(vu16 *)0x10006100 |= 0x402u; //SDDATACTL32 + *(vu16 *)0x100060D8 = (*(vu16 *)0x100060D8 & 0xFFDD) | 2; + *(vu16 *)0x10006100 &= 0xFFFFu; //SDDATACTL32 + *(vu16 *)0x100060D8 &= 0xFFDFu; //SDDATACTL + *(vu16 *)0x10006104 = 512; //SDBLKLEN32 + *(vu16 *)0x10006108 = 1; //SDBLKCOUNT32 + *(vu16 *)0x100060E0 &= 0xFFFEu; //SDRESET + *(vu16 *)0x100060E0 |= 1u; //SDRESET + *(vu16 *)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0 + *(vu16 *)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1 + *(vu16 *)0x100060FC |= 0xDBu; //SDCTL_RESERVED7 + *(vu16 *)0x100060FE |= 0xDBu; //SDCTL_RESERVED8 + *(vu16 *)0x10006002 &= 0xFFFCu; //SDPORTSEL + *(vu16 *)0x10006024 = 0x20; + *(vu16 *)0x10006028 = 0x40EE; + *(vu16 *)0x10006002 &= 0xFFFCu; ////SDPORTSEL + *(vu16 *)0x10006026 = 512; //SDBLKLEN + *(vu16 *)0x10006008 = 0; //SDSTOP +} + +static int Nand_Init() { //NAND handleNAND.isSDHC = 0; @@ -249,80 +323,50 @@ static void InitSD() handleNAND.clk = 0x80; handleNAND.devicenumber = 1; - //SD - handleSD.isSDHC = 0; - handleSD.SDOPT = 0; - handleSD.res = 0; - handleSD.initarg = 0; - handleSD.clk = 0x80; - handleSD.devicenumber = 0; - - *(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32 - *(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32 - *(vu16*)0x10006100 |= 0x402u; //SDDATACTL32 - *(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2; - *(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32 - *(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL - *(vu16*)0x10006104 = 0; //SDBLKLEN32 - *(vu16*)0x10006108 = 1; //SDBLKCOUNT32 - *(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET - *(vu16*)0x100060E0 |= 1u; //SDRESET - *(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0 - *(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1 - *(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7 - *(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8 - *(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL - *(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20 - *(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE - *(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL - *(vu16*)0x10006026 = 512; //SDBLKLEN - *(vu16*)0x10006008 = 0; //SDSTOP - - inittarget(&handleSD); -} - -static int Nand_Init() -{ inittarget(&handleNAND); - ioDelay(0xF000); + waitcycles(0xF000); - sdmmc_send_command(&handleNAND,0,0); + sdmmc_send_command(&handleNAND, 0, 0); - do { - do { - sdmmc_send_command(&handleNAND,0x10701,0x100000); - } while ( !(handleNAND.error & 1) ); - } while((handleNAND.ret[0] & 0x80000000) == 0); + do + { + do + { + sdmmc_send_command(&handleNAND, 0x10701, 0x100000); + } + while(!(handleNAND.error & 1)); + } + while((handleNAND.ret[0] & 0x80000000) == 0); - sdmmc_send_command(&handleNAND,0x10602,0x0); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10602, 0x0); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x10403,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10403, handleNAND.initarg << 0x10); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x10609,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10609, handleNAND.initarg << 0x10); + if((handleNAND.error & 0x4)) return -1; - handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0],0); + handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0], 0); handleNAND.clk = 1; setckl(1); - sdmmc_send_command(&handleNAND,0x10407,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10407, handleNAND.initarg << 0x10); + if((handleNAND.error & 0x4)) return -1; handleNAND.SDOPT = 1; - sdmmc_send_command(&handleNAND,0x10506,0x3B70100); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10506, 0x3B70100); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x10506,0x3B90100); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10506, 0x3B90100); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x1040D,handleNAND.initarg << 0x10); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x1040D, handleNAND.initarg << 0x10); + if((handleNAND.error & 0x4)) return -1; - sdmmc_send_command(&handleNAND,0x10410,0x200); - if (handleNAND.error & 0x4) return -1; + sdmmc_send_command(&handleNAND, 0x10410, 0x200); + if((handleNAND.error & 0x4)) return -1; handleNAND.clk |= 0x200; @@ -333,113 +377,102 @@ static int Nand_Init() static int SD_Init() { + //SD + handleSD.isSDHC = 0; + handleSD.SDOPT = 0; + handleSD.res = 0; + handleSD.initarg = 0; + handleSD.clk = 0x80; + handleSD.devicenumber = 0; + inittarget(&handleSD); - ioDelay(1u << 18); //Card needs a little bit of time to be detected, it seems - + waitcycles(1u << 22); //Card needs a little bit of time to be detected, it seems FIXME test again to see what a good number is for the delay + //If not inserted - if (!(*((vu16*)0x1000601c) & TMIO_STAT0_SIGSTATE)) return -1; - - sdmmc_send_command(&handleSD,0,0); - sdmmc_send_command(&handleSD,0x10408,0x1AA); - //u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E; + if(!(*((vu16 *)(SDMMC_BASE + REG_SDSTATUS0)) & TMIO_STAT0_SIGSTATE)) return 5; + + sdmmc_send_command(&handleSD, 0, 0); + sdmmc_send_command(&handleSD, 0x10408, 0x1AA); u32 temp = (handleSD.error & 0x1) << 0x1E; - //int count = 0; u32 temp2 = 0; - do { - do { - sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); - sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp); + do + { + do + { + sdmmc_send_command(&handleSD, 0x10437, handleSD.initarg << 0x10); + sdmmc_send_command(&handleSD, 0x10769, 0x00FF8000 | temp); temp2 = 1; - } while ( !(handleSD.error & 1) ); - - } while((handleSD.ret[0] & 0x80000000) == 0); + } + while(!(handleSD.error & 1)); + } + while((handleSD.ret[0] & 0x80000000) == 0); if(!((handleSD.ret[0] >> 30) & 1) || !temp) temp2 = 0; handleSD.isSDHC = temp2; - sdmmc_send_command(&handleSD,0x10602,0); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10602, 0); + if((handleSD.error & 0x4)) return -1; - sdmmc_send_command(&handleSD,0x10403,0); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10403, 0); + if((handleSD.error & 0x4)) return -2; handleSD.initarg = handleSD.ret[0] >> 0x10; - sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10609, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -3; - handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-1); + handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0], -1); handleSD.clk = 1; setckl(1); - sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10507, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -4; - sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10437, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -5; handleSD.SDOPT = 1; - sdmmc_send_command(&handleSD,0x10446,0x2); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10446, 0x2); + if((handleSD.error & 0x4)) return -6; - sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x1040D, handleSD.initarg << 0x10); + if((handleSD.error & 0x4)) return -7; - sdmmc_send_command(&handleSD,0x10410,0x200); - if (handleSD.error & 0x4) return -1; + sdmmc_send_command(&handleSD, 0x10410, 0x200); + if((handleSD.error & 0x4)) return -8; handleSD.clk |= 0x200; return 0; } -int sdmmc_sdcard_init() +void sdmmc_get_cid(bool isNand, u32 *info) { - InitSD(); - int result = Nand_Init(); - return result | SD_Init(); + struct mmcdevice *device = isNand ? &handleNAND : &handleSD; + + inittarget(device); + + // use cmd7 to put sd card in standby mode + // CMD7 + sdmmc_send_command(device, 0x10507, 0); + + // get sd card info + // use cmd10 to read CID + sdmmc_send_command(device, 0x1060A, device->initarg << 0x10); + + for(int i = 0; i < 4; ++i) + info[i] = device->ret[i]; + + // put sd card back to transfer mode + // CMD7 + sdmmc_send_command(device, 0x10507, device->initarg << 0x10); } -int sdmmc_get_cid( int isNand, uint32_t *info) +void sdmmc_sdcard_init() { - struct mmcdevice *device; - if(isNand) - device = &handleNAND; - else - device = &handleSD; - - inittarget(device); - // use cmd7 to put sd card in standby mode - // CMD7 - { - sdmmc_send_command(device,0x10507,0); - //if((device->error & 0x4)) return -1; - } - - // get sd card info - // use cmd10 to read CID - { - sdmmc_send_command(device,0x1060A,device->initarg << 0x10); - //if((device->error & 0x4)) return -2; - - for( int i = 0; i < 4; ++i ) { - info[i] = device->ret[i]; - } - } - - // put sd card back to transfer mode - // CMD7 - { - sdmmc_send_command(device,0x10507,device->initarg << 0x10); - //if((device->error & 0x4)) return -3; - } - - if(isNand) - { - inittarget(&handleSD); - } - - return 0; + InitSD(); + Nand_Init(); + SD_Init(); } \ No newline at end of file diff --git a/source/fatfs/sdmmc/sdmmc.h b/source/fatfs/sdmmc/sdmmc.h index 9c1fdd7..fa3d960 100644 --- a/source/fatfs/sdmmc/sdmmc.h +++ b/source/fatfs/sdmmc/sdmmc.h @@ -1,52 +1,48 @@ -// Copyright 2014 Normmatt -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - #pragma once -#include "common.h" +#include "../../types.h" -#define SDMMC_BASE 0x10006000u +#define SDMMC_BASE 0x10006000 -#define REG_SDCMD 0x00 -#define REG_SDPORTSEL 0x02 -#define REG_SDCMDARG 0x04 -#define REG_SDCMDARG0 0x04 -#define REG_SDCMDARG1 0x06 -#define REG_SDSTOP 0x08 -#define REG_SDBLKCOUNT 0x0a +#define REG_SDCMD 0x00 +#define REG_SDPORTSEL 0x02 +#define REG_SDCMDARG 0x04 +#define REG_SDCMDARG0 0x04 +#define REG_SDCMDARG1 0x06 +#define REG_SDSTOP 0x08 +#define REG_SDBLKCOUNT 0x0A -#define REG_SDRESP0 0x0c -#define REG_SDRESP1 0x0e -#define REG_SDRESP2 0x10 -#define REG_SDRESP3 0x12 -#define REG_SDRESP4 0x14 -#define REG_SDRESP5 0x16 -#define REG_SDRESP6 0x18 -#define REG_SDRESP7 0x1a +#define REG_SDRESP0 0x0C +#define REG_SDRESP1 0x0E +#define REG_SDRESP2 0x10 +#define REG_SDRESP3 0x12 +#define REG_SDRESP4 0x14 +#define REG_SDRESP5 0x16 +#define REG_SDRESP6 0x18 +#define REG_SDRESP7 0x1A -#define REG_SDSTATUS0 0x1c -#define REG_SDSTATUS1 0x1e +#define REG_SDSTATUS0 0x1C +#define REG_SDSTATUS1 0x1E -#define REG_SDIRMASK0 0x20 -#define REG_SDIRMASK1 0x22 -#define REG_SDCLKCTL 0x24 +#define REG_SDIRMASK0 0x20 +#define REG_SDIRMASK1 0x22 +#define REG_SDCLKCTL 0x24 -#define REG_SDBLKLEN 0x26 -#define REG_SDOPT 0x28 -#define REG_SDFIFO 0x30 +#define REG_SDBLKLEN 0x26 +#define REG_SDOPT 0x28 +#define REG_SDFIFO 0x30 -#define REG_SDDATACTL 0xd8 -#define REG_SDRESET 0xe0 -#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? +#define REG_DATACTL 0xD8 +#define REG_SDRESET 0xE0 +#define REG_SDPROTECTED 0xF6 //bit 0 determines if sd is protected or not? -#define REG_SDDATACTL32 0x100 -#define REG_SDBLKLEN32 0x104 -#define REG_SDBLKCOUNT32 0x108 -#define REG_SDFIFO32 0x10C +#define REG_DATACTL32 0x100 +#define REG_SDBLKLEN32 0x104 +#define REG_SDBLKCOUNT32 0x108 +#define REG_SDFIFO32 0x10C -#define REG_CLK_AND_WAIT_CTL 0x138 -#define REG_RESET_SDIO 0x1e0 +#define REG_CLK_AND_WAIT_CTL 0x138 +#define REG_RESET_SDIO 0x1E0 #define TMIO_STAT0_CMDRESPEND 0x0001 #define TMIO_STAT0_DATAEND 0x0004 @@ -70,31 +66,7 @@ #define TMIO_STAT1_CMD_BUSY 0x4000 #define TMIO_STAT1_ILL_ACCESS 0x8000 -//Comes from TWLSDK mongoose.tef DWARF info -#define SDMC_NORMAL 0x00000000 -#define SDMC_ERR_COMMAND 0x00000001 -#define SDMC_ERR_CRC 0x00000002 -#define SDMC_ERR_END 0x00000004 -#define SDMC_ERR_TIMEOUT 0x00000008 -#define SDMC_ERR_FIFO_OVF 0x00000010 -#define SDMC_ERR_FIFO_UDF 0x00000020 -#define SDMC_ERR_WP 0x00000040 -#define SDMC_ERR_ABORT 0x00000080 -#define SDMC_ERR_FPGA_TIMEOUT 0x00000100 -#define SDMC_ERR_PARAM 0x00000200 -#define SDMC_ERR_R1_STATUS 0x00000800 -#define SDMC_ERR_NUM_WR_SECTORS 0x00001000 -#define SDMC_ERR_RESET 0x00002000 -#define SDMC_ERR_ILA 0x00004000 -#define SDMC_ERR_INFO_DETECT 0x00008000 - -#define SDMC_STAT_ERR_UNKNOWN 0x00080000 -#define SDMC_STAT_ERR_CC 0x00100000 -#define SDMC_STAT_ERR_ECC_FAILED 0x00200000 -#define SDMC_STAT_ERR_CRC 0x00800000 -#define SDMC_STAT_ERR_OTHER 0xf9c70008 - -#define TMIO_MASK_ALL 0x837f031d +#define TMIO_MASK_ALL 0x837F031D #define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \ TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) @@ -103,7 +75,8 @@ #define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) typedef struct mmcdevice { - vu8* data; + u8 *rData; + const u8 *tData; u32 size; u32 error; u16 stat0; @@ -118,11 +91,10 @@ typedef struct mmcdevice { u32 res; } mmcdevice; -int sdmmc_sdcard_init(); -u32 sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out); -u32 sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in); -mmcdevice *getMMCDevice(int drive); -int sdmmc_get_cid( int isNand, uint32_t *info); - -u32 sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out); -u32 sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, vu8 *in); \ No newline at end of file +void sdmmc_sdcard_init(); +int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in); +int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out); +int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in); +void sdmmc_get_cid(bool isNand, u32 *info); +mmcdevice *getMMCDevice(int drive); \ No newline at end of file diff --git a/source/fs.c b/source/fs.c index 84893ab..c615ecb 100644 --- a/source/fs.c +++ b/source/fs.c @@ -1,9 +1,28 @@ /* -* fs.c +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. */ #include "fs.h" #include "memory.h" +#include "strings.h" #include "fatfs/ff.h" static FATFS fs; @@ -18,71 +37,92 @@ u32 mountCTRNAND(void) return f_mount(&fs, "1:", 1) == FR_OK; } -u32 fileRead(void *dest, const char *path) +u32 fileRead(void *dest, const char *path, u32 maxSize) { FIL file; - u32 size; + u32 ret = 0; if(f_open(&file, path, FA_READ) == FR_OK) { - unsigned int read; - size = f_size(&file); - f_read(&file, dest, size, &read); + u32 size = f_size(&file); + if(dest == NULL) ret = size; + else if(!(maxSize > 0 && size > maxSize)) + f_read(&file, dest, size, (unsigned int *)&ret); f_close(&file); } - else size = 0; - return size; + return ret; } -void fileWrite(const void *buffer, const char *path, u32 size) +bool fileWrite(const void *buffer, const char *path, u32 size) { FIL file; - if(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS) == FR_OK) + FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS); + + if(result == FR_OK) { unsigned int written; f_write(&file, buffer, size, &written); f_close(&file); + + return true; } + + if(result == FR_NO_PATH) + { + for(u32 i = 1; path[i] != 0; i++) + if(path[i] == '/') + { + char folder[i + 1]; + memcpy(folder, path, i); + folder[i] = 0; + f_mkdir(folder); + } + + return fileWrite(buffer, path, size); + } + + return false; } u32 firmRead(void *dest) { const char *firmFolders[] = { "00000002", "20000002" }; - char path[48] = "1:/title/00040138/00000000/content"; - memcpy(&path[18], firmFolders[console], 8); + char path[48] = "1:/title/00040138/"; + concatenateStrings(path, firmFolders[isN3DS ? 1 : 0]); + concatenateStrings(path, "/content"); DIR dir; FILINFO info; f_opendir(&dir, path); - u32 id = 0xFFFFFFFF, + u32 firmVersion = 0xFFFFFFFF, ret = 0; //Parse the target directory - while(f_readdir(&dir, &info) == FR_OK && info.fname[0]) + while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0) { //Not a cxi - if(info.altname[9] != 'A') continue; + if(info.fname[9] != 'a') continue; //Multiple cxis were found - if(id != 0xFFFFFFFF) ret = 1; + if(firmVersion != 0xFFFFFFFF) ret = 1; //Convert the .app name to an integer - u32 tempId = 0; + u32 tempVersion = 0; for(char *tmp = info.altname; *tmp != '.'; tmp++) { - tempId <<= 4; - tempId += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0'; + tempVersion <<= 4; + tempVersion += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0'; } //FIRM is equal or newer than 11.0 - if(tempId >= (console ? 0x21 : 0x52)) ret = 2; + if(tempVersion >= (isN3DS ? 0x21 : 0x52)) ret = 2; //Found an older cxi - if(tempId < id) id = tempId; + if(tempVersion < firmVersion) firmVersion = tempVersion; } f_closedir(&dir); @@ -90,20 +130,12 @@ u32 firmRead(void *dest) if(!ret) { //Complete the string with the .app name - memcpy(&path[34], "/00000000.app", 14); - - //Last digit of the .app - u32 i = 42; + concatenateStrings(path, "/00000000.app"); //Convert back the .app name from integer to array - while(id) - { - static const char hexDigits[] = "0123456789ABCDEF"; - path[i--] = hexDigits[id & 0xF]; - id >>= 4; - } + hexItoa(firmVersion, &path[35], 8); - fileRead(dest, path); + fileRead(dest, path, 0); } return ret; diff --git a/source/fs.h b/source/fs.h index a7b63a5..3717057 100644 --- a/source/fs.h +++ b/source/fs.h @@ -1,15 +1,33 @@ /* -* fs.h +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. */ #pragma once #include "types.h" -extern u32 console; +extern bool isN3DS; u32 mountSD(void); u32 mountCTRNAND(void); -u32 fileRead(void *dest, const char *path); -void fileWrite(const void *buffer, const char *path, u32 size); +u32 fileRead(void *dest, const char *path, u32 maxSize); +bool fileWrite(const void *buffer, const char *path, u32 size); u32 firmRead(void *dest); \ No newline at end of file diff --git a/source/i2c.c b/source/i2c.c index 6f03579..103bd9d 100644 --- a/source/i2c.c +++ b/source/i2c.c @@ -10,50 +10,58 @@ static const struct { u8 bus_id, reg_addr; } dev_data[] = { {2, 0xA4}, {2, 0x9A}, {2, 0xA0}, }; -static inline u8 i2cGetDeviceBusId(u8 device_id) { +static inline u8 i2cGetDeviceBusId(u8 device_id) +{ return dev_data[device_id].bus_id; } -static inline u8 i2cGetDeviceRegAddr(u8 device_id) { +static inline u8 i2cGetDeviceRegAddr(u8 device_id) +{ return dev_data[device_id].reg_addr; } //----------------------------------------------------------------------------- -static vu8* reg_data_addrs[] = { - (vu8*)(I2C1_REG_OFF + I2C_REG_DATA), - (vu8*)(I2C2_REG_OFF + I2C_REG_DATA), - (vu8*)(I2C3_REG_OFF + I2C_REG_DATA), +static vu8 *reg_data_addrs[] = { + (vu8 *)(I2C1_REG_OFF + I2C_REG_DATA), + (vu8 *)(I2C2_REG_OFF + I2C_REG_DATA), + (vu8 *)(I2C3_REG_OFF + I2C_REG_DATA), }; -static inline vu8* i2cGetDataReg(u8 bus_id) { +static inline vu8 *i2cGetDataReg(u8 bus_id) +{ return reg_data_addrs[bus_id]; } //----------------------------------------------------------------------------- -static vu8* reg_cnt_addrs[] = { - (vu8*)(I2C1_REG_OFF + I2C_REG_CNT), - (vu8*)(I2C2_REG_OFF + I2C_REG_CNT), - (vu8*)(I2C3_REG_OFF + I2C_REG_CNT), +static vu8 *reg_cnt_addrs[] = { + (vu8 *)(I2C1_REG_OFF + I2C_REG_CNT), + (vu8 *)(I2C2_REG_OFF + I2C_REG_CNT), + (vu8 *)(I2C3_REG_OFF + I2C_REG_CNT), }; -static inline vu8* i2cGetCntReg(u8 bus_id) { +static inline vu8 *i2cGetCntReg(u8 bus_id) +{ return reg_cnt_addrs[bus_id]; } //----------------------------------------------------------------------------- -static inline void i2cWaitBusy(u8 bus_id) { +static inline void i2cWaitBusy(u8 bus_id) +{ while (*i2cGetCntReg(bus_id) & 0x80); } -static inline u32 i2cGetResult(u8 bus_id) { +static inline bool i2cGetResult(u8 bus_id) +{ i2cWaitBusy(bus_id); + return (*i2cGetCntReg(bus_id) >> 4) & 1; } -static void i2cStop(u8 bus_id, u8 arg0) { +static void i2cStop(u8 bus_id, u8 arg0) +{ *i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0; i2cWaitBusy(bus_id); *i2cGetCntReg(bus_id) = 0xC5; @@ -61,38 +69,45 @@ static void i2cStop(u8 bus_id, u8 arg0) { //----------------------------------------------------------------------------- -static u32 i2cSelectDevice(u8 bus_id, u8 dev_reg) { +static bool i2cSelectDevice(u8 bus_id, u8 dev_reg) +{ i2cWaitBusy(bus_id); *i2cGetDataReg(bus_id) = dev_reg; *i2cGetCntReg(bus_id) = 0xC2; + return i2cGetResult(bus_id); } -static u32 i2cSelectRegister(u8 bus_id, u8 reg) { +static bool i2cSelectRegister(u8 bus_id, u8 reg) +{ i2cWaitBusy(bus_id); *i2cGetDataReg(bus_id) = reg; *i2cGetCntReg(bus_id) = 0xC0; + return i2cGetResult(bus_id); } //----------------------------------------------------------------------------- -u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data) { - u8 bus_id = i2cGetDeviceBusId(dev_id); - u8 dev_addr = i2cGetDeviceRegAddr(dev_id); +bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data) +{ + u8 bus_id = i2cGetDeviceBusId(dev_id), + dev_addr = i2cGetDeviceRegAddr(dev_id); - for (int i = 0; i < 8; i++) { - if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) { + for(u32 i = 0; i < 8; i++) + { + if(i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) + { i2cWaitBusy(bus_id); *i2cGetDataReg(bus_id) = data; *i2cGetCntReg(bus_id) = 0xC1; i2cStop(bus_id, 0); - if (i2cGetResult(bus_id)) - return 1; + + if(i2cGetResult(bus_id)) return true; } *i2cGetCntReg(bus_id) = 0xC5; i2cWaitBusy(bus_id); } - return 0; -} + return false; +} \ No newline at end of file diff --git a/source/i2c.h b/source/i2c.h index 00658ea..07554f7 100644 --- a/source/i2c.h +++ b/source/i2c.h @@ -15,4 +15,4 @@ #define I2C_DEV_GYRO 10 #define I2C_DEV_IR 13 -u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data); \ No newline at end of file +bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data); \ No newline at end of file diff --git a/source/installer.c b/source/installer.c index d0b8c0e..6b44a4e 100755 --- a/source/installer.c +++ b/source/installer.c @@ -6,7 +6,7 @@ #include "memory.h" #include "fs.h" #include "crypto.h" -#include "screeninit.h" +#include "screen.h" #include "draw.h" #include "utils.h" #include "fatfs/sdmmc/sdmmc.h" @@ -31,9 +31,8 @@ static const u8 firm1Hash[0x20] = { 0x2D, 0x3D, 0x56, 0x6C, 0x6A, 0x1A, 0x8E, 0x52, 0x54, 0xE3, 0x89, 0xC2, 0x95, 0x06, 0x23, 0xE5 }; -int posY; - -u32 console; +u32 posY; +bool isN3DS; void main(void) { @@ -41,54 +40,54 @@ void main(void) sdmmc_sdcard_init(); //Determine if booting with A9LH - u32 a9lhBoot = !PDN_SPI_CNT; + bool isA9lh = !PDN_SPI_CNT; + //Detect the console being used - console = PDN_MPCORE_CFG == 7; + isN3DS = PDN_MPCORE_CFG == 7; drawString(TITLE, 10, 10, COLOR_TITLE); posY = drawString("Thanks to delebile, #cakey and StandardBus", 10, 40, COLOR_WHITE); - posY = drawString(a9lhBoot ? "Press SELECT to update A9LH, START to uninstall" : "Press SELECT for a full install", 10, posY + SPACING_Y, COLOR_WHITE); + posY = drawString(isA9lh ? "Press SELECT to update A9LH, START to uninstall" : "Press SELECT for a full install", 10, posY + SPACING_Y, COLOR_WHITE); posY = drawString("Press any other button to shutdown", 10, posY, COLOR_WHITE); u32 pressed = waitInput(); - if(pressed == BUTTON_SELECT) installer(a9lhBoot); - if(pressed == BUTTON_START && a9lhBoot) uninstaller(); + + if(pressed == BUTTON_SELECT) installer(isA9lh); + if(pressed == BUTTON_START && isA9lh) uninstaller(); shutdown(0, NULL); } -static inline void installer(u32 a9lhBoot) +static inline void installer(bool isA9lh) { if(!mountSD()) shutdown(1, "Error: failed to mount the SD card"); - const char *path; - u32 updatea9lh = 0; + bool updateA9lh = false; //If making a first install, we need the OTP - if(!a9lhBoot) + if(!isA9lh) { - // Prefer OTP from memory if available + const char otpPath[] = "a9lh/otp.bin"; const u8 zeroes[256] = {0}; - path = "a9lh/otp.bin"; + + //Prefer OTP from memory if available if(memcmp((void *)OTP_FROM_MEM, zeroes, 256) == 0) { // Read OTP from file - if(fileRead((void *)OTP_OFFSET, path) != 256) - { - shutdown(1, "Error: otp.bin doesn't exist and can't be dumped"); - } + if(!fileRead((void *)OTP_OFFSET, otpPath, 256)) + shutdown(1, "Error: otp.bin doesn't exist and can't be dumped"); } else { - // Write OTP from memory to file - fileWrite((void *)OTP_FROM_MEM, path, 256); + //Write OTP from memory to file + fileWrite((void *)OTP_FROM_MEM, otpPath, 256); memcpy((void *)OTP_OFFSET, (void *)OTP_FROM_MEM, 256); } } //Setup the key sector de/encryption with the SHA register or otp.bin - setupKeyslot0x11(a9lhBoot, (void *)OTP_OFFSET); + setupKeyslot0x11(isA9lh, (void *)OTP_OFFSET); //Calculate the CTR for the 3DS partitions getNandCTR(); @@ -99,7 +98,7 @@ static inline void installer(u32 a9lhBoot) shutdown(1, "Error: failed to setup FIRM encryption"); //If booting from A9LH or on N3DS, we can use the key sector from NAND - if(a9lhBoot || console) + if(isA9lh || isN3DS) { getSector((u8 *)SECTOR_OFFSET); @@ -107,28 +106,26 @@ static inline void installer(u32 a9lhBoot) for(i = 0; i < 3; i++) if(memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[i], 0x10) == 0) break; - if(i == 3) shutdown(1, a9lhBoot ? "Error: the OTP hash or the NAND key sector\nare invalid" : + if(i == 3) shutdown(1, isA9lh ? "Error: the OTP hash or the NAND key sector\nare invalid" : "Error: the otp.bin or the NAND key sector\nare invalid"); - else if(i == 1) updatea9lh = 1; + else if(i == 1) updateA9lh = true; } else { //Read decrypted key sector - path = "a9lh/secret_sector.bin"; - if(fileRead((void *)SECTOR_OFFSET, path) != 0x200) + if(!fileRead((void *)SECTOR_OFFSET, "a9lh/secret_sector.bin", 0x200)) shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size"); if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash)) shutdown(1, "Error: secret_sector.bin is invalid or corrupted"); } - if(!a9lhBoot || updatea9lh) + if(!isA9lh || updateA9lh) { //Generate and encrypt a per-console A9LH key sector generateSector((u8 *)SECTOR_OFFSET, 0); //Read FIRM0 - path = "a9lh/firm0.bin"; - if(fileRead((void *)FIRM0_OFFSET, path) != FIRM0_SIZE) + if(!fileRead((void *)FIRM0_OFFSET, "a9lh/firm0.bin", FIRM0_SIZE)) shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size"); if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, firm0Hash)) @@ -137,11 +134,10 @@ static inline void installer(u32 a9lhBoot) else if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash)) shutdown(1, "Error: NAND FIRM0 is invalid"); - if(!a9lhBoot) + if(!isA9lh) { //Read FIRM1 - path = "a9lh/firm1.bin"; - if(fileRead((void *)FIRM1_OFFSET, path) != FIRM1_SIZE) + if(!fileRead((void *)FIRM1_OFFSET, "a9lh/firm1.bin", FIRM1_SIZE)) shutdown(1, "Error: firm1.bin doesn't exist or has a wrong size"); if(!verifyHash((void *)FIRM1_OFFSET, FIRM1_SIZE, firm1Hash)) @@ -150,9 +146,7 @@ static inline void installer(u32 a9lhBoot) //Inject stage1 memset32((void *)STAGE1_OFFSET, 0, MAX_STAGE1_SIZE); - path = "a9lh/payload_stage1.bin"; - u32 size = fileRead((void *)STAGE1_OFFSET, path); - if(!size || size > MAX_STAGE1_SIZE) + if(!fileRead((void *)STAGE1_OFFSET, "a9lh/payload_stage1.bin", MAX_STAGE1_SIZE)) shutdown(1, "Error: payload_stage1.bin doesn't exist or\nexceeds max size"); const u8 zeroes[688] = {0}; @@ -161,20 +155,18 @@ static inline void installer(u32 a9lhBoot) //Read stage2 memset32((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE); - path = "a9lh/payload_stage2.bin"; - size = fileRead((void *)STAGE2_OFFSET, path); - if(!size || size > MAX_STAGE2_SIZE) + if(!fileRead((void *)STAGE2_OFFSET, "a9lh/payload_stage2.bin", MAX_STAGE2_SIZE)) shutdown(1, "Error: payload_stage2.bin doesn't exist or\nexceeds max size"); posY = drawString("All checks passed, installing...", 10, posY + SPACING_Y, COLOR_WHITE); //Point of no return, install stuff in the safest order - sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (vu8 *)STAGE2_OFFSET); - if(!a9lhBoot) writeFirm((u8 *)FIRM1_OFFSET, 1, FIRM1_SIZE); - if(!a9lhBoot || updatea9lh) sdmmc_nand_writesectors(0x96, 1, (vu8 *)SECTOR_OFFSET); - writeFirm((u8 *)FIRM0_OFFSET, 0, FIRM0_SIZE); + sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET); + if(!isA9lh) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE); + if(!isA9lh || updateA9lh) sdmmc_nand_writesectors(0x96, 1, (u8 *)SECTOR_OFFSET); + writeFirm((u8 *)FIRM0_OFFSET, false, FIRM0_SIZE); - shutdown(2, a9lhBoot ? "Update: success!" : "Full install: success!"); + shutdown(2, isA9lh ? "Update: success!" : "Full install: success!"); } static inline void uninstaller(void) @@ -194,7 +186,7 @@ static inline void uninstaller(void) } //New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector - if(console) + if(isN3DS) { setupKeyslot0x11(1, NULL); getSector((u8 *)SECTOR_OFFSET); @@ -232,10 +224,10 @@ static inline void uninstaller(void) posY = drawString("All checks passed, uninstalling...", 10, posY + SPACING_Y, COLOR_WHITE); //Point of no return, install stuff in the safest order - sdmmc_nand_writesectors(0x96, 1, (vu8 *)SECTOR_OFFSET); - writeFirm((u8 *)FIRM0_OFFSET, 0, firmSize); - writeFirm((u8 *)FIRM1_OFFSET, 1, firmSize); - sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (vu8 *)STAGE2_OFFSET); + sdmmc_nand_writesectors(0x96, 1, (u8 *)SECTOR_OFFSET); + writeFirm((u8 *)FIRM0_OFFSET, false, firmSize); + writeFirm((u8 *)FIRM1_OFFSET, true, firmSize); + sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET); shutdown(2, "Uninstall: success!"); -} +} \ No newline at end of file diff --git a/source/installer.h b/source/installer.h index d603633..b5d73d3 100644 --- a/source/installer.h +++ b/source/installer.h @@ -6,8 +6,8 @@ #include "types.h" -#define PDN_MPCORE_CFG (*(vu8 *)0x10140FFC) -#define PDN_SPI_CNT (*(vu8 *)0x101401C0) +#define PDN_MPCORE_CFG (*(vu8 *)0x10140FFC) +#define PDN_SPI_CNT (*(vu8 *)0x101401C0) #define OTP_FROM_MEM 0x10012000 #define OTP_OFFSET 0x24000000 @@ -23,5 +23,5 @@ #define MAX_STAGE1_SIZE 0x1E70 #define MAX_STAGE2_SIZE 0x89A00 -static inline void installer(u32 a9lhBoot); +static inline void installer(bool isA9lh); static inline void uninstaller(void); \ No newline at end of file diff --git a/source/memory.c b/source/memory.c index 4b6efd3..9f37af7 100644 --- a/source/memory.c +++ b/source/memory.c @@ -1,5 +1,7 @@ /* * memory.c +* +* memcpy, memset32 and memcmp adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c */ #include "memory.h" @@ -17,19 +19,19 @@ void memset32(void *dest, u32 filler, u32 size) { u32 *dest32 = (u32 *)dest; - for (u32 i = 0; i < size / 4; i++) + for(u32 i = 0; i < size / 4; i++) dest32[i] = filler; } int memcmp(const void *buf1, const void *buf2, u32 size) { - const u8 *buf1c = (const u8 *)buf1; - const u8 *buf2c = (const u8 *)buf2; + const u8 *buf1c = (const u8 *)buf1, + *buf2c = (const u8 *)buf2; for(u32 i = 0; i < size; i++) { int cmp = buf1c[i] - buf2c[i]; - if(cmp) return cmp; + if(cmp != 0) return cmp; } return 0; diff --git a/source/memory.h b/source/memory.h index 3dbef14..bc0004c 100644 --- a/source/memory.h +++ b/source/memory.h @@ -1,5 +1,7 @@ /* * memory.h +* +* memcpy, memset32 and memcmp adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c */ #pragma once diff --git a/source/screen.c b/source/screen.c new file mode 100644 index 0000000..bcf3384 --- /dev/null +++ b/source/screen.c @@ -0,0 +1,209 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* +* Screen init code by dark_samus, bil1s, Normmatt, delebile and others +*/ + +/* +* About cache coherency: +* +* Flushing the data cache for all memory regions read from/written to by both processors is mandatory on the ARM9 processor. +* Thus, we make sure there'll be a cache miss on the ARM9 next time it's read. +* Otherwise the ARM9 won't see the changes made and things will break. +* +* On the ARM11, in the environment we're in, the MMU isn't enabled and nothing is cached. +*/ + +#include "screen.h" +#include "draw.h" +#include "cache.h" +#include "i2c.h" + +vu32 *const arm11Entry = (vu32 *)BRAHMA_ARM11_ENTRY; + +static void invokeArm11Function(void (*func)()) +{ + *arm11Entry = (u32)func; + while(*arm11Entry); +} + +void clearScreens(void) +{ + void __attribute__((naked)) ARM11(void) + { + //Setting up two simultaneous memory fills using the GPU + vu32 *REGs_PSC0 = (vu32 *)0x10400010; + REGs_PSC0[0] = (u32)fb->top_left >> 3; //Start address + REGs_PSC0[1] = (u32)(fb->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address + REGs_PSC0[2] = 0; //Fill value + REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start + + vu32 *REGs_PSC1 = (vu32 *)0x10400020; + REGs_PSC1[0] = (u32)fb->bottom >> 3; //Start address + REGs_PSC1[1] = (u32)(fb->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address + REGs_PSC1[2] = 0; //Fill value + REGs_PSC1[3] = (2 << 8) | 1; //32-bit pattern; start + + while(!((REGs_PSC0[3] & 2) && (REGs_PSC1[3] & 2))); + + if(fb->top_right != fb->top_left) + { + REGs_PSC0[0] = (u32)fb->top_right >> 3; //Start address + REGs_PSC0[1] = (u32)(fb->top_right + SCREEN_TOP_FBSIZE) >> 3; //End address + REGs_PSC0[2] = 0; //Fill value + REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start + + while(!(REGs_PSC0[3] & 2)); + } + + WAIT_FOR_ARM9(); + } + + invokeArm11Function(ARM11); +} + +void initScreens(void) +{ + void __attribute__((naked)) initSequence(void) + { + //Disable interrupts + __asm(".word 0xF10C01C0"); + + *(vu32 *)0x10141200 = 0x1007F; + *(vu32 *)0x10202014 = 0x00000001; + *(vu32 *)0x1020200C &= 0xFFFEFFFE; + *(vu32 *)0x10202240 = 0x45; + *(vu32 *)0x10202A40 = 0x45; + *(vu32 *)0x10202244 = 0x1023E; + *(vu32 *)0x10202A44 = 0x1023E; + + //Top screen + *(vu32 *)0x10400400 = 0x000001c2; + *(vu32 *)0x10400404 = 0x000000d1; + *(vu32 *)0x10400408 = 0x000001c1; + *(vu32 *)0x1040040c = 0x000001c1; + *(vu32 *)0x10400410 = 0x00000000; + *(vu32 *)0x10400414 = 0x000000cf; + *(vu32 *)0x10400418 = 0x000000d1; + *(vu32 *)0x1040041c = 0x01c501c1; + *(vu32 *)0x10400420 = 0x00010000; + *(vu32 *)0x10400424 = 0x0000019d; + *(vu32 *)0x10400428 = 0x00000002; + *(vu32 *)0x1040042c = 0x00000192; + *(vu32 *)0x10400430 = 0x00000192; + *(vu32 *)0x10400434 = 0x00000192; + *(vu32 *)0x10400438 = 0x00000001; + *(vu32 *)0x1040043c = 0x00000002; + *(vu32 *)0x10400440 = 0x01960192; + *(vu32 *)0x10400444 = 0x00000000; + *(vu32 *)0x10400448 = 0x00000000; + *(vu32 *)0x1040045C = 0x00f00190; + *(vu32 *)0x10400460 = 0x01c100d1; + *(vu32 *)0x10400464 = 0x01920002; + *(vu32 *)0x10400468 = 0x18300000; + *(vu32 *)0x10400470 = 0x80341; + *(vu32 *)0x10400474 = 0x00010501; + *(vu32 *)0x10400478 = 0; + *(vu32 *)0x10400490 = 0x000002D0; + *(vu32 *)0x1040049C = 0x00000000; + + //Disco register + for(u32 i = 0; i < 256; i++) + *(vu32 *)0x10400484 = 0x10101 * i; + + //Bottom screen + *(vu32 *)0x10400500 = 0x000001c2; + *(vu32 *)0x10400504 = 0x000000d1; + *(vu32 *)0x10400508 = 0x000001c1; + *(vu32 *)0x1040050c = 0x000001c1; + *(vu32 *)0x10400510 = 0x000000cd; + *(vu32 *)0x10400514 = 0x000000cf; + *(vu32 *)0x10400518 = 0x000000d1; + *(vu32 *)0x1040051c = 0x01c501c1; + *(vu32 *)0x10400520 = 0x00010000; + *(vu32 *)0x10400524 = 0x0000019d; + *(vu32 *)0x10400528 = 0x00000052; + *(vu32 *)0x1040052c = 0x00000192; + *(vu32 *)0x10400530 = 0x00000192; + *(vu32 *)0x10400534 = 0x0000004f; + *(vu32 *)0x10400538 = 0x00000050; + *(vu32 *)0x1040053c = 0x00000052; + *(vu32 *)0x10400540 = 0x01980194; + *(vu32 *)0x10400544 = 0x00000000; + *(vu32 *)0x10400548 = 0x00000011; + *(vu32 *)0x1040055C = 0x00f00140; + *(vu32 *)0x10400560 = 0x01c100d1; + *(vu32 *)0x10400564 = 0x01920052; + *(vu32 *)0x10400568 = 0x18300000 + 0x46500; + *(vu32 *)0x10400570 = 0x80301; + *(vu32 *)0x10400574 = 0x00010501; + *(vu32 *)0x10400578 = 0; + *(vu32 *)0x10400590 = 0x000002D0; + *(vu32 *)0x1040059C = 0x00000000; + + //Disco register + for(u32 i = 0; i < 256; i++) + *(vu32 *)0x10400584 = 0x10101 * i; + + WAIT_FOR_ARM9(); + } + + //Set CakeBrah framebuffers + void __attribute__((naked)) setupFramebuffers(void) + { + //Disable interrupts + __asm(".word 0xF10C01C0"); + + fb->top_left = (u8 *)0x18300000; + fb->top_right = (u8 *)0x18300000; + fb->bottom = (u8 *)0x18346500; + + *(vu32 *)0x10400468 = (u32)fb->top_left; + *(vu32 *)0x1040046c = (u32)fb->top_left; + *(vu32 *)0x10400494 = (u32)fb->top_right; + *(vu32 *)0x10400498 = (u32)fb->top_right; + *(vu32 *)0x10400568 = (u32)fb->bottom; + *(vu32 *)0x1040056c = (u32)fb->bottom; + + WAIT_FOR_ARM9(); + } + + static bool needToSetup = true; + + if(needToSetup) + { + if(PDN_GPU_CNT == 1) + { + invokeArm11Function(initSequence); + + //Turn on backlight + i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); + } + + flushDCacheRange((void *)fb, sizeof(struct fb)); + invokeArm11Function(setupFramebuffers); + needToSetup = false; + } + + clearScreens(); +} \ No newline at end of file diff --git a/source/screen.h b/source/screen.h new file mode 100644 index 0000000..ce8da5f --- /dev/null +++ b/source/screen.h @@ -0,0 +1,49 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +/* +* Screen init code by dark_samus, bil1s, Normmatt, delebile and others +*/ + +#pragma once + +#include "types.h" + +#define PDN_GPU_CNT (*(vu8 *)0x10141200) + +#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8 +#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)(); + +#define SCREEN_TOP_WIDTH 400 +#define SCREEN_BOTTOM_WIDTH 320 +#define SCREEN_HEIGHT 240 +#define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT) +#define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT) + +static struct fb { + u8 *top_left; + u8 *top_right; + u8 *bottom; +} *const fb = (struct fb *)0x23FFFE00; + +void clearScreens(void); +void initScreens(void); \ No newline at end of file diff --git a/source/screeninit.c b/source/screeninit.c deleted file mode 100644 index e1f247a..0000000 --- a/source/screeninit.c +++ /dev/null @@ -1,122 +0,0 @@ -/* -* screeninit.c -* -* Screen init code by dark_samus, bil1s, Normmatt, delebile and others. -*/ - -#include "screeninit.h" -#include "draw.h" -#include "i2c.h" - -void initScreens(void) -{ - vu32 *const arm11 = (u32 *)0x1FFFFFF8; - - void __attribute__((naked)) ARM11(void) - { - __asm(".word 0xF10C01C0"); - *(vu32 *)0x10141200 = 0x1007F; - *(vu32 *)0x10202014 = 0x00000001; - *(vu32 *)0x1020200C &= 0xFFFEFFFE; - *(vu32 *)0x10202240 = 0x45; - *(vu32 *)0x10202A40 = 0x45; - *(vu32 *)0x10202244 = 0x1023E; - *(vu32 *)0x10202A44 = 0x1023E; - - // Top screen - *(vu32 *)0x10400400 = 0x000001c2; - *(vu32 *)0x10400404 = 0x000000d1; - *(vu32 *)0x10400408 = 0x000001c1; - *(vu32 *)0x1040040c = 0x000001c1; - *(vu32 *)0x10400410 = 0x00000000; - *(vu32 *)0x10400414 = 0x000000cf; - *(vu32 *)0x10400418 = 0x000000d1; - *(vu32 *)0x1040041c = 0x01c501c1; - *(vu32 *)0x10400420 = 0x00010000; - *(vu32 *)0x10400424 = 0x0000019d; - *(vu32 *)0x10400428 = 0x00000002; - *(vu32 *)0x1040042c = 0x00000192; - *(vu32 *)0x10400430 = 0x00000192; - *(vu32 *)0x10400434 = 0x00000192; - *(vu32 *)0x10400438 = 0x00000001; - *(vu32 *)0x1040043c = 0x00000002; - *(vu32 *)0x10400440 = 0x01960192; - *(vu32 *)0x10400444 = 0x00000000; - *(vu32 *)0x10400448 = 0x00000000; - *(vu32 *)0x1040045C = 0x00f00190; - *(vu32 *)0x10400460 = 0x01c100d1; - *(vu32 *)0x10400464 = 0x01920002; - *(vu32 *)0x10400468 = 0x18300000; - *(vu32 *)0x10400470 = 0x80341; - *(vu32 *)0x10400474 = 0x00010501; - *(vu32 *)0x10400478 = 0; - *(vu32 *)0x10400490 = 0x000002D0; - *(vu32 *)0x1040049C = 0x00000000; - - // Disco register - for(vu32 i = 0; i < 256; i++) - *(vu32 *)0x10400484 = 0x10101 * i; - - // Bottom screen - *(vu32 *)0x10400500 = 0x000001c2; - *(vu32 *)0x10400504 = 0x000000d1; - *(vu32 *)0x10400508 = 0x000001c1; - *(vu32 *)0x1040050c = 0x000001c1; - *(vu32 *)0x10400510 = 0x000000cd; - *(vu32 *)0x10400514 = 0x000000cf; - *(vu32 *)0x10400518 = 0x000000d1; - *(vu32 *)0x1040051c = 0x01c501c1; - *(vu32 *)0x10400520 = 0x00010000; - *(vu32 *)0x10400524 = 0x0000019d; - *(vu32 *)0x10400528 = 0x00000052; - *(vu32 *)0x1040052c = 0x00000192; - *(vu32 *)0x10400530 = 0x00000192; - *(vu32 *)0x10400534 = 0x0000004f; - *(vu32 *)0x10400538 = 0x00000050; - *(vu32 *)0x1040053c = 0x00000052; - *(vu32 *)0x10400540 = 0x01980194; - *(vu32 *)0x10400544 = 0x00000000; - *(vu32 *)0x10400548 = 0x00000011; - *(vu32 *)0x1040055C = 0x00f00140; - *(vu32 *)0x10400560 = 0x01c100d1; - *(vu32 *)0x10400564 = 0x01920052; - *(vu32 *)0x10400568 = 0x18300000 + 0x46500; - *(vu32 *)0x10400570 = 0x80301; - *(vu32 *)0x10400574 = 0x00010501; - *(vu32 *)0x10400578 = 0; - *(vu32 *)0x10400590 = 0x000002D0; - *(vu32 *)0x1040059C = 0x00000000; - - // Disco register - for(vu32 i = 0; i < 256; i++) - *(vu32 *)0x10400584 = 0x10101 * i; - - // Enable backlight - i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); - - *(vu32 *)0x10400468 = 0x18300000; - *(vu32 *)0x1040046c = 0x18300000; - *(vu32 *)0x10400494 = 0x18300000; - *(vu32 *)0x10400498 = 0x18300000; - *(vu32 *)0x10400568 = 0x18346500; - *(vu32 *)0x1040056c = 0x18346500; - - //Set CakeBrah framebuffers - *((vu32 *)0x23FFFE00) = 0x18300000; - *((vu32 *)0x23FFFE04) = 0x18300000; - *((vu32 *)0x23FFFE08) = 0x18346500; - - //Clear ARM11 entry offset - *arm11 = 0; - - while(1); - } - - if(PDN_GPU_CNT == 1) - { - *arm11 = (u32)ARM11; - while(*arm11); - } - - clearScreens(); -} \ No newline at end of file diff --git a/source/screeninit.h b/source/screeninit.h deleted file mode 100644 index 9c60ef3..0000000 --- a/source/screeninit.h +++ /dev/null @@ -1,13 +0,0 @@ -/* -* screeninit.h -* -* Screen init code by dark_samus, bil1s, Normmatt, delebile and others. -*/ - -#pragma once - -#include "types.h" - -#define PDN_GPU_CNT (*(vu8 *)0x10141200) - -void initScreens(void); \ No newline at end of file diff --git a/source/start.s b/source/start.s index 4c91264..13e747d 100644 --- a/source/start.s +++ b/source/start.s @@ -1,3 +1,25 @@ +@ This file is part of Luma3DS +@ Copyright (C) 2016 Aurora Wright, TuxSH +@ +@ This program is free software: you can redistribute it and/or modify +@ it under the terms of the GNU General Public License as published by +@ the Free Software Foundation, either version 3 of the License, or +@ (at your option) any later version. +@ +@ 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. +@ +@ You should have received a copy of the GNU General Public License +@ along with this program. If not, see . +@ +@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +@ reasonable legal notices or author attributions in that material or in the Appropriate Legal +@ Notices displayed by works containing it. + +@ Thanks to the numerous people who took part in writing this file + .section .text.start .align 4 .global _start @@ -5,6 +27,11 @@ _start: @ Change the stack pointer mov sp, #0x27000000 + @ Disable interrupts + mrs r0, cpsr + orr r0, #0x1C0 + msr cpsr_cx, r0 + @ Disable caches / MPU mrc p15, 0, r0, c1, c0, 0 @ read control register bic r0, #(1<<12) @ - instruction cache disable @@ -12,21 +39,25 @@ _start: bic r0, #(1<<0) @ - mpu disable mcr p15, 0, r0, c1, c0, 0 @ write control register + @ Flush caches + bl flushEntireDCache + bl flushEntireICache + @ Give read/write access to all the memory regions - ldr r0, =0x33333333 + ldr r0, =0x3333333 mcr p15, 0, r0, c5, c0, 2 @ write data access mcr p15, 0, r0, c5, c0, 3 @ write instruction access @ Set MPU permissions and cache settings - ldr r0, =0xFFFF001D @ ffff0000 32k | bootrom (unprotected part) - ldr r1, =0x3000801B @ fff00000 16k | dtcm - ldr r2, =0x01FF801D @ 01ff8000 32k | itcm - ldr r3, =0x08000029 @ 08000000 2M | arm9 mem (O3DS / N3DS) - ldr r4, =0x10000029 @ 10000000 2M | io mem (ARM9 / first 2MB) - ldr r5, =0x20000037 @ 20000000 256M | fcram (O3DS / N3DS) - ldr r6, =0x1FF00027 @ 1FF00000 1M | dsp / axi wram - ldr r7, =0x1800002D @ 18000000 8M | vram (+ 2MB) - mov r8, #0x29 + ldr r0, =0xFFFF001D @ ffff0000 32k | bootrom (unprotected part) + ldr r1, =0x01FF801D @ 01ff8000 32k | itcm + ldr r2, =0x08000029 @ 08000000 2M | arm9 mem (O3DS / N3DS) + ldr r3, =0x10000029 @ 10000000 2M | io mem (ARM9 / first 2MB) + ldr r4, =0x20000037 @ 20000000 256M | fcram (O3DS / N3DS) + ldr r5, =0x1FF00027 @ 1FF00000 1M | dsp / axi wram + ldr r6, =0x1800002D @ 18000000 8M | vram (+ 2MB) + mov r7, #0 + mov r8, #0x15 mcr p15, 0, r0, c6, c0, 0 mcr p15, 0, r1, c6, c1, 0 mcr p15, 0, r2, c6, c2, 0 @@ -35,23 +66,19 @@ _start: mcr p15, 0, r5, c6, c5, 0 mcr p15, 0, r6, c6, c6, 0 mcr p15, 0, r7, c6, c7, 0 - mcr p15, 0, r8, c3, c0, 0 @ Write bufferable 0, 3, 5 - mcr p15, 0, r8, c2, c0, 0 @ Data cacheable 0, 3, 5 - mcr p15, 0, r8, c2, c0, 1 @ Inst cacheable 0, 3, 5 + mcr p15, 0, r8, c3, c0, 0 @ Write bufferable 0, 2, 4 + mcr p15, 0, r8, c2, c0, 0 @ Data cacheable 0, 2, 4 + mcr p15, 0, r8, c2, c0, 1 @ Inst cacheable 0, 2, 4 - @ Enable caches / MPU + @ Enable caches / MPU / ITCM mrc p15, 0, r0, c1, c0, 0 @ read control register + orr r0, r0, #(1<<18) @ - ITCM enable + orr r0, r0, #(1<<13) @ - alternate exception vectors enable orr r0, r0, #(1<<12) @ - instruction cache enable orr r0, r0, #(1<<2) @ - data cache enable orr r0, r0, #(1<<0) @ - mpu enable mcr p15, 0, r0, c1, c0, 0 @ write control register - @ Flush caches - mov r0, #0 - mcr p15, 0, r0, c7, c5, 0 @ flush I-cache - mcr p15, 0, r0, c7, c6, 0 @ flush D-cache - mcr p15, 0, r0, c7, c10, 4 @ drain write buffer - @ Fix mounting of SDMC ldr r0, =0x10000020 mov r1, #0x340 diff --git a/source/strings.c b/source/strings.c new file mode 100644 index 0000000..55fee26 --- /dev/null +++ b/source/strings.c @@ -0,0 +1,55 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +#include "strings.h" +#include "memory.h" + +u32 strlen(const char *string) +{ + char *stringEnd = (char *)string; + + while(*stringEnd) stringEnd++; + + return stringEnd - string; +} + +void concatenateStrings(char *destination, const char *source) +{ + u32 i = strlen(source), + j = strlen(destination); + + memcpy(&destination[j], source, i + 1); +} + +void hexItoa(u32 number, char *out, u32 digits) +{ + const char hexDigits[] = "0123456789ABCDEF"; + u32 i = 0; + + while(number > 0) + { + out[digits - 1 - i++] = hexDigits[number & 0xF]; + number >>= 4; + } + + while(i < digits) out[digits - 1 - i++] = '0'; +} \ No newline at end of file diff --git a/source/strings.h b/source/strings.h new file mode 100644 index 0000000..fc40e19 --- /dev/null +++ b/source/strings.h @@ -0,0 +1,29 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +#pragma once + +#include "types.h" + +u32 strlen(const char *string); +void concatenateStrings(char *destination, const char *source); +void hexItoa(u32 number, char *out, u32 digits); \ No newline at end of file diff --git a/source/types.h b/source/types.h index 7caf623..bce6f07 100644 --- a/source/types.h +++ b/source/types.h @@ -6,6 +6,7 @@ #include #include +#include //Common data types typedef uint8_t u8; diff --git a/source/utils.c b/source/utils.c index 84cbb45..4e1891d 100755 --- a/source/utils.c +++ b/source/utils.c @@ -4,12 +4,14 @@ #include "utils.h" #include "draw.h" +#include "screen.h" +#include "cache.h" #include "i2c.h" u32 waitInput(void) { - u32 pressedKey = 0, - key; + bool pressedKey = false; + u32 key; //Wait for no keys to be pressed while(HID_PAD); @@ -22,10 +24,10 @@ u32 waitInput(void) key = HID_PAD; //Make sure it's pressed - for(u32 i = 0x13000; i; i--) + for(u32 i = 0x13000; i > 0; i--) { if(key != HID_PAD) break; - if(i == 1) pressedKey = 1; + if(i == 1) pressedKey = true; } } while(!pressedKey); @@ -41,6 +43,9 @@ void shutdown(u32 mode, const char *message) drawString("Press any button to shutdown", 10, posY, COLOR_WHITE); waitInput(); } - i2cWriteRegister(I2C_DEV_MCU, 0x20, 1); - while(1); + + if(PDN_GPU_CNT != 1) clearScreens(); + flushEntireDCache(); + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0); + while(true); } \ No newline at end of file diff --git a/source/utils.h b/source/utils.h index fcee33a..e519de3 100644 --- a/source/utils.h +++ b/source/utils.h @@ -21,7 +21,7 @@ #define COLOR_RED 0x0000FF #define COLOR_GREEN 0x00FF00 -extern int posY; +extern u32 posY; u32 waitInput(void); void shutdown(u32 mode, const char *message); \ No newline at end of file