Code cleanup, tentatively fix clearScreens for 2.1
This commit is contained in:
parent
9e579395af
commit
4ba801938b
2
CakeBrah
2
CakeBrah
@ -1 +1 @@
|
|||||||
Subproject commit 42ebe0d0bc075ba98fa631441590de9bd2733d2b
|
Subproject commit 9f7cea77d4db4d743e45b2e5193df76ffed0a571
|
2
CakeHax
2
CakeHax
@ -1 +1 @@
|
|||||||
Subproject commit 6b8fca0b37a370a605f76b34b133da91a0b40f5e
|
Subproject commit 5245c7b9dc232956a8578a36468f9024d8cf7001
|
2
Makefile
2
Makefile
@ -82,7 +82,7 @@ $(dir_build)/main.bin: $(dir_build)/main.elf
|
|||||||
$(dir_build)/main.elf: $(objects)
|
$(dir_build)/main.elf: $(objects)
|
||||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
$(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)/installer.o: CFLAGS += -DTITLE="\"$(name) $(revision)\""
|
||||||
|
|
||||||
$(dir_build)/%.o: $(dir_source)/%.c
|
$(dir_build)/%.o: $(dir_source)/%.c
|
||||||
|
29
source/cache.h
Normal file
29
source/cache.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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);
|
74
source/cache.s
Normal file
74
source/cache.s
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
@
|
||||||
|
@ 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
|
460
source/crypto.c
460
source/crypto.c
@ -9,301 +9,297 @@
|
|||||||
#include "fatfs/sdmmc/sdmmc.h"
|
#include "fatfs/sdmmc/sdmmc.h"
|
||||||
|
|
||||||
/****************************************************************
|
/****************************************************************
|
||||||
* Crypto Libs
|
* Crypto libs
|
||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
|
||||||
/* original version by megazig */
|
/* original version by megazig */
|
||||||
|
|
||||||
#ifndef __thumb__
|
#ifndef __thumb__
|
||||||
#define BSWAP32(x) {\
|
#define BSWAP32(x) {\
|
||||||
__asm__\
|
__asm__\
|
||||||
(\
|
(\
|
||||||
"eor r1, %1, %1, ror #16\n\t"\
|
"eor r1, %1, %1, ror #16\n\t"\
|
||||||
"bic r1, r1, #0xFF0000\n\t"\
|
"bic r1, r1, #0xFF0000\n\t"\
|
||||||
"mov %0, %1, ror #8\n\t"\
|
"mov %0, %1, ror #8\n\t"\
|
||||||
"eor %0, %0, r1, lsr #8\n\t"\
|
"eor %0, %0, r1, lsr #8\n\t"\
|
||||||
:"=r"(x)\
|
:"=r"(x)\
|
||||||
:"0"(x)\
|
:"0"(x)\
|
||||||
:"r1"\
|
:"r1"\
|
||||||
);\
|
);\
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\
|
#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\
|
||||||
__asm__\
|
__asm__\
|
||||||
(\
|
(\
|
||||||
"adds %0, %4\n\t"\
|
"adds %0, %4\n\t"\
|
||||||
"addcss %1, %1, #1\n\t"\
|
"addcss %1, %1, #1\n\t"\
|
||||||
"addcss %2, %2, #1\n\t"\
|
"addcss %2, %2, #1\n\t"\
|
||||||
"addcs %3, %3, #1\n\t"\
|
"addcs %3, %3, #1\n\t"\
|
||||||
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
||||||
: "r"(u32_0)\
|
: "r"(u32_0)\
|
||||||
: "cc"\
|
: "cc"\
|
||||||
);\
|
);\
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define BSWAP32(x) {x = __builtin_bswap32(x);}
|
#define BSWAP32(x) {x = __builtin_bswap32(x);}
|
||||||
|
|
||||||
#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\
|
#define ADD_u128_u32(u128_0, u128_1, u128_2, u128_3, u32_0) {\
|
||||||
__asm__\
|
__asm__\
|
||||||
(\
|
(\
|
||||||
"mov r4, #0\n\t"\
|
"mov r4, #0\n\t"\
|
||||||
"add %0, %0, %4\n\t"\
|
"add %0, %0, %4\n\t"\
|
||||||
"adc %1, %1, r4\n\t"\
|
"adc %1, %1, r4\n\t"\
|
||||||
"adc %2, %2, r4\n\t"\
|
"adc %2, %2, r4\n\t"\
|
||||||
"adc %3, %3, r4\n\t"\
|
"adc %3, %3, r4\n\t"\
|
||||||
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
: "+r"(u128_0), "+r"(u128_1), "+r"(u128_2), "+r"(u128_3)\
|
||||||
: "r"(u32_0)\
|
: "r"(u32_0)\
|
||||||
: "cc", "r4"\
|
: "cc", "r4"\
|
||||||
);\
|
);\
|
||||||
}
|
}
|
||||||
#endif /*__thumb__*/
|
#endif /*__thumb__*/
|
||||||
|
|
||||||
static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode)
|
static void aes_setkey(u8 keyslot, const void *key, u32 keyType, u32 mode)
|
||||||
{
|
{
|
||||||
if(keyslot <= 0x03) return; // Ignore TWL keys for now
|
if(keyslot <= 0x03) return; // Ignore TWL keys for now
|
||||||
u32 *key32 = (u32 *)key;
|
u32 *key32 = (u32 *)key;
|
||||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
||||||
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE;
|
*REG_AESKEYCNT = (*REG_AESKEYCNT >> 6 << 6) | keyslot | AES_KEYCNT_WRITE;
|
||||||
|
|
||||||
REG_AESKEYFIFO[keyType] = key32[0];
|
REG_AESKEYFIFO[keyType] = key32[0];
|
||||||
REG_AESKEYFIFO[keyType] = key32[1];
|
REG_AESKEYFIFO[keyType] = key32[1];
|
||||||
REG_AESKEYFIFO[keyType] = key32[2];
|
REG_AESKEYFIFO[keyType] = key32[2];
|
||||||
REG_AESKEYFIFO[keyType] = key32[3];
|
REG_AESKEYFIFO[keyType] = key32[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes_use_keyslot(u8 keyslot)
|
static void aes_use_keyslot(u8 keyslot)
|
||||||
{
|
{
|
||||||
if(keyslot > 0x3F)
|
if(keyslot > 0x3F)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
*REG_AESKEYSEL = keyslot;
|
*REG_AESKEYSEL = keyslot;
|
||||||
*REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */
|
*REG_AESCNT = *REG_AESCNT | 0x04000000; /* mystery bit */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes_setiv(const void *iv, u32 mode)
|
static void aes_setiv(const void *iv, u32 mode)
|
||||||
{
|
{
|
||||||
const u32 *iv32 = (const u32 *)iv;
|
const u32 *iv32 = (const u32 *)iv;
|
||||||
*REG_AESCNT = (*REG_AESCNT & ~(AES_CNT_INPUT_ENDIAN | AES_CNT_INPUT_ORDER)) | mode;
|
*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
|
// Word order for IV can't be changed in REG_AESCNT and always default to reversed
|
||||||
if(mode & AES_INPUT_NORMAL)
|
if(mode & AES_INPUT_NORMAL)
|
||||||
{
|
{
|
||||||
REG_AESCTR[0] = iv32[3];
|
REG_AESCTR[0] = iv32[3];
|
||||||
REG_AESCTR[1] = iv32[2];
|
REG_AESCTR[1] = iv32[2];
|
||||||
REG_AESCTR[2] = iv32[1];
|
REG_AESCTR[2] = iv32[1];
|
||||||
REG_AESCTR[3] = iv32[0];
|
REG_AESCTR[3] = iv32[0];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
REG_AESCTR[0] = iv32[0];
|
REG_AESCTR[0] = iv32[0];
|
||||||
REG_AESCTR[1] = iv32[1];
|
REG_AESCTR[1] = iv32[1];
|
||||||
REG_AESCTR[2] = iv32[2];
|
REG_AESCTR[2] = iv32[2];
|
||||||
REG_AESCTR[3] = iv32[3];
|
REG_AESCTR[3] = iv32[3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes_advctr(void *ctr, u32 val, u32 mode)
|
static void aes_advctr(void *ctr, u32 val, u32 mode)
|
||||||
{
|
{
|
||||||
u32 *ctr32 = (u32 *)ctr;
|
u32 *ctr32 = (u32 *)ctr;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
if(mode & AES_INPUT_BE)
|
if(mode & AES_INPUT_BE)
|
||||||
{
|
{
|
||||||
for(i = 0; i < 4; ++i) // Endian swap
|
for(i = 0; i < 4; ++i) // Endian swap
|
||||||
BSWAP32(ctr32[i]);
|
BSWAP32(ctr32[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mode & AES_INPUT_NORMAL)
|
if(mode & AES_INPUT_NORMAL)
|
||||||
{
|
{
|
||||||
ADD_u128_u32(ctr32[3], ctr32[2], ctr32[1], ctr32[0], val);
|
ADD_u128_u32(ctr32[3], ctr32[2], ctr32[1], ctr32[0], val);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ADD_u128_u32(ctr32[0], ctr32[1], ctr32[2], ctr32[3], val);
|
ADD_u128_u32(ctr32[0], ctr32[1], ctr32[2], ctr32[3], val);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mode & AES_INPUT_BE)
|
if(mode & AES_INPUT_BE)
|
||||||
{
|
{
|
||||||
for(i = 0; i < 4; ++i) // Endian swap
|
for(i = 0; i < 4; ++i) // Endian swap
|
||||||
BSWAP32(ctr32[i]);
|
BSWAP32(ctr32[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes_change_ctrmode(void *ctr, u32 fromMode, u32 toMode)
|
static void aes_change_ctrmode(void *ctr, u32 fromMode, u32 toMode)
|
||||||
{
|
{
|
||||||
u32 *ctr32 = (u32 *)ctr;
|
u32 *ctr32 = (u32 *)ctr;
|
||||||
int i;
|
int i;
|
||||||
if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN)
|
if((fromMode ^ toMode) & AES_CNT_INPUT_ENDIAN)
|
||||||
{
|
{
|
||||||
for(i = 0; i < 4; ++i)
|
for(i = 0; i < 4; ++i)
|
||||||
BSWAP32(ctr32[i]);
|
BSWAP32(ctr32[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((fromMode ^ toMode) & AES_CNT_INPUT_ORDER)
|
if((fromMode ^ toMode) & AES_CNT_INPUT_ORDER)
|
||||||
{
|
{
|
||||||
u32 temp = ctr32[0];
|
u32 temp = ctr32[0];
|
||||||
ctr32[0] = ctr32[3];
|
ctr32[0] = ctr32[3];
|
||||||
ctr32[3] = temp;
|
ctr32[3] = temp;
|
||||||
|
|
||||||
temp = ctr32[1];
|
temp = ctr32[1];
|
||||||
ctr32[1] = ctr32[2];
|
ctr32[1] = ctr32[2];
|
||||||
ctr32[2] = temp;
|
ctr32[2] = temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes_batch(void *dst, const void *src, u32 blockCount)
|
static void aes_batch(void *dst, const void *src, u32 blockCount)
|
||||||
{
|
{
|
||||||
*REG_AESBLKCNT = blockCount << 16;
|
*REG_AESBLKCNT = blockCount << 16;
|
||||||
*REG_AESCNT |= AES_CNT_START;
|
*REG_AESCNT |= AES_CNT_START;
|
||||||
|
|
||||||
const u32 *src32 = (const u32 *)src;
|
const u32 *src32 = (const u32 *)src;
|
||||||
u32 *dst32 = (u32 *)dst;
|
u32 *dst32 = (u32 *)dst;
|
||||||
|
|
||||||
u32 wbc = blockCount;
|
u32 wbc = blockCount;
|
||||||
u32 rbc = blockCount;
|
u32 rbc = blockCount;
|
||||||
|
|
||||||
while(rbc)
|
while(rbc)
|
||||||
{
|
{
|
||||||
if(wbc && ((*REG_AESCNT & 0x1F) <= 0xC)) // There's space for at least 4 ints
|
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++;
|
||||||
*REG_AESWRFIFO = *src32++;
|
*REG_AESWRFIFO = *src32++;
|
||||||
*REG_AESWRFIFO = *src32++;
|
*REG_AESWRFIFO = *src32++;
|
||||||
wbc--;
|
wbc--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rbc && ((*REG_AESCNT & (0x1F << 0x5)) >= (0x4 << 0x5))) // At least 4 ints available for read
|
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;
|
||||||
*dst32++ = *REG_AESRDFIFO;
|
*dst32++ = *REG_AESRDFIFO;
|
||||||
*dst32++ = *REG_AESRDFIFO;
|
*dst32++ = *REG_AESRDFIFO;
|
||||||
rbc--;
|
rbc--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode)
|
static void aes(void *dst, const void *src, u32 blockCount, void *iv, u32 mode, u32 ivMode)
|
||||||
{
|
{
|
||||||
*REG_AESCNT = mode |
|
*REG_AESCNT = mode |
|
||||||
AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER |
|
AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER |
|
||||||
AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN |
|
AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN |
|
||||||
AES_CNT_FLUSH_READ | AES_CNT_FLUSH_WRITE;
|
AES_CNT_FLUSH_READ | AES_CNT_FLUSH_WRITE;
|
||||||
|
|
||||||
u32 blocks;
|
u32 blocks;
|
||||||
while(blockCount != 0)
|
while(blockCount != 0)
|
||||||
{
|
{
|
||||||
if((mode & AES_ALL_MODES) != AES_ECB_ENCRYPT_MODE
|
if((mode & AES_ALL_MODES) != AES_ECB_ENCRYPT_MODE
|
||||||
&& (mode & AES_ALL_MODES) != AES_ECB_DECRYPT_MODE)
|
&& (mode & AES_ALL_MODES) != AES_ECB_DECRYPT_MODE)
|
||||||
aes_setiv(iv, ivMode);
|
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
|
// Save the last block for the next decryption CBC batch's iv
|
||||||
if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE)
|
if((mode & AES_ALL_MODES) == AES_CBC_DECRYPT_MODE)
|
||||||
{
|
{
|
||||||
memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
memcpy(iv, src + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
||||||
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the current batch
|
// Process the current batch
|
||||||
aes_batch(dst, src, blocks);
|
aes_batch(dst, src, blocks);
|
||||||
|
|
||||||
// Save the last block for the next encryption CBC batch's iv
|
// Save the last block for the next encryption CBC batch's iv
|
||||||
if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE)
|
if((mode & AES_ALL_MODES) == AES_CBC_ENCRYPT_MODE)
|
||||||
{
|
{
|
||||||
memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
memcpy(iv, dst + (blocks - 1) * AES_BLOCK_SIZE, AES_BLOCK_SIZE);
|
||||||
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
aes_change_ctrmode(iv, AES_INPUT_BE | AES_INPUT_NORMAL, ivMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance counter for CTR mode
|
// Advance counter for CTR mode
|
||||||
else if((mode & AES_ALL_MODES) == AES_CTR_MODE)
|
else if((mode & AES_ALL_MODES) == AES_CTR_MODE)
|
||||||
aes_advctr(iv, blocks, ivMode);
|
aes_advctr(iv, blocks, ivMode);
|
||||||
|
|
||||||
src += blocks * AES_BLOCK_SIZE;
|
src += blocks * AES_BLOCK_SIZE;
|
||||||
dst += blocks * AES_BLOCK_SIZE;
|
dst += blocks * AES_BLOCK_SIZE;
|
||||||
blockCount -= blocks;
|
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();
|
sha_wait_idle();
|
||||||
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
|
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
|
||||||
|
|
||||||
const u32 *src32 = (const u32 *)src;
|
const u32 *src32 = (const u32 *)src;
|
||||||
int i;
|
int i;
|
||||||
while(size >= 0x40)
|
while(size >= 0x40)
|
||||||
{
|
{
|
||||||
sha_wait_idle();
|
sha_wait_idle();
|
||||||
for(i = 0; i < 4; ++i)
|
for(i = 0; i < 4; ++i)
|
||||||
{
|
{
|
||||||
*REG_SHA_INFIFO = *src32++;
|
*REG_SHA_INFIFO = *src32++;
|
||||||
*REG_SHA_INFIFO = *src32++;
|
*REG_SHA_INFIFO = *src32++;
|
||||||
*REG_SHA_INFIFO = *src32++;
|
*REG_SHA_INFIFO = *src32++;
|
||||||
*REG_SHA_INFIFO = *src32++;
|
*REG_SHA_INFIFO = *src32++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size -= 0x40;
|
size -= 0x40;
|
||||||
}
|
}
|
||||||
|
|
||||||
sha_wait_idle();
|
sha_wait_idle();
|
||||||
memcpy((void *)REG_SHA_INFIFO, src32, size);
|
memcpy((void *)REG_SHA_INFIFO, src32, size);
|
||||||
|
|
||||||
*REG_SHA_CNT = (*REG_SHA_CNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND;
|
*REG_SHA_CNT = (*REG_SHA_CNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND;
|
||||||
|
|
||||||
while(*REG_SHA_CNT & SHA_FINAL_ROUND);
|
while(*REG_SHA_CNT & SHA_FINAL_ROUND);
|
||||||
sha_wait_idle();
|
sha_wait_idle();
|
||||||
|
|
||||||
u32 hashSize = SHA_256_HASH_SIZE;
|
u32 hashSize = SHA_256_HASH_SIZE;
|
||||||
if(mode == SHA_224_MODE)
|
if(mode == SHA_224_MODE)
|
||||||
hashSize = SHA_224_HASH_SIZE;
|
hashSize = SHA_224_HASH_SIZE;
|
||||||
else if(mode == SHA_1_MODE)
|
else if(mode == SHA_1_MODE)
|
||||||
hashSize = SHA_1_HASH_SIZE;
|
hashSize = SHA_1_HASH_SIZE;
|
||||||
|
|
||||||
memcpy(res, (void *)REG_SHA_HASH, hashSize);
|
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;
|
static u32 fatStart;
|
||||||
|
const u8 __attribute__((aligned(4))) key2s[3][AES_BLOCK_SIZE] = {
|
||||||
const u8 key2s[3][0x10] = {
|
|
||||||
{0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0},
|
{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},
|
{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}
|
{0x65, 0x29, 0x3E, 0x12, 0x56, 0x0C, 0x0B, 0xD1, 0xDD, 0xB5, 0x63, 0x1C, 0xB6, 0xD9, 0x52, 0x75}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Get Nand CTR key
|
|
||||||
void getNandCTR(void)
|
void getNandCTR(void)
|
||||||
{
|
{
|
||||||
u8 cid[0x10];
|
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||||
u8 shaSum[0x20];
|
u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE];
|
||||||
|
|
||||||
sdmmc_get_cid(1, (u32 *)cid);
|
sdmmc_get_cid(1, (u32 *)cid);
|
||||||
sha(shaSum, cid, 0x10, SHA_256_MODE);
|
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
||||||
memcpy(nandCTR, shaSum, 0x10);
|
memcpy(nandCtr, shaSum, sizeof(nandCtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Initialize the CTRNAND crypto
|
|
||||||
void ctrNandInit(void)
|
void ctrNandInit(void)
|
||||||
{
|
{
|
||||||
getNandCTR();
|
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);
|
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
nandSlot = 0x05;
|
nandSlot = 0x05;
|
||||||
fatStart = 0x5CAD7;
|
fatStart = 0x5CAD7;
|
||||||
}
|
}
|
||||||
@ -314,86 +310,81 @@ void ctrNandInit(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read and decrypt from CTRNAND
|
|
||||||
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
||||||
{
|
{
|
||||||
u8 tmpCTR[0x10];
|
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
|
||||||
memcpy(tmpCTR, nandCTR, 0x10);
|
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||||
aes_advctr(tmpCTR, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
//Read
|
//Read
|
||||||
u32 result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf);
|
u32 result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf);
|
||||||
|
|
||||||
//Decrypt
|
//Decrypt
|
||||||
aes_use_keyslot(nandSlot);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read and decrypt from the FIRM0 partition on NAND
|
|
||||||
void readFirm0(u8 *outbuf, u32 size)
|
void readFirm0(u8 *outbuf, u32 size)
|
||||||
{
|
{
|
||||||
u8 CTRtmp[0x10];
|
u8 __attribute__((aligned(4))) ctrTmp[sizeof(nandCtr)];
|
||||||
memcpy(CTRtmp, nandCTR, 0x10);
|
memcpy(ctrTmp, nandCtr, sizeof(nandCtr));
|
||||||
|
|
||||||
//Read FIRM0 data
|
//Read FIRM0 data
|
||||||
sdmmc_nand_readsectors(0x0B130000 / 0x200, size / 0x200, outbuf);
|
sdmmc_nand_readsectors(0x0B130000 / 0x200, size / 0x200, outbuf);
|
||||||
|
|
||||||
//Decrypt
|
//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_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, bool isFirm1, u32 size)
|
||||||
void writeFirm(u8 *inbuf, u32 firm, u32 size)
|
|
||||||
{
|
{
|
||||||
u32 offset = firm ? 0x0B530000 : 0x0B130000;
|
u32 offset = isFirm1 ? 0x0B530000 : 0x0B130000;
|
||||||
u8 CTRtmp[0x10];
|
u8 __attribute__((aligned(4))) ctrTmp[sizeof(nandCtr)];
|
||||||
memcpy(CTRtmp, nandCTR, 0x10);
|
memcpy(ctrTmp, nandCtr, sizeof(nandCtr));
|
||||||
|
|
||||||
//Encrypt FIRM data
|
//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_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
|
//Write to NAND
|
||||||
sdmmc_nand_writesectors(offset / 0x200, size / 0x200, inbuf);
|
sdmmc_nand_writesectors(offset / 0x200, size / 0x200, inbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setup keyslot 0x11 for key sector de/encryption
|
void setupKeyslot0x11(bool isA9lh, const void *otp)
|
||||||
void setupKeyslot0x11(u32 a9lhBoot, const void *otp)
|
|
||||||
{
|
{
|
||||||
u8 shasum[0x20];
|
u8 __attribute__((aligned(4))) shasum[SHA_256_HASH_SIZE];
|
||||||
u8 keyX[0x10];
|
u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE];
|
||||||
u8 keyY[0x10];
|
u8 __attribute__((aligned(4))) keyY[AES_BLOCK_SIZE];
|
||||||
|
|
||||||
//If booting via A9LH, use the leftover contents of the SHA register
|
//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 calculate the otp.bin hash
|
||||||
else sha(shasum, otp, 0x90, SHA_256_MODE);
|
else sha(shasum, otp, 0x90, SHA_256_MODE);
|
||||||
|
|
||||||
//Set keyX and keyY
|
//Set keyX and keyY
|
||||||
memcpy(keyX, shasum, 0x10);
|
memcpy(keyX, shasum, sizeof(keyX));
|
||||||
memcpy(keyY, shasum + 0x10, 0x10);
|
memcpy(keyY, shasum + sizeof(keyX), sizeof(keyY));
|
||||||
aes_setkey(0x11, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(0x11, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
aes_setkey(0x11, keyY, AES_KEYY, 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)
|
void generateSector(u8 *keySector, u32 mode)
|
||||||
{
|
{
|
||||||
//Inject key2
|
//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
|
//Encrypt key sector
|
||||||
aes_use_keyslot(0x11);
|
aes_use_keyslot(0x11);
|
||||||
for(u32 i = 0; i < 32; i++)
|
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)
|
void getSector(u8 *keySector)
|
||||||
{
|
{
|
||||||
//Read keysector from NAND
|
//Read keysector from NAND
|
||||||
@ -402,33 +393,30 @@ void getSector(u8 *keySector)
|
|||||||
//Decrypt key sector
|
//Decrypt key sector
|
||||||
aes_use_keyslot(0x11);
|
aes_use_keyslot(0x11);
|
||||||
for(u32 i = 0; i < 32; i++)
|
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)
|
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);
|
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)
|
u32 decryptExeFs(u8 *inbuf)
|
||||||
{
|
{
|
||||||
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
|
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
|
||||||
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 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++)
|
for(u32 i = 0; i < 8; i++)
|
||||||
ncchCTR[7 - i] = *(inbuf + 0x108 + i);
|
ncchCtr[7 - i] = *(inbuf + 0x108 + i);
|
||||||
ncchCTR[8] = 2;
|
ncchCtr[8] = 2;
|
||||||
|
|
||||||
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
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_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;
|
return exeFsSize - 0x200;
|
||||||
}
|
}
|
@ -79,16 +79,15 @@
|
|||||||
#define SHA_224_HASH_SIZE (224 / 8)
|
#define SHA_224_HASH_SIZE (224 / 8)
|
||||||
#define SHA_1_HASH_SIZE (160 / 8)
|
#define SHA_1_HASH_SIZE (160 / 8)
|
||||||
|
|
||||||
//NAND/FIRM stuff
|
extern bool isN3DS;
|
||||||
extern u32 console;
|
|
||||||
const u8 key2s[3][0x10];
|
const u8 key2s[3][0x10];
|
||||||
|
|
||||||
void getNandCTR(void);
|
void getNandCTR(void);
|
||||||
void ctrNandInit(void);
|
void ctrNandInit(void);
|
||||||
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||||
void readFirm0(u8 *outbuf, u32 size);
|
void readFirm0(u8 *outbuf, u32 size);
|
||||||
void writeFirm(u8 *inbuf, u32 offset, u32 size);
|
void writeFirm(u8 *inbuf, bool isFirm1, u32 size);
|
||||||
void setupKeyslot0x11(u32 a9lhBoot, const void *otp);
|
void setupKeyslot0x11(bool isA9lh, const void *otp);
|
||||||
void generateSector(u8 *keySector, u32 mode);
|
void generateSector(u8 *keySector, u32 mode);
|
||||||
void getSector(u8 *keySector);
|
void getSector(u8 *keySector);
|
||||||
u32 verifyHash(const void *data, u32 size, const u8 *hash);
|
u32 verifyHash(const void *data, u32 size, const u8 *hash);
|
||||||
|
102
source/draw.c
102
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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
|
* Code to print to the screen by mid-kid @CakesFW
|
||||||
|
* https://github.com/mid-kid/CakesForeveryWan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "memory.h"
|
#include "screen.h"
|
||||||
|
#include "strings.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
|
||||||
static const struct fb {
|
static void drawCharacter(char character, u32 posX, u32 posY, u32 color)
|
||||||
u8 *top_left;
|
|
||||||
u8 *top_right;
|
|
||||||
u8 *bottom;
|
|
||||||
} *const fb = (struct fb *)0x23FFFE00;
|
|
||||||
|
|
||||||
static inline int strlen(const char *string)
|
|
||||||
{
|
{
|
||||||
char *stringEnd = (char *)string;
|
u8 *select = fb->top_left;
|
||||||
|
|
||||||
while(*stringEnd) stringEnd++;
|
for(u32 y = 0; y < 8; y++)
|
||||||
|
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
char charPos = font[character * 8 + y];
|
char charPos = font[character * 8 + y];
|
||||||
|
|
||||||
for(int x = 7; x >= 0; x--)
|
for(u32 x = 0; x < 8; x++)
|
||||||
if ((charPos >> x) & 1)
|
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] = color >> 16;
|
||||||
select[screenPos + 1] = color >> 8;
|
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++)
|
for(u32 i = 0, line_i = 0; i < strlen(string); i++)
|
||||||
{
|
switch(string[i])
|
||||||
if(string[i] == '\n')
|
|
||||||
{
|
{
|
||||||
posY += SPACING_Y;
|
case '\n':
|
||||||
line_i = 0;
|
posY += SPACING_Y;
|
||||||
i++;
|
line_i = 0;
|
||||||
}
|
break;
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return posY + SPACING_Y;
|
||||||
}
|
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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
|
* Code to print to the screen by mid-kid @CakesFW
|
||||||
|
* https://github.com/mid-kid/CakesForeveryWan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define SCREEN_TOP_WIDTH 400
|
|
||||||
#define SCREEN_TOP_HEIGHT 240
|
|
||||||
|
|
||||||
#define SPACING_Y 10
|
#define SPACING_Y 10
|
||||||
#define SPACING_X 8
|
#define SPACING_X 8
|
||||||
|
|
||||||
void clearScreens(void);
|
u32 drawString(const char *string, u32 posX, u32 posY, u32 color);
|
||||||
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);
|
|
33
source/fatfs/00history.txt
Normal file → Executable file
33
source/fatfs/00history.txt
Normal file → Executable file
@ -10,7 +10,7 @@ R0.00 (February 26, 2006)
|
|||||||
|
|
||||||
R0.01 (April 29, 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)
|
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)
|
Added f_expand(). (_USE_EXPAND)
|
||||||
Changed some members in FINFO structure and behavior of f_readdir().
|
Changed some members in FINFO structure and behavior of f_readdir().
|
||||||
Added an option _USE_CHMOD and removed an option _WORD_ACCESS.
|
Added an option _USE_CHMOD.
|
||||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
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)
|
||||||
|
|
||||||
|
2
source/fatfs/00readme.txt
Normal file → Executable file
2
source/fatfs/00readme.txt
Normal file → Executable file
@ -1,4 +1,4 @@
|
|||||||
FatFs Module Source Files R0.12
|
FatFs Module Source Files R0.12a
|
||||||
|
|
||||||
|
|
||||||
FILES
|
FILES
|
||||||
|
@ -33,6 +33,7 @@ DSTATUS disk_status (
|
|||||||
/* Inidialize a Drive */
|
/* Inidialize a Drive */
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
DSTATUS disk_initialize (
|
DSTATUS disk_initialize (
|
||||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||||
)
|
)
|
||||||
|
1698
source/fatfs/ff.c
Normal file → Executable file
1698
source/fatfs/ff.c
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
104
source/fatfs/ff.h
Normal file → Executable file
104
source/fatfs/ff.h
Normal file → Executable file
@ -1,11 +1,13 @@
|
|||||||
/*---------------------------------------------------------------------------/
|
/*----------------------------------------------------------------------------/
|
||||||
/ FatFs - FAT file system module include 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.
|
/ 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,
|
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||||
/ this condition and the following disclaimer.
|
/ this condition and the following disclaimer.
|
||||||
/
|
/
|
||||||
@ -13,11 +15,11 @@
|
|||||||
/ and any warranties related to this software are DISCLAIMED.
|
/ and any warranties related to this software are DISCLAIMED.
|
||||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||||
/ by use of this software.
|
/ by use of this software.
|
||||||
/---------------------------------------------------------------------------*/
|
/----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef _FATFS
|
#ifndef _FATFS
|
||||||
#define _FATFS 88100 /* Revision ID */
|
#define _FATFS 68020 /* Revision ID */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -25,6 +27,7 @@ extern "C" {
|
|||||||
|
|
||||||
#include "integer.h" /* Basic integer types */
|
#include "integer.h" /* Basic integer types */
|
||||||
#include "ffconf.h" /* FatFs configuration options */
|
#include "ffconf.h" /* FatFs configuration options */
|
||||||
|
|
||||||
#if _FATFS != _FFCONF
|
#if _FATFS != _FFCONF
|
||||||
#error Wrong configuration file (ffconf.h).
|
#error Wrong configuration file (ffconf.h).
|
||||||
#endif
|
#endif
|
||||||
@ -52,7 +55,7 @@ extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
|||||||
|
|
||||||
/* Type of path name strings on FatFs API */
|
/* Type of path name strings on FatFs API */
|
||||||
|
|
||||||
#if _LFN_UNICODE /* Unicode string */
|
#if _LFN_UNICODE /* Unicode (UTF-16) string */
|
||||||
#if _USE_LFN == 0
|
#if _USE_LFN == 0
|
||||||
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
#error _LFN_UNICODE must be 0 at non-LFN cfg.
|
||||||
#endif
|
#endif
|
||||||
@ -61,14 +64,25 @@ typedef WCHAR TCHAR;
|
|||||||
#define _T(x) L ## x
|
#define _T(x) L ## x
|
||||||
#define _TEXT(x) L ## x
|
#define _TEXT(x) L ## x
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else /* ANSI/OEM string */
|
#else /* ANSI/OEM string */
|
||||||
#ifndef _INC_TCHAR
|
#ifndef _INC_TCHAR
|
||||||
typedef char TCHAR;
|
typedef char TCHAR;
|
||||||
#define _T(x) x
|
#define _T(x) x
|
||||||
#define _TEXT(x) x
|
#define _TEXT(x) x
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -87,6 +101,9 @@ typedef struct {
|
|||||||
#if _MAX_SS != _MIN_SS
|
#if _MAX_SS != _MIN_SS
|
||||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||||
#endif
|
#endif
|
||||||
|
#if _USE_LFN != 0
|
||||||
|
WCHAR* lfnbuf; /* LFN working buffer */
|
||||||
|
#endif
|
||||||
#if _FS_EXFAT
|
#if _FS_EXFAT
|
||||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer */
|
BYTE* dirbuf; /* Directory entry block scratchpad buffer */
|
||||||
#endif
|
#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) */
|
/* Object ID and allocation information (_FDID) */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -155,18 +159,18 @@ typedef struct {
|
|||||||
/* File object structure (FIL) */
|
/* File object structure (FIL) */
|
||||||
|
|
||||||
typedef struct {
|
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 flag; /* File status flags */
|
||||||
BYTE err; /* Abort flag (error code) */
|
BYTE err; /* Abort flag (error code) */
|
||||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
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) */
|
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||||
#if !_FS_READONLY
|
#if !_FS_READONLY
|
||||||
DWORD dir_sect; /* Sector number containing the directory entry */
|
DWORD dir_sect; /* Sector number containing the directory entry */
|
||||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
|
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
|
||||||
#endif
|
#endif
|
||||||
#if _USE_FASTSEEK
|
#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
|
#endif
|
||||||
#if !_FS_TINY
|
#if !_FS_TINY
|
||||||
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
BYTE buf[_MAX_SS]; /* File private data read/write window */
|
||||||
@ -183,10 +187,9 @@ typedef struct {
|
|||||||
DWORD clust; /* Current cluster */
|
DWORD clust; /* Current cluster */
|
||||||
DWORD sect; /* Current sector */
|
DWORD sect; /* Current sector */
|
||||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
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
|
#if _USE_LFN != 0
|
||||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||||
WCHAR* lfn; /* Pointer to the LFN working buffer */
|
|
||||||
#endif
|
#endif
|
||||||
#if _USE_FIND
|
#if _USE_FIND
|
||||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
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_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
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_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_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
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_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_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_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 a 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 a file object */
|
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||||
FRESULT f_truncate (FIL* fp); /* Truncate file */
|
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||||
FRESULT f_sync (FIL* fp); /* Flush cached data of a writing 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_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
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_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_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_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_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 the 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_chdir (const TCHAR* path); /* Change current directory */
|
||||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
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_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_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_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_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 */
|
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_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_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 */
|
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 */
|
/* 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_READ 0x01
|
||||||
#define FA_WRITE 0x02
|
#define FA_WRITE 0x02
|
||||||
#define FA_OPEN_EXISTING 0x00
|
#define FA_OPEN_EXISTING 0x00
|
||||||
#define FA_CREATE_NEW 0x04
|
#define FA_CREATE_NEW 0x04
|
||||||
#define FA_CREATE_ALWAYS 0x08
|
#define FA_CREATE_ALWAYS 0x08
|
||||||
#define FA_OPEN_ALWAYS 0x10
|
#define FA_OPEN_ALWAYS 0x10
|
||||||
#define _FA_MODIFIED 0x20
|
#define FA_OPEN_APPEND 0x30
|
||||||
#define _FA_DIRTY 0x40
|
|
||||||
|
|
||||||
|
/* 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_FAT12 1
|
||||||
#define FS_FAT16 2
|
#define FS_FAT16 2
|
||||||
#define FS_FAT32 3
|
#define FS_FAT32 3
|
||||||
#define FS_EXFAT 4
|
#define FS_EXFAT 4
|
||||||
|
|
||||||
|
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||||
/* File attribute bits for directory entry */
|
|
||||||
|
|
||||||
#define AM_RDO 0x01 /* Read only */
|
#define AM_RDO 0x01 /* Read only */
|
||||||
#define AM_HID 0x02 /* Hidden */
|
#define AM_HID 0x02 /* Hidden */
|
||||||
#define AM_SYS 0x04 /* System */
|
#define AM_SYS 0x04 /* System */
|
||||||
#define AM_VOL 0x08 /* Volume label */
|
|
||||||
#define AM_LFN 0x0F /* LFN entry */
|
|
||||||
#define AM_DIR 0x10 /* Directory */
|
#define AM_DIR 0x10 /* Directory */
|
||||||
#define AM_ARC 0x20 /* Archive */
|
#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
|
#ifdef __cplusplus
|
||||||
|
27
source/fatfs/ffconf.h
Normal file → Executable file
27
source/fatfs/ffconf.h
Normal file → Executable file
@ -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
|
/ Function Configurations
|
||||||
@ -15,7 +15,7 @@
|
|||||||
/ and optional writing functions as well. */
|
/ 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.
|
/* This option defines minimization level to remove some basic API functions.
|
||||||
/
|
/
|
||||||
/ 0: All basic functions are enabled.
|
/ 0: All basic functions are enabled.
|
||||||
@ -62,8 +62,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define _USE_FORWARD 0
|
#define _USE_FORWARD 0
|
||||||
/* This option switches f_forward() function. (0:Disable or 1:Enable)
|
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||||
/ To enable it, also _FS_TINY need to be 1. */
|
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------/
|
/*---------------------------------------------------------------------------/
|
||||||
@ -118,13 +117,13 @@
|
|||||||
|
|
||||||
|
|
||||||
#define _LFN_UNICODE 0
|
#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.
|
/ 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. */
|
/ This option also affects behavior of string I/O functions. */
|
||||||
|
|
||||||
|
|
||||||
#define _STRF_ENCODE 3
|
#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().
|
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||||
/
|
/
|
||||||
/ 0: ANSI/OEM
|
/ 0: ANSI/OEM
|
||||||
@ -153,7 +152,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define _STR_VOLUME_ID 0
|
#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.
|
/* _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
|
/ 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
|
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
|
||||||
@ -205,19 +204,19 @@
|
|||||||
|
|
||||||
#define _FS_TINY 0
|
#define _FS_TINY 0
|
||||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
/* 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
|
/ 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. */
|
/ buffer in the file system object (FATFS) is used for the file data transfer. */
|
||||||
|
|
||||||
|
|
||||||
#define _FS_EXFAT 0
|
#define _FS_EXFAT 0
|
||||||
/* This option switches support of exFAT file system in addition to the traditional
|
/* This option switches support of exFAT file system. (0:Disable or 1:Enable)
|
||||||
/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled.
|
/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)
|
||||||
/ Note that enabling exFAT discards C89 compatibility. */
|
/ Note that enabling exFAT discards C89 compatibility. */
|
||||||
|
|
||||||
|
|
||||||
#define _FS_NORTC 1
|
#define _FS_NORTC 1
|
||||||
#define _NORTC_MON 3
|
#define _NORTC_MON 1
|
||||||
#define _NORTC_MDAY 1
|
#define _NORTC_MDAY 1
|
||||||
#define _NORTC_YEAR 2016
|
#define _NORTC_YEAR 2016
|
||||||
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
|
/* 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 _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*,
|
/ 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
|
/ 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 <windows.h> // O/S definitions */
|
||||||
|
|
||||||
|
|
||||||
/*--- End of configuration options ---*/
|
/*--- End of configuration options ---*/
|
||||||
|
0
source/fatfs/integer.h
Normal file → Executable file
0
source/fatfs/integer.h
Normal file → Executable file
0
source/fatfs/option/ccsbcs.c
Normal file → Executable file
0
source/fatfs/option/ccsbcs.c
Normal file → Executable file
@ -1,4 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "../../types.h"
|
|
@ -1,9 +1,5 @@
|
|||||||
// Copyright 2014 Normmatt
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "../../types.h"
|
||||||
|
|
||||||
void ioDelay(u32 us);
|
void waitcycles(u32 us);
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
// Copyright 2014 Normmatt
|
.text
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
.arm
|
.arm
|
||||||
.global ioDelay
|
.align 4
|
||||||
.type ioDelay STT_FUNC
|
|
||||||
|
|
||||||
@ioDelay ( u32 us )
|
.global waitcycles
|
||||||
ioDelay:
|
.type waitcycles, %function
|
||||||
ldr r1, =0x18000000 @ VRAM
|
waitcycles:
|
||||||
1:
|
push {r0-r2, lr}
|
||||||
@ Loop doing uncached reads from VRAM to make loop timing more reliable
|
str r0, [sp, #4]
|
||||||
ldr r2, [r1]
|
waitcycles_loop:
|
||||||
subs r0, #1
|
ldr r3, [sp, #4]
|
||||||
bgt 1b
|
subs r2, r3, #1
|
||||||
bx lr
|
str r2, [sp, #4]
|
||||||
|
cmp r3, #0
|
||||||
|
bne waitcycles_loop
|
||||||
|
pop {r0-r2, pc}
|
||||||
|
@ -1,30 +1,55 @@
|
|||||||
// Copyright 2014 Normmatt
|
/*
|
||||||
// Licensed under GPLv2 or any later version
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// Refer to the license.txt file included.
|
* 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 "sdmmc.h"
|
||||||
#include "delay.h"
|
#include "delay.h"
|
||||||
|
|
||||||
struct mmcdevice handleNAND;
|
static struct mmcdevice handleNAND;
|
||||||
struct mmcdevice handleSD;
|
static struct mmcdevice handleSD;
|
||||||
|
|
||||||
static inline u16 sdmmc_read16(u16 reg) {
|
static inline u16 sdmmc_read16(u16 reg)
|
||||||
return *(vu16*)(SDMMC_BASE + reg);
|
{
|
||||||
|
return *(vu16 *)(SDMMC_BASE + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sdmmc_write16(u16 reg, u16 val) {
|
static inline void sdmmc_write16(u16 reg, u16 val)
|
||||||
*(vu16*)(SDMMC_BASE + reg) = val;
|
{
|
||||||
|
*(vu16 *)(SDMMC_BASE + reg) = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 sdmmc_read32(u16 reg) {
|
static inline u32 sdmmc_read32(u16 reg)
|
||||||
return *(vu32*)(SDMMC_BASE + reg);
|
{
|
||||||
|
return *(vu32 *)(SDMMC_BASE + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void sdmmc_write32(u16 reg, u32 val) {
|
static inline void sdmmc_write32(u16 reg, u32 val)
|
||||||
*(vu32*)(SDMMC_BASE + reg) = 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);
|
u16 val = sdmmc_read16(reg);
|
||||||
val &= ~clear;
|
val &= ~clear;
|
||||||
val |= set;
|
val |= set;
|
||||||
@ -38,188 +63,213 @@ static inline void setckl(u32 data)
|
|||||||
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
|
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mmcdevice *getMMCDevice(int drive)
|
mmcdevice *getMMCDevice(int drive)
|
||||||
{
|
{
|
||||||
if(drive==0) return &handleNAND;
|
if(drive == 0) return &handleNAND;
|
||||||
return &handleSD;
|
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);
|
setckl(ctx->clk);
|
||||||
if (ctx->SDOPT == 0) {
|
if(ctx->SDOPT == 0) sdmmc_mask16(REG_SDOPT, 0, 0x8000);
|
||||||
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
|
else sdmmc_mask16(REG_SDOPT, 0x8000, 0);
|
||||||
} else {
|
|
||||||
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args)
|
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;
|
u16 flags = (cmd << 15) >> 31;
|
||||||
const bool readdata = cmd & 0x20000;
|
const int readdata = cmd & 0x20000;
|
||||||
const bool writedata = cmd & 0x40000;
|
const int writedata = cmd & 0x40000;
|
||||||
|
|
||||||
if (readdata || writedata)
|
if(readdata || writedata)
|
||||||
flags |= TMIO_STAT0_DATAEND;
|
flags |= TMIO_STAT0_DATAEND;
|
||||||
|
|
||||||
ctx->error = 0;
|
ctx->error = 0;
|
||||||
while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working?
|
while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working?
|
||||||
sdmmc_write16(REG_SDIRMASK0,0);
|
sdmmc_write16(REG_SDIRMASK0, 0);
|
||||||
sdmmc_write16(REG_SDIRMASK1,0);
|
sdmmc_write16(REG_SDIRMASK1, 0);
|
||||||
sdmmc_write16(REG_SDSTATUS0,0);
|
sdmmc_write16(REG_SDSTATUS0, 0);
|
||||||
sdmmc_write16(REG_SDSTATUS1,0);
|
sdmmc_write16(REG_SDSTATUS1, 0);
|
||||||
sdmmc_mask16(REG_SDDATACTL32,0x1800,0);
|
sdmmc_mask16(REG_DATACTL32, 0x1800, 0);
|
||||||
|
sdmmc_write16(REG_SDCMDARG0, args & 0xFFFF);
|
||||||
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
|
sdmmc_write16(REG_SDCMDARG1, args >> 16);
|
||||||
sdmmc_write16(REG_SDCMDARG1,args >> 16);
|
sdmmc_write16(REG_SDCMD, cmd & 0xFFFF);
|
||||||
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
|
|
||||||
|
|
||||||
u32 size = ctx->size;
|
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;
|
u16 status0 = 0;
|
||||||
while(true) {
|
while(true)
|
||||||
u16 status1 = sdmmc_read16(REG_SDSTATUS1);
|
{
|
||||||
if (status1 & TMIO_STAT1_RXRDY) {
|
vu16 status1 = sdmmc_read16(REG_SDSTATUS1);
|
||||||
if (readdata && useBuf) {
|
vu16 ctl32 = sdmmc_read16(REG_DATACTL32);
|
||||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
|
if((ctl32 & 0x100))
|
||||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
|
{
|
||||||
if (size > 0x1FF) {
|
if(readdata)
|
||||||
for(int i = 0; i<0x200; i+=2) {
|
{
|
||||||
u16 data = sdmmc_read16(REG_SDFIFO);
|
if(rUseBuf)
|
||||||
*dataPtr++ = data & 0xFF;
|
{
|
||||||
*dataPtr++ = data >> 8;
|
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) {
|
sdmmc_mask16(REG_DATACTL32, 0x800, 0);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
ctx->error |= 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(status1 & TMIO_STAT1_CMD_BUSY)) {
|
if(!(status1 & TMIO_STAT1_CMD_BUSY))
|
||||||
|
{
|
||||||
status0 = sdmmc_read16(REG_SDSTATUS0);
|
status0 = sdmmc_read16(REG_SDSTATUS0);
|
||||||
if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
||||||
|
{
|
||||||
ctx->error |= 0x1;
|
ctx->error |= 0x1;
|
||||||
if (status0 & TMIO_STAT0_DATAEND)
|
}
|
||||||
|
if(status0 & TMIO_STAT0_DATAEND)
|
||||||
|
{
|
||||||
ctx->error |= 0x2;
|
ctx->error |= 0x2;
|
||||||
|
}
|
||||||
|
|
||||||
if ((status0 & flags) == flags)
|
if((status0 & flags) == flags)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
|
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
|
||||||
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
|
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
|
||||||
sdmmc_write16(REG_SDSTATUS0,0);
|
sdmmc_write16(REG_SDSTATUS0, 0);
|
||||||
sdmmc_write16(REG_SDSTATUS1,0);
|
sdmmc_write16(REG_SDSTATUS1, 0);
|
||||||
|
|
||||||
if (getSDRESP != 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[0] = (u32)(sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16));
|
||||||
ctx->ret[2] = (u32)sdmmc_read16(REG_SDRESP4) | (u32)(sdmmc_read16(REG_SDRESP5) << 16);
|
ctx->ret[1] = (u32)(sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16));
|
||||||
ctx->ret[3] = (u32)sdmmc_read16(REG_SDRESP6) | (u32)(sdmmc_read16(REG_SDRESP7) << 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)
|
if(handleSD.isSDHC == 0) sector_no <<= 9;
|
||||||
sector_no <<= 9;
|
|
||||||
inittarget(&handleSD);
|
inittarget(&handleSD);
|
||||||
sdmmc_write16(REG_SDSTOP,0x100);
|
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||||
|
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||||
handleSD.data = in;
|
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||||
|
handleSD.tData = in;
|
||||||
handleSD.size = numsectors << 9;
|
handleSD.size = numsectors << 9;
|
||||||
sdmmc_send_command(&handleSD,0x52C19,sector_no);
|
sdmmc_send_command(&handleSD, 0x52C19, sector_no);
|
||||||
return geterror(&handleSD);
|
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)
|
if(handleSD.isSDHC == 0) sector_no <<= 9;
|
||||||
sector_no <<= 9;
|
|
||||||
inittarget(&handleSD);
|
inittarget(&handleSD);
|
||||||
sdmmc_write16(REG_SDSTOP,0x100);
|
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||||
|
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||||
handleSD.data = out;
|
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||||
|
handleSD.rData = out;
|
||||||
handleSD.size = numsectors << 9;
|
handleSD.size = numsectors << 9;
|
||||||
sdmmc_send_command(&handleSD,0x33C12,sector_no);
|
sdmmc_send_command(&handleSD, 0x33C12, sector_no);
|
||||||
return geterror(&handleSD);
|
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)
|
if(handleNAND.isSDHC == 0) sector_no <<= 9;
|
||||||
sector_no <<= 9;
|
|
||||||
inittarget(&handleNAND);
|
inittarget(&handleNAND);
|
||||||
sdmmc_write16(REG_SDSTOP,0x100);
|
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||||
|
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||||
|
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||||
handleNAND.data = out;
|
handleNAND.rData = out;
|
||||||
handleNAND.size = numsectors << 9;
|
handleNAND.size = numsectors << 9;
|
||||||
sdmmc_send_command(&handleNAND,0x33C12,sector_no);
|
sdmmc_send_command(&handleNAND, 0x33C12, sector_no);
|
||||||
inittarget(&handleSD);
|
inittarget(&handleSD);
|
||||||
return geterror(&handleNAND);
|
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)
|
if(handleNAND.isSDHC == 0) sector_no <<= 9;
|
||||||
sector_no <<= 9;
|
|
||||||
inittarget(&handleNAND);
|
inittarget(&handleNAND);
|
||||||
sdmmc_write16(REG_SDSTOP,0x100);
|
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||||
|
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||||
|
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||||
handleNAND.data = in;
|
handleNAND.tData = in;
|
||||||
handleNAND.size = numsectors << 9;
|
handleNAND.size = numsectors << 9;
|
||||||
sdmmc_send_command(&handleNAND,0x52C19,sector_no);
|
sdmmc_send_command(&handleNAND, 0x52C19, sector_no);
|
||||||
inittarget(&handleSD);
|
inittarget(&handleSD);
|
||||||
return geterror(&handleNAND);
|
return geterror(&handleNAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 calcSDSize(u8* csd, int type)
|
static u32 calcSDSize(u8 *csd, int type)
|
||||||
{
|
{
|
||||||
u32 result = 0;
|
u32 result = 0;
|
||||||
if (type == -1) type = csd[14] >> 6;
|
if(type == -1) type = csd[14] >> 6;
|
||||||
switch (type) {
|
switch(type)
|
||||||
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
u32 block_len = csd[9] & 0xf;
|
u32 block_len = csd[9] & 0xF;
|
||||||
block_len = 1u << block_len;
|
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);
|
mult = 1u << (mult + 2);
|
||||||
result = csd[8] & 3;
|
result = csd[8] & 3;
|
||||||
result = (result << 8) | csd[7];
|
result = (result << 8) | csd[7];
|
||||||
@ -228,18 +278,42 @@ static u32 calcSDSize(u8* csd, int type)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
result = csd[7] & 0x3f;
|
result = csd[7] & 0x3F;
|
||||||
result = (result << 8) | csd[6];
|
result = (result << 8) | csd[6];
|
||||||
result = (result << 8) | csd[5];
|
result = (result << 8) | csd[5];
|
||||||
result = (result + 1) * 1024;
|
result = (result + 1) * 1024;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break; //Do nothing otherwise
|
break; //Do nothing otherwise FIXME perhaps return some error?
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void InitSD()
|
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
|
//NAND
|
||||||
handleNAND.isSDHC = 0;
|
handleNAND.isSDHC = 0;
|
||||||
@ -249,80 +323,50 @@ static void InitSD()
|
|||||||
handleNAND.clk = 0x80;
|
handleNAND.clk = 0x80;
|
||||||
handleNAND.devicenumber = 1;
|
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);
|
inittarget(&handleNAND);
|
||||||
ioDelay(0xF000);
|
waitcycles(0xF000);
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0,0);
|
sdmmc_send_command(&handleNAND, 0, 0);
|
||||||
|
|
||||||
do {
|
do
|
||||||
do {
|
{
|
||||||
sdmmc_send_command(&handleNAND,0x10701,0x100000);
|
do
|
||||||
} while ( !(handleNAND.error & 1) );
|
{
|
||||||
} while((handleNAND.ret[0] & 0x80000000) == 0);
|
sdmmc_send_command(&handleNAND, 0x10701, 0x100000);
|
||||||
|
}
|
||||||
|
while(!(handleNAND.error & 1));
|
||||||
|
}
|
||||||
|
while((handleNAND.ret[0] & 0x80000000) == 0);
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x10602,0x0);
|
sdmmc_send_command(&handleNAND, 0x10602, 0x0);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
if((handleNAND.error & 0x4)) return -1;
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x10403,handleNAND.initarg << 0x10);
|
sdmmc_send_command(&handleNAND, 0x10403, handleNAND.initarg << 0x10);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
if((handleNAND.error & 0x4)) return -1;
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x10609,handleNAND.initarg << 0x10);
|
sdmmc_send_command(&handleNAND, 0x10609, handleNAND.initarg << 0x10);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
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;
|
handleNAND.clk = 1;
|
||||||
setckl(1);
|
setckl(1);
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x10407,handleNAND.initarg << 0x10);
|
sdmmc_send_command(&handleNAND, 0x10407, handleNAND.initarg << 0x10);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
if((handleNAND.error & 0x4)) return -1;
|
||||||
|
|
||||||
handleNAND.SDOPT = 1;
|
handleNAND.SDOPT = 1;
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x10506,0x3B70100);
|
sdmmc_send_command(&handleNAND, 0x10506, 0x3B70100);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
if((handleNAND.error & 0x4)) return -1;
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x10506,0x3B90100);
|
sdmmc_send_command(&handleNAND, 0x10506, 0x3B90100);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
if((handleNAND.error & 0x4)) return -1;
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x1040D,handleNAND.initarg << 0x10);
|
sdmmc_send_command(&handleNAND, 0x1040D, handleNAND.initarg << 0x10);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
if((handleNAND.error & 0x4)) return -1;
|
||||||
|
|
||||||
sdmmc_send_command(&handleNAND,0x10410,0x200);
|
sdmmc_send_command(&handleNAND, 0x10410, 0x200);
|
||||||
if (handleNAND.error & 0x4) return -1;
|
if((handleNAND.error & 0x4)) return -1;
|
||||||
|
|
||||||
handleNAND.clk |= 0x200;
|
handleNAND.clk |= 0x200;
|
||||||
|
|
||||||
@ -333,113 +377,102 @@ static int Nand_Init()
|
|||||||
|
|
||||||
static int SD_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);
|
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 not inserted
|
||||||
if (!(*((vu16*)0x1000601c) & TMIO_STAT0_SIGSTATE)) return -1;
|
if(!(*((vu16 *)(SDMMC_BASE + REG_SDSTATUS0)) & TMIO_STAT0_SIGSTATE)) return 5;
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0,0);
|
sdmmc_send_command(&handleSD, 0, 0);
|
||||||
sdmmc_send_command(&handleSD,0x10408,0x1AA);
|
sdmmc_send_command(&handleSD, 0x10408, 0x1AA);
|
||||||
//u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E;
|
|
||||||
u32 temp = (handleSD.error & 0x1) << 0x1E;
|
u32 temp = (handleSD.error & 0x1) << 0x1E;
|
||||||
|
|
||||||
//int count = 0;
|
|
||||||
u32 temp2 = 0;
|
u32 temp2 = 0;
|
||||||
do {
|
do
|
||||||
do {
|
{
|
||||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
do
|
||||||
sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp);
|
{
|
||||||
|
sdmmc_send_command(&handleSD, 0x10437, handleSD.initarg << 0x10);
|
||||||
|
sdmmc_send_command(&handleSD, 0x10769, 0x00FF8000 | temp);
|
||||||
temp2 = 1;
|
temp2 = 1;
|
||||||
} while ( !(handleSD.error & 1) );
|
}
|
||||||
|
while(!(handleSD.error & 1));
|
||||||
} while((handleSD.ret[0] & 0x80000000) == 0);
|
}
|
||||||
|
while((handleSD.ret[0] & 0x80000000) == 0);
|
||||||
|
|
||||||
if(!((handleSD.ret[0] >> 30) & 1) || !temp)
|
if(!((handleSD.ret[0] >> 30) & 1) || !temp)
|
||||||
temp2 = 0;
|
temp2 = 0;
|
||||||
|
|
||||||
handleSD.isSDHC = temp2;
|
handleSD.isSDHC = temp2;
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0x10602,0);
|
sdmmc_send_command(&handleSD, 0x10602, 0);
|
||||||
if (handleSD.error & 0x4) return -1;
|
if((handleSD.error & 0x4)) return -1;
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0x10403,0);
|
sdmmc_send_command(&handleSD, 0x10403, 0);
|
||||||
if (handleSD.error & 0x4) return -1;
|
if((handleSD.error & 0x4)) return -2;
|
||||||
handleSD.initarg = handleSD.ret[0] >> 0x10;
|
handleSD.initarg = handleSD.ret[0] >> 0x10;
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10);
|
sdmmc_send_command(&handleSD, 0x10609, handleSD.initarg << 0x10);
|
||||||
if (handleSD.error & 0x4) return -1;
|
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;
|
handleSD.clk = 1;
|
||||||
setckl(1);
|
setckl(1);
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10);
|
sdmmc_send_command(&handleSD, 0x10507, handleSD.initarg << 0x10);
|
||||||
if (handleSD.error & 0x4) return -1;
|
if((handleSD.error & 0x4)) return -4;
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
sdmmc_send_command(&handleSD, 0x10437, handleSD.initarg << 0x10);
|
||||||
if (handleSD.error & 0x4) return -1;
|
if((handleSD.error & 0x4)) return -5;
|
||||||
|
|
||||||
handleSD.SDOPT = 1;
|
handleSD.SDOPT = 1;
|
||||||
sdmmc_send_command(&handleSD,0x10446,0x2);
|
sdmmc_send_command(&handleSD, 0x10446, 0x2);
|
||||||
if (handleSD.error & 0x4) return -1;
|
if((handleSD.error & 0x4)) return -6;
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10);
|
sdmmc_send_command(&handleSD, 0x1040D, handleSD.initarg << 0x10);
|
||||||
if (handleSD.error & 0x4) return -1;
|
if((handleSD.error & 0x4)) return -7;
|
||||||
|
|
||||||
sdmmc_send_command(&handleSD,0x10410,0x200);
|
sdmmc_send_command(&handleSD, 0x10410, 0x200);
|
||||||
if (handleSD.error & 0x4) return -1;
|
if((handleSD.error & 0x4)) return -8;
|
||||||
handleSD.clk |= 0x200;
|
handleSD.clk |= 0x200;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sdmmc_sdcard_init()
|
void sdmmc_get_cid(bool isNand, u32 *info)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sdmmc_sdcard_init()
|
||||||
{
|
{
|
||||||
InitSD();
|
InitSD();
|
||||||
int result = Nand_Init();
|
Nand_Init();
|
||||||
return result | SD_Init();
|
SD_Init();
|
||||||
}
|
|
||||||
|
|
||||||
int sdmmc_get_cid( int isNand, uint32_t *info)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
}
|
@ -1,52 +1,48 @@
|
|||||||
// Copyright 2014 Normmatt
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common.h"
|
#include "../../types.h"
|
||||||
|
|
||||||
#define SDMMC_BASE 0x10006000u
|
#define SDMMC_BASE 0x10006000
|
||||||
|
|
||||||
#define REG_SDCMD 0x00
|
#define REG_SDCMD 0x00
|
||||||
#define REG_SDPORTSEL 0x02
|
#define REG_SDPORTSEL 0x02
|
||||||
#define REG_SDCMDARG 0x04
|
#define REG_SDCMDARG 0x04
|
||||||
#define REG_SDCMDARG0 0x04
|
#define REG_SDCMDARG0 0x04
|
||||||
#define REG_SDCMDARG1 0x06
|
#define REG_SDCMDARG1 0x06
|
||||||
#define REG_SDSTOP 0x08
|
#define REG_SDSTOP 0x08
|
||||||
#define REG_SDBLKCOUNT 0x0a
|
#define REG_SDBLKCOUNT 0x0A
|
||||||
|
|
||||||
#define REG_SDRESP0 0x0c
|
#define REG_SDRESP0 0x0C
|
||||||
#define REG_SDRESP1 0x0e
|
#define REG_SDRESP1 0x0E
|
||||||
#define REG_SDRESP2 0x10
|
#define REG_SDRESP2 0x10
|
||||||
#define REG_SDRESP3 0x12
|
#define REG_SDRESP3 0x12
|
||||||
#define REG_SDRESP4 0x14
|
#define REG_SDRESP4 0x14
|
||||||
#define REG_SDRESP5 0x16
|
#define REG_SDRESP5 0x16
|
||||||
#define REG_SDRESP6 0x18
|
#define REG_SDRESP6 0x18
|
||||||
#define REG_SDRESP7 0x1a
|
#define REG_SDRESP7 0x1A
|
||||||
|
|
||||||
#define REG_SDSTATUS0 0x1c
|
#define REG_SDSTATUS0 0x1C
|
||||||
#define REG_SDSTATUS1 0x1e
|
#define REG_SDSTATUS1 0x1E
|
||||||
|
|
||||||
#define REG_SDIRMASK0 0x20
|
#define REG_SDIRMASK0 0x20
|
||||||
#define REG_SDIRMASK1 0x22
|
#define REG_SDIRMASK1 0x22
|
||||||
#define REG_SDCLKCTL 0x24
|
#define REG_SDCLKCTL 0x24
|
||||||
|
|
||||||
#define REG_SDBLKLEN 0x26
|
#define REG_SDBLKLEN 0x26
|
||||||
#define REG_SDOPT 0x28
|
#define REG_SDOPT 0x28
|
||||||
#define REG_SDFIFO 0x30
|
#define REG_SDFIFO 0x30
|
||||||
|
|
||||||
#define REG_SDDATACTL 0xd8
|
#define REG_DATACTL 0xD8
|
||||||
#define REG_SDRESET 0xe0
|
#define REG_SDRESET 0xE0
|
||||||
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
|
#define REG_SDPROTECTED 0xF6 //bit 0 determines if sd is protected or not?
|
||||||
|
|
||||||
#define REG_SDDATACTL32 0x100
|
#define REG_DATACTL32 0x100
|
||||||
#define REG_SDBLKLEN32 0x104
|
#define REG_SDBLKLEN32 0x104
|
||||||
#define REG_SDBLKCOUNT32 0x108
|
#define REG_SDBLKCOUNT32 0x108
|
||||||
#define REG_SDFIFO32 0x10C
|
#define REG_SDFIFO32 0x10C
|
||||||
|
|
||||||
#define REG_CLK_AND_WAIT_CTL 0x138
|
#define REG_CLK_AND_WAIT_CTL 0x138
|
||||||
#define REG_RESET_SDIO 0x1e0
|
#define REG_RESET_SDIO 0x1E0
|
||||||
|
|
||||||
#define TMIO_STAT0_CMDRESPEND 0x0001
|
#define TMIO_STAT0_CMDRESPEND 0x0001
|
||||||
#define TMIO_STAT0_DATAEND 0x0004
|
#define TMIO_STAT0_DATAEND 0x0004
|
||||||
@ -70,31 +66,7 @@
|
|||||||
#define TMIO_STAT1_CMD_BUSY 0x4000
|
#define TMIO_STAT1_CMD_BUSY 0x4000
|
||||||
#define TMIO_STAT1_ILL_ACCESS 0x8000
|
#define TMIO_STAT1_ILL_ACCESS 0x8000
|
||||||
|
|
||||||
//Comes from TWLSDK mongoose.tef DWARF info
|
#define TMIO_MASK_ALL 0x837F031D
|
||||||
#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_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
|
#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)
|
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)
|
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
|
||||||
|
|
||||||
typedef struct mmcdevice {
|
typedef struct mmcdevice {
|
||||||
vu8* data;
|
u8 *rData;
|
||||||
|
const u8 *tData;
|
||||||
u32 size;
|
u32 size;
|
||||||
u32 error;
|
u32 error;
|
||||||
u16 stat0;
|
u16 stat0;
|
||||||
@ -118,11 +91,10 @@ typedef struct mmcdevice {
|
|||||||
u32 res;
|
u32 res;
|
||||||
} mmcdevice;
|
} mmcdevice;
|
||||||
|
|
||||||
int sdmmc_sdcard_init();
|
void sdmmc_sdcard_init();
|
||||||
u32 sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out);
|
||||||
u32 sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in);
|
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);
|
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);
|
|
96
source/fs.c
96
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "fs.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "strings.h"
|
||||||
#include "fatfs/ff.h"
|
#include "fatfs/ff.h"
|
||||||
|
|
||||||
static FATFS fs;
|
static FATFS fs;
|
||||||
@ -18,71 +37,92 @@ u32 mountCTRNAND(void)
|
|||||||
return f_mount(&fs, "1:", 1) == FR_OK;
|
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;
|
FIL file;
|
||||||
u32 size;
|
u32 ret = 0;
|
||||||
|
|
||||||
if(f_open(&file, path, FA_READ) == FR_OK)
|
if(f_open(&file, path, FA_READ) == FR_OK)
|
||||||
{
|
{
|
||||||
unsigned int read;
|
u32 size = f_size(&file);
|
||||||
size = f_size(&file);
|
if(dest == NULL) ret = size;
|
||||||
f_read(&file, dest, size, &read);
|
else if(!(maxSize > 0 && size > maxSize))
|
||||||
|
f_read(&file, dest, size, (unsigned int *)&ret);
|
||||||
f_close(&file);
|
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;
|
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;
|
unsigned int written;
|
||||||
f_write(&file, buffer, size, &written);
|
f_write(&file, buffer, size, &written);
|
||||||
f_close(&file);
|
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)
|
u32 firmRead(void *dest)
|
||||||
{
|
{
|
||||||
const char *firmFolders[] = { "00000002", "20000002" };
|
const char *firmFolders[] = { "00000002", "20000002" };
|
||||||
char path[48] = "1:/title/00040138/00000000/content";
|
char path[48] = "1:/title/00040138/";
|
||||||
memcpy(&path[18], firmFolders[console], 8);
|
concatenateStrings(path, firmFolders[isN3DS ? 1 : 0]);
|
||||||
|
concatenateStrings(path, "/content");
|
||||||
|
|
||||||
DIR dir;
|
DIR dir;
|
||||||
FILINFO info;
|
FILINFO info;
|
||||||
|
|
||||||
f_opendir(&dir, path);
|
f_opendir(&dir, path);
|
||||||
|
|
||||||
u32 id = 0xFFFFFFFF,
|
u32 firmVersion = 0xFFFFFFFF,
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
//Parse the target directory
|
//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
|
//Not a cxi
|
||||||
if(info.altname[9] != 'A') continue;
|
if(info.fname[9] != 'a') continue;
|
||||||
|
|
||||||
//Multiple cxis were found
|
//Multiple cxis were found
|
||||||
if(id != 0xFFFFFFFF) ret = 1;
|
if(firmVersion != 0xFFFFFFFF) ret = 1;
|
||||||
|
|
||||||
//Convert the .app name to an integer
|
//Convert the .app name to an integer
|
||||||
u32 tempId = 0;
|
u32 tempVersion = 0;
|
||||||
for(char *tmp = info.altname; *tmp != '.'; tmp++)
|
for(char *tmp = info.altname; *tmp != '.'; tmp++)
|
||||||
{
|
{
|
||||||
tempId <<= 4;
|
tempVersion <<= 4;
|
||||||
tempId += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0';
|
tempVersion += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
//FIRM is equal or newer than 11.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
|
//Found an older cxi
|
||||||
if(tempId < id) id = tempId;
|
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
f_closedir(&dir);
|
f_closedir(&dir);
|
||||||
@ -90,20 +130,12 @@ u32 firmRead(void *dest)
|
|||||||
if(!ret)
|
if(!ret)
|
||||||
{
|
{
|
||||||
//Complete the string with the .app name
|
//Complete the string with the .app name
|
||||||
memcpy(&path[34], "/00000000.app", 14);
|
concatenateStrings(path, "/00000000.app");
|
||||||
|
|
||||||
//Last digit of the .app
|
|
||||||
u32 i = 42;
|
|
||||||
|
|
||||||
//Convert back the .app name from integer to array
|
//Convert back the .app name from integer to array
|
||||||
while(id)
|
hexItoa(firmVersion, &path[35], 8);
|
||||||
{
|
|
||||||
static const char hexDigits[] = "0123456789ABCDEF";
|
|
||||||
path[i--] = hexDigits[id & 0xF];
|
|
||||||
id >>= 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileRead(dest, path);
|
fileRead(dest, path, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
26
source/fs.h
26
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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
|
#pragma once
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
extern u32 console;
|
extern bool isN3DS;
|
||||||
|
|
||||||
u32 mountSD(void);
|
u32 mountSD(void);
|
||||||
u32 mountCTRNAND(void);
|
u32 mountCTRNAND(void);
|
||||||
u32 fileRead(void *dest, const char *path);
|
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||||
void fileWrite(const void *buffer, const char *path, u32 size);
|
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||||
u32 firmRead(void *dest);
|
u32 firmRead(void *dest);
|
65
source/i2c.c
65
source/i2c.c
@ -10,50 +10,58 @@ static const struct { u8 bus_id, reg_addr; } dev_data[] = {
|
|||||||
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
|
{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;
|
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;
|
return dev_data[device_id].reg_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static vu8* reg_data_addrs[] = {
|
static vu8 *reg_data_addrs[] = {
|
||||||
(vu8*)(I2C1_REG_OFF + I2C_REG_DATA),
|
(vu8 *)(I2C1_REG_OFF + I2C_REG_DATA),
|
||||||
(vu8*)(I2C2_REG_OFF + I2C_REG_DATA),
|
(vu8 *)(I2C2_REG_OFF + I2C_REG_DATA),
|
||||||
(vu8*)(I2C3_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];
|
return reg_data_addrs[bus_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static vu8* reg_cnt_addrs[] = {
|
static vu8 *reg_cnt_addrs[] = {
|
||||||
(vu8*)(I2C1_REG_OFF + I2C_REG_CNT),
|
(vu8 *)(I2C1_REG_OFF + I2C_REG_CNT),
|
||||||
(vu8*)(I2C2_REG_OFF + I2C_REG_CNT),
|
(vu8 *)(I2C2_REG_OFF + I2C_REG_CNT),
|
||||||
(vu8*)(I2C3_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];
|
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);
|
while (*i2cGetCntReg(bus_id) & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 i2cGetResult(u8 bus_id) {
|
static inline bool i2cGetResult(u8 bus_id)
|
||||||
|
{
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
|
|
||||||
return (*i2cGetCntReg(bus_id) >> 4) & 1;
|
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;
|
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetCntReg(bus_id) = 0xC5;
|
*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);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetDataReg(bus_id) = dev_reg;
|
*i2cGetDataReg(bus_id) = dev_reg;
|
||||||
*i2cGetCntReg(bus_id) = 0xC2;
|
*i2cGetCntReg(bus_id) = 0xC2;
|
||||||
|
|
||||||
return i2cGetResult(bus_id);
|
return i2cGetResult(bus_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 i2cSelectRegister(u8 bus_id, u8 reg) {
|
static bool i2cSelectRegister(u8 bus_id, u8 reg)
|
||||||
|
{
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetDataReg(bus_id) = reg;
|
*i2cGetDataReg(bus_id) = reg;
|
||||||
*i2cGetCntReg(bus_id) = 0xC0;
|
*i2cGetCntReg(bus_id) = 0xC0;
|
||||||
|
|
||||||
return i2cGetResult(bus_id);
|
return i2cGetResult(bus_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data) {
|
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||||
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
{
|
||||||
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
u8 bus_id = i2cGetDeviceBusId(dev_id),
|
||||||
|
dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for(u32 i = 0; i < 8; i++)
|
||||||
if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) {
|
{
|
||||||
|
if(i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg))
|
||||||
|
{
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetDataReg(bus_id) = data;
|
*i2cGetDataReg(bus_id) = data;
|
||||||
*i2cGetCntReg(bus_id) = 0xC1;
|
*i2cGetCntReg(bus_id) = 0xC1;
|
||||||
i2cStop(bus_id, 0);
|
i2cStop(bus_id, 0);
|
||||||
if (i2cGetResult(bus_id))
|
|
||||||
return 1;
|
if(i2cGetResult(bus_id)) return true;
|
||||||
}
|
}
|
||||||
*i2cGetCntReg(bus_id) = 0xC5;
|
*i2cGetCntReg(bus_id) = 0xC5;
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
@ -15,4 +15,4 @@
|
|||||||
#define I2C_DEV_GYRO 10
|
#define I2C_DEV_GYRO 10
|
||||||
#define I2C_DEV_IR 13
|
#define I2C_DEV_IR 13
|
||||||
|
|
||||||
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
@ -6,7 +6,7 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "screeninit.h"
|
#include "screen.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "fatfs/sdmmc/sdmmc.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
|
0x2D, 0x3D, 0x56, 0x6C, 0x6A, 0x1A, 0x8E, 0x52, 0x54, 0xE3, 0x89, 0xC2, 0x95, 0x06, 0x23, 0xE5
|
||||||
};
|
};
|
||||||
|
|
||||||
int posY;
|
u32 posY;
|
||||||
|
bool isN3DS;
|
||||||
u32 console;
|
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
@ -41,54 +40,54 @@ void main(void)
|
|||||||
sdmmc_sdcard_init();
|
sdmmc_sdcard_init();
|
||||||
|
|
||||||
//Determine if booting with A9LH
|
//Determine if booting with A9LH
|
||||||
u32 a9lhBoot = !PDN_SPI_CNT;
|
bool isA9lh = !PDN_SPI_CNT;
|
||||||
|
|
||||||
//Detect the console being used
|
//Detect the console being used
|
||||||
console = PDN_MPCORE_CFG == 7;
|
isN3DS = PDN_MPCORE_CFG == 7;
|
||||||
|
|
||||||
drawString(TITLE, 10, 10, COLOR_TITLE);
|
drawString(TITLE, 10, 10, COLOR_TITLE);
|
||||||
posY = drawString("Thanks to delebile, #cakey and StandardBus", 10, 40, COLOR_WHITE);
|
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);
|
posY = drawString("Press any other button to shutdown", 10, posY, COLOR_WHITE);
|
||||||
|
|
||||||
u32 pressed = waitInput();
|
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);
|
shutdown(0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void installer(u32 a9lhBoot)
|
static inline void installer(bool isA9lh)
|
||||||
{
|
{
|
||||||
if(!mountSD())
|
if(!mountSD())
|
||||||
shutdown(1, "Error: failed to mount the SD card");
|
shutdown(1, "Error: failed to mount the SD card");
|
||||||
|
|
||||||
const char *path;
|
bool updateA9lh = false;
|
||||||
u32 updatea9lh = 0;
|
|
||||||
|
|
||||||
//If making a first install, we need the OTP
|
//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};
|
const u8 zeroes[256] = {0};
|
||||||
path = "a9lh/otp.bin";
|
|
||||||
|
//Prefer OTP from memory if available
|
||||||
if(memcmp((void *)OTP_FROM_MEM, zeroes, 256) == 0)
|
if(memcmp((void *)OTP_FROM_MEM, zeroes, 256) == 0)
|
||||||
{
|
{
|
||||||
// Read OTP from file
|
// Read OTP from file
|
||||||
if(fileRead((void *)OTP_OFFSET, path) != 256)
|
if(!fileRead((void *)OTP_OFFSET, otpPath, 256))
|
||||||
{
|
|
||||||
shutdown(1, "Error: otp.bin doesn't exist and can't be dumped");
|
shutdown(1, "Error: otp.bin doesn't exist and can't be dumped");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Write OTP from memory to file
|
//Write OTP from memory to file
|
||||||
fileWrite((void *)OTP_FROM_MEM, path, 256);
|
fileWrite((void *)OTP_FROM_MEM, otpPath, 256);
|
||||||
memcpy((void *)OTP_OFFSET, (void *)OTP_FROM_MEM, 256);
|
memcpy((void *)OTP_OFFSET, (void *)OTP_FROM_MEM, 256);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setup the key sector de/encryption with the SHA register or otp.bin
|
//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
|
//Calculate the CTR for the 3DS partitions
|
||||||
getNandCTR();
|
getNandCTR();
|
||||||
@ -99,7 +98,7 @@ static inline void installer(u32 a9lhBoot)
|
|||||||
shutdown(1, "Error: failed to setup FIRM encryption");
|
shutdown(1, "Error: failed to setup FIRM encryption");
|
||||||
|
|
||||||
//If booting from A9LH or on N3DS, we can use the key sector from NAND
|
//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);
|
getSector((u8 *)SECTOR_OFFSET);
|
||||||
|
|
||||||
@ -107,28 +106,26 @@ static inline void installer(u32 a9lhBoot)
|
|||||||
for(i = 0; i < 3; i++)
|
for(i = 0; i < 3; i++)
|
||||||
if(memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[i], 0x10) == 0) break;
|
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");
|
"Error: the otp.bin or the NAND key sector\nare invalid");
|
||||||
else if(i == 1) updatea9lh = 1;
|
else if(i == 1) updateA9lh = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Read decrypted key sector
|
//Read decrypted key sector
|
||||||
path = "a9lh/secret_sector.bin";
|
if(!fileRead((void *)SECTOR_OFFSET, "a9lh/secret_sector.bin", 0x200))
|
||||||
if(fileRead((void *)SECTOR_OFFSET, path) != 0x200)
|
|
||||||
shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size");
|
shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size");
|
||||||
if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash))
|
if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash))
|
||||||
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
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
|
//Generate and encrypt a per-console A9LH key sector
|
||||||
generateSector((u8 *)SECTOR_OFFSET, 0);
|
generateSector((u8 *)SECTOR_OFFSET, 0);
|
||||||
|
|
||||||
//Read FIRM0
|
//Read FIRM0
|
||||||
path = "a9lh/firm0.bin";
|
if(!fileRead((void *)FIRM0_OFFSET, "a9lh/firm0.bin", FIRM0_SIZE))
|
||||||
if(fileRead((void *)FIRM0_OFFSET, path) != FIRM0_SIZE)
|
|
||||||
shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size");
|
shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size");
|
||||||
|
|
||||||
if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, firm0Hash))
|
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))
|
else if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash))
|
||||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
shutdown(1, "Error: NAND FIRM0 is invalid");
|
||||||
|
|
||||||
if(!a9lhBoot)
|
if(!isA9lh)
|
||||||
{
|
{
|
||||||
//Read FIRM1
|
//Read FIRM1
|
||||||
path = "a9lh/firm1.bin";
|
if(!fileRead((void *)FIRM1_OFFSET, "a9lh/firm1.bin", FIRM1_SIZE))
|
||||||
if(fileRead((void *)FIRM1_OFFSET, path) != FIRM1_SIZE)
|
|
||||||
shutdown(1, "Error: firm1.bin doesn't exist or has a wrong size");
|
shutdown(1, "Error: firm1.bin doesn't exist or has a wrong size");
|
||||||
|
|
||||||
if(!verifyHash((void *)FIRM1_OFFSET, FIRM1_SIZE, firm1Hash))
|
if(!verifyHash((void *)FIRM1_OFFSET, FIRM1_SIZE, firm1Hash))
|
||||||
@ -150,9 +146,7 @@ static inline void installer(u32 a9lhBoot)
|
|||||||
|
|
||||||
//Inject stage1
|
//Inject stage1
|
||||||
memset32((void *)STAGE1_OFFSET, 0, MAX_STAGE1_SIZE);
|
memset32((void *)STAGE1_OFFSET, 0, MAX_STAGE1_SIZE);
|
||||||
path = "a9lh/payload_stage1.bin";
|
if(!fileRead((void *)STAGE1_OFFSET, "a9lh/payload_stage1.bin", MAX_STAGE1_SIZE))
|
||||||
u32 size = fileRead((void *)STAGE1_OFFSET, path);
|
|
||||||
if(!size || size > MAX_STAGE1_SIZE)
|
|
||||||
shutdown(1, "Error: payload_stage1.bin doesn't exist or\nexceeds max size");
|
shutdown(1, "Error: payload_stage1.bin doesn't exist or\nexceeds max size");
|
||||||
|
|
||||||
const u8 zeroes[688] = {0};
|
const u8 zeroes[688] = {0};
|
||||||
@ -161,20 +155,18 @@ static inline void installer(u32 a9lhBoot)
|
|||||||
|
|
||||||
//Read stage2
|
//Read stage2
|
||||||
memset32((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE);
|
memset32((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE);
|
||||||
path = "a9lh/payload_stage2.bin";
|
if(!fileRead((void *)STAGE2_OFFSET, "a9lh/payload_stage2.bin", MAX_STAGE2_SIZE))
|
||||||
size = fileRead((void *)STAGE2_OFFSET, path);
|
|
||||||
if(!size || size > MAX_STAGE2_SIZE)
|
|
||||||
shutdown(1, "Error: payload_stage2.bin doesn't exist or\nexceeds max 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);
|
posY = drawString("All checks passed, installing...", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||||
|
|
||||||
//Point of no return, install stuff in the safest order
|
//Point of no return, install stuff in the safest order
|
||||||
sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (vu8 *)STAGE2_OFFSET);
|
sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET);
|
||||||
if(!a9lhBoot) writeFirm((u8 *)FIRM1_OFFSET, 1, FIRM1_SIZE);
|
if(!isA9lh) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE);
|
||||||
if(!a9lhBoot || updatea9lh) sdmmc_nand_writesectors(0x96, 1, (vu8 *)SECTOR_OFFSET);
|
if(!isA9lh || updateA9lh) sdmmc_nand_writesectors(0x96, 1, (u8 *)SECTOR_OFFSET);
|
||||||
writeFirm((u8 *)FIRM0_OFFSET, 0, FIRM0_SIZE);
|
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)
|
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
|
//New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector
|
||||||
if(console)
|
if(isN3DS)
|
||||||
{
|
{
|
||||||
setupKeyslot0x11(1, NULL);
|
setupKeyslot0x11(1, NULL);
|
||||||
getSector((u8 *)SECTOR_OFFSET);
|
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);
|
posY = drawString("All checks passed, uninstalling...", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||||
|
|
||||||
//Point of no return, install stuff in the safest order
|
//Point of no return, install stuff in the safest order
|
||||||
sdmmc_nand_writesectors(0x96, 1, (vu8 *)SECTOR_OFFSET);
|
sdmmc_nand_writesectors(0x96, 1, (u8 *)SECTOR_OFFSET);
|
||||||
writeFirm((u8 *)FIRM0_OFFSET, 0, firmSize);
|
writeFirm((u8 *)FIRM0_OFFSET, false, firmSize);
|
||||||
writeFirm((u8 *)FIRM1_OFFSET, 1, firmSize);
|
writeFirm((u8 *)FIRM1_OFFSET, true, firmSize);
|
||||||
sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (vu8 *)STAGE2_OFFSET);
|
sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET);
|
||||||
|
|
||||||
shutdown(2, "Uninstall: success!");
|
shutdown(2, "Uninstall: success!");
|
||||||
}
|
}
|
@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define PDN_MPCORE_CFG (*(vu8 *)0x10140FFC)
|
#define PDN_MPCORE_CFG (*(vu8 *)0x10140FFC)
|
||||||
#define PDN_SPI_CNT (*(vu8 *)0x101401C0)
|
#define PDN_SPI_CNT (*(vu8 *)0x101401C0)
|
||||||
|
|
||||||
#define OTP_FROM_MEM 0x10012000
|
#define OTP_FROM_MEM 0x10012000
|
||||||
#define OTP_OFFSET 0x24000000
|
#define OTP_OFFSET 0x24000000
|
||||||
@ -23,5 +23,5 @@
|
|||||||
#define MAX_STAGE1_SIZE 0x1E70
|
#define MAX_STAGE1_SIZE 0x1E70
|
||||||
#define MAX_STAGE2_SIZE 0x89A00
|
#define MAX_STAGE2_SIZE 0x89A00
|
||||||
|
|
||||||
static inline void installer(u32 a9lhBoot);
|
static inline void installer(bool isA9lh);
|
||||||
static inline void uninstaller(void);
|
static inline void uninstaller(void);
|
@ -1,5 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* memory.c
|
* memory.c
|
||||||
|
*
|
||||||
|
* memcpy, memset32 and memcmp adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
@ -17,19 +19,19 @@ void memset32(void *dest, u32 filler, u32 size)
|
|||||||
{
|
{
|
||||||
u32 *dest32 = (u32 *)dest;
|
u32 *dest32 = (u32 *)dest;
|
||||||
|
|
||||||
for (u32 i = 0; i < size / 4; i++)
|
for(u32 i = 0; i < size / 4; i++)
|
||||||
dest32[i] = filler;
|
dest32[i] = filler;
|
||||||
}
|
}
|
||||||
|
|
||||||
int memcmp(const void *buf1, const void *buf2, u32 size)
|
int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||||
{
|
{
|
||||||
const u8 *buf1c = (const u8 *)buf1;
|
const u8 *buf1c = (const u8 *)buf1,
|
||||||
const u8 *buf2c = (const u8 *)buf2;
|
*buf2c = (const u8 *)buf2;
|
||||||
|
|
||||||
for(u32 i = 0; i < size; i++)
|
for(u32 i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
int cmp = buf1c[i] - buf2c[i];
|
int cmp = buf1c[i] - buf2c[i];
|
||||||
if(cmp) return cmp;
|
if(cmp != 0) return cmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* memory.h
|
* memory.h
|
||||||
|
*
|
||||||
|
* memcpy, memset32 and memcmp adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
209
source/screen.c
Normal file
209
source/screen.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
}
|
49
source/screen.h
Normal file
49
source/screen.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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);
|
@ -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();
|
|
||||||
}
|
|
@ -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);
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
@
|
||||||
|
@ 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
|
.section .text.start
|
||||||
.align 4
|
.align 4
|
||||||
.global _start
|
.global _start
|
||||||
@ -5,6 +27,11 @@ _start:
|
|||||||
@ Change the stack pointer
|
@ Change the stack pointer
|
||||||
mov sp, #0x27000000
|
mov sp, #0x27000000
|
||||||
|
|
||||||
|
@ Disable interrupts
|
||||||
|
mrs r0, cpsr
|
||||||
|
orr r0, #0x1C0
|
||||||
|
msr cpsr_cx, r0
|
||||||
|
|
||||||
@ Disable caches / MPU
|
@ Disable caches / MPU
|
||||||
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||||
bic r0, #(1<<12) @ - instruction cache disable
|
bic r0, #(1<<12) @ - instruction cache disable
|
||||||
@ -12,21 +39,25 @@ _start:
|
|||||||
bic r0, #(1<<0) @ - mpu disable
|
bic r0, #(1<<0) @ - mpu disable
|
||||||
mcr p15, 0, r0, c1, c0, 0 @ write control register
|
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
|
@ 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, 2 @ write data access
|
||||||
mcr p15, 0, r0, c5, c0, 3 @ write instruction access
|
mcr p15, 0, r0, c5, c0, 3 @ write instruction access
|
||||||
|
|
||||||
@ Set MPU permissions and cache settings
|
@ Set MPU permissions and cache settings
|
||||||
ldr r0, =0xFFFF001D @ ffff0000 32k | bootrom (unprotected part)
|
ldr r0, =0xFFFF001D @ ffff0000 32k | bootrom (unprotected part)
|
||||||
ldr r1, =0x3000801B @ fff00000 16k | dtcm
|
ldr r1, =0x01FF801D @ 01ff8000 32k | itcm
|
||||||
ldr r2, =0x01FF801D @ 01ff8000 32k | itcm
|
ldr r2, =0x08000029 @ 08000000 2M | arm9 mem (O3DS / N3DS)
|
||||||
ldr r3, =0x08000029 @ 08000000 2M | arm9 mem (O3DS / N3DS)
|
ldr r3, =0x10000029 @ 10000000 2M | io mem (ARM9 / first 2MB)
|
||||||
ldr r4, =0x10000029 @ 10000000 2M | io mem (ARM9 / first 2MB)
|
ldr r4, =0x20000037 @ 20000000 256M | fcram (O3DS / N3DS)
|
||||||
ldr r5, =0x20000037 @ 20000000 256M | fcram (O3DS / N3DS)
|
ldr r5, =0x1FF00027 @ 1FF00000 1M | dsp / axi wram
|
||||||
ldr r6, =0x1FF00027 @ 1FF00000 1M | dsp / axi wram
|
ldr r6, =0x1800002D @ 18000000 8M | vram (+ 2MB)
|
||||||
ldr r7, =0x1800002D @ 18000000 8M | vram (+ 2MB)
|
mov r7, #0
|
||||||
mov r8, #0x29
|
mov r8, #0x15
|
||||||
mcr p15, 0, r0, c6, c0, 0
|
mcr p15, 0, r0, c6, c0, 0
|
||||||
mcr p15, 0, r1, c6, c1, 0
|
mcr p15, 0, r1, c6, c1, 0
|
||||||
mcr p15, 0, r2, c6, c2, 0
|
mcr p15, 0, r2, c6, c2, 0
|
||||||
@ -35,23 +66,19 @@ _start:
|
|||||||
mcr p15, 0, r5, c6, c5, 0
|
mcr p15, 0, r5, c6, c5, 0
|
||||||
mcr p15, 0, r6, c6, c6, 0
|
mcr p15, 0, r6, c6, c6, 0
|
||||||
mcr p15, 0, r7, c6, c7, 0
|
mcr p15, 0, r7, c6, c7, 0
|
||||||
mcr p15, 0, r8, c3, c0, 0 @ Write bufferable 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, 3, 5
|
mcr p15, 0, r8, c2, c0, 0 @ Data cacheable 0, 2, 4
|
||||||
mcr p15, 0, r8, c2, c0, 1 @ Inst cacheable 0, 3, 5
|
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
|
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<<12) @ - instruction cache enable
|
||||||
orr r0, r0, #(1<<2) @ - data cache enable
|
orr r0, r0, #(1<<2) @ - data cache enable
|
||||||
orr r0, r0, #(1<<0) @ - mpu enable
|
orr r0, r0, #(1<<0) @ - mpu enable
|
||||||
mcr p15, 0, r0, c1, c0, 0 @ write control register
|
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
|
@ Fix mounting of SDMC
|
||||||
ldr r0, =0x10000020
|
ldr r0, =0x10000020
|
||||||
mov r1, #0x340
|
mov r1, #0x340
|
||||||
|
55
source/strings.c
Normal file
55
source/strings.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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';
|
||||||
|
}
|
29
source/strings.h
Normal file
29
source/strings.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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);
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
//Common data types
|
//Common data types
|
||||||
typedef uint8_t u8;
|
typedef uint8_t u8;
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
|
#include "screen.h"
|
||||||
|
#include "cache.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
|
|
||||||
u32 waitInput(void)
|
u32 waitInput(void)
|
||||||
{
|
{
|
||||||
u32 pressedKey = 0,
|
bool pressedKey = false;
|
||||||
key;
|
u32 key;
|
||||||
|
|
||||||
//Wait for no keys to be pressed
|
//Wait for no keys to be pressed
|
||||||
while(HID_PAD);
|
while(HID_PAD);
|
||||||
@ -22,10 +24,10 @@ u32 waitInput(void)
|
|||||||
key = HID_PAD;
|
key = HID_PAD;
|
||||||
|
|
||||||
//Make sure it's pressed
|
//Make sure it's pressed
|
||||||
for(u32 i = 0x13000; i; i--)
|
for(u32 i = 0x13000; i > 0; i--)
|
||||||
{
|
{
|
||||||
if(key != HID_PAD) break;
|
if(key != HID_PAD) break;
|
||||||
if(i == 1) pressedKey = 1;
|
if(i == 1) pressedKey = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(!pressedKey);
|
while(!pressedKey);
|
||||||
@ -41,6 +43,9 @@ void shutdown(u32 mode, const char *message)
|
|||||||
drawString("Press any button to shutdown", 10, posY, COLOR_WHITE);
|
drawString("Press any button to shutdown", 10, posY, COLOR_WHITE);
|
||||||
waitInput();
|
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);
|
||||||
}
|
}
|
@ -21,7 +21,7 @@
|
|||||||
#define COLOR_RED 0x0000FF
|
#define COLOR_RED 0x0000FF
|
||||||
#define COLOR_GREEN 0x00FF00
|
#define COLOR_GREEN 0x00FF00
|
||||||
|
|
||||||
extern int posY;
|
extern u32 posY;
|
||||||
|
|
||||||
u32 waitInput(void);
|
u32 waitInput(void);
|
||||||
void shutdown(u32 mode, const char *message);
|
void shutdown(u32 mode, const char *message);
|
Loading…
x
Reference in New Issue
Block a user