mirror of
https://github.com/LNH-team/dspico-firmware.git
synced 2026-06-02 09:16:50 +02:00
Initial commit
This commit is contained in:
28
src/blowfish.c
Normal file
28
src/blowfish.c
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "common.h"
|
||||
#include "blowfish.h"
|
||||
|
||||
void bf_init(bf_context_t* context, const u32* pTable, const bf_sboxes_t* sBoxes)
|
||||
{
|
||||
context->pTable = pTable;
|
||||
context->sBoxes = sBoxes;
|
||||
}
|
||||
|
||||
u64 __time_critical_func(bf_decrypt)(const bf_context_t* context, u64 block)
|
||||
{
|
||||
const u32* pTable = context->pTable;
|
||||
const bf_sboxes_t* sBoxes = context->sBoxes;
|
||||
u32 y = block;
|
||||
u32 x = block >> 32;
|
||||
for (int i = 0x11; i >= 2; i--)
|
||||
{
|
||||
u32 z = pTable[i] ^ x;
|
||||
u32 a = sBoxes->sbox0[z >> 24];
|
||||
u32 b = sBoxes->sbox1[(u8)(z >> 16)];
|
||||
u32 c = sBoxes->sbox2[(u8)(z >> 8)];
|
||||
u32 d = sBoxes->sbox3[(u8)z];
|
||||
x = (d + (c ^ (b + a))) ^ y;
|
||||
y = z;
|
||||
}
|
||||
|
||||
return (x ^ pTable[1]) | ((u64) (y ^ pTable[0]) << 32);
|
||||
}
|
||||
38
src/blowfish.h
Normal file
38
src/blowfish.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
/// @brief Blowfish s boxes struct.
|
||||
typedef struct
|
||||
{
|
||||
u32 sbox0[0x100];
|
||||
u32 sbox1[0x100];
|
||||
u32 sbox2[0x100];
|
||||
u32 sbox3[0x100];
|
||||
} bf_sboxes_t;
|
||||
|
||||
/// @brief Blowfish context struct.
|
||||
typedef struct
|
||||
{
|
||||
const u32* pTable;
|
||||
const bf_sboxes_t* sBoxes;
|
||||
} bf_context_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Initializes the given blowfish \p context with the given \p pTable and \p sBoxes.
|
||||
/// @param context The blowfish context to initialize.
|
||||
/// @param pTable The p table to use.
|
||||
/// @param sBoxes The s boxes to use.
|
||||
void bf_init(bf_context_t* context, const u32* pTable, const bf_sboxes_t* sBoxes);
|
||||
|
||||
/// @brief Decrypts the given blowfish \p block using the given \p context.
|
||||
/// @param context The blowfish context to use.
|
||||
/// @param block The blowfish block to decrypt.
|
||||
/// @return The decrypted blowfish block.
|
||||
u64 bf_decrypt(const bf_context_t* context, u64 block);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
67
src/common.h
Normal file
67
src/common.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/time.h"
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef int8_t s8;
|
||||
typedef uint16_t u16;
|
||||
typedef int16_t s16;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t s32;
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef volatile uint8_t vu8;
|
||||
typedef volatile int8_t vs8;
|
||||
typedef volatile uint16_t vu16;
|
||||
typedef volatile int16_t vs16;
|
||||
typedef volatile uint32_t vu32;
|
||||
typedef volatile int32_t vs32;
|
||||
typedef volatile uint64_t vu64;
|
||||
typedef volatile int64_t vs64;
|
||||
|
||||
#define SD_USE_SDIO
|
||||
|
||||
#define SDIO_CLK 3
|
||||
#define SDIO_CMD 4
|
||||
#define SDIO_D0 5
|
||||
#define SDIO_D1 6
|
||||
#define SDIO_D2 7
|
||||
#define SDIO_D3 8
|
||||
|
||||
#define PIN_RST 9
|
||||
#define PIN_CEB 10
|
||||
#define PIN_WREB 11 //clk
|
||||
#define PIN_D0 12
|
||||
#define PIN_D1 13
|
||||
#define PIN_D2 14
|
||||
#define PIN_D3 15
|
||||
#define PIN_D4 16
|
||||
#define PIN_D5 17
|
||||
#define PIN_D6 18
|
||||
#define PIN_D7 19
|
||||
#define PIN_IRQ 20
|
||||
#define PIN_CS2 21
|
||||
|
||||
#define PIN_USB_VBUS 24
|
||||
|
||||
#define PIN_DEV_TX0 0
|
||||
#define PIN_DEV_RX0 1
|
||||
|
||||
#define PIN_INPUT_MASK 0x2FFE00
|
||||
|
||||
#define CARD_ID_NTR 0x800000C2
|
||||
#define CARD_ID_TWL 0xC00000C2
|
||||
|
||||
typedef void (*sd_callback_t)(uint32_t bytes_complete);
|
||||
|
||||
static inline uint millis(void)
|
||||
{
|
||||
return us_to_ms(time_us_64());
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "sd/SdCard.h"
|
||||
extern SdCard gSdCard;
|
||||
#endif
|
||||
299
src/main.cpp
Normal file
299
src/main.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/structs/scb.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "ntrCard.pio.h"
|
||||
#include "romData.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "blowfish.h"
|
||||
#include "scrambler.h"
|
||||
#include "sd/fatfs/ff.h"
|
||||
#include "ntrCardRom.h"
|
||||
#include "ntrCardSpiUart.h"
|
||||
#include "r4.h"
|
||||
#include "sd/SdCard.h"
|
||||
#include "scramblerRing.h"
|
||||
#include "pico/bootrom.h"
|
||||
#include "hardware/xosc.h"
|
||||
#include "powerSaving.h"
|
||||
|
||||
static u32 sProgramOffset;
|
||||
FATFS sFatFs;
|
||||
SdCard gSdCard;
|
||||
static bool sIsSdCardMounted;
|
||||
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
static void setRomToDsiRom(void)
|
||||
{
|
||||
gNtrRomEmu.romData = gDsiRom;
|
||||
gNtrRomEmu.romSize = ((u32)gDsiRomSize + 511) & ~511;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void resetNtrCard(void)
|
||||
{
|
||||
ntrc_resetUsb();
|
||||
pwr_disableAfterBootPowerSaving();
|
||||
ntrc_setNormalMode();
|
||||
gNtrRomEmu.securePhase1 = false;
|
||||
gNtrRomEmu.cmdScramble = false;
|
||||
gNtrRomEmu.dataScramble = false;
|
||||
gComputeScrambler = false;
|
||||
gNtrRomEmu.scrRingRPtr = gScramblerRing;
|
||||
gScramblerRingWPtr = gScramblerRing;
|
||||
gNtrRomEmu.wordIdx = 0;
|
||||
gNtrRomEmu.twlMode = false;
|
||||
gNtrRomEmu.readDataDestination = nullptr;
|
||||
gNtrRomEmu.readDataCompleteHandler = nullptr;
|
||||
gNtrRomEmu.readDataLimit = 0;
|
||||
#ifdef ENABLE_R4_MODE
|
||||
ntrc_resetR4();
|
||||
#endif
|
||||
dma_channel_abort(0);
|
||||
pio_sm_set_enabled(pio0, 0, false);
|
||||
pio_sm_set_pindirs_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
|
||||
pio_sm_clear_fifos(pio0, 0);
|
||||
pio_sm_restart(pio0, 0);
|
||||
pio_sm_clkdiv_restart(pio0, 0);
|
||||
irq_clear(PIO0_IRQ_0);
|
||||
irq_set_enabled(PIO0_IRQ_0, true);
|
||||
pio_sm_exec(pio0, 0, pio_encode_jmp(sProgramOffset));
|
||||
pio_sm_set_enabled(pio0, 0, true);
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
setRomToDsiRom();
|
||||
gNtrRomEmu.cardId = 0xC00000C2;
|
||||
#endif
|
||||
#ifdef DSPICO_ENABLE_WRFUXXED
|
||||
ntrc_resetSpiUart();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_PREVENT_DSI_AUTOBOOT
|
||||
static u64 sResetStart;
|
||||
#endif
|
||||
|
||||
static void __time_critical_func(gpioIrq)(uint gpio, u32 events)
|
||||
{
|
||||
#ifdef ENABLE_PREVENT_DSI_AUTOBOOT
|
||||
u64 time = time_us_64();
|
||||
#endif
|
||||
if (gpio == PIN_RST)
|
||||
{
|
||||
if (events & GPIO_IRQ_EDGE_FALL)
|
||||
{
|
||||
pio_sm_set_enabled(pio0, 0, false);
|
||||
pio_sm_set_pindirs_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
|
||||
}
|
||||
if (events & GPIO_IRQ_EDGE_RISE)
|
||||
{
|
||||
resetNtrCard();
|
||||
#ifdef ENABLE_PREVENT_DSI_AUTOBOOT
|
||||
u32 resetTime = time - sResetStart;
|
||||
if (resetTime > 700000)
|
||||
pio_sm_set_enabled(pio0, 0, false);
|
||||
sResetStart = time;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __scratch_x("cpu1") core1_entry(void)
|
||||
{
|
||||
irq_set_mask_enabled(~0u, false);
|
||||
scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS;
|
||||
while (!gComputeScrambler)
|
||||
{
|
||||
gScramblerRingWPtr = gScramblerRing;
|
||||
__wfe();
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
u32* wPtr = gScramblerRingWPtr;
|
||||
u32* next = SCR_RING_WRAP(wPtr + 1);
|
||||
if (next == gNtrRomEmu.scrRingRPtr)
|
||||
{
|
||||
__wfe();
|
||||
continue;
|
||||
}
|
||||
|
||||
*wPtr = scr_getNext32(&gScramblerState);
|
||||
gScramblerRingWPtr = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void initSd(void)
|
||||
{
|
||||
memset(&sFatFs, 0, sizeof(sFatFs));
|
||||
|
||||
//try mounting 16 times
|
||||
bool ok = false;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (f_mount(&sFatFs, "0:", 1) == FR_OK)
|
||||
{
|
||||
ok = true;
|
||||
sIsSdCardMounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
sIsSdCardMounted = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void tryRebootToBootsel(void)
|
||||
{
|
||||
if (!sIsSdCardMounted)
|
||||
{
|
||||
xosc_init();
|
||||
reset_usb_boot(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int __time_critical_func(main)()
|
||||
{
|
||||
bi_decl(bi_program_description("Ntr card emulator"));
|
||||
bi_decl(bi_pin_mask_with_name(0xFF000, "Ntr card D0-D7"));
|
||||
bi_decl(bi_1pin_with_name(PIN_IRQ, "Ntr card irq"));
|
||||
bi_decl(bi_1pin_with_name(PIN_CEB, "Ntr card ceb (rom enable)"));
|
||||
bi_decl(bi_1pin_with_name(PIN_WREB, "Ntr card wreb (clock)"));
|
||||
bi_decl(bi_1pin_with_name(PIN_RST, "Ntr card reset"));
|
||||
bi_decl(bi_1pin_with_name(PIN_CS2, "Ntr card cs2 (spi enable)"));
|
||||
|
||||
// u64 bootTime = time_us_64();
|
||||
|
||||
// set_sys_clock_khz(/*125000*/200000, true);
|
||||
// 200 MHz = 1200 MHz / 6 / 1
|
||||
set_sys_clock_pll(1200000000, 6, 1);
|
||||
|
||||
dma_channel_claim(0);
|
||||
|
||||
memset(&gNtrRomEmu, 0, sizeof(gNtrRomEmu));
|
||||
|
||||
|
||||
// We support DSi mode if the firmware is dual mode, or if a single rom has the DSi flag set
|
||||
#ifndef DETECT_CONSOLE_TYPE
|
||||
if (gDefaultRom[0x12] & 2)
|
||||
{
|
||||
#endif
|
||||
gNtrRomEmu.cardId = CARD_ID_TWL;
|
||||
#ifndef DETECT_CONSOLE_TYPE
|
||||
}
|
||||
else
|
||||
{
|
||||
gNtrRomEmu.cardId = CARD_ID_NTR;
|
||||
}
|
||||
#endif
|
||||
|
||||
multicore_launch_core1(core1_entry);
|
||||
|
||||
gpio_init_mask(PIN_INPUT_MASK);
|
||||
gpio_set_dir_in_masked(PIN_INPUT_MASK);
|
||||
gpio_init(PIN_IRQ);
|
||||
gpio_put(PIN_IRQ, 0);
|
||||
gpio_set_dir(PIN_IRQ, GPIO_OUT);
|
||||
gpio_disable_pulls(PIN_D0);
|
||||
gpio_disable_pulls(PIN_D1);
|
||||
gpio_disable_pulls(PIN_D2);
|
||||
gpio_disable_pulls(PIN_D3);
|
||||
gpio_disable_pulls(PIN_D4);
|
||||
gpio_disable_pulls(PIN_D5);
|
||||
gpio_disable_pulls(PIN_D6);
|
||||
gpio_disable_pulls(PIN_D7);
|
||||
gpio_set_slew_rate(PIN_D0, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(PIN_D1, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(PIN_D2, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(PIN_D3, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(PIN_D4, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(PIN_D5, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(PIN_D6, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(PIN_D7, GPIO_SLEW_RATE_FAST);
|
||||
gpio_pull_up(PIN_CEB);
|
||||
gpio_pull_up(PIN_WREB);
|
||||
gpio_pull_down(PIN_RST);
|
||||
gpio_pull_up(PIN_CS2);
|
||||
gpio_set_drive_strength(PIN_D0, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_drive_strength(PIN_D1, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_drive_strength(PIN_D2, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_drive_strength(PIN_D3, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_drive_strength(PIN_D4, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_drive_strength(PIN_D5, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_drive_strength(PIN_D6, GPIO_DRIVE_STRENGTH_2MA);
|
||||
gpio_set_drive_strength(PIN_D7, GPIO_DRIVE_STRENGTH_2MA);
|
||||
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
setRomToDsiRom();
|
||||
gNtrRomEmu.isDSMode = true;
|
||||
#else
|
||||
gNtrRomEmu.romData = gDefaultRom;
|
||||
gNtrRomEmu.romSize = (u32)gDefaultRomSize;
|
||||
gNtrRomEmu.romSize = (gNtrRomEmu.romSize + 511) & ~511;
|
||||
#endif
|
||||
|
||||
sProgramOffset = pio_add_program(pio0, &ntr_card_program);
|
||||
#ifdef DSPICO_ENABLE_WRFUXXED
|
||||
u32 spiUartProgOffs = pio_add_program(pio0, &ntr_card_spi_program);
|
||||
#endif
|
||||
pio_sm_config c = ntr_card_program_get_default_config(sProgramOffset);
|
||||
sm_config_set_out_pins(&c, PIN_D0, 8);
|
||||
sm_config_set_in_pins(&c, PIN_D0);
|
||||
sm_config_set_sideset_pins(&c, PIN_D5);
|
||||
sm_config_set_set_pins(&c, PIN_D0, 5);
|
||||
sm_config_set_out_shift(&c, true, true, 32);
|
||||
sm_config_set_in_shift(&c, false, true, 32);
|
||||
sm_config_set_clkdiv(&c, 1);
|
||||
pio_sm_set_pindirs_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
|
||||
pio_sm_set_pins_with_mask(pio0, 0, 0, PIN_INPUT_MASK);
|
||||
pio0->input_sync_bypass = 0xFF000;
|
||||
pio_gpio_init(pio0, PIN_D0);
|
||||
pio_gpio_init(pio0, PIN_D1);
|
||||
pio_gpio_init(pio0, PIN_D2);
|
||||
pio_gpio_init(pio0, PIN_D3);
|
||||
pio_gpio_init(pio0, PIN_D4);
|
||||
pio_gpio_init(pio0, PIN_D5);
|
||||
pio_gpio_init(pio0, PIN_D6);
|
||||
pio_gpio_init(pio0, PIN_D7);
|
||||
|
||||
pio_sm_init(pio0, 0, sProgramOffset, &c);
|
||||
pio_set_irq0_source_enabled(pio0, pis_sm0_rx_fifo_not_empty, true);
|
||||
irq_set_exclusive_handler(PIO0_IRQ_0, ntrc_pioIrq);
|
||||
#ifdef DSPICO_ENABLE_WRFUXXED
|
||||
ntrc_initSpiUart(spiUartProgOffs);
|
||||
#endif
|
||||
|
||||
gpio_set_irq_callback(gpioIrq);
|
||||
gpio_set_irq_enabled(PIN_RST, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
|
||||
irq_set_enabled(IO_IRQ_BANK0, true);
|
||||
|
||||
irq_init_priorities();
|
||||
irq_set_priority(PIO0_IRQ_0, 0x40);
|
||||
irq_set_priority(IO_IRQ_BANK0, 0x40);
|
||||
irq_set_priority(USBCTRL_IRQ, 0x80);
|
||||
irq_set_priority(DMA_IRQ_1, 0x80);
|
||||
irq_set_priority(TIMER_IRQ_0, 0x80);
|
||||
|
||||
// printf("Starting\n");
|
||||
// printf("sProgramOffset %d\n", sProgramOffset);
|
||||
// printf("Boot time %d\n", (u32)bootTime);
|
||||
resetNtrCard();
|
||||
sIsSdCardMounted = false;
|
||||
initSd();
|
||||
|
||||
tryRebootToBootsel();
|
||||
|
||||
pwr_initPowerSaving();
|
||||
|
||||
while (1)
|
||||
{
|
||||
gSdCard.Update();
|
||||
gSdCard.Update();
|
||||
#ifdef ENABLE_R4_MODE
|
||||
ntrc_gameR4Update();
|
||||
#endif
|
||||
__wfi();
|
||||
}
|
||||
}
|
||||
67
src/ntrCard.pio
Normal file
67
src/ntrCard.pio
Normal file
@@ -0,0 +1,67 @@
|
||||
.program ntr_card
|
||||
.side_set 3 opt pindirs
|
||||
|
||||
.define PIN_CEB 10
|
||||
.define PIN_WREB 11
|
||||
|
||||
.wrap_target
|
||||
start:
|
||||
set pindirs, 0x00 side 0 ; set the bus to input
|
||||
|
||||
wait 1 gpio PIN_CEB
|
||||
wait 0 gpio PIN_CEB
|
||||
|
||||
set x, 7
|
||||
cmd_loop:
|
||||
wait 0 gpio PIN_WREB
|
||||
wait 1 gpio PIN_WREB
|
||||
in pins, 8
|
||||
jmp x-- cmd_loop
|
||||
|
||||
out x, 31 ; payload length
|
||||
out y, 1 ; direction
|
||||
|
||||
jmp !x start ;end
|
||||
|
||||
jmp !y write
|
||||
|
||||
read_loop:
|
||||
wait 0 gpio PIN_WREB
|
||||
wait 1 gpio PIN_WREB
|
||||
in pins, 8
|
||||
jmp x-- read_loop
|
||||
|
||||
jmp start
|
||||
|
||||
write:
|
||||
pull ifempty
|
||||
set pindirs, 0x1F side 0x7 [1] ; set the bus to output
|
||||
|
||||
write_loop:
|
||||
wait 0 gpio PIN_WREB
|
||||
nop [1]
|
||||
out pins, 8 [1]
|
||||
wait 1 gpio PIN_WREB
|
||||
jmp x-- write_loop
|
||||
|
||||
.wrap
|
||||
|
||||
.program ntr_card_spi
|
||||
.side_set 2 opt pindirs
|
||||
|
||||
.define PIN_WREB 11
|
||||
.define PIN_CS2 21
|
||||
|
||||
.wrap_target
|
||||
spi_start:
|
||||
wait 1 gpio PIN_CS2 side 0
|
||||
wait 0 gpio PIN_CS2
|
||||
|
||||
set x, 7
|
||||
spi_loop:
|
||||
wait 0 gpio PIN_WREB side 1
|
||||
out pins, 1
|
||||
wait 1 gpio PIN_WREB
|
||||
in pins, 1
|
||||
jmp x-- spi_loop
|
||||
.wrap
|
||||
339
src/ntrCardIrq.S
Normal file
339
src/ntrCardIrq.S
Normal file
@@ -0,0 +1,339 @@
|
||||
.syntax unified
|
||||
.section .scratch_y.cpu0, "ax"
|
||||
.thumb
|
||||
|
||||
#define PIO0_BASE 0x50200000
|
||||
#define PIO0_TXF0 (PIO0_BASE + 0x010)
|
||||
#define PIO0_RXF0 (PIO0_BASE + 0x020)
|
||||
|
||||
.global ntrc_pioIrq
|
||||
.type ntrc_pioIrq,%function
|
||||
.thumb_func
|
||||
ntrc_pioIrq:
|
||||
ldr r0,= gNtrRomEmu
|
||||
ldr r2,= PIO0_BASE
|
||||
ldr r3, [r0, #8] //wordIdx
|
||||
ldr r1, [r2, #(PIO0_RXF0 - PIO0_BASE)]
|
||||
cmp r3, #2
|
||||
bge payload
|
||||
lsls r3, r3, #2
|
||||
ldr r3, [r0, r3]
|
||||
bx r3
|
||||
|
||||
payload:
|
||||
ldr r2, [r0, #0x18] // readDataDestination
|
||||
rev r1, r1
|
||||
stmia r2!, {r1}
|
||||
str r2, [r0, #0x18] // readDataDestination
|
||||
ldr r2, [r0, #0x1C] // readDataLimit
|
||||
cmp r3, r2
|
||||
bhs payloadComplete // complete when wordIdx >= readDataLimit
|
||||
adds r3, r3, #1
|
||||
str r3, [r0, #8] // wordIdx++
|
||||
bx lr
|
||||
|
||||
payloadComplete:
|
||||
movs r3, #0
|
||||
ldr r2, [r0, #0x20] // readDataCompleteHandler
|
||||
str r3, [r0, #8] // wordIdx = 0
|
||||
str r3, [r0, #0x18] // readDataDestination = 0
|
||||
str r3, [r0, #0x1C] // readDataLimit = 0
|
||||
str r3, [r0, #0x20] // readDataCompleteHandler = 0
|
||||
bx r2
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
nop
|
||||
|
||||
.global ntrc_gameCmd0Handler
|
||||
.type ntrc_gameCmd0Handler,%function
|
||||
.thumb_func
|
||||
ntrc_gameCmd0Handler:
|
||||
ldr r3, [r0, #0xC] //scrRingRPtr
|
||||
ldr r3, [r3]
|
||||
rev r3, r3
|
||||
eors r1, r1, r3
|
||||
str r1, [r0, #0x10] //cmd0
|
||||
lsrs r3, r1, #24
|
||||
cmp r3, #0xB7
|
||||
beq 1f
|
||||
cmp r3, #0xB8
|
||||
beq 2f
|
||||
ldr r3,= ntrc_gameCmd0Dummy
|
||||
bx r3
|
||||
|
||||
1:
|
||||
ldr r3,= ntrc_gameReadPageCmd0
|
||||
bx r3
|
||||
|
||||
2:
|
||||
ldr r3,= ntrc_gameReadIdCmd0
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
nop
|
||||
|
||||
.global ntrc_gameCmd1Handler
|
||||
.type ntrc_gameCmd1Handler,%function
|
||||
.thumb_func
|
||||
ntrc_gameCmd1Handler:
|
||||
ldr r3, [r0, #0xC] //scrRingRPtr
|
||||
ldr r3, [r3]
|
||||
rev r3, r3
|
||||
eors r1, r1, r3
|
||||
str r1, [r0, #0x14] //cmd1
|
||||
ldrb r3, [r0, #0x13] //cmd0 >> 24
|
||||
cmp r3, #0xB7
|
||||
beq 1f
|
||||
cmp r3, #0xB8
|
||||
beq 2f
|
||||
cmp r3, #0xFC
|
||||
beq 3f
|
||||
ldr r3,= ntrc_gameCmd0Dummy
|
||||
bx r3
|
||||
|
||||
1:
|
||||
ldr r3,= ntrc_gameReadPageCmd1
|
||||
bx r3
|
||||
|
||||
2:
|
||||
ldr r3,= ntrc_gameCmd1Dummy4
|
||||
bx r3
|
||||
|
||||
3:
|
||||
ldr r3,= ntrc_gameDisableScrambleCmd1
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
lessThanB0Cmd0:
|
||||
#ifdef ENABLE_R4_MODE
|
||||
cmp r1, #0
|
||||
beq r4Dummy0Cmd0
|
||||
#endif
|
||||
ldr r3,= ntrc_gameNoScrambleCmd0Dummy
|
||||
bx r3
|
||||
|
||||
#ifdef ENABLE_R4_MODE
|
||||
r4Dummy0Cmd0:
|
||||
ldr r3,= ntrc_gameR4DummyCmd0
|
||||
bx r3
|
||||
#endif
|
||||
|
||||
nop
|
||||
|
||||
.global ntrc_gameNoScrambleCmd0Handler
|
||||
.type ntrc_gameNoScrambleCmd0Handler,%function
|
||||
.thumb_func
|
||||
ntrc_gameNoScrambleCmd0Handler:
|
||||
str r1, [r0, #0x10] //cmd0
|
||||
lsrs r3, r1, #24
|
||||
subs r3, r3, #0xB0
|
||||
bmi lessThanB0Cmd0
|
||||
lsls r3, r3, #2
|
||||
add r3, r3, pc
|
||||
ldr r3, [r3, #(gGameNoScrambleCmd0HandlerTable - . - 2)]
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
//from 0xB0 to 0xFF
|
||||
.global gGameNoScrambleCmd0HandlerTable
|
||||
gGameNoScrambleCmd0HandlerTable:
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameR4GetCardInfoCmd0 //B0
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B0
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B1
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameR4StartSaveReadCmd0 //B2
|
||||
.word ntrc_gameR4GetSaveDataCmd0 //B3
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B4
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B2
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B3
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B4
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B5
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameR4StartRomReadCmd0 //B6
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B6
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleReadPageCmd0 //B7
|
||||
.word ntrc_gameNoScrambleReadIdCmd0 //B8
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameR4StartSdReadCmd0 //B9
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //BA
|
||||
.word ntrc_gameR4StartSdWriteCmd0 //BB
|
||||
.word ntrc_gameR4GetSdWriteStatCmd0 //BC
|
||||
.word ntrc_gameR4StartSaveWriteCmd0 //BD
|
||||
.word ntrc_gameR4GetSaveStatCmd0 //BE
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //B9
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //BA
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //BB
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //BC
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //BD
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //BE
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //BF
|
||||
//C0-DF
|
||||
.rept 32
|
||||
.word ntrc_gameNoScrambleCmd0Dummy
|
||||
.endr
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //E0
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //E1
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //E2
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //E3
|
||||
.word ntrc_gameGetSdStatCmd0 //E4
|
||||
.word ntrc_gameGetSdDataCmd0 //E5
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //E6
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //E7
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //E8
|
||||
.word ntrc_gameWriteUsbDataCmd0 //E9
|
||||
.word ntrc_gameReadUsbDataCmd0 //EA
|
||||
.word ntrc_gameUsbGetEventCmd0 //EB
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //EC
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //ED
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //EE
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //EF
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F0
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F1
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F2
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F3
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F4
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F5
|
||||
.word ntrc_gameWriteSdDataCmd0 //F6
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F7
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F8
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //F9
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //FA
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //FB
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //FC
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //FD
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //FE
|
||||
.word ntrc_gameNoScrambleCmd0Dummy //FF
|
||||
|
||||
lessThanB0Cmd1:
|
||||
#ifdef ENABLE_R4_MODE
|
||||
ldr r3, [r0, #0x10] //cmd0
|
||||
cmp r3, #0
|
||||
beq r4Dummy0Cmd1
|
||||
#endif
|
||||
ldr r3,= ntrc_gameNoScrambleCmd1Unknown
|
||||
bx r3
|
||||
|
||||
#ifdef ENABLE_R4_MODE
|
||||
r4Dummy0Cmd1:
|
||||
ldr r3,= ntrc_gameNoScrambleCmd1Dummy4
|
||||
bx r3
|
||||
#endif
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
nop
|
||||
|
||||
.global ntrc_gameNoScrambleCmd1Handler
|
||||
.type ntrc_gameNoScrambleCmd1Handler,%function
|
||||
.thumb_func
|
||||
ntrc_gameNoScrambleCmd1Handler:
|
||||
str r1, [r0, #0x14] //cmd1
|
||||
ldrb r3, [r0, #0x13] //cmd0 >> 24
|
||||
subs r3, r3, #0xB0
|
||||
bmi lessThanB0Cmd1
|
||||
lsls r3, r3, #2
|
||||
add r3, r3, pc
|
||||
ldr r3, [r3, #(gGameNoScrambleCmd1HandlerTable - . - 2)]
|
||||
bx r3
|
||||
|
||||
.balign 4
|
||||
.pool
|
||||
|
||||
//from 0xB0 to 0xFF
|
||||
.global gGameNoScrambleCmd1HandlerTable
|
||||
gGameNoScrambleCmd1HandlerTable:
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //B0
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B0
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B1
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //B2
|
||||
.word ntrc_gameR4GetSaveDataCmd1 //B3
|
||||
.word ntrc_gameR4SendMapCmd1 //B4
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B2
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B3
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B4
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B5
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //B6
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B6
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleReadPageCmd1 //B7
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //B8
|
||||
#ifdef ENABLE_R4_MODE
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //B9
|
||||
.word ntrc_gameR4GetSdDataCmd1 //BA
|
||||
.word ntrc_gameR4StartSdWriteCmd1 //BB
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //BC
|
||||
.word ntrc_gameR4StartSaveWriteCmd1 //BD
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //BE
|
||||
#else
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //B9
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //BA
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //BB
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //BC
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //BD
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //BE
|
||||
#endif
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //BF
|
||||
//C0-DF
|
||||
.rept 32
|
||||
.word ntrc_gameNoScrambleCmd1Unknown
|
||||
.endr
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //E0
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //E1
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //E2
|
||||
.word ntrc_gameReqSdReadCmd1 //E3
|
||||
.word ntrc_gameNoScrambleCmd1Dummy4 //E4
|
||||
.word ntrc_gameGetSdDataCmd1 //E5
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //E6
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //E7
|
||||
.word ntrc_gameReqUsbCommandCmd1 //E8
|
||||
.word ntrc_gameWriteUsbDataCmd1 //E9
|
||||
.word ntrc_gameReadUsbDataCmd1 //EA
|
||||
.word ntrc_gameUsbGetEventCmd1 //EB
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //EC
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //ED
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //EE
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //EF
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F0
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F1
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F2
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F3
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F4
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F5
|
||||
.word ntrc_gameWriteSdDataCmd1 //F6
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F7
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F8
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //F9
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //FA
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //FB
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //FC
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //FD
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //FE
|
||||
.word ntrc_gameNoScrambleCmd1Unknown //FF
|
||||
|
||||
.end
|
||||
19
src/ntrCardRom.c
Normal file
19
src/ntrCardRom.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "common.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "ntrCardRom.h"
|
||||
|
||||
ntr_rom_emu_t gNtrRomEmu;
|
||||
|
||||
void __scratch_y("cpu0") ntrc_dmaToBus(const void* data, u32 length)
|
||||
{
|
||||
dma_channel_config c = dma_channel_get_default_config(0);
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, false);
|
||||
channel_config_set_dreq(&c, pio_get_dreq(pio0, 0, true));
|
||||
dma_channel_configure(0, &c,
|
||||
&pio0->txf[0], // Destination pointer
|
||||
data, // Source pointer
|
||||
length >> 2, // Number of transfers
|
||||
true // Start immediately
|
||||
);
|
||||
}
|
||||
182
src/ntrCardRom.h
Normal file
182
src/ntrCardRom.h
Normal file
@@ -0,0 +1,182 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "blowfish.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NTR_CARD_MODE_NORMAL,
|
||||
NTR_CARD_MODE_SECURE,
|
||||
NTR_CARD_MODE_GAME,
|
||||
NTR_CARD_MODE_GAME_NO_SCRAMBLE
|
||||
} NtrCardMode;
|
||||
|
||||
// normal mode commands
|
||||
#define NTR_CMD_ID_NORMAL_READ_PAGE 0x00 // read unprotected header page
|
||||
#define NTR_CMD_ID_NORMAL_CHANGE_MODE_NTR 0x3C // change to secure mode (ntr)
|
||||
#define NTR_CMD_ID_NORMAL_CHANGE_MODE_TWL 0x3D // change to secure mode (twl)
|
||||
#define NTR_CMD_ID_NORMAL_READ_ID 0x90 // read card id
|
||||
#define NTR_CMD_ID_NORMAL_LOAD_TABLE 0x9F // load blowfish table
|
||||
#define NTR_CMD_ID_NORMAL_3DS_DETECT 0x71 // 3ds detection
|
||||
|
||||
// secure mode commands
|
||||
#define NTR_CMD_ID_SECURE_READ_ID 0x1 // read card id
|
||||
#define NTR_CMD_ID_SECURE_READ_SEGMENT 0x2 // read secure segment
|
||||
#define NTR_CMD_ID_SECURE_SCRAMBLER_ON 0x4 // enable scrambler
|
||||
#define NTR_CMD_ID_SECURE_SCRAMBLER_OFF 0x6 // disable scrambler
|
||||
#define NTR_CMD_ID_SECURE_CHANGE_MODE 0xA // change to game mode
|
||||
|
||||
// game mode commands
|
||||
#define NTR_CMD_ID_GAME_READ_PAGE 0xB7 // read rom page
|
||||
#define NTR_CMD_ID_GAME_READ_ID 0xB8 // read card id
|
||||
|
||||
// custom game mode commands
|
||||
#define NTR_CMD_ID_GAME_REQ_SD_READ 0xE3
|
||||
#define NTR_CMD_ID_GAME_GET_SD_STAT 0xE4
|
||||
#define NTR_CMD_ID_GAME_GET_SD_DATA 0xE5
|
||||
#define NTR_CMD_ID_GAME_WRITE_SD_DATA 0xF6E10D98
|
||||
#define NTR_CMD_ID_GAME_DISABLE_SCRAMBLING 0xFC
|
||||
|
||||
#define WRITE_SD_DATA_IS_FIRST_FLAG 2
|
||||
#define WRITE_SD_DATA_IS_LAST_FLAG 1
|
||||
#define WRITE_SD_DATA_FLAGS_MASK (WRITE_SD_DATA_IS_FIRST_FLAG | WRITE_SD_DATA_IS_LAST_FLAG)
|
||||
|
||||
// custom game mode commands for USB
|
||||
#define NTR_CMD_ID_GAME_USB_COMMAND 0xE8
|
||||
#define NTR_CMD_ID_GAME_USB_WRITE_DATA 0xE9
|
||||
#define NTR_CMD_ID_GAME_USB_READ_DATA 0xEA
|
||||
#define NTR_CMD_ID_GAME_USB_GET_EVENT 0xEB
|
||||
|
||||
struct ntr_rom_emu_t;
|
||||
|
||||
typedef void (*ntrc_cmd_handler_t)(struct ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
typedef void (*ntrc_game_cmd_handler_t)(struct ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
typedef void (*ntrc_read_data_complete_handler_t)(struct ntr_rom_emu_t* romEmu);
|
||||
|
||||
typedef struct ntr_rom_emu_t
|
||||
{
|
||||
ntrc_cmd_handler_t cmd0Handler;
|
||||
ntrc_cmd_handler_t cmd1Handler;
|
||||
|
||||
int wordIdx;
|
||||
u32* volatile scrRingRPtr;
|
||||
u32 cmd0;
|
||||
u32 cmd1;
|
||||
u32* readDataDestination;
|
||||
u32 readDataLimit;
|
||||
ntrc_read_data_complete_handler_t readDataCompleteHandler;
|
||||
|
||||
// These are not used by assembly code
|
||||
NtrCardMode mode;
|
||||
bool securePhase1;
|
||||
int secureBlock;
|
||||
bool cmdScramble;
|
||||
bool dataScramble;
|
||||
u32 cardId;
|
||||
bool twlMode;
|
||||
#ifdef ENABLE_R4_MODE
|
||||
bool r4Mode;
|
||||
#endif
|
||||
u32 romSize;
|
||||
bf_context_t blowfish;
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
u16 previousCommand;
|
||||
bool isDSMode;
|
||||
#endif
|
||||
u8* romData;
|
||||
} ntr_rom_emu_t;
|
||||
|
||||
// These asserts make sure the assembly code doesn't break
|
||||
// Do not change these asserts without updating the assembly code
|
||||
static_assert(offsetof(ntr_rom_emu_t, cmd0Handler) == 0, "cmd0Handler field offset not equal to 0");
|
||||
static_assert(offsetof(ntr_rom_emu_t, cmd1Handler) == 4, "cmd1Handler field offset not equal to 4");
|
||||
static_assert(offsetof(ntr_rom_emu_t, wordIdx) == 8, "wordIdx field offset not equal to 8");
|
||||
static_assert(offsetof(ntr_rom_emu_t, scrRingRPtr) == 0xC, "scrRingRPtr field offset not equal to 0xC");
|
||||
static_assert(offsetof(ntr_rom_emu_t, cmd0) == 0x10, "cmd0 field offset not equal to 0x10");
|
||||
static_assert(offsetof(ntr_rom_emu_t, cmd1) == 0x14, "cmd1 field offset not equal to 0x14");
|
||||
static_assert(offsetof(ntr_rom_emu_t, readDataDestination) == 0x18, "readDataDestination field offset not equal to 0x18");
|
||||
static_assert(offsetof(ntr_rom_emu_t, readDataLimit) == 0x1C, "readDataLimit field offset not equal to 0x1C");
|
||||
static_assert(offsetof(ntr_rom_emu_t, readDataCompleteHandler) == 0x20, "readDataCompleteHandler field offset not equal to 0x20");
|
||||
|
||||
extern ntr_rom_emu_t gNtrRomEmu;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void ntrc_pioIrq(void);
|
||||
|
||||
/// @brief Switches the rom emulator to normal mode.
|
||||
void ntrc_setNormalMode(void);
|
||||
|
||||
/// @brief Switches the rom emulator to secure mode.
|
||||
void ntrc_setSecureMode(void);
|
||||
|
||||
/// @brief Switches the rom emulator to game mode.
|
||||
void ntrc_setGameMode(void);
|
||||
|
||||
/// @brief Switches the rom emulator to unscrambled game mode.
|
||||
void ntrc_setGameNoScrambleMode(void);
|
||||
|
||||
void ntrc_gameCmd1Unknown(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
void ntrc_gameCmd0Dummy(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
void ntrc_gameCmd1Dummy(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
|
||||
/// @brief Sends the specified \p data with the given \p length to the cartridge bus using DMA.
|
||||
/// @param data The data to send to the cartridge bus.
|
||||
/// @param length The length of the data to send. Must be a multiple of 4.
|
||||
void ntrc_dmaToBus(const void* data, u32 length);
|
||||
|
||||
#ifdef ENABLE_R4_MODE
|
||||
void ntrc_gameR4Update(void);
|
||||
void ntrc_resetR4(void);
|
||||
#endif
|
||||
|
||||
void ntrc_resetUsb(void);
|
||||
|
||||
/// @brief Signals to the cartridge bus \p pio that the current command has no payload.
|
||||
/// @param pio The pio instance to use.
|
||||
static inline void ntrc_noPayload(pio_hw_t* pio)
|
||||
{
|
||||
pio->txf[0] = 0;
|
||||
}
|
||||
|
||||
/// @brief Signals to the cartridge bus \p pio that the current command has a
|
||||
/// DSpico -> DS payload of the specified number of \p bytes.
|
||||
/// @param pio The pio instance to use.
|
||||
/// @param bytes The number of payload bytes that will be send to the DS.
|
||||
static inline void ntrc_beginWrite(pio_hw_t* pio, int bytes)
|
||||
{
|
||||
pio->txf[0] = bytes - 1;
|
||||
}
|
||||
|
||||
/// @brief Signals to the cartridge bus \p pio that the current command has a
|
||||
/// DS -> DSpico payload of the specified number of \p bytes.
|
||||
/// @param pio The pio instance to use.
|
||||
/// @param bytes The number of payload bytes that will be received from the DS.
|
||||
static inline void ntrc_beginRead(pio_hw_t* pio, int bytes)
|
||||
{
|
||||
pio->txf[0] = 0x80000000 | (bytes - 1);
|
||||
}
|
||||
|
||||
/// @brief Queues a \p word to the cartridge bus \p pio that will be send to the DS.
|
||||
/// @note This function assumes there is sufficient space in the pio fifo.
|
||||
/// @param pio The pio instance to use.
|
||||
/// @param word The word to send to the DS.
|
||||
static inline void ntrc_writeWord(pio_hw_t* pio, u32 word)
|
||||
{
|
||||
pio->txf[0] = word;
|
||||
}
|
||||
|
||||
/// @brief Queues a \p word to the cartridge bus \p pio that will be send to the DS.
|
||||
/// If there is not enough space in the pio fifo, this function will block until space becomes available.
|
||||
/// @param pio The pio instance to use.
|
||||
/// @param word The word to send to the DS.
|
||||
static inline void ntrc_writeWordBlocking(pio_hw_t* pio, u32 word)
|
||||
{
|
||||
pio_sm_put_blocking(pio, 0, word);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
87
src/ntrCardRomGame.c
Normal file
87
src/ntrCardRomGame.c
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include "xor.h"
|
||||
#include "romData.h"
|
||||
#include "scramblerRing.h"
|
||||
#include "ntrCardRom.h"
|
||||
#include "ntrCardRomGameNoScramble.h"
|
||||
#include "ntrCardRomGame.h"
|
||||
|
||||
void ntrc_gameCmd1Unknown(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
ntrc_finishGameCmd1(romEmu, 0);
|
||||
|
||||
//do not receive further commands until card reset
|
||||
irq_set_enabled(PIO0_IRQ_0, false);
|
||||
|
||||
puts("Unknown game command");
|
||||
printf("%08X%08X\n", romEmu->cmd0, romEmu->cmd1);
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_gameCmd0Dummy)(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameCmd0(romEmu);
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_gameCmd1Dummy0)(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameCmd1(romEmu, 0);
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_gameCmd1Dummy4)(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameCmd1(romEmu, 1);
|
||||
}
|
||||
|
||||
void __scratch_y("cpu0") ntrc_gameReadPageCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 512);
|
||||
ntrc_finishGameCmd0(romEmu);
|
||||
}
|
||||
|
||||
void __scratch_y("cpu0") ntrc_gameReadPageCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
u32 address = romEmu->cmd0 << 8;
|
||||
|
||||
if (address + 512 <= romEmu->romSize)
|
||||
{
|
||||
u32 rPtr = gScramblerRingRPtr;
|
||||
u32* outPtr = (u32*)&gScrambleBuffers[gFreeScrambleBuf][0];
|
||||
u32* inPtr = (u32*)&romEmu->romData[address];
|
||||
|
||||
applyXor(inPtr, romEmu->scrRingRPtr + 1, outPtr, 512);
|
||||
}
|
||||
else
|
||||
{
|
||||
//just send some garbage
|
||||
}
|
||||
|
||||
ntrc_dmaToBus(&gScrambleBuffers[gFreeScrambleBuf][0], 512);
|
||||
gFreeScrambleBuf = 1 - gFreeScrambleBuf;
|
||||
ntrc_finishGameCmd1(romEmu, 128);
|
||||
}
|
||||
|
||||
void __scratch_y("cpu0") ntrc_gameReadIdCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
ntrc_writeWord(pio, romEmu->cardId ^ romEmu->scrRingRPtr[2]); //0=cmd0,1=cmd1,2=payload
|
||||
ntrc_finishGameCmd0(romEmu);
|
||||
}
|
||||
|
||||
void __scratch_y("cpu0") ntrc_gameDisableScrambleCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_setGameNoScrambleMode();
|
||||
ntrc_noPayload(pio);
|
||||
ntrc_finishGameCmd1(romEmu, 0);
|
||||
}
|
||||
|
||||
extern void ntrc_gameCmd0Handler(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
extern void ntrc_gameCmd1Handler(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
|
||||
void __time_critical_func(ntrc_setGameMode)(void)
|
||||
{
|
||||
gNtrRomEmu.cmd0Handler = ntrc_gameCmd0Handler;
|
||||
gNtrRomEmu.cmd1Handler = ntrc_gameCmd1Handler;
|
||||
gNtrRomEmu.mode = NTR_CARD_MODE_GAME;
|
||||
}
|
||||
29
src/ntrCardRomGame.h
Normal file
29
src/ntrCardRomGame.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "ntrCardRom.h"
|
||||
#include "scramblerRing.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Finishes handling the first command word in game mode.
|
||||
/// @param romEmu The rom emulator context.
|
||||
static inline void ntrc_finishGameCmd0(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
scr_advanceRing(romEmu, 1); //cmd
|
||||
romEmu->wordIdx = 1;
|
||||
}
|
||||
|
||||
/// @brief Finishes handling the second command word in game mode.
|
||||
/// @param romEmu The rom emulator context.
|
||||
/// @param writePayloadWords The number of response words for this command (DSpico -> DS).
|
||||
static inline void ntrc_finishGameCmd1(ntr_rom_emu_t* romEmu, int writePayloadWords)
|
||||
{
|
||||
scr_advanceRing(romEmu, 1 + writePayloadWords); //cmd+payload
|
||||
romEmu->wordIdx = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
50
src/ntrCardRomGameNoScramble.c
Normal file
50
src/ntrCardRomGameNoScramble.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include "ntrCardRom.h"
|
||||
#include "powerSaving.h"
|
||||
#include "ntrCardRomGameNoScramble.h"
|
||||
|
||||
void ntrc_gameNoScrambleCmd1Unknown(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
|
||||
//do not receive further commands until card reset
|
||||
irq_set_enabled(PIO0_IRQ_0, false);
|
||||
|
||||
puts("Unknown game command");
|
||||
printf("%08X%08X\n", romEmu->cmd0, romEmu->cmd1);
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_gameNoScrambleCmd0Dummy)(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_gameNoScrambleCmd1Dummy0)(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_gameNoScrambleCmd1Dummy4)(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
void __scratch_y("cpu0") ntrc_gameNoScrambleReadIdCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
ntrc_writeWord(pio, romEmu->cardId);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern void ntrc_gameNoScrambleCmd0Handler(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
extern void ntrc_gameNoScrambleCmd1Handler(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio);
|
||||
|
||||
void __time_critical_func(ntrc_setGameNoScrambleMode)(void)
|
||||
{
|
||||
gNtrRomEmu.cmd0Handler = ntrc_gameNoScrambleCmd0Handler;
|
||||
gNtrRomEmu.cmd1Handler = ntrc_gameNoScrambleCmd1Handler;
|
||||
gNtrRomEmu.mode = NTR_CARD_MODE_GAME_NO_SCRAMBLE;
|
||||
pwr_enableAfterBootPowerSaving();
|
||||
}
|
||||
39
src/ntrCardRomGameNoScramble.h
Normal file
39
src/ntrCardRomGameNoScramble.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "ntrCardRom.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Finishes handling the first command word in unscrambled game mode.
|
||||
/// @param romEmu The rom emulator context.
|
||||
static inline void ntrc_finishGameNoScrambleCmd0(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
romEmu->wordIdx = 1;
|
||||
}
|
||||
|
||||
/// @brief Finishes handling the second command word in unscrambled game mode.
|
||||
/// @param romEmu The rom emulator context.
|
||||
static inline void ntrc_finishGameNoScrambleCmd1(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
romEmu->wordIdx = 0;
|
||||
}
|
||||
|
||||
/// @brief Finishes handling the second command word in unscrambled game mode with a read payload (DS -> DSpico).
|
||||
/// @param romEmu The rom emulator context.
|
||||
/// @param payloadDestination Destination buffer for the payload data.
|
||||
/// @param payloadLength The number of payload bytes (must be a multiple of 4).
|
||||
/// @param payloadCompleteCallback Callback function to be called when reading the payload is complete.
|
||||
static inline void ntrc_finishGameNoScrambleCmd1WithReadPayload(ntr_rom_emu_t* romEmu, u32* payloadDestination,
|
||||
u32 payloadLength, ntrc_read_data_complete_handler_t payloadCompleteCallback)
|
||||
{
|
||||
romEmu->readDataDestination = payloadDestination;
|
||||
romEmu->readDataLimit = (payloadLength >> 2) + 1;
|
||||
romEmu->readDataCompleteHandler = payloadCompleteCallback;
|
||||
romEmu->wordIdx = 2;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
305
src/ntrCardRomGameR4.cpp
Normal file
305
src/ntrCardRomGameR4.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sd/fatfs/ff.h"
|
||||
#include "ntrCardRom.h"
|
||||
#include "ntrCardRomGameNoScramble.h"
|
||||
|
||||
#ifdef ENABLE_R4_MODE
|
||||
|
||||
static FIL sRomFile;
|
||||
static FIL sSaveFile;
|
||||
|
||||
static volatile bool sInitR4Rom = false;
|
||||
static volatile u32 sR4RomFatEntryAddr = 0;
|
||||
static volatile u32 sR4RomFetchAddr = 0;
|
||||
static volatile u32 sR4CurRomBlockAddr = 0;
|
||||
static volatile u32 sR4RomReadStat = 1;
|
||||
|
||||
static volatile bool sInitR4Save = false;
|
||||
static volatile u32 sR4SaveFatEntryAddr = 0;
|
||||
static volatile u32 sR4SaveFetchAddr = 0xFFFFFFFF;
|
||||
static volatile u32 sR4CurSaveBlockAddr = 0xFFFFFFFF;
|
||||
|
||||
static volatile bool sSaveWriteBusy = true;
|
||||
static volatile u32 sWriteSaveAddr = 0xFFFFFFFF;
|
||||
|
||||
static u8 sR4RomBlock[512];
|
||||
static u8 sR4SaveBlock[512];
|
||||
|
||||
static u32 sR4RomBlockLargeAddr = 0xFFFFFFFF;
|
||||
static u8 sR4RomBlockLarge[16384];//512];
|
||||
|
||||
static DWORD sClusterTab[16384];
|
||||
static DWORD sSaveClusterTab[4096];
|
||||
|
||||
extern FATFS sFatFs;
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4DummyCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
ntrc_writeWord(pio, 0);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4GetCardInfoCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
ntrc_writeWord(pio, 0x1F4);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4StartSaveReadCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
u32 addr = word << 8;
|
||||
u32 result = 1;
|
||||
if (addr == sR4CurSaveBlockAddr)
|
||||
{
|
||||
result = 0;
|
||||
sR4CurSaveBlockAddr = 0xFFFFFFFF;
|
||||
}
|
||||
else if (sR4SaveFetchAddr != addr)
|
||||
{
|
||||
sR4SaveFetchAddr = addr;
|
||||
}
|
||||
ntrc_writeWord(pio, result); //0=cmd0,1=cmd1,2=payload
|
||||
|
||||
romEmu->wordIdx = 1;
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4GetSaveDataCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 512);
|
||||
ntrc_dmaToBus(sR4SaveBlock, 512);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4GetSaveDataCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4GetSaveStatCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
u32 sdStat = 1;
|
||||
if (!sSaveWriteBusy && sWriteSaveAddr == 0xFFFFFFFF)
|
||||
{
|
||||
sdStat = 0;
|
||||
}
|
||||
ntrc_writeWord(pio, sdStat);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4SendMapCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
|
||||
u64 cmd = (((u64)romEmu->cmd0) << 32) | word;
|
||||
u32 addr = (cmd >> 24) & 0xFFFFFFFF;
|
||||
u32 result = 1;
|
||||
if (!(addr & 1))
|
||||
{
|
||||
//rom
|
||||
if (sR4RomFatEntryAddr == addr)
|
||||
{
|
||||
result = sInitR4Rom;
|
||||
}
|
||||
else
|
||||
{
|
||||
sR4RomFatEntryAddr = addr;
|
||||
sInitR4Rom = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//save
|
||||
addr &= ~1;
|
||||
if (sR4SaveFatEntryAddr == addr)
|
||||
{
|
||||
result = sInitR4Save;
|
||||
}
|
||||
else
|
||||
{
|
||||
sR4SaveFatEntryAddr = addr;
|
||||
sInitR4Save = true;
|
||||
}
|
||||
}
|
||||
|
||||
ntrc_writeWord(pio, result);
|
||||
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
static void __no_inline_not_in_flash_func(gameR4StartRomReadCmd0Cont)(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
u32 addr = romEmu->cmd0 << 8;
|
||||
if (addr == sR4CurRomBlockAddr)
|
||||
{
|
||||
sR4CurRomBlockAddr = 0;
|
||||
}
|
||||
else if (addr != sR4CurRomBlockAddr && sR4RomFetchAddr != addr)
|
||||
{
|
||||
sR4RomFetchAddr = addr;
|
||||
}
|
||||
|
||||
romEmu->wordIdx = 1;
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4StartRomReadCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
u32 addr = romEmu->cmd0 << 8;
|
||||
u32 stat = (addr == sR4CurRomBlockAddr) ? 0 : 1;
|
||||
ntrc_writeWord(pio, stat);
|
||||
gameR4StartRomReadCmd0Cont(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4StartSaveWriteCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginRead(pio, 512);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
static void __scratch_y("cpu0") r4SaveWritePayloadComplete(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
sWriteSaveAddr = romEmu->cmd0 << 8;
|
||||
sSaveWriteBusy = true;
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4StartSaveWriteCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd1WithReadPayload(romEmu, (u32*)sR4SaveBlock, 512, r4SaveWritePayloadComplete);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameNoScrambleReadPageCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 512);
|
||||
#ifdef ENABLE_R4_MODE
|
||||
if (romEmu->r4Mode)
|
||||
{
|
||||
u32 address = romEmu->cmd0 << 8;
|
||||
ntrc_dmaToBus(&sR4RomBlockLarge[address & 0x3FFF], 512);
|
||||
}
|
||||
#endif
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameNoScrambleReadPageCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
#ifdef ENABLE_R4_MODE
|
||||
if (!romEmu->r4Mode)
|
||||
#endif
|
||||
{
|
||||
//just send some garbage
|
||||
ntrc_dmaToBus(sR4RomBlockLarge, 512);
|
||||
}
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
static void initFileFromFatEntry(FIL* file, u32 fatEntryAddr, bool save)
|
||||
{
|
||||
while (!gSdCard.TryReadSectorsSync(sR4RomBlock, fatEntryAddr >> 9, 1));
|
||||
const u8* fatEntry = &sR4RomBlock[fatEntryAddr & 0x1FF];
|
||||
|
||||
u32 firstCluster = *(const u16*)&fatEntry[26];
|
||||
firstCluster |= *(const u16*)&fatEntry[20] << 16;
|
||||
|
||||
file->obj.fs = &sFatFs;
|
||||
file->obj.id = sFatFs.id;
|
||||
file->obj.sclust = firstCluster;
|
||||
file->obj.attr = 0;
|
||||
file->obj.objsize = *(const u32*)&fatEntry[28];
|
||||
file->dir_sect = fatEntryAddr >> 9;
|
||||
file->dir_ptr = &sFatFs.win[fatEntryAddr & 0x1FF];
|
||||
file->cltbl = 0;
|
||||
file->flag = save ? (FA_READ | FA_WRITE) : FA_READ;
|
||||
file->err = 0;
|
||||
file->sect = 0;
|
||||
file->fptr = 0;
|
||||
}
|
||||
|
||||
extern "C" void __time_critical_func(ntrc_gameR4Update)(void)
|
||||
{
|
||||
if (sInitR4Rom)
|
||||
{
|
||||
initFileFromFatEntry(&sRomFile, sR4RomFatEntryAddr, false);
|
||||
gNtrRomEmu.romSize = f_size(&sRomFile);
|
||||
|
||||
sRomFile.cltbl = sClusterTab;
|
||||
sClusterTab[0] = 16384;
|
||||
while (f_lseek(&sRomFile, CREATE_LINKMAP) != FR_OK);
|
||||
|
||||
gNtrRomEmu.r4Mode = true;
|
||||
sInitR4Rom = false;
|
||||
}
|
||||
|
||||
if (sInitR4Save)
|
||||
{
|
||||
initFileFromFatEntry(&sSaveFile, sR4SaveFatEntryAddr, true);
|
||||
|
||||
sSaveFile.cltbl = sSaveClusterTab;
|
||||
sSaveClusterTab[0] = 4096;
|
||||
while (f_lseek(&sSaveFile, CREATE_LINKMAP) != FR_OK);
|
||||
|
||||
sInitR4Save = false;
|
||||
}
|
||||
|
||||
if (sR4RomFetchAddr)
|
||||
{
|
||||
if ((sR4RomFetchAddr & ~0x3FFF) != sR4RomBlockLargeAddr)
|
||||
{
|
||||
while (f_lseek(&sRomFile, sR4RomFetchAddr & ~0x3FFF) != FR_OK);
|
||||
UINT br;
|
||||
while (f_read(&sRomFile, sR4RomBlockLarge, sizeof(sR4RomBlockLarge), &br) != FR_OK);
|
||||
sR4RomBlockLargeAddr = sR4RomFetchAddr & ~0x3FFF;
|
||||
}
|
||||
|
||||
sR4CurRomBlockAddr = sR4RomFetchAddr;
|
||||
sR4RomFetchAddr = 0;
|
||||
}
|
||||
|
||||
if (sR4SaveFetchAddr != 0xFFFFFFFF)
|
||||
{
|
||||
while (f_lseek(&sSaveFile, sR4SaveFetchAddr) != FR_OK);
|
||||
UINT br;
|
||||
while (f_read(&sSaveFile, sR4SaveBlock, 512, &br) != FR_OK);
|
||||
|
||||
sR4CurSaveBlockAddr = sR4SaveFetchAddr;
|
||||
sR4SaveFetchAddr = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if (sSaveWriteBusy && sWriteSaveAddr != 0xFFFFFFFF)
|
||||
{
|
||||
while (f_lseek(&sSaveFile, sWriteSaveAddr) != FR_OK);
|
||||
UINT br;
|
||||
while (f_write(&sSaveFile, sR4SaveBlock, 512, &br) != FR_OK);
|
||||
while (f_sync(&sSaveFile) != FR_OK);
|
||||
|
||||
sWriteSaveAddr = 0xFFFFFFFF;
|
||||
sSaveWriteBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void ntrc_resetR4(void)
|
||||
{
|
||||
gNtrRomEmu.r4Mode = false;
|
||||
sInitR4Rom = false;
|
||||
sR4RomFatEntryAddr = 0;
|
||||
sR4RomFetchAddr = 0;
|
||||
sR4CurRomBlockAddr = 0;
|
||||
sR4RomReadStat = 1;
|
||||
|
||||
sInitR4Save = false;
|
||||
sR4SaveFatEntryAddr = 0;
|
||||
sR4SaveFetchAddr = 0xFFFFFFFF;
|
||||
sR4CurSaveBlockAddr = 0xFFFFFFFF;
|
||||
|
||||
sSaveWriteBusy = true;
|
||||
sWriteSaveAddr = 0xFFFFFFFF;
|
||||
|
||||
sR4RomBlockLargeAddr = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
#endif
|
||||
243
src/ntrCardRomGameSd.cpp
Normal file
243
src/ntrCardRomGameSd.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include "r4.h"
|
||||
#include "ntrCardRom.h"
|
||||
#include "ntrCardRomGameNoScramble.h"
|
||||
|
||||
static u8 sSdSectorBuf[1024];
|
||||
static u32 sCurSdSector = 0xFFFFFFFF;
|
||||
static u32 sSdSectorBuffersSectors[2] = { 0xFFFFFFFF, 0xFFFFFFFF };
|
||||
static u32 sBufferIndex = 0;
|
||||
static u32 sReadSector;
|
||||
static bool sReadBusy = false;
|
||||
static bool sWriteBusy = false;
|
||||
static bool sNextWriteBlockQueued = false;
|
||||
static bool sNextWriteIsLast = false;
|
||||
static u32 sNextWriteSector = 0xFFFFFFFF;
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameReqSdReadCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
sCurSdSector = 0xFFFFFFFF;
|
||||
sReadSector = word;
|
||||
if (sSdSectorBuffersSectors[sBufferIndex] != word)
|
||||
{
|
||||
sSdSectorBuffersSectors[0] = 0xFFFFFFFF;
|
||||
sSdSectorBuffersSectors[1] = 0xFFFFFFFF;
|
||||
sBufferIndex = 0;
|
||||
if (!gSdCard.TryBeginReadSectors(&sSdSectorBuf[0], sReadSector, 1))
|
||||
{
|
||||
__breakpoint();
|
||||
}
|
||||
sReadBusy = true;
|
||||
}
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameGetSdStatCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
|
||||
bool sdReady;
|
||||
if (sWriteBusy)
|
||||
{
|
||||
sdReady = gSdCard.IsReady();
|
||||
if (sdReady && !sNextWriteBlockQueued)
|
||||
{
|
||||
sWriteBusy = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sSdSectorBuffersSectors[sBufferIndex] == sReadSector)
|
||||
{
|
||||
sdReady = true;
|
||||
}
|
||||
else if (sReadBusy && gSdCard.IsReady())
|
||||
{
|
||||
sSdSectorBuffersSectors[sBufferIndex] = sReadSector;
|
||||
sdReady = true;
|
||||
sReadBusy = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sdReady = false;
|
||||
}
|
||||
}
|
||||
|
||||
ntrc_writeWord(pio, sdReady ? 1 : 0);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
|
||||
if (sWriteBusy && sdReady && sNextWriteBlockQueued)
|
||||
{
|
||||
if (!gSdCard.TryBeginWriteSectors(&sSdSectorBuf[sBufferIndex * 512], sNextWriteSector, 1, !sNextWriteIsLast))
|
||||
{
|
||||
__breakpoint();
|
||||
}
|
||||
|
||||
sNextWriteBlockQueued = false;
|
||||
sNextWriteIsLast = false;
|
||||
sNextWriteSector = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameGetSdDataCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 512);
|
||||
|
||||
// without scrambling to save time
|
||||
ntrc_dmaToBus(&sSdSectorBuf[sBufferIndex * 512], 512);
|
||||
sReadSector++;
|
||||
|
||||
sBufferIndex = 1 - sBufferIndex;
|
||||
|
||||
if (!sReadBusy)
|
||||
{
|
||||
if (!gSdCard.TryBeginReadSectors(&sSdSectorBuf[sBufferIndex * 512], sReadSector, 1))
|
||||
{
|
||||
__breakpoint();
|
||||
}
|
||||
sReadBusy = true;
|
||||
}
|
||||
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameGetSdDataCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
// we still need to advance the state
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameWriteSdDataCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
if ((word & ~WRITE_SD_DATA_FLAGS_MASK) != NTR_CMD_ID_GAME_WRITE_SD_DATA)
|
||||
{
|
||||
ntrc_gameCmd0Dummy(romEmu, word, pio);
|
||||
return;
|
||||
}
|
||||
|
||||
ntrc_beginRead(pio, 512);
|
||||
sCurSdSector = 0xFFFFFFFF;
|
||||
sSdSectorBuffersSectors[0] = 0xFFFFFFFF;
|
||||
sSdSectorBuffersSectors[1] = 0xFFFFFFFF;
|
||||
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
static void __scratch_y("cpu0") sdWritePayloadComplete(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
bool isFirst = (romEmu->cmd0 & WRITE_SD_DATA_IS_FIRST_FLAG) != 0;
|
||||
bool isLast = (romEmu->cmd0 & WRITE_SD_DATA_IS_LAST_FLAG) != 0;
|
||||
if (isFirst)
|
||||
{
|
||||
if (!gSdCard.TryBeginWriteSectors(sSdSectorBuf, romEmu->cmd1, 1, !isLast))
|
||||
{
|
||||
__breakpoint();
|
||||
}
|
||||
sWriteBusy = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sNextWriteBlockQueued = true;
|
||||
sNextWriteIsLast = isLast;
|
||||
sNextWriteSector = romEmu->cmd1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameWriteSdDataCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
if ((romEmu->cmd0 & ~WRITE_SD_DATA_FLAGS_MASK) != NTR_CMD_ID_GAME_WRITE_SD_DATA)
|
||||
{
|
||||
ntrc_gameCmd1Unknown(romEmu, word, pio);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isFirst = (romEmu->cmd0 & WRITE_SD_DATA_IS_FIRST_FLAG) != 0;
|
||||
if (isFirst)
|
||||
{
|
||||
sNextWriteBlockQueued = false;
|
||||
sNextWriteIsLast = false;
|
||||
sNextWriteSector = 0xFFFFFFFF;
|
||||
sBufferIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sBufferIndex = 1 - sBufferIndex;
|
||||
}
|
||||
|
||||
ntrc_finishGameNoScrambleCmd1WithReadPayload(romEmu,
|
||||
(u32*)&sSdSectorBuf[sBufferIndex * 512], 512, sdWritePayloadComplete);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_R4_MODE
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4StartSdReadCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
u32 sector = (romEmu->cmd0 << 8) >> 9;
|
||||
u32 result = 0x1F4;
|
||||
if (gSdCard.IsReady())
|
||||
{
|
||||
if (sector == sCurSdSector)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!gSdCard.TryBeginReadSectors(sSdSectorBuf, sector, 1))
|
||||
{
|
||||
__breakpoint();
|
||||
}
|
||||
sCurSdSector = sector;
|
||||
sSdSectorBuffersSectors[0] = 0xFFFFFFFF;
|
||||
sSdSectorBuffersSectors[1] = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
ntrc_writeWord(pio, result);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4GetSdDataCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 512);
|
||||
ntrc_dmaToBus(sSdSectorBuf, 512);
|
||||
romEmu->wordIdx = 0;
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4StartSdWriteCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginRead(pio, 512);
|
||||
sCurSdSector = 0xFFFFFFFF;
|
||||
sSdSectorBuffersSectors[0] = 0xFFFFFFFF;
|
||||
sSdSectorBuffersSectors[1] = 0xFFFFFFFF;
|
||||
sNextWriteBlockQueued = false;
|
||||
sNextWriteIsLast = false;
|
||||
sNextWriteSector = 0xFFFFFFFF;
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
static void __scratch_y("cpu0") r4SdWritePayloadComplete(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
if (__builtin_expect(!gSdCard.TryBeginWriteSectors(sSdSectorBuf, (romEmu->cmd0 << 8) >> 9, 1, false), false))
|
||||
{
|
||||
__breakpoint();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4StartSdWriteCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd1WithReadPayload(romEmu, (u32*)sSdSectorBuf, 512, r4SdWritePayloadComplete);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameR4GetSdWriteStatCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
u32 sdStat = 1;
|
||||
if (gSdCard.IsReady())
|
||||
sdStat = 0;
|
||||
ntrc_writeWord(pio, sdStat);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
#endif
|
||||
209
src/ntrCardRomGameUsb.cpp
Normal file
209
src/ntrCardRomGameUsb.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#include "common.h"
|
||||
#include "hardware/structs/usb.h"
|
||||
#include "tinyusb/dcd.h"
|
||||
#include "ntrCardRom.h"
|
||||
#include "ntrCardRomGameNoScramble.h"
|
||||
#include "usbEventQueue.h"
|
||||
#include "powerSaving.h"
|
||||
#include "ntrCardRomGameUsb.h"
|
||||
|
||||
static u8 sUsbDataBuffers[32][1024] alignas(4);
|
||||
static bool sUsbActive;
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameReqUsbCommandCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
u32 subCommand = (romEmu->cmd0 >> 16) & 0xFF;
|
||||
switch (subCommand)
|
||||
{
|
||||
case USB_SUB_COMMAND_INIT:
|
||||
{
|
||||
pwr_disableUsbPowerSaving();
|
||||
dcd_init(0, nullptr);
|
||||
sUsbActive = true;
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_BEGIN_SET_ADDRESS:
|
||||
{
|
||||
dcd_set_address(0, 0);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_REMOTE_WAKEUP:
|
||||
{
|
||||
dcd_remote_wakeup(0);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_CONNECT:
|
||||
{
|
||||
dcd_connect(0);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_DISCONNECT:
|
||||
{
|
||||
dcd_disconnect(0);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_SOF_ENABLE:
|
||||
{
|
||||
dcd_sof_enable(0, true);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_SOF_DISABLE:
|
||||
{
|
||||
dcd_sof_enable(0, false);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_EP_CLOSE_ALL:
|
||||
{
|
||||
dcd_edpt_close_all(0);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_EP_STALL:
|
||||
{
|
||||
dcd_edpt_stall(0, (romEmu->cmd0 >> 8) & 0xFF);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_EP_CLEAR_STALL:
|
||||
{
|
||||
dcd_edpt_clear_stall(0, (romEmu->cmd0 >> 8) & 0xFF);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_EP_CLOSE:
|
||||
{
|
||||
dcd_edpt_close(0, (romEmu->cmd0 >> 8) & 0xFF);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_FINISH_SET_ADDRESS:
|
||||
{
|
||||
usb_hw->dev_addr_ctrl = (romEmu->cmd0 >> 8) & 0xFF;
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_EP_OPEN:
|
||||
{
|
||||
tusb_desc_endpoint_t endpoint;
|
||||
endpoint.bEndpointAddress = (romEmu->cmd0 >> 8) & 0xFF;
|
||||
endpoint.wMaxPacketSize = romEmu->cmd1 & 0x7FF;
|
||||
endpoint.bmAttributes.xfer = romEmu->cmd0 & 3;
|
||||
dcd_edpt_open(0, &endpoint);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_CLEAR_EVENT_QUEUE:
|
||||
{
|
||||
usb_clearEventQueue();
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_DEINIT:
|
||||
{
|
||||
dcd_deinit(0);
|
||||
usb_clearEventQueue();
|
||||
pwr_enableUsbPowerSaving();
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_BEGIN_TRANSFER:
|
||||
{
|
||||
u32 byteCount = romEmu->cmd1;
|
||||
if (byteCount > 512)
|
||||
{
|
||||
byteCount = 512;
|
||||
}
|
||||
u32 endpoint = (romEmu->cmd0 >> 8) & 0xFF;
|
||||
u32 offset = romEmu->cmd0 & 1;
|
||||
u8* buffer = &sUsbDataBuffers[(endpoint & ~0x80) * 2 + (endpoint >> 7)][offset * 512];
|
||||
dcd_edpt_xfer(0, endpoint, buffer, byteCount);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_INTERRUPT_ENABLE:
|
||||
{
|
||||
dcd_int_enable(0);
|
||||
break;
|
||||
}
|
||||
case USB_SUB_COMMAND_INTERRUPT_DISABLE:
|
||||
{
|
||||
dcd_int_disable(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameReadUsbDataCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 512);
|
||||
u32 bufferIndex = romEmu->cmd0 & 1;
|
||||
u32 endpoint = (romEmu->cmd0 >> 8) & 0xFF;
|
||||
u8* buffer = &sUsbDataBuffers[(endpoint & ~0x80) * 2 + (endpoint >> 7)][bufferIndex * 512];
|
||||
ntrc_dmaToBus(buffer, 512);
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameReadUsbDataCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameWriteUsbDataCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
static void __scratch_y("cpu0") usbWritePayloadComplete(ntr_rom_emu_t* romEmu)
|
||||
{
|
||||
if (romEmu->cmd0 & 1)
|
||||
{
|
||||
u32 bufferIndex = (romEmu->cmd0 >> 16) & 1;
|
||||
u32 endpoint = (romEmu->cmd0 >> 8) & 0xFF;
|
||||
u8* buffer = &sUsbDataBuffers[(endpoint & ~0x80) * 2 + (endpoint >> 7)][bufferIndex * 512];
|
||||
u32 byteCount = romEmu->cmd1;
|
||||
if (byteCount > 512)
|
||||
{
|
||||
byteCount = 512;
|
||||
}
|
||||
dcd_edpt_xfer(0, endpoint, buffer, byteCount);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameWriteUsbDataCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
u32 bufferIndex = (romEmu->cmd0 >> 16) & 1;
|
||||
u32 endpoint = (romEmu->cmd0 >> 8) & 0xFF;
|
||||
u8* buffer = &sUsbDataBuffers[(endpoint & ~0x80) * 2 + (endpoint >> 7)][bufferIndex * 512];
|
||||
if ((romEmu->cmd0 & 1) && word == 0)
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
dcd_edpt_xfer(0, endpoint, buffer, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ntrc_beginRead(pio, 512);
|
||||
ntrc_finishGameNoScrambleCmd1WithReadPayload(romEmu, (u32*)buffer, 512, usbWritePayloadComplete);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameUsbGetEventCmd0(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
usb_prepareDequeueEvent();
|
||||
ntrc_finishGameNoScrambleCmd0(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void __scratch_y("cpu0") ntrc_gameUsbGetEventCmd1(ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
ntrc_writeWord(pio, usb_dequeueEvent());
|
||||
ntrc_finishGameNoScrambleCmd1(romEmu);
|
||||
}
|
||||
|
||||
extern "C" void ntrc_resetUsb(void)
|
||||
{
|
||||
if (sUsbActive)
|
||||
{
|
||||
dcd_disconnect(0);
|
||||
dcd_int_disable(0);
|
||||
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_RESET;
|
||||
usb_hw->sie_ctrl = 0;
|
||||
usb_hw->inte = 0;
|
||||
usb_clearEventQueue();
|
||||
pwr_enableUsbPowerSaving();
|
||||
sUsbActive = false;
|
||||
}
|
||||
}
|
||||
23
src/ntrCardRomGameUsb.h
Normal file
23
src/ntrCardRomGameUsb.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
enum
|
||||
{
|
||||
USB_SUB_COMMAND_INIT = 1,
|
||||
USB_SUB_COMMAND_BEGIN_SET_ADDRESS = 2,
|
||||
USB_SUB_COMMAND_REMOTE_WAKEUP = 3,
|
||||
USB_SUB_COMMAND_CONNECT = 4,
|
||||
USB_SUB_COMMAND_DISCONNECT = 5,
|
||||
USB_SUB_COMMAND_SOF_ENABLE = 6,
|
||||
USB_SUB_COMMAND_SOF_DISABLE = 7,
|
||||
USB_SUB_COMMAND_EP_CLOSE_ALL = 8,
|
||||
USB_SUB_COMMAND_EP_STALL = 9,
|
||||
USB_SUB_COMMAND_EP_CLEAR_STALL = 10,
|
||||
USB_SUB_COMMAND_EP_CLOSE = 11,
|
||||
USB_SUB_COMMAND_FINISH_SET_ADDRESS = 12,
|
||||
USB_SUB_COMMAND_EP_OPEN = 13,
|
||||
USB_SUB_COMMAND_CLEAR_EVENT_QUEUE = 14,
|
||||
USB_SUB_COMMAND_DEINIT = 15,
|
||||
USB_SUB_COMMAND_BEGIN_TRANSFER = 16,
|
||||
USB_SUB_COMMAND_INTERRUPT_ENABLE = 17,
|
||||
USB_SUB_COMMAND_INTERRUPT_DISABLE = 18
|
||||
};
|
||||
120
src/ntrCardRomNorm.c
Normal file
120
src/ntrCardRomNorm.c
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "common.h"
|
||||
#include "romData.h"
|
||||
#include "ntrCardRom.h"
|
||||
|
||||
#define ROM_HEADER_TWL_AREA_START_OFFSET 0x92
|
||||
#define TWL_AREA_START_STEP_SIZE 0x80000
|
||||
#define NTR_P_TABLE_OFFSET 0x1600
|
||||
#define NTR_S_BOXES_OFFSET 0x1C00
|
||||
#define TWL_P_TABLE_OFFSET 0x600
|
||||
#define TWL_S_BOXES_OFFSET 0xC00
|
||||
|
||||
static void __time_critical_func(normCmd0Handler)(struct ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
romEmu->cmd0 = word;
|
||||
switch (word >> 24)
|
||||
{
|
||||
case NTR_CMD_ID_NORMAL_LOAD_TABLE:
|
||||
{
|
||||
ntrc_noPayload(pio); //ignore load table cycles
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
romEmu->previousCommand = NTR_CMD_ID_NORMAL_LOAD_TABLE;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_NORMAL_3DS_DETECT:
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
romEmu->isDSMode = false;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_NORMAL_READ_ID:
|
||||
{
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
if (romEmu->previousCommand == NTR_CMD_ID_NORMAL_LOAD_TABLE) // This command order is used on DSi
|
||||
{
|
||||
romEmu->isDSMode = false;
|
||||
}
|
||||
romEmu->previousCommand = NTR_CMD_ID_NORMAL_READ_ID;
|
||||
#endif
|
||||
ntrc_beginWrite(pio, 4);
|
||||
ntrc_writeWord(pio, romEmu->cardId);
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_NORMAL_READ_PAGE:
|
||||
{
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
if (romEmu->previousCommand == NTR_CMD_ID_NORMAL_LOAD_TABLE && romEmu->isDSMode)
|
||||
{
|
||||
//Console is DS, load normal rom in romData
|
||||
romEmu->romData = gDefaultRom;
|
||||
romEmu->romSize = (u32)gDefaultRomSize;
|
||||
romEmu->romSize = (romEmu->romSize + 511) & ~511;
|
||||
romEmu->cardId = CARD_ID_NTR;
|
||||
}
|
||||
romEmu->previousCommand = NTR_CMD_ID_NORMAL_READ_PAGE;
|
||||
#endif
|
||||
ntrc_beginWrite(pio, 512);
|
||||
u32 address = (word << 8) & 0xFFF;
|
||||
ntrc_dmaToBus(&romEmu->romData[address], 512);
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_NORMAL_CHANGE_MODE_NTR:
|
||||
{
|
||||
ntrc_noPayload(pio); //no data
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_NORMAL_CHANGE_MODE_TWL:
|
||||
{
|
||||
ntrc_noPayload(pio); //no data
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
break;
|
||||
}
|
||||
}
|
||||
romEmu->wordIdx = 1;
|
||||
}
|
||||
|
||||
static void __time_critical_func(normCmd1Handler)(struct ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
romEmu->cmd1 = word;
|
||||
if ((romEmu->cmd0 >> 24) == NTR_CMD_ID_NORMAL_CHANGE_MODE_NTR)
|
||||
{
|
||||
ntrc_setSecureMode();
|
||||
romEmu->cmdScramble = false;
|
||||
romEmu->dataScramble = false;
|
||||
//ntr blowfish
|
||||
bf_init(&romEmu->blowfish, (const u32*)&romEmu->romData[NTR_P_TABLE_OFFSET],
|
||||
(const bf_sboxes_t*)&romEmu->romData[NTR_S_BOXES_OFFSET]);
|
||||
}
|
||||
else if ((romEmu->cmd0 >> 24) == NTR_CMD_ID_NORMAL_CHANGE_MODE_TWL)
|
||||
{
|
||||
ntrc_setSecureMode();
|
||||
romEmu->cmdScramble = false;
|
||||
romEmu->dataScramble = false;
|
||||
romEmu->twlMode = true;
|
||||
//twl blowfish
|
||||
u32 twlAreaStart = *(const u16*)&romEmu->romData[ROM_HEADER_TWL_AREA_START_OFFSET] * TWL_AREA_START_STEP_SIZE;
|
||||
bf_init(&romEmu->blowfish, (const u32*)&romEmu->romData[twlAreaStart + TWL_P_TABLE_OFFSET],
|
||||
(const bf_sboxes_t*)&romEmu->romData[twlAreaStart + TWL_S_BOXES_OFFSET]);
|
||||
}
|
||||
romEmu->wordIdx = 0; //no normal commands have extra payload to read
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_setNormalMode)(void)
|
||||
{
|
||||
gNtrRomEmu.cmd0Handler = normCmd0Handler;
|
||||
gNtrRomEmu.cmd1Handler = normCmd1Handler;
|
||||
gNtrRomEmu.mode = NTR_CARD_MODE_NORMAL;
|
||||
}
|
||||
147
src/ntrCardRomSecure.c
Normal file
147
src/ntrCardRomSecure.c
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "common.h"
|
||||
#include "xor.h"
|
||||
#include "romData.h"
|
||||
#include "scramblerRing.h"
|
||||
#include "ntrCardRom.h"
|
||||
|
||||
#define KEY2_DEFAULT_SEED0 0b101100011000101011011011110000011101000ULL
|
||||
#define KEY2_DEFAULT_SEED1 0b101110010000111100110111001101100000101ULL
|
||||
|
||||
static const u8 sKey2SeedTable[] = { 0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5 };
|
||||
|
||||
static void __time_critical_func(secureCmd0Handler)(struct ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
if (!romEmu->securePhase1)
|
||||
{
|
||||
romEmu->cmd0 = word;
|
||||
ntrc_noPayload(pio); // first phase is dummy
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (romEmu->cmd0 >> 28)
|
||||
{
|
||||
case NTR_CMD_ID_SECURE_SCRAMBLER_ON:
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_SECURE_CHANGE_MODE:
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_SECURE_READ_ID:
|
||||
{
|
||||
ntrc_beginWrite(pio, 4);
|
||||
u32 cardId = romEmu->cardId;
|
||||
if (romEmu->dataScramble)
|
||||
{
|
||||
cardId ^= *romEmu->scrRingRPtr;
|
||||
}
|
||||
ntrc_writeWord(pio, cardId);
|
||||
if (romEmu->dataScramble)
|
||||
{
|
||||
scr_advanceRing(romEmu, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NTR_CMD_ID_SECURE_READ_SEGMENT:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
ntrc_noPayload(pio);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
romEmu->wordIdx = 1;
|
||||
}
|
||||
|
||||
static void __time_critical_func(secureCmd1Handler)(struct ntr_rom_emu_t* romEmu, u32 word, pio_hw_t* pio)
|
||||
{
|
||||
if (!romEmu->securePhase1)
|
||||
{
|
||||
u64 dec = bf_decrypt(&romEmu->blowfish, ((u64)romEmu->cmd0 << 32) | word);
|
||||
romEmu->cmd0 = dec >> 32;
|
||||
romEmu->cmd1 = (u32)dec;
|
||||
|
||||
if ((romEmu->cmd0 >> 28) == NTR_CMD_ID_SECURE_READ_SEGMENT)
|
||||
{
|
||||
romEmu->secureBlock = 0;
|
||||
}
|
||||
|
||||
romEmu->securePhase1 = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((romEmu->cmd0 >> 28) == NTR_CMD_ID_SECURE_READ_SEGMENT)
|
||||
{
|
||||
ntrc_beginWrite(pio, 512);
|
||||
|
||||
u32 address = (romEmu->cmd0 & 0x0FFFF000) + romEmu->secureBlock * 512;
|
||||
if (romEmu->twlMode)
|
||||
{
|
||||
address += *(const u16*)&romEmu->romData[0x92] * 0x80000 - 0x1000;
|
||||
}
|
||||
u32* xorPtr = romEmu->scrRingRPtr;
|
||||
u32* inPtr = (u32*)&romEmu->romData[address];
|
||||
u32* outPtr = (u32*)&gScrambleBuffers[gFreeScrambleBuf][0];
|
||||
|
||||
applyXor(inPtr, xorPtr, outPtr, 512);
|
||||
|
||||
ntrc_dmaToBus(&gScrambleBuffers[gFreeScrambleBuf][0], 512);
|
||||
|
||||
if (++romEmu->secureBlock == 8)
|
||||
{
|
||||
romEmu->secureBlock = 0;
|
||||
romEmu->securePhase1 = false;
|
||||
}
|
||||
|
||||
romEmu->scrRingRPtr = SCR_RING_WRAP(xorPtr + 128);
|
||||
__sev();
|
||||
gFreeScrambleBuf = 1 - gFreeScrambleBuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
romEmu->secureBlock = 0;
|
||||
romEmu->securePhase1 = false;
|
||||
|
||||
if ((romEmu->cmd0 >> 28) == NTR_CMD_ID_SECURE_SCRAMBLER_ON)
|
||||
{
|
||||
u64 cmd = (((u64)romEmu->cmd0) << 32) | romEmu->cmd1;
|
||||
u32 mmmnnn = (u32)(cmd >> 20) & 0xFFFFFF;
|
||||
u8 key2SeedIdx = romEmu->romData[0x13] & 7;
|
||||
u64 seed0 = ((u64)mmmnnn << 15) | (0x60 << 8) | sKey2SeedTable[key2SeedIdx];
|
||||
scr_init(&gScramblerState, seed0);
|
||||
gNtrRomEmu.dataScramble = true;
|
||||
gNtrRomEmu.scrRingRPtr = gScramblerRing;
|
||||
gScramblerRingWPtr = gScramblerRing;
|
||||
gComputeScrambler = true;
|
||||
__sev();
|
||||
}
|
||||
else if ((romEmu->cmd0 >> 28) == NTR_CMD_ID_SECURE_CHANGE_MODE)
|
||||
{
|
||||
ntrc_setGameMode();
|
||||
if (romEmu->dataScramble)
|
||||
{
|
||||
romEmu->cmdScramble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
romEmu->wordIdx = 0; //no secure commands have extra payload to read
|
||||
}
|
||||
|
||||
void __time_critical_func(ntrc_setSecureMode)(void)
|
||||
{
|
||||
gNtrRomEmu.cmd0Handler = secureCmd0Handler;
|
||||
gNtrRomEmu.cmd1Handler = secureCmd1Handler;
|
||||
gNtrRomEmu.mode = NTR_CARD_MODE_SECURE;
|
||||
gNtrRomEmu.securePhase1 = false;
|
||||
}
|
||||
141
src/ntrCardSpiUart.c
Normal file
141
src/ntrCardSpiUart.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include "hardware/pio.h"
|
||||
#include "ntrCard.pio.h"
|
||||
#include "wrfuxxed.h"
|
||||
#include "ntrCardSpiUart.h"
|
||||
|
||||
#ifdef DSPICO_ENABLE_WRFUXXED
|
||||
|
||||
static ntrc_spi_uart_t sState;
|
||||
|
||||
static void dataIrq(void)
|
||||
{
|
||||
u8 data = pio0->rxf[1];
|
||||
|
||||
if (sState.writeDataCnt > 0)
|
||||
{
|
||||
//data is write data, ignore
|
||||
sState.writeDataCnt--;
|
||||
}
|
||||
else if ((data & 0xF0) == NTRC_SPI_UART_CMD_ECHO_1 || (data & 0xF0) == NTRC_SPI_UART_CMD_ECHO_2)
|
||||
{
|
||||
sState.retDataBuf[0] = data;
|
||||
sState.retDataCnt = 1;
|
||||
sState.retDataPtr = 0;
|
||||
}
|
||||
else if ((data & 0xF0) == NTRC_SPI_UART_CMD_WRITE)
|
||||
{
|
||||
sState.writeDataCnt = (data & 0xF) + 1;
|
||||
}
|
||||
else if ((data & 0xF0) == NTRC_SPI_UART_CMD_READ)
|
||||
{
|
||||
int len = (data & 0xF) + 1;
|
||||
sState.retDataBuf[0] = sState.status;
|
||||
for (int i = 0; i < len; i++)
|
||||
sState.retDataBuf[1 + i] = *sState.readBufPtr++;
|
||||
sState.readBufCount -= len;
|
||||
//set read data
|
||||
sState.retDataCnt = len + 1;
|
||||
sState.retDataPtr = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
case NTRC_SPI_UART_CMD_RESET:
|
||||
{
|
||||
sState.status = 0x90;
|
||||
break;
|
||||
}
|
||||
|
||||
case NTRC_SPI_UART_CMD_GET_WRITABLE_LENGTH:
|
||||
{
|
||||
sState.retDataBuf[0] = 0x90;
|
||||
sState.retDataBuf[1] = 0x00; //lsb
|
||||
sState.retDataBuf[2] = 0x00; //msb
|
||||
sState.retDataCnt = 3;
|
||||
sState.retDataPtr = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case NTRC_SPI_UART_CMD_GET_READABLE_LENGTH:
|
||||
{
|
||||
sState.retDataBuf[0] = 0x90;
|
||||
int len = sState.readBufCount;
|
||||
if (len > 1024)
|
||||
len = 1024;
|
||||
sState.retDataBuf[1] = len & 0xFF; //lsb
|
||||
sState.retDataBuf[2] = len >> 8; //msb
|
||||
sState.retDataCnt = 3;
|
||||
sState.retDataPtr = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case NTRC_SPI_UART_CMD_INIT:
|
||||
{
|
||||
sState.retDataBuf[0] = 0x90;
|
||||
sState.retDataBuf[1] = 'S';
|
||||
sState.retDataBuf[2] = 'P';
|
||||
sState.retDataBuf[3] = 'I';
|
||||
sState.retDataBuf[4] = 'U';
|
||||
sState.retDataCnt = 5;
|
||||
sState.retDataPtr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sState.retDataCnt != 0)
|
||||
{
|
||||
pio0->txf[1] = sState.retDataBuf[sState.retDataPtr++] << 24;
|
||||
sState.retDataCnt--;
|
||||
}
|
||||
else
|
||||
{
|
||||
pio0->txf[1] = sState.status << 24;
|
||||
}
|
||||
}
|
||||
|
||||
void ntrc_initSpiUart(u32 programOffs)
|
||||
{
|
||||
sState.progOffs = programOffs;
|
||||
pio_sm_config c = ntr_card_spi_program_get_default_config(programOffs);
|
||||
sm_config_set_out_pins(&c, PIN_D6, 1);
|
||||
sm_config_set_in_pins(&c, PIN_D7);
|
||||
sm_config_set_sideset_pins(&c, PIN_D6);
|
||||
sm_config_set_out_shift(&c, false, true, 8);
|
||||
sm_config_set_in_shift(&c, false, true, 8);
|
||||
sm_config_set_clkdiv(&c, 1);
|
||||
pio_sm_set_pindirs_with_mask(pio0, 1, 0, 0xC0000);
|
||||
pio_sm_set_pins_with_mask(pio0, 1, 0, 0xC0000);
|
||||
pio_sm_init(pio0, 1, programOffs, &c);
|
||||
pio_set_irq1_source_enabled(pio0, pis_sm1_rx_fifo_not_empty, true);
|
||||
irq_set_exclusive_handler(PIO0_IRQ_1, dataIrq);
|
||||
pio_sm_set_enabled(pio0, 1, true);
|
||||
ntrc_resetSpiUart();
|
||||
}
|
||||
|
||||
void ntrc_resetSpiUart(void)
|
||||
{
|
||||
pio_sm_set_enabled(pio0, 1, false);
|
||||
|
||||
sState.status = 0x80;
|
||||
sState.retDataPtr = 0;
|
||||
sState.retDataCnt = 0;
|
||||
sState.writeDataCnt = 0;
|
||||
sState.readBufPtr = gWrfuxxedPayload;
|
||||
sState.readBufCount = (int)gWrfuxxedPayloadSize;
|
||||
|
||||
pio_sm_set_pindirs_with_mask(pio0, 1, 0, 0xC0000);
|
||||
pio_sm_clear_fifos(pio0, 1);
|
||||
pio_sm_restart(pio0, 1);
|
||||
pio_sm_clkdiv_restart(pio0, 1);
|
||||
irq_clear(PIO0_IRQ_1);
|
||||
irq_set_enabled(PIO0_IRQ_1, true);
|
||||
pio_sm_exec(pio0, 1, pio_encode_jmp(sState.progOffs));
|
||||
pio_sm_set_enabled(pio0, 1, true);
|
||||
pio0->txf[1] = 0x80 << 24; //put first byte
|
||||
}
|
||||
|
||||
#endif
|
||||
44
src/ntrCardSpiUart.h
Normal file
44
src/ntrCardSpiUart.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
#ifdef DSPICO_ENABLE_WRFUXXED
|
||||
|
||||
#define NTRC_SPI_UART_CMD_ECHO_1 0x60
|
||||
#define NTRC_SPI_UART_CMD_NOP 0x80
|
||||
#define NTRC_SPI_UART_CMD_RESET 0x90
|
||||
#define NTRC_SPI_UART_CMD_ECHO_2 0xA0
|
||||
#define NTRC_SPI_UART_CMD_GET_WRITABLE_LENGTH 0xB0
|
||||
#define NTRC_SPI_UART_CMD_GET_READABLE_LENGTH 0xC0
|
||||
#define NTRC_SPI_UART_CMD_WRITE 0xD0
|
||||
#define NTRC_SPI_UART_CMD_READ 0xE0
|
||||
#define NTRC_SPI_UART_CMD_INIT 0xF0
|
||||
|
||||
/// @brief Context struct for spi uart adapter emulation.
|
||||
typedef struct
|
||||
{
|
||||
u8 status;
|
||||
u8 retDataBuf[17];
|
||||
u8 retDataPtr;
|
||||
u8 retDataCnt;
|
||||
u8 writeDataCnt;
|
||||
const u8* readBufPtr;
|
||||
int readBufCount;
|
||||
u32 progOffs;
|
||||
} ntrc_spi_uart_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Initializes spi uart adapter emulation.
|
||||
/// @param programOffs The pio program offset to use.
|
||||
void ntrc_initSpiUart(u32 programOffs);
|
||||
|
||||
/// @brief Resets the spi uart adapter.
|
||||
void ntrc_resetSpiUart(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
112
src/powerSaving.c
Normal file
112
src/powerSaving.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "common.h"
|
||||
#include "hardware/pll.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/structs/scb.h"
|
||||
#include "hardware/structs/rosc.h"
|
||||
#include "hardware/structs/syscfg.h"
|
||||
|
||||
static void stopUnusedClocks(void)
|
||||
{
|
||||
clock_stop(clk_gpout0);
|
||||
clock_stop(clk_gpout1);
|
||||
clock_stop(clk_gpout2);
|
||||
clock_stop(clk_gpout3);
|
||||
clock_stop(clk_usb);
|
||||
clock_stop(clk_adc);
|
||||
clock_stop(clk_rtc);
|
||||
clock_stop(clk_peri);
|
||||
pll_deinit(pll_usb);
|
||||
uint32_t tmp = rosc_hw->ctrl;
|
||||
tmp &= ~ROSC_CTRL_ENABLE_BITS;
|
||||
tmp |= ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB;
|
||||
hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS);
|
||||
rosc_hw->ctrl = tmp;
|
||||
clocks_hw->wake_en0 &= ~(
|
||||
CLOCKS_WAKE_EN0_CLK_ADC_ADC_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_ADC_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_I2C0_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_I2C1_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_PWM_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_RTC_RTC_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_RTC_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_PERI_SPI0_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_SPI0_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_PERI_SPI1_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_SPI1_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_ROSC_BITS |
|
||||
CLOCKS_WAKE_EN0_CLK_SYS_PLL_USB_BITS);
|
||||
clocks_hw->wake_en1 &= ~(
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_UART0_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_PERI_UART0_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_UART1_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_PERI_UART1_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_WATCHDOG_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_TIMER_BITS);
|
||||
}
|
||||
|
||||
static void initDeepSleep(void)
|
||||
{
|
||||
// set clocks enabled during deep sleep
|
||||
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_SYS_PIO0_BITS;
|
||||
clocks_hw->sleep_en1 = 0x0;
|
||||
|
||||
// enable deep sleep
|
||||
scb_hw->scr |= M0PLUS_SCR_SLEEPDEEP_BITS;
|
||||
}
|
||||
|
||||
void pwr_initPowerSaving(void)
|
||||
{
|
||||
stopUnusedClocks();
|
||||
initDeepSleep();
|
||||
}
|
||||
|
||||
void pwr_enableAfterBootPowerSaving(void)
|
||||
{
|
||||
hw_set_bits(&syscfg_hw->mempowerdown, SYSCFG_MEMPOWERDOWN_USB_BITS);
|
||||
hw_clear_bits(&clocks_hw->wake_en1,
|
||||
CLOCKS_WAKE_EN1_CLK_USB_USBCTRL_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_USBCTRL_BITS);
|
||||
}
|
||||
|
||||
void pwr_disableAfterBootPowerSaving(void)
|
||||
{
|
||||
hw_set_bits(&clocks_hw->wake_en1,
|
||||
CLOCKS_WAKE_EN1_CLK_USB_USBCTRL_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_USBCTRL_BITS);
|
||||
hw_clear_bits(&syscfg_hw->mempowerdown, SYSCFG_MEMPOWERDOWN_USB_BITS);
|
||||
}
|
||||
|
||||
void pwr_disableUsbPowerSaving(void)
|
||||
{
|
||||
hw_set_bits(&clocks_hw->wake_en0, CLOCKS_WAKE_EN0_CLK_SYS_PLL_USB_BITS);
|
||||
hw_set_bits(&clocks_hw->sleep_en0, CLOCKS_SLEEP_EN0_CLK_SYS_PLL_USB_BITS);
|
||||
hw_set_bits(&clocks_hw->wake_en1,
|
||||
CLOCKS_WAKE_EN1_CLK_USB_USBCTRL_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_USBCTRL_BITS);
|
||||
hw_set_bits(&clocks_hw->sleep_en1,
|
||||
CLOCKS_SLEEP_EN1_CLK_USB_USBCTRL_BITS |
|
||||
CLOCKS_SLEEP_EN1_CLK_SYS_USBCTRL_BITS);
|
||||
hw_clear_bits(&syscfg_hw->mempowerdown, SYSCFG_MEMPOWERDOWN_USB_BITS);
|
||||
pll_init(pll_usb, 1, 1200000000, 5, 5);
|
||||
clock_configure(
|
||||
clk_usb,
|
||||
0,
|
||||
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
|
||||
48 * MHZ,
|
||||
48 * MHZ);
|
||||
}
|
||||
|
||||
void pwr_enableUsbPowerSaving(void)
|
||||
{
|
||||
clock_stop(clk_usb);
|
||||
pll_deinit(pll_usb);
|
||||
hw_clear_bits(&clocks_hw->wake_en0, CLOCKS_WAKE_EN0_CLK_SYS_PLL_USB_BITS);
|
||||
hw_clear_bits(&clocks_hw->sleep_en0, CLOCKS_SLEEP_EN0_CLK_SYS_PLL_USB_BITS);
|
||||
hw_clear_bits(&clocks_hw->wake_en1,
|
||||
CLOCKS_WAKE_EN1_CLK_USB_USBCTRL_BITS |
|
||||
CLOCKS_WAKE_EN1_CLK_SYS_USBCTRL_BITS);
|
||||
hw_clear_bits(&clocks_hw->sleep_en1,
|
||||
CLOCKS_SLEEP_EN1_CLK_USB_USBCTRL_BITS |
|
||||
CLOCKS_SLEEP_EN1_CLK_SYS_USBCTRL_BITS);
|
||||
hw_set_bits(&syscfg_hw->mempowerdown, SYSCFG_MEMPOWERDOWN_USB_BITS);
|
||||
}
|
||||
24
src/powerSaving.h
Normal file
24
src/powerSaving.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Initializes power saving.
|
||||
void pwr_initPowerSaving(void);
|
||||
|
||||
/// @brief Enables additional power saving for when unscrambled game mode has been reached.
|
||||
void pwr_enableAfterBootPowerSaving(void);
|
||||
|
||||
/// @brief Disables additional power saving such that scrambling can be used again.
|
||||
void pwr_disableAfterBootPowerSaving(void);
|
||||
|
||||
/// @brief Disables USB power saving, such that USB can be used.
|
||||
void pwr_disableUsbPowerSaving(void);
|
||||
|
||||
/// @brief Enables USB power saving. USB can no longer be used.
|
||||
void pwr_enableUsbPowerSaving(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
18
src/r4.h
Normal file
18
src/r4.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ENABLE_R4_MODE
|
||||
|
||||
#define NTR_CMD_ID_GAME_R4_DUMMY_READ 0x00
|
||||
#define NTR_CMD_ID_GAME_R4_GET_CARD_INFO 0xB0
|
||||
#define NTR_CMD_ID_GAME_R4_START_SAVE_READ 0xB2
|
||||
#define NTR_CMD_ID_GAME_R4_GET_SAVE_DATA 0xB3
|
||||
#define NTR_CMD_ID_GAME_R4_SEND_MAP 0xB4
|
||||
#define NTR_CMD_ID_GAME_R4_START_ROM_READ 0xB6
|
||||
#define NTR_CMD_ID_GAME_R4_START_SD_READ 0xB9
|
||||
#define NTR_CMD_ID_GAME_R4_GET_SD_DATA 0xBA
|
||||
#define NTR_CMD_ID_GAME_R4_START_SD_WRITE 0xBB
|
||||
#define NTR_CMD_ID_GAME_R4_GET_WRITE_STAT 0xBC
|
||||
#define NTR_CMD_ID_GAME_R4_START_SAVE_WRITE 0xBD
|
||||
#define NTR_CMD_ID_GAME_R4_GET_SAVE_STAT 0xBE
|
||||
|
||||
#endif
|
||||
20
src/romData.S
Normal file
20
src/romData.S
Normal file
@@ -0,0 +1,20 @@
|
||||
.section .flashdata,"a"
|
||||
|
||||
.global gDefaultRom
|
||||
gDefaultRom:
|
||||
.incbin "../roms/default.nds"
|
||||
|
||||
gDefaultRomDataEnd:
|
||||
.global gDefaultRomSize
|
||||
.equ gDefaultRomSize, gDefaultRomDataEnd - gDefaultRom
|
||||
|
||||
#if defined(DETECT_CONSOLE_TYPE)
|
||||
.global gDsiRom
|
||||
gDsiRom:
|
||||
.incbin "../roms/dsimode.nds"
|
||||
|
||||
gDsiRomDataEnd:
|
||||
.global gDsiRomSize
|
||||
.equ gDsiRomSize, gDsiRomDataEnd - gDsiRom
|
||||
#endif
|
||||
.end
|
||||
11
src/romData.h
Normal file
11
src/romData.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
extern uint8_t gDefaultRom[];
|
||||
extern uint8_t gDefaultRomSize[];
|
||||
|
||||
#ifdef DETECT_CONSOLE_TYPE
|
||||
|
||||
extern uint8_t gDsiRom[];
|
||||
extern uint8_t gDsiRomSize[];
|
||||
|
||||
#endif
|
||||
59
src/scrambler.c
Normal file
59
src/scrambler.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "common.h"
|
||||
#include "scrambler.h"
|
||||
|
||||
void __time_critical_func(scr_init)(scr_state_t* state, u64 seed0)
|
||||
{
|
||||
u64 x = 0;
|
||||
for (int i = 0; i < 39; i++)
|
||||
{
|
||||
x = (x << 1) | (seed0 & 1);
|
||||
seed0 >>= 1;
|
||||
}
|
||||
state->x = x;
|
||||
state->y = 0b101000001101100111011001111000010011101ull;
|
||||
}
|
||||
|
||||
__attribute__((optimize("O3")))
|
||||
u32 __scratch_x("cpu1") scr_getNext32(scr_state_t* state)
|
||||
{
|
||||
u64 x = state->x;
|
||||
u32 xm1 = x;
|
||||
((vu32*)&state->x)[1] = xm1;
|
||||
__compiler_memory_barrier();
|
||||
|
||||
u32 xlo = (u32)(x >> 7);
|
||||
u32 tmp = xm1 << 6;
|
||||
xlo ^= tmp;
|
||||
tmp <<= 1;
|
||||
xlo ^= tmp;
|
||||
xlo ^= tmp << 12;
|
||||
tmp = xlo >> 25;
|
||||
xlo ^= tmp;
|
||||
xlo ^= tmp << 12;
|
||||
xlo ^= xlo >> 26;
|
||||
xlo ^= (xlo << 7) >> 20;
|
||||
|
||||
((vu32*)&state->x)[0] = xlo;
|
||||
__compiler_memory_barrier();
|
||||
|
||||
u64 y = state->y;
|
||||
u32 ym1 = y;
|
||||
((vu32*)&state->y)[1] = ym1;
|
||||
__compiler_memory_barrier();
|
||||
|
||||
u32 ylo = (u32)(y >> 7);
|
||||
tmp = ym1 << 1;
|
||||
ylo ^= tmp;
|
||||
tmp <<= 5;
|
||||
ylo ^= tmp;
|
||||
ylo ^= tmp << 13;
|
||||
tmp = ylo >> 26;
|
||||
ylo ^= tmp;
|
||||
ylo ^= tmp << 13;
|
||||
ylo ^= ylo >> 31;
|
||||
ylo ^= (ylo << 6) >> 19;
|
||||
|
||||
((vu32*)&state->y)[0] = ylo;
|
||||
|
||||
return __builtin_bswap32(((vu32*)&state->x)[0] ^ ylo);
|
||||
}
|
||||
27
src/scrambler.h
Normal file
27
src/scrambler.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
/// @brief Struct representing the scrambler state.
|
||||
typedef struct
|
||||
{
|
||||
u64 x;
|
||||
u64 y;
|
||||
} scr_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Initializes the scrambler with the given \p seed0.
|
||||
/// @param state The scrambler state.
|
||||
/// @param seed0 The scrambler seed 0.
|
||||
void scr_init(scr_state_t* state, u64 seed0);
|
||||
|
||||
/// @brief Calculates the next 32 bits of the scrambler.
|
||||
/// @param state The scrambler state.
|
||||
/// @return The next 32 bits of the scrambler.
|
||||
u32 scr_getNext32(scr_state_t* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
11
src/scramblerRing.c
Normal file
11
src/scramblerRing.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "common.h"
|
||||
#include "scrambler.h"
|
||||
#include "scramblerRing.h"
|
||||
|
||||
u32* volatile gScramblerRingWPtr = gScramblerRing;
|
||||
|
||||
scr_state_t gScramblerState;
|
||||
volatile bool gComputeScrambler = false;
|
||||
|
||||
u8 gScrambleBuffers[2][512];
|
||||
volatile int gFreeScrambleBuf = 0;
|
||||
37
src/scramblerRing.h
Normal file
37
src/scramblerRing.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "ntrCardRom.h"
|
||||
#include "scrambler.h"
|
||||
|
||||
#define SCR_RING_SIZE 1024
|
||||
|
||||
#define gScramblerRing ((u32*)USBCTRL_DPRAM_BASE)
|
||||
|
||||
#define gScramblerRingRPtr (*(vu32*)&gNtrRomEmu.scrRingRPtr)
|
||||
|
||||
#define SCR_RING_WRAP(x) ((u32*)((u32)(x) & ~0xF000))
|
||||
|
||||
extern u32* volatile gScramblerRingWPtr;
|
||||
extern scr_state_t gScramblerState;
|
||||
extern volatile bool gComputeScrambler;
|
||||
|
||||
extern u8 gScrambleBuffers[2][512];
|
||||
extern volatile int gFreeScrambleBuf;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Advances the scrambler ring by the given number of \p words.
|
||||
/// @param romEmu The rom emulator context.
|
||||
/// @param words The number of words to advance the scrambler ring by.
|
||||
static inline void scr_advanceRing(ntr_rom_emu_t* romEmu, int words)
|
||||
{
|
||||
romEmu->scrRingRPtr = SCR_RING_WRAP(romEmu->scrRingRPtr + words);
|
||||
__sev();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
535
src/sd/SdCard.cpp
Normal file
535
src/sd/SdCard.cpp
Normal file
@@ -0,0 +1,535 @@
|
||||
#include "../common.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "rp2040_sdio.h"
|
||||
#include "SdCardInfo.h"
|
||||
#include "SdCard.h"
|
||||
|
||||
#define SEQUENTIAL_READ_TIMEOUT_MICROSECONDS 1000000 // 1 second
|
||||
|
||||
sdio_status_t SdCard::Cmd0GoIdleState() const
|
||||
{
|
||||
return rp2040_sdio_command_R1(CMD0, 0, NULL); // GO_IDLE_STATE
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd2AllSendCid(cid_t& cid) const
|
||||
{
|
||||
return rp2040_sdio_command_R2(CMD2, 0, (u8*)&cid); // ALL_SEND_CID
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd3SendRelativeAddr(u32& rca) const
|
||||
{
|
||||
return rp2040_sdio_command_R1(CMD3, 0, &rca);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd7SelectCard(u32 rca) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(CMD7, rca, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd8SendIfCond(u32 argument, u32& response) const
|
||||
{
|
||||
return rp2040_sdio_command_R1(CMD8, argument, &response); // SEND_IF_COND
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd9SendCsd(u32 rca, csd_t& csd) const
|
||||
{
|
||||
return rp2040_sdio_command_R2(CMD9, rca, (u8*)&csd);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd12StopTransmission() const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(CMD12, 0, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd16SetBlocklen(u32 blockLength) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(16, blockLength, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd17ReadSingleBlock(u32 address) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(CMD17, address, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd18ReadMultipleBlock(u32 address) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(CMD18, address, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd24WriteBlock(u32 address) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(CMD24, address, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd25WriteMultipleBlock(u32 address) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(CMD25, address, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::Cmd55AppCmd(u32 rca) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(CMD55, rca, &response); // APP_CMD
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::ACmd6SetBusWidth(u32 argument) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(ACMD6, argument, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::ACmd23SetWrBlkEraseCount(u32 count) const
|
||||
{
|
||||
u32 response;
|
||||
return rp2040_sdio_command_R1(ACMD23, count, &response);
|
||||
}
|
||||
|
||||
sdio_status_t SdCard::ACmd41SdSendOpCond(u32 argument, u32& response) const
|
||||
{
|
||||
return rp2040_sdio_command_R3(ACMD41, argument, &response); // SD_SEND_OP_COND
|
||||
}
|
||||
|
||||
bool SdCard::TryInitialize()
|
||||
{
|
||||
u32 reply;
|
||||
sdio_status_t status;
|
||||
|
||||
// Initialize at 1 MHz clock speed
|
||||
rp2040_sdio_init(25);
|
||||
|
||||
// Establish initial connection with the card
|
||||
for (int retries = 0; retries < 5; retries++)
|
||||
{
|
||||
// sleep_us(1000);
|
||||
reply = 0;
|
||||
Cmd0GoIdleState();
|
||||
status = Cmd8SendIfCond(0x1AA, reply);
|
||||
|
||||
if (status == SDIO_OK && reply == 0x1AA)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reply != 0x1AA || status != SDIO_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send ACMD41 to begin card initialization and wait for it to complete
|
||||
u32 start = millis();
|
||||
do
|
||||
{
|
||||
if (Cmd55AppCmd(0) != SDIO_OK ||
|
||||
ACmd41SdSendOpCond(0xD0040000, _sdioOcr) != SDIO_OK) // 3.0V voltage
|
||||
// !checkReturnOk(rp2040_sdio_command_R1(ACMD41, 0xC0100000, &g_sdio_ocr)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((u32)(millis() - start) > 1000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} while (!(_sdioOcr & (1 << 31)));
|
||||
|
||||
// Get CID
|
||||
if (Cmd2AllSendCid(_sdioCid) != SDIO_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get relative card address
|
||||
if (Cmd3SendRelativeAddr(_sdioRca) != SDIO_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get CSD
|
||||
if (Cmd9SendCsd(_sdioRca, _sdioCsd) != SDIO_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Select card
|
||||
if (Cmd7SelectCard(_sdioRca) != SDIO_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set 4-bit bus mode
|
||||
if (Cmd55AppCmd(_sdioRca) != SDIO_OK ||
|
||||
ACmd6SetBusWidth(2) != SDIO_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Increase to 25 MHz clock rate
|
||||
rp2040_sdio_init(1);
|
||||
|
||||
_lastSdSector = CalculateSdCapacity() - 1;
|
||||
|
||||
irq_set_exclusive_handler(TIMER_IRQ_0, []
|
||||
{
|
||||
hw_clear_bits(&timer_hw->intr, TIMER_INTR_ALARM_0_BITS);
|
||||
gSdCard.NotifySequentialReadAlarm();
|
||||
});
|
||||
|
||||
_state = State::Idle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SdCard::Update()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case State::Idle:
|
||||
{
|
||||
if (_stopSequentialRead)
|
||||
{
|
||||
_stopSequentialRead = false;
|
||||
if (_sequentialState == SequentialState::SequentialRead)
|
||||
{
|
||||
StopSequentialReadWrite();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case State::ReadBegin:
|
||||
{
|
||||
StateReadBegin();
|
||||
break;
|
||||
}
|
||||
case State::ReadBusy:
|
||||
{
|
||||
StateReadBusy();
|
||||
break;
|
||||
}
|
||||
case State::ReadWriteCancel:
|
||||
{
|
||||
StateReadWriteCancel();
|
||||
break;
|
||||
}
|
||||
case State::WriteBegin:
|
||||
{
|
||||
StateWriteBegin();
|
||||
break;
|
||||
}
|
||||
case State::WriteBusy:
|
||||
{
|
||||
StateWriteBusy();
|
||||
break;
|
||||
}
|
||||
case State::Uninitialized:
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdCard::StateReadBegin()
|
||||
{
|
||||
u32 sectorsLeft = _sectorCount - _sectorsCompleted;
|
||||
u32 startSector = _sectorAddress + _sectorsCompleted;
|
||||
if (startSector > _lastSdSector)
|
||||
{
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((u64)startSector + sectorsLeft - 1 > _lastSdSector)
|
||||
{
|
||||
sectorsLeft = _lastSdSector - startSector + 1;
|
||||
}
|
||||
|
||||
if (sectorsLeft == 0)
|
||||
{
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 sdAddress = startSector;
|
||||
if (!IsSdhcCard())
|
||||
{
|
||||
sdAddress <<= 9; // for non-hc sd cards it's a byte address
|
||||
}
|
||||
|
||||
if (_cancelRequested)
|
||||
{
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
bool started = false;
|
||||
if (_sequentialState == SequentialState::SequentialRead &&
|
||||
_nextSequentialSector == startSector)
|
||||
{
|
||||
StopSequentialReadAlarm();
|
||||
rp2040_sdio_rx_continue(_buffer + _sectorsCompleted * 512, sectorsLeft);
|
||||
started = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_sequentialState != SequentialState::None)
|
||||
{
|
||||
StopSequentialReadWrite();
|
||||
}
|
||||
|
||||
rp2040_sdio_rx_start(_buffer + _sectorsCompleted * 512, sectorsLeft);
|
||||
|
||||
if (_cancelRequested)
|
||||
{
|
||||
rp2040_sdio_stop();
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
_doStopTransmission = true;
|
||||
while (!_cancelRequested)
|
||||
{
|
||||
if (Cmd18ReadMultipleBlock(sdAddress) == SDIO_OK)
|
||||
{
|
||||
started = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_cancelRequested && started)
|
||||
{
|
||||
_state = State::ReadWriteCancel;
|
||||
}
|
||||
else
|
||||
{
|
||||
_state = State::ReadBusy;
|
||||
}
|
||||
}
|
||||
|
||||
void SdCard::StateReadBusy()
|
||||
{
|
||||
if (_cancelRequested)
|
||||
{
|
||||
_state = State::ReadWriteCancel;
|
||||
return;
|
||||
}
|
||||
auto blockStatus = rp2040_sdio_rx_poll_one_block();
|
||||
switch (blockStatus)
|
||||
{
|
||||
case SDIO_BLOCK_CRC_FAIL:
|
||||
case SDIO_BLOCK_TIMEOUT:
|
||||
{
|
||||
StopSequentialReadWrite();
|
||||
_state = State::ReadBegin; // restart from the failed sector
|
||||
break;
|
||||
}
|
||||
case SDIO_BLOCK_OK:
|
||||
{
|
||||
_sectorsCompleted++;
|
||||
break;
|
||||
}
|
||||
case SDIO_BLOCK_ALL_DONE:
|
||||
{
|
||||
_sectorsCompleted = _sectorCount;
|
||||
_nextSequentialSector = _sectorAddress + _sectorsCompleted;
|
||||
_sequentialState = SequentialState::SequentialRead;
|
||||
StartSequentialReadAlarm();
|
||||
_state = State::Idle;
|
||||
break;
|
||||
}
|
||||
case SDIO_BLOCK_NOT_READY:
|
||||
default:
|
||||
{
|
||||
__wfi();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdCard::StateReadWriteCancel()
|
||||
{
|
||||
StopSequentialReadWrite();
|
||||
_cancelRequested = false;
|
||||
_state = State::Idle;
|
||||
}
|
||||
|
||||
void SdCard::StateWriteBegin()
|
||||
{
|
||||
_writeOffset = _sectorsCompleted;
|
||||
u32 sectorsLeft = _sectorCount - _sectorsCompleted;
|
||||
u32 startSector = _sectorAddress + _sectorsCompleted;
|
||||
if (startSector > _lastSdSector)
|
||||
{
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((u64)startSector + sectorsLeft - 1 > _lastSdSector)
|
||||
{
|
||||
sectorsLeft = _lastSdSector - startSector + 1;
|
||||
}
|
||||
|
||||
if (sectorsLeft == 0)
|
||||
{
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 sdAddress = startSector;
|
||||
if (!IsSdhcCard())
|
||||
{
|
||||
sdAddress <<= 9; // for non-hc sd cards it's a byte address
|
||||
}
|
||||
|
||||
if (_cancelRequested)
|
||||
{
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sequentialState == SequentialState::SequentialWrite &&
|
||||
_nextSequentialSector == startSector)
|
||||
{
|
||||
StopSequentialReadAlarm();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_sequentialState != SequentialState::None)
|
||||
{
|
||||
StopSequentialReadWrite();
|
||||
}
|
||||
|
||||
_doStopTransmission = true;
|
||||
bool started = false;
|
||||
while (!_cancelRequested)
|
||||
{
|
||||
if (Cmd25WriteMultipleBlock(sdAddress) == SDIO_OK)
|
||||
{
|
||||
started = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_cancelRequested)
|
||||
{
|
||||
if (started)
|
||||
_state = State::ReadWriteCancel;
|
||||
else
|
||||
_state = State::Idle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rp2040_sdio_tx_start(_buffer + _sectorsCompleted * 512, sectorsLeft);
|
||||
|
||||
if (_cancelRequested)
|
||||
_state = State::ReadWriteCancel;
|
||||
else
|
||||
_state = State::WriteBusy;
|
||||
}
|
||||
|
||||
void SdCard::StateWriteBusy()
|
||||
{
|
||||
if (_cancelRequested)
|
||||
{
|
||||
_state = State::ReadWriteCancel;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 bytesCompleted = 0;
|
||||
auto status = rp2040_sdio_tx_poll(&bytesCompleted);
|
||||
switch (status)
|
||||
{
|
||||
case SDIO_BUSY:
|
||||
{
|
||||
_sectorsCompleted = _writeOffset + (bytesCompleted >> 9);
|
||||
break;
|
||||
}
|
||||
case SDIO_OK:
|
||||
{
|
||||
_sectorsCompleted = _sectorCount;
|
||||
_nextSequentialSector = _sectorAddress + _sectorsCompleted;
|
||||
if (_keepSequentialWriteOpen)
|
||||
{
|
||||
_sequentialState = SequentialState::SequentialWrite;
|
||||
}
|
||||
else
|
||||
{
|
||||
StopSequentialReadWrite();
|
||||
}
|
||||
_state = State::Idle;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// bad things, retry
|
||||
StopSequentialReadWrite();
|
||||
_state = State::WriteBegin; // restart from the failed sector
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SdCard::StopSequentialReadWrite()
|
||||
{
|
||||
StopSequentialReadAlarm();
|
||||
rp2040_sdio_stop();
|
||||
if (_doStopTransmission)
|
||||
{
|
||||
while (Cmd12StopTransmission() != SDIO_OK);
|
||||
while (IsCardBusy());
|
||||
}
|
||||
_sequentialState = SequentialState::None;
|
||||
_stopSequentialRead = false;
|
||||
}
|
||||
|
||||
void SdCard::StartSequentialReadAlarm()
|
||||
{
|
||||
_stopSequentialRead = false;
|
||||
irq_set_enabled(TIMER_IRQ_0, false);
|
||||
hw_set_bits(&clocks_hw->sleep_en1, CLOCKS_SLEEP_EN1_CLK_SYS_TIMER_BITS);
|
||||
hw_set_bits(&clocks_hw->wake_en1, CLOCKS_WAKE_EN1_CLK_SYS_TIMER_BITS);
|
||||
hw_set_bits(&timer_hw->armed, 1);
|
||||
hw_clear_bits(&timer_hw->intr, TIMER_INTR_ALARM_0_BITS);
|
||||
hw_set_bits(&timer_hw->inte, TIMER_INTE_ALARM_0_BITS);
|
||||
irq_set_enabled(TIMER_IRQ_0, true);
|
||||
u64 target = timer_hw->timerawl + SEQUENTIAL_READ_TIMEOUT_MICROSECONDS;
|
||||
timer_hw->alarm[0] = (u32)target;
|
||||
}
|
||||
|
||||
void SdCard::StopSequentialReadAlarm()
|
||||
{
|
||||
irq_set_enabled(TIMER_IRQ_0, false);
|
||||
hw_clear_bits(&clocks_hw->sleep_en1, CLOCKS_SLEEP_EN1_CLK_SYS_TIMER_BITS);
|
||||
hw_clear_bits(&clocks_hw->wake_en1, CLOCKS_WAKE_EN1_CLK_SYS_TIMER_BITS);
|
||||
}
|
||||
|
||||
u32 SdCard::CalculateSdCapacity() const
|
||||
{
|
||||
if (_sdioCsd.v1.csd_ver == 0)
|
||||
{
|
||||
u16 cSize = (_sdioCsd.v1.c_size_high << 10) | (_sdioCsd.v1.c_size_mid << 2) | _sdioCsd.v1.c_size_low;
|
||||
u8 cSizeMult = (_sdioCsd.v1.c_size_mult_high << 1) | _sdioCsd.v1.c_size_mult_low;
|
||||
return (u32)(cSize + 1) << (cSizeMult + _sdioCsd.v1.read_bl_len - 7);
|
||||
}
|
||||
else if (_sdioCsd.v2.csd_ver == 1)
|
||||
{
|
||||
return (((u32)_sdioCsd.v2.c_size_high << 16) + ((u16)_sdioCsd.v2.c_size_mid << 8) + _sdioCsd.v2.c_size_low + 1) << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
195
src/sd/SdCard.h
Normal file
195
src/sd/SdCard.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#pragma once
|
||||
#include "rp2040_sdio.h"
|
||||
#include "SdCardInfo.h"
|
||||
|
||||
class SdCard
|
||||
{
|
||||
public:
|
||||
/// @brief Tries to initialize the SD card.
|
||||
/// @return \c true if initialization was successful, or \c false otherwise.
|
||||
bool TryInitialize();
|
||||
|
||||
/// @brief Returns if the SD card is ready.
|
||||
/// @return \c true if the SD card is ready, or \c false otherwise.
|
||||
bool IsReady() const { return _state == State::Idle; }
|
||||
|
||||
/// @brief Returns if the SD card is currently writing.
|
||||
/// @return \c true if the SD card is writing, or \c false otherwise.
|
||||
bool IsWriting() const { return _state == State::WriteBegin || _state == State::WriteBusy; }
|
||||
|
||||
/// @brief Tries to begin a read at the given \p sector and sector \p count. The data will be written to the \p dst buffer.
|
||||
/// @param dst The destination buffer.
|
||||
/// @param sector The sector to start reading at.
|
||||
/// @param count The number of sectors to read.
|
||||
/// @return \c true if starting the read was successful, or \c false otherwise.
|
||||
bool TryBeginReadSectors(u8* dst, u32 sector, u32 count)
|
||||
{
|
||||
if (!IsReady())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_buffer = dst;
|
||||
_sectorAddress = sector;
|
||||
_sectorCount = count;
|
||||
_sectorsCompleted = 0;
|
||||
_cancelRequested = false;
|
||||
_state = State::ReadBegin;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Tries to begin a write to the given \p sector and sector \p count. The data will be read from the \p src buffer.
|
||||
/// @param src The source buffer.
|
||||
/// @param sector The sector to start writing at.
|
||||
/// @param count The number of sectors to write.
|
||||
/// @param keepSequentialWriteOpen When \c true, the sequential write is kept open such that
|
||||
/// more sectors can be sequentially written afterwards.
|
||||
/// When \c false the sequential write will end.
|
||||
/// @return \c true if starting the write was successful, or \c false otherwise.
|
||||
bool TryBeginWriteSectors(const u8* src, u32 sector, u32 count, bool keepSequentialWriteOpen)
|
||||
{
|
||||
if (!IsReady())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_buffer = (u8*)src;
|
||||
_sectorAddress = sector;
|
||||
_sectorCount = count;
|
||||
_sectorsCompleted = 0;
|
||||
_cancelRequested = false;
|
||||
_keepSequentialWriteOpen = keepSequentialWriteOpen;
|
||||
_state = State::WriteBegin;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Requests a cancel of the current read or write.
|
||||
void Cancel()
|
||||
{
|
||||
if (!IsReady() && _state != State::Uninitialized)
|
||||
{
|
||||
_cancelRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Updates the SD state machine.
|
||||
void Update();
|
||||
|
||||
/// @brief Returns the number of sectors that have been completed in the current read or write.
|
||||
/// @return The number of sectors that have been completed in the current read or write.
|
||||
u32 GetSectorsCompleted() const { return _sectorsCompleted; }
|
||||
|
||||
/// @brief Reads the given number of sectors from the given \p sector to the \p dst buffer.
|
||||
/// This function blocks until the read is complete.
|
||||
/// @param dst The destination buffer.
|
||||
/// @param sector The sector to start reading at.
|
||||
/// @param count The number of sectors to read.
|
||||
/// @return \c true if the read was successful, or \c false otherwise.
|
||||
bool TryReadSectorsSync(u8* dst, u32 sector, u32 count)
|
||||
{
|
||||
bool success = TryBeginReadSectors(dst, sector, count);
|
||||
if (success)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/// @brief Writes the given number of sectors from the \p src buffer to the given \p sector.
|
||||
/// This function blocks until the write is complete.
|
||||
/// @param src The source buffer.
|
||||
/// @param sector The sector to start writing at.
|
||||
/// @param count The number of sectors to write.
|
||||
/// @return \c true if the write was successful, or \c false otherwise.
|
||||
bool TryWriteSectorsSync(const u8* src, u32 sector, u32 count)
|
||||
{
|
||||
bool success = TryBeginWriteSectors(src, sector, count, false);
|
||||
if (success)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class State
|
||||
{
|
||||
Uninitialized,
|
||||
Idle,
|
||||
ReadBegin,
|
||||
ReadBusy,
|
||||
ReadWriteCancel,
|
||||
WriteBegin,
|
||||
WriteBusy
|
||||
};
|
||||
|
||||
enum class SequentialState
|
||||
{
|
||||
None,
|
||||
SequentialRead,
|
||||
SequentialWrite
|
||||
};
|
||||
|
||||
volatile State _state = State::Uninitialized;
|
||||
|
||||
u32 _sdioOcr;
|
||||
u32 _sdioRca;
|
||||
cid_t _sdioCid;
|
||||
csd_t _sdioCsd;
|
||||
u32 _lastSdSector;
|
||||
|
||||
u32 _sectorAddress;
|
||||
u32 _sectorCount;
|
||||
volatile u32 _sectorsCompleted;
|
||||
u8* _buffer;
|
||||
bool _doStopTransmission;
|
||||
bool _keepSequentialWriteOpen;
|
||||
u32 _writeOffset;
|
||||
|
||||
SequentialState _sequentialState = SequentialState::None;
|
||||
u32 _nextSequentialSector = 0xFFFFFFFFu;
|
||||
volatile bool _stopSequentialRead = false;
|
||||
|
||||
volatile bool _cancelRequested = false;
|
||||
|
||||
sdio_status_t Cmd0GoIdleState() const;
|
||||
sdio_status_t Cmd2AllSendCid(cid_t& cid) const;
|
||||
sdio_status_t Cmd3SendRelativeAddr(u32& rca) const;
|
||||
sdio_status_t Cmd7SelectCard(u32 rca) const;
|
||||
sdio_status_t Cmd8SendIfCond(u32 argument, u32& response) const;
|
||||
sdio_status_t Cmd9SendCsd(u32 rca, csd_t& csd) const;
|
||||
sdio_status_t Cmd12StopTransmission() const;
|
||||
sdio_status_t Cmd16SetBlocklen(u32 blockLength) const;
|
||||
sdio_status_t Cmd17ReadSingleBlock(u32 address) const;
|
||||
sdio_status_t Cmd18ReadMultipleBlock(u32 address) const;
|
||||
sdio_status_t Cmd24WriteBlock(u32 address) const;
|
||||
sdio_status_t Cmd25WriteMultipleBlock(u32 address) const;
|
||||
sdio_status_t Cmd55AppCmd(u32 rca) const;
|
||||
sdio_status_t ACmd6SetBusWidth(u32 argument) const;
|
||||
sdio_status_t ACmd23SetWrBlkEraseCount(u32 count) const;
|
||||
sdio_status_t ACmd41SdSendOpCond(u32 argument, u32& response) const;
|
||||
|
||||
bool IsSdhcCard() const { return (_sdioOcr & (1 << 30)) != 0; }
|
||||
bool IsCardBusy() const { return (sio_hw->gpio_in & (1 << SDIO_D0)) == 0; }
|
||||
|
||||
void StateReadBegin();
|
||||
void StateReadBusy();
|
||||
void StateReadWriteCancel();
|
||||
void StateWriteBegin();
|
||||
void StateWriteBusy();
|
||||
|
||||
void StopSequentialReadWrite();
|
||||
|
||||
void StartSequentialReadAlarm();
|
||||
void StopSequentialReadAlarm();
|
||||
void NotifySequentialReadAlarm()
|
||||
{
|
||||
StopSequentialReadAlarm();
|
||||
if (IsReady() && _sequentialState == SequentialState::SequentialRead)
|
||||
{
|
||||
_stopSequentialRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
u32 CalculateSdCapacity() const;
|
||||
};
|
||||
528
src/sd/SdCardInfo.h
Normal file
528
src/sd/SdCardInfo.h
Normal file
@@ -0,0 +1,528 @@
|
||||
/**
|
||||
* Copyright (c) 2011-2021 Bill Greiman
|
||||
* This file is part of the SdFat library for SD memory cards.
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SdCardInfo_h
|
||||
#define SdCardInfo_h
|
||||
#include <stdint.h>
|
||||
// #include "../common/SysCall.h"
|
||||
// Based on the document:
|
||||
//
|
||||
// SD Specifications
|
||||
// Part 1
|
||||
// Physical Layer
|
||||
// Simplified Specification
|
||||
// Version 5.00
|
||||
// Aug 10, 2016
|
||||
//
|
||||
// https://www.sdcard.org/downloads/pls/
|
||||
//------------------------------------------------------------------------------
|
||||
// SD card errors
|
||||
// See the SD Specification for command info.
|
||||
#define SD_ERROR_CODE_LIST\
|
||||
SD_CARD_ERROR(NONE, "No error")\
|
||||
SD_CARD_ERROR(CMD0, "Card reset failed")\
|
||||
SD_CARD_ERROR(CMD2, "SDIO read CID")\
|
||||
SD_CARD_ERROR(CMD3, "SDIO publish RCA")\
|
||||
SD_CARD_ERROR(CMD6, "Switch card function")\
|
||||
SD_CARD_ERROR(CMD7, "SDIO card select")\
|
||||
SD_CARD_ERROR(CMD8, "Send and check interface settings")\
|
||||
SD_CARD_ERROR(CMD9, "Read CSD data")\
|
||||
SD_CARD_ERROR(CMD10, "Read CID data")\
|
||||
SD_CARD_ERROR(CMD12, "Stop multiple block read")\
|
||||
SD_CARD_ERROR(CMD13, "Read card status")\
|
||||
SD_CARD_ERROR(CMD17, "Read single block")\
|
||||
SD_CARD_ERROR(CMD18, "Read multiple blocks")\
|
||||
SD_CARD_ERROR(CMD24, "Write single block")\
|
||||
SD_CARD_ERROR(CMD25, "Write multiple blocks")\
|
||||
SD_CARD_ERROR(CMD32, "Set first erase block")\
|
||||
SD_CARD_ERROR(CMD33, "Set last erase block")\
|
||||
SD_CARD_ERROR(CMD38, "Erase selected blocks")\
|
||||
SD_CARD_ERROR(CMD58, "Read OCR register")\
|
||||
SD_CARD_ERROR(CMD59, "Set CRC mode")\
|
||||
SD_CARD_ERROR(ACMD6, "Set SDIO bus width")\
|
||||
SD_CARD_ERROR(ACMD13, "Read extended status")\
|
||||
SD_CARD_ERROR(ACMD23, "Set pre-erased count")\
|
||||
SD_CARD_ERROR(ACMD41, "Activate card initialization")\
|
||||
SD_CARD_ERROR(READ_TOKEN, "Bad read data token")\
|
||||
SD_CARD_ERROR(READ_CRC, "Read CRC error")\
|
||||
SD_CARD_ERROR(READ_FIFO, "SDIO fifo read timeout")\
|
||||
SD_CARD_ERROR(READ_REG, "Read CID or CSD failed.")\
|
||||
SD_CARD_ERROR(READ_START, "Bad readStart argument")\
|
||||
SD_CARD_ERROR(READ_TIMEOUT, "Read data timeout")\
|
||||
SD_CARD_ERROR(STOP_TRAN, "Multiple block stop failed")\
|
||||
SD_CARD_ERROR(TRANSFER_COMPLETE, "SDIO transfer complete")\
|
||||
SD_CARD_ERROR(WRITE_DATA, "Write data not accepted")\
|
||||
SD_CARD_ERROR(WRITE_FIFO, "SDIO fifo write timeout")\
|
||||
SD_CARD_ERROR(WRITE_START, "Bad writeStart argument")\
|
||||
SD_CARD_ERROR(WRITE_PROGRAMMING, "Flash programming")\
|
||||
SD_CARD_ERROR(WRITE_TIMEOUT, "Write timeout")\
|
||||
SD_CARD_ERROR(DMA, "DMA transfer failed")\
|
||||
SD_CARD_ERROR(ERASE, "Card did not accept erase commands")\
|
||||
SD_CARD_ERROR(ERASE_SINGLE_SECTOR, "Card does not support erase")\
|
||||
SD_CARD_ERROR(ERASE_TIMEOUT, "Erase command timeout")\
|
||||
SD_CARD_ERROR(INIT_NOT_CALLED, "Card has not been initialized")\
|
||||
SD_CARD_ERROR(INVALID_CARD_CONFIG, "Invalid card config")\
|
||||
SD_CARD_ERROR(FUNCTION_NOT_SUPPORTED, "Unsupported SDIO command")
|
||||
|
||||
enum {
|
||||
#define SD_CARD_ERROR(e, m) SD_CARD_ERROR_##e,
|
||||
SD_ERROR_CODE_LIST
|
||||
#undef SD_CARD_ERROR
|
||||
SD_CARD_ERROR_UNKNOWN
|
||||
};
|
||||
// void printSdErrorSymbol(print_t* pr, uint8_t code);
|
||||
// void printSdErrorText(print_t* pr, uint8_t code);
|
||||
//------------------------------------------------------------------------------
|
||||
// card types
|
||||
/** Standard capacity V1 SD card */
|
||||
const uint8_t SD_CARD_TYPE_SD1 = 1;
|
||||
/** Standard capacity V2 SD card */
|
||||
const uint8_t SD_CARD_TYPE_SD2 = 2;
|
||||
/** High Capacity SD card */
|
||||
const uint8_t SD_CARD_TYPE_SDHC = 3;
|
||||
//------------------------------------------------------------------------------
|
||||
// SD operation timeouts
|
||||
/** CMD0 retry count */
|
||||
const uint8_t SD_CMD0_RETRY = 10;
|
||||
/** command timeout ms */
|
||||
const uint16_t SD_CMD_TIMEOUT = 300;
|
||||
/** erase timeout ms */
|
||||
const uint16_t SD_ERASE_TIMEOUT = 10000;
|
||||
/** init timeout ms */
|
||||
const uint16_t SD_INIT_TIMEOUT = 2000;
|
||||
/** read timeout ms */
|
||||
const uint16_t SD_READ_TIMEOUT = 300;
|
||||
/** write time out ms */
|
||||
const uint16_t SD_WRITE_TIMEOUT = 600;
|
||||
//------------------------------------------------------------------------------
|
||||
// SD card commands
|
||||
/** GO_IDLE_STATE - init card in spi mode if CS low */
|
||||
const uint8_t CMD0 = 0X00;
|
||||
/** ALL_SEND_CID - Asks any card to send the CID. */
|
||||
const uint8_t CMD2 = 0X02;
|
||||
/** SEND_RELATIVE_ADDR - Ask the card to publish a new RCA. */
|
||||
const uint8_t CMD3 = 0X03;
|
||||
/** SWITCH_FUNC - Switch Function Command */
|
||||
const uint8_t CMD6 = 0X06;
|
||||
/** SELECT/DESELECT_CARD - toggles between the stand-by and transfer states. */
|
||||
const uint8_t CMD7 = 0X07;
|
||||
/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/
|
||||
const uint8_t CMD8 = 0X08;
|
||||
/** SEND_CSD - read the Card Specific Data (CSD register) */
|
||||
const uint8_t CMD9 = 0X09;
|
||||
/** SEND_CID - read the card identification information (CID register) */
|
||||
const uint8_t CMD10 = 0X0A;
|
||||
/** VOLTAGE_SWITCH -Switch to 1.8V bus signaling level. */
|
||||
const uint8_t CMD11 = 0X0B;
|
||||
/** STOP_TRANSMISSION - end multiple sector read sequence */
|
||||
const uint8_t CMD12 = 0X0C;
|
||||
/** SEND_STATUS - read the card status register */
|
||||
const uint8_t CMD13 = 0X0D;
|
||||
/** READ_SINGLE_SECTOR - read a single data sector from the card */
|
||||
const uint8_t CMD17 = 0X11;
|
||||
/** READ_MULTIPLE_SECTOR - read multiple data sectors from the card */
|
||||
const uint8_t CMD18 = 0X12;
|
||||
/** WRITE_SECTOR - write a single data sector to the card */
|
||||
const uint8_t CMD24 = 0X18;
|
||||
/** WRITE_MULTIPLE_SECTOR - write sectors of data until a STOP_TRANSMISSION */
|
||||
const uint8_t CMD25 = 0X19;
|
||||
/** ERASE_WR_BLK_START - sets the address of the first sector to be erased */
|
||||
const uint8_t CMD32 = 0X20;
|
||||
/** ERASE_WR_BLK_END - sets the address of the last sector of the continuous
|
||||
range to be erased*/
|
||||
const uint8_t CMD33 = 0X21;
|
||||
/** ERASE - erase all previously selected sectors */
|
||||
const uint8_t CMD38 = 0X26;
|
||||
/** APP_CMD - escape for application specific command */
|
||||
const uint8_t CMD55 = 0X37;
|
||||
/** READ_OCR - read the OCR register of a card */
|
||||
const uint8_t CMD58 = 0X3A;
|
||||
/** CRC_ON_OFF - enable or disable CRC checking */
|
||||
const uint8_t CMD59 = 0X3B;
|
||||
/** SET_BUS_WIDTH - Defines the data bus width for data transfer. */
|
||||
const uint8_t ACMD6 = 0X06;
|
||||
/** SD_STATUS - Send the SD Status. */
|
||||
const uint8_t ACMD13 = 0X0D;
|
||||
/** SET_WR_BLK_ERASE_COUNT - Set the number of write sectors to be
|
||||
pre-erased before writing */
|
||||
const uint8_t ACMD23 = 0X17;
|
||||
/** SD_SEND_OP_COMD - Sends host capacity support information and
|
||||
activates the card's initialization process */
|
||||
const uint8_t ACMD41 = 0X29;
|
||||
//==============================================================================
|
||||
// CARD_STATUS
|
||||
/** The command's argument was out of the allowed range for this card. */
|
||||
const uint32_t CARD_STATUS_OUT_OF_RANGE = 1UL << 31;
|
||||
/** A misaligned address which did not match the sector length. */
|
||||
const uint32_t CARD_STATUS_ADDRESS_ERROR = 1UL << 30;
|
||||
/** The transferred sector length is not allowed for this card. */
|
||||
const uint32_t CARD_STATUS_SECTOR_LEN_ERROR = 1UL << 29;
|
||||
/** An error in the sequence of erase commands occurred. */
|
||||
const uint32_t CARD_STATUS_ERASE_SEQ_ERROR = 1UL <<28;
|
||||
/** An invalid selection of write-sectors for erase occurred. */
|
||||
const uint32_t CARD_STATUS_ERASE_PARAM = 1UL << 27;
|
||||
/** Set when the host attempts to write to a protected sector. */
|
||||
const uint32_t CARD_STATUS_WP_VIOLATION = 1UL << 26;
|
||||
/** When set, signals that the card is locked by the host. */
|
||||
const uint32_t CARD_STATUS_CARD_IS_LOCKED = 1UL << 25;
|
||||
/** Set when a sequence or password error has been detected. */
|
||||
const uint32_t CARD_STATUS_LOCK_UNLOCK_FAILED = 1UL << 24;
|
||||
/** The CRC check of the previous command failed. */
|
||||
const uint32_t CARD_STATUS_COM_CRC_ERROR = 1UL << 23;
|
||||
/** Command not legal for the card state. */
|
||||
const uint32_t CARD_STATUS_ILLEGAL_COMMAND = 1UL << 22;
|
||||
/** Card internal ECC was applied but failed to correct the data. */
|
||||
const uint32_t CARD_STATUS_CARD_ECC_FAILED = 1UL << 21;
|
||||
/** Internal card controller error */
|
||||
const uint32_t CARD_STATUS_CC_ERROR = 1UL << 20;
|
||||
/** A general or an unknown error occurred during the operation. */
|
||||
const uint32_t CARD_STATUS_ERROR = 1UL << 19;
|
||||
// bits 19, 18, and 17 reserved.
|
||||
/** Permanent WP set or attempt to change read only values of CSD. */
|
||||
const uint32_t CARD_STATUS_CSD_OVERWRITE = 1UL <<16;
|
||||
/** partial address space was erased due to write protect. */
|
||||
const uint32_t CARD_STATUS_WP_ERASE_SKIP = 1UL << 15;
|
||||
/** The command has been executed without using the internal ECC. */
|
||||
const uint32_t CARD_STATUS_CARD_ECC_DISABLED = 1UL << 14;
|
||||
/** out of erase sequence command was received. */
|
||||
const uint32_t CARD_STATUS_ERASE_RESET = 1UL << 13;
|
||||
/** The state of the card when receiving the command.
|
||||
* 0 = idle
|
||||
* 1 = ready
|
||||
* 2 = ident
|
||||
* 3 = stby
|
||||
* 4 = tran
|
||||
* 5 = data
|
||||
* 6 = rcv
|
||||
* 7 = prg
|
||||
* 8 = dis
|
||||
* 9-14 = reserved
|
||||
* 15 = reserved for I/O mode
|
||||
*/
|
||||
const uint32_t CARD_STATUS_CURRENT_STATE = 0XF << 9;
|
||||
/** Shift for current state. */
|
||||
const uint32_t CARD_STATUS_CURRENT_STATE_SHIFT = 9;
|
||||
/** Corresponds to buffer empty signaling on the bus. */
|
||||
const uint32_t CARD_STATUS_READY_FOR_DATA = 1UL << 8;
|
||||
// bit 7 reserved.
|
||||
/** Extension Functions may set this bit to get host to deal with events. */
|
||||
const uint32_t CARD_STATUS_FX_EVENT = 1UL << 6;
|
||||
/** The card will expect ACMD, or the command has been interpreted as ACMD */
|
||||
const uint32_t CARD_STATUS_APP_CMD = 1UL << 5;
|
||||
// bit 4 reserved.
|
||||
/** Error in the sequence of the authentication process. */
|
||||
const uint32_t CARD_STATUS_AKE_SEQ_ERROR = 1UL << 3;
|
||||
// bits 2,1, and 0 reserved for manufacturer test mode.
|
||||
//==============================================================================
|
||||
/** status for card in the ready state */
|
||||
const uint8_t R1_READY_STATE = 0X00;
|
||||
/** status for card in the idle state */
|
||||
const uint8_t R1_IDLE_STATE = 0X01;
|
||||
/** status bit for illegal command */
|
||||
const uint8_t R1_ILLEGAL_COMMAND = 0X04;
|
||||
/** start data token for read or write single sector*/
|
||||
const uint8_t DATA_START_SECTOR = 0XFE;
|
||||
/** stop token for write multiple sectors*/
|
||||
const uint8_t STOP_TRAN_TOKEN = 0XFD;
|
||||
/** start data token for write multiple sectors*/
|
||||
const uint8_t WRITE_MULTIPLE_TOKEN = 0XFC;
|
||||
/** mask for data response tokens after a write sector operation */
|
||||
const uint8_t DATA_RES_MASK = 0X1F;
|
||||
/** write data accepted token */
|
||||
const uint8_t DATA_RES_ACCEPTED = 0X05;
|
||||
//==============================================================================
|
||||
/**
|
||||
* \class CID
|
||||
* \brief Card IDentification (CID) register.
|
||||
*/
|
||||
typedef struct CID {
|
||||
// byte 0
|
||||
/** Manufacturer ID */
|
||||
unsigned char mid;
|
||||
// byte 1-2
|
||||
/** OEM/Application ID */
|
||||
char oid[2];
|
||||
// byte 3-7
|
||||
/** Product name */
|
||||
char pnm[5];
|
||||
// byte 8
|
||||
/** Product revision least significant digit */
|
||||
unsigned char prv_m : 4;
|
||||
/** Product revision most significant digit */
|
||||
unsigned char prv_n : 4;
|
||||
// byte 9-12
|
||||
/** Product serial number */
|
||||
uint32_t psn;
|
||||
// byte 13
|
||||
/** Manufacturing date year high digit */
|
||||
unsigned char mdt_year_high : 4;
|
||||
/** not used */
|
||||
unsigned char reserved : 4;
|
||||
// byte 14
|
||||
/** Manufacturing date month */
|
||||
unsigned char mdt_month : 4;
|
||||
/** Manufacturing date year low digit */
|
||||
unsigned char mdt_year_low : 4;
|
||||
// byte 15
|
||||
/** not used always 1 */
|
||||
unsigned char always1 : 1;
|
||||
/** CRC7 checksum */
|
||||
unsigned char crc : 7;
|
||||
} __attribute__((packed)) cid_t;
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
||||
/**
|
||||
* \class CSDV1
|
||||
* \brief CSD register for version 1.00 cards .
|
||||
*/
|
||||
typedef struct CSDV1 {
|
||||
// byte 0
|
||||
unsigned char reserved1 : 6;
|
||||
unsigned char csd_ver : 2;
|
||||
// byte 1
|
||||
unsigned char taac;
|
||||
// byte 2
|
||||
unsigned char nsac;
|
||||
// byte 3
|
||||
unsigned char tran_speed;
|
||||
// byte 4
|
||||
unsigned char ccc_high;
|
||||
// byte 5
|
||||
unsigned char read_bl_len : 4;
|
||||
unsigned char ccc_low : 4;
|
||||
// byte 6
|
||||
unsigned char c_size_high : 2;
|
||||
unsigned char reserved2 : 2;
|
||||
unsigned char dsr_imp : 1;
|
||||
unsigned char read_blk_misalign : 1;
|
||||
unsigned char write_blk_misalign : 1;
|
||||
unsigned char read_bl_partial : 1;
|
||||
// byte 7
|
||||
unsigned char c_size_mid;
|
||||
// byte 8
|
||||
unsigned char vdd_r_curr_max : 3;
|
||||
unsigned char vdd_r_curr_min : 3;
|
||||
unsigned char c_size_low : 2;
|
||||
// byte 9
|
||||
unsigned char c_size_mult_high : 2;
|
||||
unsigned char vdd_w_cur_max : 3;
|
||||
unsigned char vdd_w_curr_min : 3;
|
||||
// byte 10
|
||||
unsigned char sector_size_high : 6;
|
||||
unsigned char erase_blk_en : 1;
|
||||
unsigned char c_size_mult_low : 1;
|
||||
// byte 11
|
||||
unsigned char wp_grp_size : 7;
|
||||
unsigned char sector_size_low : 1;
|
||||
// byte 12
|
||||
unsigned char write_bl_len_high : 2;
|
||||
unsigned char r2w_factor : 3;
|
||||
unsigned char reserved3 : 2;
|
||||
unsigned char wp_grp_enable : 1;
|
||||
// byte 13
|
||||
unsigned char reserved4 : 5;
|
||||
unsigned char write_partial : 1;
|
||||
unsigned char write_bl_len_low : 2;
|
||||
// byte 14
|
||||
unsigned char reserved5: 2;
|
||||
unsigned char file_format : 2;
|
||||
unsigned char tmp_write_protect : 1;
|
||||
unsigned char perm_write_protect : 1;
|
||||
unsigned char copy : 1;
|
||||
/** Indicates the file format on the card */
|
||||
unsigned char file_format_grp : 1;
|
||||
// byte 15
|
||||
unsigned char always1 : 1;
|
||||
unsigned char crc : 7;
|
||||
} __attribute__((packed)) csd1_t;
|
||||
//==============================================================================
|
||||
/**
|
||||
* \class CSDV2
|
||||
* \brief CSD register for version 2.00 cards.
|
||||
*/
|
||||
typedef struct CSDV2 {
|
||||
// byte 0
|
||||
unsigned char reserved1 : 6;
|
||||
unsigned char csd_ver : 2;
|
||||
// byte 1
|
||||
/** fixed to 0X0E */
|
||||
unsigned char taac;
|
||||
// byte 2
|
||||
/** fixed to 0 */
|
||||
unsigned char nsac;
|
||||
// byte 3
|
||||
unsigned char tran_speed;
|
||||
// byte 4
|
||||
unsigned char ccc_high;
|
||||
// byte 5
|
||||
/** This field is fixed to 9h, which indicates READ_BL_LEN=512 Byte */
|
||||
unsigned char read_bl_len : 4;
|
||||
unsigned char ccc_low : 4;
|
||||
// byte 6
|
||||
/** not used */
|
||||
unsigned char reserved2 : 4;
|
||||
unsigned char dsr_imp : 1;
|
||||
/** fixed to 0 */
|
||||
unsigned char read_blk_misalign : 1;
|
||||
/** fixed to 0 */
|
||||
unsigned char write_blk_misalign : 1;
|
||||
/** fixed to 0 - no partial read */
|
||||
unsigned char read_bl_partial : 1;
|
||||
// byte 7
|
||||
/** high part of card size */
|
||||
unsigned char c_size_high : 6;
|
||||
/** not used */
|
||||
unsigned char reserved3 : 2;
|
||||
// byte 8
|
||||
/** middle part of card size */
|
||||
unsigned char c_size_mid;
|
||||
// byte 9
|
||||
/** low part of card size */
|
||||
unsigned char c_size_low;
|
||||
// byte 10
|
||||
/** sector size is fixed at 64 KB */
|
||||
unsigned char sector_size_high : 6;
|
||||
/** fixed to 1 - erase single is supported */
|
||||
unsigned char erase_blk_en : 1;
|
||||
/** not used */
|
||||
unsigned char reserved4 : 1;
|
||||
// byte 11
|
||||
unsigned char wp_grp_size : 7;
|
||||
/** sector size is fixed at 64 KB */
|
||||
unsigned char sector_size_low : 1;
|
||||
// byte 12
|
||||
/** write_bl_len fixed for 512 byte sectors */
|
||||
unsigned char write_bl_len_high : 2;
|
||||
/** fixed value of 2 */
|
||||
unsigned char r2w_factor : 3;
|
||||
/** not used */
|
||||
unsigned char reserved5 : 2;
|
||||
/** fixed value of 0 - no write protect groups */
|
||||
unsigned char wp_grp_enable : 1;
|
||||
// byte 13
|
||||
unsigned char reserved6 : 5;
|
||||
/** always zero - no partial sector read*/
|
||||
unsigned char write_partial : 1;
|
||||
/** write_bl_len fixed for 512 byte sectors */
|
||||
unsigned char write_bl_len_low : 2;
|
||||
// byte 14
|
||||
unsigned char reserved7: 2;
|
||||
/** Do not use always 0 */
|
||||
unsigned char file_format : 2;
|
||||
unsigned char tmp_write_protect : 1;
|
||||
unsigned char perm_write_protect : 1;
|
||||
unsigned char copy : 1;
|
||||
/** Do not use always 0 */
|
||||
unsigned char file_format_grp : 1;
|
||||
// byte 15
|
||||
/** not used always 1 */
|
||||
unsigned char always1 : 1;
|
||||
/** checksum */
|
||||
unsigned char crc : 7;
|
||||
} __attribute__((packed)) csd2_t;
|
||||
//==============================================================================
|
||||
/**
|
||||
* \class csd_t
|
||||
* \brief Union of old and new style CSD register.
|
||||
*/
|
||||
union csd_t {
|
||||
csd1_t v1;
|
||||
csd2_t v2;
|
||||
};
|
||||
//-----------------------------------------------------------------------------
|
||||
inline uint32_t sdCardCapacity(csd_t* csd) {
|
||||
if (csd->v1.csd_ver == 0) {
|
||||
uint8_t read_bl_len = csd->v1.read_bl_len;
|
||||
uint16_t c_size = (csd->v1.c_size_high << 10)
|
||||
| (csd->v1.c_size_mid << 2) | csd->v1.c_size_low;
|
||||
uint8_t c_size_mult = (csd->v1.c_size_mult_high << 1)
|
||||
| csd->v1.c_size_mult_low;
|
||||
return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7);
|
||||
} else if (csd->v2.csd_ver == 1) {
|
||||
return (((uint32_t)csd->v2.c_size_high << 16) +
|
||||
((uint16_t)csd->v2.c_size_mid << 8) + csd->v2.c_size_low + 1) << 10;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// fields are big endian
|
||||
typedef struct SdStatus {
|
||||
uint8_t busWidthSecureMode;
|
||||
uint8_t reserved1;
|
||||
uint8_t sdCardType[2];
|
||||
uint8_t sizeOfProtectedArea[4];
|
||||
uint8_t speedClass;
|
||||
uint8_t performanceMove;
|
||||
uint8_t auSize;
|
||||
uint8_t eraseSize[2];
|
||||
uint8_t eraseTimeoutOffset;
|
||||
uint8_t uhsSpeedAuSize;
|
||||
uint8_t videoSpeed;
|
||||
uint8_t vscAuSize[2];
|
||||
uint8_t susAddr[3];
|
||||
uint8_t reserved2[3];
|
||||
uint8_t reservedManufacturer[40];
|
||||
} SdStatus_t;
|
||||
|
||||
typedef struct SCR {
|
||||
/** Bytes 0-3 SD Association, bytes 4-7 reserved for manufacturer. */
|
||||
uint8_t scr[8];
|
||||
/** \return SCR_STRUCTURE field - must be zero.*/
|
||||
uint8_t srcStructure() {return scr[0] >> 4;}
|
||||
/** \return SD_SPEC field 0 - v1.0 or V1.01, 1 - 1.10, 2 - V2.00 or greater */
|
||||
uint8_t sdSpec() {return scr[0] & 0XF;}
|
||||
/** \return false if all zero, true if all one. */
|
||||
bool dataAfterErase() {return 0X80 & scr[1];}
|
||||
/** \return CPRM Security Version. */
|
||||
uint8_t sdSecurity() {return (scr[1] >> 4) & 0X7;}
|
||||
/** \return 0101b. */
|
||||
uint8_t sdBusWidths() {return scr[1] & 0XF;}
|
||||
/** \return true if V3.0 or greater. */
|
||||
bool sdSpec3() {return scr[2] & 0X80;}
|
||||
/** \return if true and sdSpecX is zero V4.xx. */
|
||||
bool sdSpec4() {return scr[2] & 0X4;}
|
||||
/** \return nonzero for version 5 or greater if sdSpec == 2,
|
||||
sdSpec3 == true. Version is return plus four.*/
|
||||
uint8_t sdSpecX() {return (scr[2] & 0X3) << 2 | scr[3] >> 6;}
|
||||
/** \return bit map for support CMD58/59, CMD48/49, CMD23, and CMD20 */
|
||||
uint8_t cmdSupport() {return scr[3] &0XF;}
|
||||
/** \return SD spec version */
|
||||
int16_t sdSpecVer() {
|
||||
if (sdSpec() > 2) {
|
||||
return -1;
|
||||
} else if (sdSpec() < 2) {
|
||||
return sdSpec() ? 110 : 101;
|
||||
} else if (!sdSpec3()) {
|
||||
return 200;
|
||||
} else if (!sdSpec4() && !sdSpecX()) {
|
||||
return 300;
|
||||
}
|
||||
return 400 + 100*sdSpecX();
|
||||
}
|
||||
} scr_t;
|
||||
|
||||
#endif // DOXYGEN_SHOULD_SKIP_THIS
|
||||
#endif // SdCardInfo_h
|
||||
127
src/sd/fatfs/diskio.cpp
Normal file
127
src/sd/fatfs/diskio.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "../../common.h"
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "ff.h" /* Obtains integer types */
|
||||
#include "diskio.h" /* Declarations of disk functions */
|
||||
|
||||
/* Definitions of physical drive number for each drive */
|
||||
#define DEV_SD 0
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
extern "C" DSTATUS disk_status (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
extern "C" DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
switch (pdrv)
|
||||
{
|
||||
case DEV_SD:
|
||||
{
|
||||
if (!gSdCard.TryInitialize())
|
||||
{
|
||||
return STA_NOINIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
extern "C" DRESULT __time_critical_func(disk_read) (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv)
|
||||
{
|
||||
case DEV_SD:
|
||||
{
|
||||
while (!gSdCard.TryReadSectorsSync(buff, sector, count));
|
||||
return RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if FF_FS_READONLY == 0
|
||||
|
||||
extern "C" DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv)
|
||||
{
|
||||
case DEV_SD:
|
||||
{
|
||||
while (!gSdCard.TryWriteSectorsSync(buff, sector, count));
|
||||
return RES_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
extern "C" DRESULT disk_ioctl (
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE cmd, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
77
src/sd/fatfs/diskio.h
Normal file
77
src/sd/fatfs/diskio.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2019 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
7084
src/sd/fatfs/ff.c
Normal file
7084
src/sd/fatfs/ff.c
Normal file
File diff suppressed because it is too large
Load Diff
429
src/sd/fatfs/ff.h
Normal file
429
src/sd/fatfs/ff.h
Normal file
@@ -0,0 +1,429 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.15 /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2022, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 80286 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
/* Integer types used for FatFs API */
|
||||
|
||||
#if defined(_WIN32) /* Windows VC++ (for development only) */
|
||||
#define FF_INTDEF 2
|
||||
#include <windows.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
#include <float.h>
|
||||
#define isnan(v) _isnan(v)
|
||||
#define isinf(v) (!_finite(v))
|
||||
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
#endif
|
||||
|
||||
|
||||
/* Type of file size and LBA variables */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
#if FF_INTDEF != 2
|
||||
#error exFAT feature wants C99 or later
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#if FF_LBA64
|
||||
typedef QWORD LBA_t;
|
||||
#else
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
#else
|
||||
#if FF_LBA64
|
||||
#error exFAT needs to be enabled when enable 64-bit LBA
|
||||
#endif
|
||||
typedef DWORD FSIZE_t;
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API (TCHAR) */
|
||||
|
||||
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) u8 ## x
|
||||
#define _TEXT(x) u8 ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
||||
typedef DWORD TCHAR;
|
||||
#define _T(x) U ## x
|
||||
#define _TEXT(x) U ## x
|
||||
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
||||
#error Wrong FF_LFN_UNICODE setting
|
||||
#else /* ANSI/OEM code in SBCS/DBCS */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#ifndef FF_VOLUME_STRS
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE pdrv; /* Volume hosting physical drive */
|
||||
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] status (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if FF_USE_LFN
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
#endif
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Number of sectors per FAT */
|
||||
LBA_t volbase; /* Volume base sector */
|
||||
LBA_t fatbase; /* FAT base sector */
|
||||
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
|
||||
LBA_t database; /* Data base sector */
|
||||
#if FF_FS_EXFAT
|
||||
LBA_t bitbase; /* Allocation bitmap base sector */
|
||||
#endif
|
||||
LBA_t winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (FFOBJID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||
WORD id; /* Hosting volume's mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||
#endif
|
||||
#if FF_FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} FFOBJID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !FF_FS_READONLY
|
||||
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if FF_USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||
#endif
|
||||
#if !FF_FS_TINY
|
||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
LBA_t sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if FF_USE_LFN
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
#endif
|
||||
#if FF_USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if FF_USE_LFN
|
||||
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
|
||||
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[12 + 1]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* Format parameter structure (MKFS_PARM) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
||||
BYTE n_fat; /* Number of FATs */
|
||||
UINT align; /* Data area alignment (sector) */
|
||||
UINT n_root; /* Number of root directory entries */
|
||||
DWORD au_size; /* Cluster size (byte) */
|
||||
} MKFS_PARM;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs Module Application Interface */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
/* Some API fucntions are implemented as macro */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional Functions */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
/* RTC function (provided by user) */
|
||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||
DWORD get_fattime (void); /* Get current time */
|
||||
#endif
|
||||
|
||||
|
||||
/* LFN support functions (defined in ffunicode.c) */
|
||||
|
||||
#if FF_USE_LFN >= 1
|
||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
||||
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||
#endif
|
||||
|
||||
|
||||
/* O/S dependent functions (samples available in ffsystem.c) */
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
#if FF_FS_REENTRANT /* Sync functions */
|
||||
int ff_mutex_create (int vol); /* Create a sync object */
|
||||
void ff_mutex_delete (int vol); /* Delete a sync object */
|
||||
int ff_mutex_take (int vol); /* Lock sync object */
|
||||
void ff_mutex_give (int vol); /* Unlock sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and Offset Address */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
/* File access mode and open method flags (3rd argument of f_open) */
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA_OPEN_APPEND 0x30
|
||||
|
||||
/* Fast seek controls (2nd argument of f_lseek) */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
/* Format options (2nd argument of f_mkfs) */
|
||||
#define FM_FAT 0x01
|
||||
#define FM_FAT32 0x02
|
||||
#define FM_EXFAT 0x04
|
||||
#define FM_ANY 0x07
|
||||
#define FM_SFD 0x08
|
||||
|
||||
/* Filesystem type (FATFS.fs_type) */
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FF_DEFINED */
|
||||
296
src/sd/fatfs/ffconf.h
Normal file
296
src/sd/fatfs/ffconf.h
Normal file
@@ -0,0 +1,296 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Configurations of FatFs Module
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 80286 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define FF_FS_MINIMIZE 1
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define FF_USE_FIND 0
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
|
||||
#define FF_USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FASTSEEK 1
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_CHMOD 0
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_STRFUNC 0
|
||||
#define FF_PRINT_LLI 1
|
||||
#define FF_PRINT_FLOAT 1
|
||||
#define FF_STRF_ENCODE 3
|
||||
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
/
|
||||
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion.
|
||||
/
|
||||
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
|
||||
/ makes f_printf() support floating point argument. These features want C99 or later.
|
||||
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
|
||||
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
|
||||
/ to be read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP
|
||||
/ 1: Unicode in UTF-16LE
|
||||
/ 2: Unicode in UTF-16BE
|
||||
/ 3: Unicode in UTF-8
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_CODE_PAGE 437
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
|
||||
#define FF_USE_LFN 1
|
||||
#define FF_MAX_LFN 255
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
|
||||
|
||||
|
||||
#define FF_LFN_UNICODE 0
|
||||
/* This option switches the character encoding on the API when LFN is enabled.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||
/
|
||||
/ Also behavior of string I/O functions will be affected by this option.
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
/ the file names to read. The maximum possible length of the read file name depends
|
||||
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_VOLUME_STRS "SD","NAND","CF","RAM","SD2","USB","USB2","USB3"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table is needed as:
|
||||
/
|
||||
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||
*/
|
||||
|
||||
|
||||
#define FF_MULTI_PARTITION 0
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ function will be available. */
|
||||
|
||||
|
||||
#define FF_MIN_SS 512
|
||||
#define FF_MAX_SS 512
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk, but a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define FF_LBA64 0
|
||||
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
|
||||
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||
|
||||
|
||||
#define FF_MIN_GPT 0x10000000
|
||||
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
|
||||
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||
|
||||
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
|
||||
#define FF_FS_EXFAT 0
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2022
|
||||
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
|
||||
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
|
||||
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at the first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
#define FF_FS_LOCK 0
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
|
||||
#define FF_FS_REENTRANT 0
|
||||
#define FF_FS_TIMEOUT 1000
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this featuer.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
|
||||
/ function, must be added to the project. Samples are available in ffsystem.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
208
src/sd/fatfs/ffsystem.c
Normal file
208
src/sd/fatfs/ffsystem.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* A Sample Code of User Provided OS Dependent Functions for FatFs */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Use dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate/Free a Memory Block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdlib.h> /* with POSIX API */
|
||||
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc((size_t)msize); /* Allocate a new memory block */
|
||||
}
|
||||
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (no effect if null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Definitions of Mutex */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */
|
||||
|
||||
|
||||
#if OS_TYPE == 0 /* Win32 */
|
||||
#include <windows.h>
|
||||
static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
|
||||
|
||||
#elif OS_TYPE == 1 /* uITRON */
|
||||
#include "itron.h"
|
||||
#include "kernel.h"
|
||||
static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
|
||||
|
||||
#elif OS_TYPE == 2 /* uc/OS-II */
|
||||
#include "includes.h"
|
||||
static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */
|
||||
|
||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */
|
||||
|
||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
||||
#include "cmsis_os.h"
|
||||
static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Mutex */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount function to create a new mutex
|
||||
/ or semaphore for the volume. When a 0 is returned, the f_mount function
|
||||
/ fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */
|
||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
||||
)
|
||||
{
|
||||
#if OS_TYPE == 0 /* Win32 */
|
||||
Mutex[vol] = CreateMutex(NULL, FALSE, NULL);
|
||||
return (int)(Mutex[vol] != INVALID_HANDLE_VALUE);
|
||||
|
||||
#elif OS_TYPE == 1 /* uITRON */
|
||||
T_CMTX cmtx = {TA_TPRI,1};
|
||||
|
||||
Mutex[vol] = acre_mtx(&cmtx);
|
||||
return (int)(Mutex[vol] > 0);
|
||||
|
||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
||||
OS_ERR err;
|
||||
|
||||
Mutex[vol] = OSMutexCreate(0, &err);
|
||||
return (int)(err == OS_NO_ERR);
|
||||
|
||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
||||
Mutex[vol] = xSemaphoreCreateMutex();
|
||||
return (int)(Mutex[vol] != NULL);
|
||||
|
||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
||||
osMutexDef(cmsis_os_mutex);
|
||||
|
||||
Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex));
|
||||
return (int)(Mutex[vol] != NULL);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Mutex */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount function to delete a mutex or
|
||||
/ semaphore of the volume created with ff_mutex_create function.
|
||||
*/
|
||||
|
||||
void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */
|
||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
||||
)
|
||||
{
|
||||
#if OS_TYPE == 0 /* Win32 */
|
||||
CloseHandle(Mutex[vol]);
|
||||
|
||||
#elif OS_TYPE == 1 /* uITRON */
|
||||
del_mtx(Mutex[vol]);
|
||||
|
||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
||||
OS_ERR err;
|
||||
|
||||
OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err);
|
||||
|
||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
||||
vSemaphoreDelete(Mutex[vol]);
|
||||
|
||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
||||
osMutexDelete(Mutex[vol]);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request a Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on enter file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */
|
||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
||||
)
|
||||
{
|
||||
#if OS_TYPE == 0 /* Win32 */
|
||||
return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0);
|
||||
|
||||
#elif OS_TYPE == 1 /* uITRON */
|
||||
return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK);
|
||||
|
||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
||||
OS_ERR err;
|
||||
|
||||
OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err));
|
||||
return (int)(err == OS_NO_ERR);
|
||||
|
||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
||||
return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE);
|
||||
|
||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
||||
return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release a Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leave file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_mutex_give (
|
||||
int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */
|
||||
)
|
||||
{
|
||||
#if OS_TYPE == 0 /* Win32 */
|
||||
ReleaseMutex(Mutex[vol]);
|
||||
|
||||
#elif OS_TYPE == 1 /* uITRON */
|
||||
unl_mtx(Mutex[vol]);
|
||||
|
||||
#elif OS_TYPE == 2 /* uC/OS-II */
|
||||
OSMutexPost(Mutex[vol]);
|
||||
|
||||
#elif OS_TYPE == 3 /* FreeRTOS */
|
||||
xSemaphoreGive(Mutex[vol]);
|
||||
|
||||
#elif OS_TYPE == 4 /* CMSIS-RTOS */
|
||||
osMutexRelease(Mutex[vol]);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* FF_FS_REENTRANT */
|
||||
|
||||
15593
src/sd/fatfs/ffunicode.c
Normal file
15593
src/sd/fatfs/ffunicode.c
Normal file
File diff suppressed because it is too large
Load Diff
833
src/sd/rp2040_sdio.cpp
Normal file
833
src/sd/rp2040_sdio.cpp
Normal file
@@ -0,0 +1,833 @@
|
||||
// Implementation of SDIO communication for RP2040
|
||||
//
|
||||
// The RP2040 official work-in-progress code at
|
||||
// https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
|
||||
// may be useful reference, but this is independent implementation.
|
||||
//
|
||||
// For official SDIO specifications, refer to:
|
||||
// https://www.sdcard.org/downloads/pls/
|
||||
// "SDIO Physical Layer Simplified Specification Version 8.00"
|
||||
|
||||
#include "../common.h"
|
||||
#include <string.h>
|
||||
#include "rp2040_sdio.h"
|
||||
#include "rp2040_sdio.pio.h"
|
||||
#include <hardware/pio.h>
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/gpio.h>
|
||||
#include <hardware/structs/scb.h>
|
||||
|
||||
#define azdbg(...)
|
||||
#define azlog(...)
|
||||
|
||||
#define SDIO_PIO pio1
|
||||
#define SDIO_CMD_SM 0
|
||||
#define SDIO_DATA_SM 1
|
||||
#define SDIO_DMA_CH 2
|
||||
#define SDIO_DMA_CHB 3
|
||||
|
||||
// Maximum number of 512 byte blocks to transfer in one request
|
||||
#define SDIO_MAX_BLOCKS 256
|
||||
|
||||
enum sdio_transfer_state_t { SDIO_IDLE, SDIO_RX, SDIO_TX, SDIO_TX_WAIT_IDLE};
|
||||
|
||||
static struct {
|
||||
uint32_t pio_cmd_clk_offset;
|
||||
uint32_t pio_data_rx_offset;
|
||||
pio_sm_config pio_cfg_data_rx;
|
||||
uint32_t pio_data_tx_offset;
|
||||
pio_sm_config pio_cfg_data_tx;
|
||||
|
||||
sdio_transfer_state_t transfer_state;
|
||||
uint32_t transfer_start_time;
|
||||
uint32_t *data_buf;
|
||||
uint32_t blocks_done; // Number of blocks transferred so far
|
||||
uint32_t total_blocks; // Total number of blocks to transfer
|
||||
uint32_t blocks_checksumed; // Number of blocks that have had CRC calculated
|
||||
uint32_t checksum_errors; // Number of checksum errors detected
|
||||
|
||||
// Variables for block writes
|
||||
uint64_t next_wr_block_checksum;
|
||||
uint32_t end_token_buf[3]; // CRC and end token for write block
|
||||
sdio_status_t wr_status;
|
||||
uint32_t card_response;
|
||||
|
||||
// Variables for block reads
|
||||
// This is used to perform DMA into data buffers and checksum buffers separately.
|
||||
struct {
|
||||
void * write_addr;
|
||||
uint32_t transfer_count;
|
||||
} dma_blocks[SDIO_MAX_BLOCKS * 2];
|
||||
struct {
|
||||
uint32_t top;
|
||||
uint32_t bottom;
|
||||
} received_checksums[SDIO_MAX_BLOCKS];
|
||||
} g_sdio;
|
||||
|
||||
void rp2040_sdio_dma_irq();
|
||||
|
||||
/*******************************************************
|
||||
* Checksum algorithms
|
||||
*******************************************************/
|
||||
|
||||
// Table lookup for calculating CRC-7 checksum that is used in SDIO command packets.
|
||||
// Usage:
|
||||
// uint8_t crc = 0;
|
||||
// crc = crc7_table[crc ^ byte];
|
||||
// .. repeat for every byte ..
|
||||
static const uint8_t crc7_table[256] = {
|
||||
0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee,
|
||||
0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c, 0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc,
|
||||
0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a, 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a,
|
||||
0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28, 0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8,
|
||||
0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26,
|
||||
0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84, 0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14,
|
||||
0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, 0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42,
|
||||
0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0, 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70,
|
||||
0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc, 0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c,
|
||||
0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce, 0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e,
|
||||
0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98, 0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08,
|
||||
0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa, 0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a,
|
||||
0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34, 0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4,
|
||||
0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06, 0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96,
|
||||
0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50, 0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0,
|
||||
0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62, 0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2
|
||||
};
|
||||
|
||||
// Calculate the CRC16 checksum for parallel 4 bit lines separately.
|
||||
// When the SDIO bus operates in 4-bit mode, the CRC16 algorithm
|
||||
// is applied to each line separately and generates total of
|
||||
// 4 x 16 = 64 bits of checksum.
|
||||
__attribute__((optimize("O3")))
|
||||
uint64_t sdio_crc16_4bit_checksum(uint32_t *data, uint32_t num_words)
|
||||
{
|
||||
uint64_t crc = 0;
|
||||
uint32_t *end = data + num_words;
|
||||
while (data < end)
|
||||
{
|
||||
for (int unroll = 0; unroll < 4; unroll++)
|
||||
{
|
||||
// Each 32-bit word contains 8 bits per line.
|
||||
// Reverse the bytes because SDIO protocol is big-endian.
|
||||
uint32_t data_in = __builtin_bswap32(*data++);
|
||||
|
||||
// Shift out 8 bits for each line
|
||||
uint32_t data_out = crc >> 32;
|
||||
crc <<= 32;
|
||||
|
||||
// XOR outgoing data to itself with 4 bit delay
|
||||
data_out ^= (data_out >> 16);
|
||||
|
||||
// XOR incoming data to outgoing data with 4 bit delay
|
||||
data_out ^= (data_in >> 16);
|
||||
|
||||
// XOR outgoing and incoming data to accumulator at each tap
|
||||
uint64_t xorred = data_out ^ data_in;
|
||||
crc ^= xorred;
|
||||
crc ^= xorred << (5 * 4);
|
||||
crc ^= xorred << (12 * 4);
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Basic SDIO command execution
|
||||
*******************************************************/
|
||||
|
||||
static void sdio_send_command(uint8_t command, uint32_t arg, uint8_t response_bits)
|
||||
{
|
||||
// azdbg("SDIO Command: ", (int)command, " arg ", arg);
|
||||
|
||||
// Format the arguments in the way expected by the PIO code.
|
||||
uint32_t word0 =
|
||||
(47 << 24) | // Number of bits in command minus one
|
||||
( 1 << 22) | // Transfer direction from host to card
|
||||
(command << 16) | // Command byte
|
||||
(((arg >> 24) & 0xFF) << 8) | // MSB byte of argument
|
||||
(((arg >> 16) & 0xFF) << 0);
|
||||
|
||||
uint32_t word1 =
|
||||
(((arg >> 8) & 0xFF) << 24) |
|
||||
(((arg >> 0) & 0xFF) << 16) | // LSB byte of argument
|
||||
( 1 << 8); // End bit
|
||||
|
||||
// Set number of bits in response minus one, or leave at 0 if no response expected
|
||||
if (response_bits)
|
||||
{
|
||||
word1 |= ((response_bits - 1) << 0);
|
||||
}
|
||||
|
||||
// Calculate checksum in the order that the bytes will be transmitted (big-endian)
|
||||
uint8_t crc = 0;
|
||||
crc = crc7_table[crc ^ ((word0 >> 16) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((word0 >> 8) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((word0 >> 0) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((word1 >> 24) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((word1 >> 16) & 0xFF)];
|
||||
word1 |= crc << 8;
|
||||
|
||||
// Transmit command
|
||||
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
|
||||
pio_sm_put(SDIO_PIO, SDIO_CMD_SM, word0);
|
||||
pio_sm_put(SDIO_PIO, SDIO_CMD_SM, word1);
|
||||
}
|
||||
|
||||
sdio_status_t rp2040_sdio_command_R1(uint8_t command, uint32_t arg, uint32_t *response)
|
||||
{
|
||||
sdio_send_command(command, arg, response ? 48 : 0);
|
||||
|
||||
// Wait for response
|
||||
uint32_t start = millis();
|
||||
uint32_t wait_words = response ? 2 : 1;
|
||||
while (pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM) < wait_words)
|
||||
{
|
||||
if ((uint32_t)(millis() - start) > 2)
|
||||
{
|
||||
azdbg("Timeout waiting for response in rp2040_sdio_command_R1(", (int)command, "), ",
|
||||
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)g_sdio.pio_cmd_clk_offset,
|
||||
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
|
||||
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
|
||||
|
||||
// Reset the state machine program
|
||||
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
|
||||
pio_sm_exec(SDIO_PIO, SDIO_CMD_SM, pio_encode_jmp(g_sdio.pio_cmd_clk_offset));
|
||||
return SDIO_ERR_RESPONSE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (response)
|
||||
{
|
||||
// Read out response packet
|
||||
uint32_t resp0 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
|
||||
uint32_t resp1 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
|
||||
// azdbg("SDIO R1 response: ", resp0, " ", resp1);
|
||||
|
||||
// Calculate response checksum
|
||||
uint8_t crc = 0;
|
||||
crc = crc7_table[crc ^ ((resp0 >> 24) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((resp0 >> 16) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((resp0 >> 8) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((resp0 >> 0) & 0xFF)];
|
||||
crc = crc7_table[crc ^ ((resp1 >> 8) & 0xFF)];
|
||||
|
||||
uint8_t actual_crc = ((resp1 >> 0) & 0xFE);
|
||||
if (crc != actual_crc)
|
||||
{
|
||||
azdbg("rp2040_sdio_command_R1(", (int)command, "): CRC error, calculated ", crc, " packet has ", actual_crc);
|
||||
return SDIO_ERR_RESPONSE_CRC;
|
||||
}
|
||||
|
||||
uint8_t response_cmd = ((resp0 >> 24) & 0xFF);
|
||||
if (response_cmd != command && command != 41)
|
||||
{
|
||||
azdbg("rp2040_sdio_command_R1(", (int)command, "): received reply for ", (int)response_cmd);
|
||||
return SDIO_ERR_RESPONSE_CODE;
|
||||
}
|
||||
|
||||
*response = ((resp0 & 0xFFFFFF) << 8) | ((resp1 >> 8) & 0xFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read out dummy marker
|
||||
pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
|
||||
}
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
sdio_status_t rp2040_sdio_command_R2(uint8_t command, uint32_t arg, uint8_t response[16])
|
||||
{
|
||||
// The response is too long to fit in the PIO FIFO, so use DMA to receive it.
|
||||
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
|
||||
uint32_t response_buf[5];
|
||||
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CH);
|
||||
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&dmacfg, false);
|
||||
channel_config_set_write_increment(&dmacfg, true);
|
||||
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_CMD_SM, false));
|
||||
dma_channel_configure(SDIO_DMA_CH, &dmacfg, &response_buf, &SDIO_PIO->rxf[SDIO_CMD_SM], 5, true);
|
||||
|
||||
sdio_send_command(command, arg, 136);
|
||||
|
||||
uint32_t start = millis();
|
||||
while (dma_channel_is_busy(SDIO_DMA_CH))
|
||||
{
|
||||
if ((uint32_t)(millis() - start) > 2)
|
||||
{
|
||||
azdbg("Timeout waiting for response in rp2040_sdio_command_R2(", (int)command, "), ",
|
||||
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)g_sdio.pio_cmd_clk_offset,
|
||||
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
|
||||
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
|
||||
|
||||
// Reset the state machine program
|
||||
dma_channel_abort(SDIO_DMA_CH);
|
||||
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
|
||||
pio_sm_exec(SDIO_PIO, SDIO_CMD_SM, pio_encode_jmp(g_sdio.pio_cmd_clk_offset));
|
||||
return SDIO_ERR_RESPONSE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
dma_channel_abort(SDIO_DMA_CH);
|
||||
|
||||
// Copy the response payload to output buffer
|
||||
response[0] = ((response_buf[0] >> 16) & 0xFF);
|
||||
response[1] = ((response_buf[0] >> 8) & 0xFF);
|
||||
response[2] = ((response_buf[0] >> 0) & 0xFF);
|
||||
response[3] = ((response_buf[1] >> 24) & 0xFF);
|
||||
response[4] = ((response_buf[1] >> 16) & 0xFF);
|
||||
response[5] = ((response_buf[1] >> 8) & 0xFF);
|
||||
response[6] = ((response_buf[1] >> 0) & 0xFF);
|
||||
response[7] = ((response_buf[2] >> 24) & 0xFF);
|
||||
response[8] = ((response_buf[2] >> 16) & 0xFF);
|
||||
response[9] = ((response_buf[2] >> 8) & 0xFF);
|
||||
response[10] = ((response_buf[2] >> 0) & 0xFF);
|
||||
response[11] = ((response_buf[3] >> 24) & 0xFF);
|
||||
response[12] = ((response_buf[3] >> 16) & 0xFF);
|
||||
response[13] = ((response_buf[3] >> 8) & 0xFF);
|
||||
response[14] = ((response_buf[3] >> 0) & 0xFF);
|
||||
response[15] = ((response_buf[4] >> 0) & 0xFF);
|
||||
|
||||
// Calculate checksum of the payload
|
||||
uint8_t crc = 0;
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
crc = crc7_table[crc ^ response[i]];
|
||||
}
|
||||
|
||||
uint8_t actual_crc = response[15] & 0xFE;
|
||||
if (crc != actual_crc)
|
||||
{
|
||||
azdbg("rp2040_sdio_command_R2(", (int)command, "): CRC error, calculated ", crc, " packet has ", actual_crc);
|
||||
return SDIO_ERR_RESPONSE_CRC;
|
||||
}
|
||||
|
||||
uint8_t response_cmd = ((response_buf[0] >> 24) & 0xFF);
|
||||
if (response_cmd != 0x3F)
|
||||
{
|
||||
azdbg("rp2040_sdio_command_R2(", (int)command, "): Expected reply code 0x3F");
|
||||
return SDIO_ERR_RESPONSE_CODE;
|
||||
}
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
|
||||
sdio_status_t rp2040_sdio_command_R3(uint8_t command, uint32_t arg, uint32_t *response)
|
||||
{
|
||||
sdio_send_command(command, arg, 48);
|
||||
|
||||
// Wait for response
|
||||
uint32_t start = millis();
|
||||
while (pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM) < 2)
|
||||
{
|
||||
if ((uint32_t)(millis() - start) > 2)
|
||||
{
|
||||
azdbg("Timeout waiting for response in rp2040_sdio_command_R3(", (int)command, "), ",
|
||||
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)g_sdio.pio_cmd_clk_offset,
|
||||
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
|
||||
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
|
||||
|
||||
// Reset the state machine program
|
||||
pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
|
||||
pio_sm_exec(SDIO_PIO, SDIO_CMD_SM, pio_encode_jmp(g_sdio.pio_cmd_clk_offset));
|
||||
return SDIO_ERR_RESPONSE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
// Read out response packet
|
||||
uint32_t resp0 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
|
||||
uint32_t resp1 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
|
||||
*response = ((resp0 & 0xFFFFFF) << 8) | ((resp1 >> 8) & 0xFF);
|
||||
// azdbg("SDIO R3 response: ", resp0, " ", resp1);
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Data reception from SD card
|
||||
*******************************************************/
|
||||
|
||||
static void setupRxTransfer(uint8_t* buffer, uint32_t num_blocks)
|
||||
{
|
||||
// Buffer must be aligned
|
||||
assert(((uint32_t)buffer & 3) == 0 && num_blocks <= SDIO_MAX_BLOCKS);
|
||||
|
||||
g_sdio.transfer_state = SDIO_RX;
|
||||
g_sdio.transfer_start_time = millis();
|
||||
g_sdio.data_buf = (uint32_t*)buffer;
|
||||
g_sdio.blocks_done = 0;
|
||||
g_sdio.total_blocks = num_blocks;
|
||||
g_sdio.blocks_checksumed = 0;
|
||||
g_sdio.checksum_errors = 0;
|
||||
|
||||
// Create DMA block descriptors to store each block of 512 bytes of data to buffer
|
||||
// and then 8 bytes to g_sdio.received_checksums.
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
g_sdio.dma_blocks[i * 2].write_addr = buffer + i * SDIO_BLOCK_SIZE;
|
||||
g_sdio.dma_blocks[i * 2].transfer_count = SDIO_BLOCK_SIZE / sizeof(uint32_t);
|
||||
|
||||
g_sdio.dma_blocks[i * 2 + 1].write_addr = &g_sdio.received_checksums[i];
|
||||
g_sdio.dma_blocks[i * 2 + 1].transfer_count = 2;
|
||||
}
|
||||
g_sdio.dma_blocks[num_blocks * 2].write_addr = 0;
|
||||
g_sdio.dma_blocks[num_blocks * 2].transfer_count = 0;
|
||||
|
||||
// Configure first DMA channel for reading from the PIO RX fifo
|
||||
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CH);
|
||||
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&dmacfg, false);
|
||||
channel_config_set_write_increment(&dmacfg, true);
|
||||
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_DATA_SM, false));
|
||||
channel_config_set_bswap(&dmacfg, true);
|
||||
channel_config_set_chain_to(&dmacfg, SDIO_DMA_CHB);
|
||||
dma_channel_configure(SDIO_DMA_CH, &dmacfg, 0, &SDIO_PIO->rxf[SDIO_DATA_SM], 0, false);
|
||||
|
||||
// Configure second DMA channel for reconfiguring the first one
|
||||
dmacfg = dma_channel_get_default_config(SDIO_DMA_CHB);
|
||||
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&dmacfg, true);
|
||||
channel_config_set_write_increment(&dmacfg, true);
|
||||
channel_config_set_ring(&dmacfg, true, 3);
|
||||
dma_channel_configure(SDIO_DMA_CHB, &dmacfg, &dma_hw->ch[SDIO_DMA_CH].al1_write_addr,
|
||||
g_sdio.dma_blocks, 2, false);
|
||||
|
||||
// Enable IRQ to trigger when block is done
|
||||
dma_hw->ints1 = 1 << SDIO_DMA_CHB;
|
||||
dma_set_irq1_channel_mask_enabled(1 << SDIO_DMA_CHB, 1);
|
||||
}
|
||||
|
||||
sdio_status_t rp2040_sdio_rx_start(uint8_t *buffer, uint32_t num_blocks)
|
||||
{
|
||||
setupRxTransfer(buffer, num_blocks);
|
||||
|
||||
// Initialize PIO state machine
|
||||
pio_sm_init(SDIO_PIO, SDIO_DATA_SM, g_sdio.pio_data_rx_offset, &g_sdio.pio_cfg_data_rx);
|
||||
pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_DATA_SM, SDIO_D0, 4, false);
|
||||
|
||||
// Write number of nibbles to receive to Y register
|
||||
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, SDIO_BLOCK_SIZE * 2 + 16 - 1);
|
||||
|
||||
// Start PIO and DMA
|
||||
dma_channel_start(SDIO_DMA_CHB);
|
||||
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, true);
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
sdio_status_t rp2040_sdio_rx_continue(uint8_t* buffer, uint32_t num_blocks)
|
||||
{
|
||||
setupRxTransfer(buffer, num_blocks);
|
||||
|
||||
dma_channel_start(SDIO_DMA_CHB);
|
||||
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, SDIO_BLOCK_SIZE * 2 + 16 - 1);
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
// Check checksums for received blocks
|
||||
static void sdio_verify_rx_checksums(uint32_t maxcount)
|
||||
{
|
||||
while (g_sdio.blocks_checksumed < g_sdio.blocks_done && maxcount-- > 0)
|
||||
{
|
||||
// Calculate checksum from received data
|
||||
int blockidx = g_sdio.blocks_checksumed++;
|
||||
uint64_t checksum = sdio_crc16_4bit_checksum(g_sdio.data_buf + blockidx * SDIO_WORDS_PER_BLOCK,
|
||||
SDIO_WORDS_PER_BLOCK);
|
||||
|
||||
// Convert received checksum to little-endian format
|
||||
uint32_t top = __builtin_bswap32(g_sdio.received_checksums[blockidx].top);
|
||||
uint32_t bottom = __builtin_bswap32(g_sdio.received_checksums[blockidx].bottom);
|
||||
uint64_t expected = ((uint64_t)top << 32) | bottom;
|
||||
|
||||
if (checksum != expected)
|
||||
{
|
||||
g_sdio.checksum_errors++;
|
||||
if (g_sdio.checksum_errors == 1)
|
||||
{
|
||||
azlog("SDIO checksum error in reception: block ", blockidx,
|
||||
" calculated ", checksum, " expected ", expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sdio_block_poll_status_t rp2040_sdio_rx_poll_one_block()
|
||||
{
|
||||
if ((uint32_t)(millis() - g_sdio.transfer_start_time) > 1000)
|
||||
{
|
||||
rp2040_sdio_stop();
|
||||
return SDIO_BLOCK_TIMEOUT;
|
||||
}
|
||||
|
||||
if (g_sdio.blocks_done < g_sdio.total_blocks)
|
||||
{
|
||||
// Check how many DMA control blocks have been consumed
|
||||
uint32_t dma_ctrl_block_count = (dma_hw->ch[SDIO_DMA_CHB].read_addr - (uint32_t)&g_sdio.dma_blocks);
|
||||
dma_ctrl_block_count /= sizeof(g_sdio.dma_blocks[0]);
|
||||
|
||||
// Compute how many complete 512 byte SDIO blocks have been transferred
|
||||
// When transfer ends, dma_ctrl_block_count == g_sdio.total_blocks * 2 + 1
|
||||
g_sdio.blocks_done = (dma_ctrl_block_count - 1) / 2;
|
||||
}
|
||||
|
||||
// Was everything done when the previous rx_poll() finished?
|
||||
if (g_sdio.blocks_done >= g_sdio.total_blocks)
|
||||
{
|
||||
g_sdio.transfer_state = SDIO_IDLE;
|
||||
}
|
||||
|
||||
if (g_sdio.blocks_checksumed >= g_sdio.total_blocks)
|
||||
{
|
||||
return SDIO_BLOCK_ALL_DONE;
|
||||
}
|
||||
else if (g_sdio.blocks_checksumed == g_sdio.blocks_done)
|
||||
{
|
||||
return SDIO_BLOCK_NOT_READY;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_sdio.blocks_checksumed + 1 < g_sdio.total_blocks)
|
||||
{
|
||||
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, SDIO_BLOCK_SIZE * 2 + 16 - 1);
|
||||
}
|
||||
|
||||
sdio_verify_rx_checksums(1);
|
||||
|
||||
if (g_sdio.checksum_errors != 0)
|
||||
{
|
||||
return SDIO_BLOCK_CRC_FAIL;
|
||||
}
|
||||
else if (g_sdio.blocks_checksumed >= g_sdio.total_blocks)
|
||||
{
|
||||
return SDIO_BLOCK_ALL_DONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SDIO_BLOCK_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************
|
||||
* Data transmission to SD card
|
||||
*******************************************************/
|
||||
|
||||
static void sdio_start_next_block_tx()
|
||||
{
|
||||
// Initialize PIO
|
||||
pio_sm_init(SDIO_PIO, SDIO_DATA_SM, g_sdio.pio_data_tx_offset, &g_sdio.pio_cfg_data_tx);
|
||||
|
||||
// Configure DMA to send the data block payload (512 bytes)
|
||||
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CH);
|
||||
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&dmacfg, true);
|
||||
channel_config_set_write_increment(&dmacfg, false);
|
||||
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_DATA_SM, true));
|
||||
channel_config_set_bswap(&dmacfg, true);
|
||||
channel_config_set_chain_to(&dmacfg, SDIO_DMA_CHB);
|
||||
dma_channel_configure(SDIO_DMA_CH, &dmacfg,
|
||||
&SDIO_PIO->txf[SDIO_DATA_SM], g_sdio.data_buf + g_sdio.blocks_done * SDIO_WORDS_PER_BLOCK,
|
||||
SDIO_WORDS_PER_BLOCK, false);
|
||||
|
||||
// Prepare second DMA channel to send the CRC and block end marker
|
||||
uint64_t crc = g_sdio.next_wr_block_checksum;
|
||||
g_sdio.end_token_buf[0] = (uint32_t)(crc >> 32);
|
||||
g_sdio.end_token_buf[1] = (uint32_t)(crc >> 0);
|
||||
g_sdio.end_token_buf[2] = 0xFFFFFFFF;
|
||||
channel_config_set_bswap(&dmacfg, false);
|
||||
dma_channel_configure(SDIO_DMA_CHB, &dmacfg,
|
||||
&SDIO_PIO->txf[SDIO_DATA_SM], g_sdio.end_token_buf, 3, false);
|
||||
|
||||
// Enable IRQ to trigger when block is done
|
||||
dma_hw->ints1 = 1 << SDIO_DMA_CHB;
|
||||
dma_set_irq1_channel_mask_enabled(1 << SDIO_DMA_CHB, 1);
|
||||
|
||||
// Initialize register X with nibble count and register Y with response bit count
|
||||
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, 1048);
|
||||
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_out(pio_x, 32));
|
||||
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, 31);
|
||||
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_out(pio_y, 32));
|
||||
|
||||
// Initialize pins to output and high
|
||||
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_set(pio_pins, 15));
|
||||
pio_sm_exec(SDIO_PIO, SDIO_DATA_SM, pio_encode_set(pio_pindirs, 15));
|
||||
|
||||
// Write start token and start the DMA transfer.
|
||||
pio_sm_put(SDIO_PIO, SDIO_DATA_SM, 0xFFFFFFF0);
|
||||
dma_channel_start(SDIO_DMA_CH);
|
||||
|
||||
// Start state machine
|
||||
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, true);
|
||||
}
|
||||
|
||||
static void sdio_compute_next_tx_checksum()
|
||||
{
|
||||
assert (g_sdio.blocks_done < g_sdio.total_blocks && g_sdio.blocks_checksumed < g_sdio.total_blocks);
|
||||
int blockidx = g_sdio.blocks_checksumed++;
|
||||
g_sdio.next_wr_block_checksum = sdio_crc16_4bit_checksum(g_sdio.data_buf + blockidx * SDIO_WORDS_PER_BLOCK,
|
||||
SDIO_WORDS_PER_BLOCK);
|
||||
}
|
||||
|
||||
// Start transferring data from memory to SD card
|
||||
sdio_status_t rp2040_sdio_tx_start(const uint8_t *buffer, uint32_t num_blocks)
|
||||
{
|
||||
// Buffer must be aligned
|
||||
assert(((uint32_t)buffer & 3) == 0 && num_blocks <= SDIO_MAX_BLOCKS);
|
||||
|
||||
g_sdio.transfer_state = SDIO_TX;
|
||||
g_sdio.transfer_start_time = millis();
|
||||
g_sdio.data_buf = (uint32_t*)buffer;
|
||||
g_sdio.blocks_done = 0;
|
||||
g_sdio.total_blocks = num_blocks;
|
||||
g_sdio.blocks_checksumed = 0;
|
||||
g_sdio.checksum_errors = 0;
|
||||
|
||||
// Compute first block checksum
|
||||
sdio_compute_next_tx_checksum();
|
||||
|
||||
// Start first DMA transfer and PIO
|
||||
sdio_start_next_block_tx();
|
||||
|
||||
if (g_sdio.blocks_checksumed < g_sdio.total_blocks)
|
||||
{
|
||||
// Precompute second block checksum
|
||||
sdio_compute_next_tx_checksum();
|
||||
}
|
||||
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
sdio_status_t check_sdio_write_response(uint32_t card_response)
|
||||
{
|
||||
// Shift card response until top bit is 0 (the start bit)
|
||||
// The format of response is poorly documented in SDIO spec but refer to e.g.
|
||||
// http://my-cool-projects.blogspot.com/2013/02/the-mysterious-sd-card-crc-status.html
|
||||
uint32_t resp = card_response;
|
||||
if (!(~resp & 0xFFFF0000)) resp <<= 16;
|
||||
if (!(~resp & 0xFF000000)) resp <<= 8;
|
||||
if (!(~resp & 0xF0000000)) resp <<= 4;
|
||||
if (!(~resp & 0xC0000000)) resp <<= 2;
|
||||
if (!(~resp & 0x80000000)) resp <<= 1;
|
||||
|
||||
uint32_t wr_status = (resp >> 28) & 7;
|
||||
|
||||
if (wr_status == 2)
|
||||
{
|
||||
return SDIO_OK;
|
||||
}
|
||||
else if (wr_status == 5)
|
||||
{
|
||||
azlog("SDIO card reports write CRC error, status ", card_response);
|
||||
return SDIO_ERR_WRITE_CRC;
|
||||
}
|
||||
else if (wr_status == 6)
|
||||
{
|
||||
azlog("SDIO card reports write failure, status ", card_response);
|
||||
return SDIO_ERR_WRITE_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
azlog("SDIO card reports unknown write status ", card_response);
|
||||
return SDIO_ERR_WRITE_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// When a block finishes, this IRQ handler starts the next one
|
||||
static void rp2040_sdio_irq()
|
||||
{
|
||||
dma_hw->ints1 = 1 << SDIO_DMA_CHB;
|
||||
|
||||
if (g_sdio.transfer_state == SDIO_TX)
|
||||
{
|
||||
if (!dma_channel_is_busy(SDIO_DMA_CH) && !dma_channel_is_busy(SDIO_DMA_CHB))
|
||||
{
|
||||
// Main data transfer is finished now.
|
||||
// When card is ready, PIO will put card response on RX fifo
|
||||
g_sdio.transfer_state = SDIO_TX_WAIT_IDLE;
|
||||
if (!pio_sm_is_rx_fifo_empty(SDIO_PIO, SDIO_DATA_SM))
|
||||
{
|
||||
// Card is already idle
|
||||
g_sdio.card_response = pio_sm_get(SDIO_PIO, SDIO_DATA_SM);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use DMA to wait for the response
|
||||
dma_channel_config dmacfg = dma_channel_get_default_config(SDIO_DMA_CHB);
|
||||
channel_config_set_transfer_data_size(&dmacfg, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&dmacfg, false);
|
||||
channel_config_set_write_increment(&dmacfg, false);
|
||||
channel_config_set_dreq(&dmacfg, pio_get_dreq(SDIO_PIO, SDIO_DATA_SM, false));
|
||||
dma_channel_configure(SDIO_DMA_CHB, &dmacfg,
|
||||
&g_sdio.card_response, &SDIO_PIO->rxf[SDIO_DATA_SM], 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_sdio.transfer_state == SDIO_TX_WAIT_IDLE)
|
||||
{
|
||||
if (!dma_channel_is_busy(SDIO_DMA_CHB))
|
||||
{
|
||||
g_sdio.wr_status = check_sdio_write_response(g_sdio.card_response);
|
||||
|
||||
if (g_sdio.wr_status != SDIO_OK)
|
||||
{
|
||||
rp2040_sdio_stop();
|
||||
return;
|
||||
}
|
||||
|
||||
g_sdio.blocks_done++;
|
||||
if (g_sdio.blocks_done < g_sdio.total_blocks)
|
||||
{
|
||||
sdio_start_next_block_tx();
|
||||
g_sdio.transfer_state = SDIO_TX;
|
||||
|
||||
if (g_sdio.blocks_checksumed < g_sdio.total_blocks)
|
||||
{
|
||||
// Precompute the CRC for next block so that it is ready when
|
||||
// we want to send it.
|
||||
sdio_compute_next_tx_checksum();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if transmission is complete
|
||||
sdio_status_t rp2040_sdio_tx_poll(uint32_t *bytes_complete)
|
||||
{
|
||||
if (scb_hw->icsr & M0PLUS_ICSR_VECTACTIVE_BITS)
|
||||
{
|
||||
// Verify that IRQ handler gets called even if we are in hardfault handler
|
||||
rp2040_sdio_irq();
|
||||
}
|
||||
|
||||
if (bytes_complete)
|
||||
{
|
||||
*bytes_complete = g_sdio.blocks_done * SDIO_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if (g_sdio.transfer_state == SDIO_TX_WAIT_IDLE && g_sdio.blocks_done == g_sdio.total_blocks)
|
||||
{
|
||||
return SDIO_OK;
|
||||
}
|
||||
else if (g_sdio.transfer_state == SDIO_IDLE)
|
||||
{
|
||||
rp2040_sdio_stop();
|
||||
return g_sdio.wr_status;
|
||||
}
|
||||
else if ((uint32_t)(millis() - g_sdio.transfer_start_time) > 1000)
|
||||
{
|
||||
azdbg("rp2040_sdio_tx_poll() timeout, "
|
||||
"PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_DATA_SM) - (int)g_sdio.pio_data_tx_offset,
|
||||
" RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_DATA_SM),
|
||||
" TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_DATA_SM),
|
||||
" DMA CNT: ", dma_hw->ch[SDIO_DMA_CH].al2_transfer_count);
|
||||
rp2040_sdio_stop();
|
||||
return SDIO_ERR_DATA_TIMEOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SDIO_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
// Force everything to idle state
|
||||
sdio_status_t rp2040_sdio_stop()
|
||||
{
|
||||
dma_channel_abort(SDIO_DMA_CH);
|
||||
dma_channel_abort(SDIO_DMA_CHB);
|
||||
dma_set_irq1_channel_mask_enabled(1 << SDIO_DMA_CHB, 0);
|
||||
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, false);
|
||||
pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_DATA_SM, SDIO_D0, 4, false);
|
||||
g_sdio.transfer_state = SDIO_IDLE;
|
||||
return SDIO_OK;
|
||||
}
|
||||
|
||||
void rp2040_sdio_init(int clock_divider)
|
||||
{
|
||||
// Mark resources as being in use, unless it has been done already.
|
||||
static bool resources_claimed = false;
|
||||
if (!resources_claimed)
|
||||
{
|
||||
pio_sm_claim(SDIO_PIO, SDIO_CMD_SM);
|
||||
pio_sm_claim(SDIO_PIO, SDIO_DATA_SM);
|
||||
dma_channel_claim(SDIO_DMA_CH);
|
||||
dma_channel_claim(SDIO_DMA_CHB);
|
||||
resources_claimed = true;
|
||||
}
|
||||
|
||||
memset(&g_sdio, 0, sizeof(g_sdio));
|
||||
|
||||
dma_channel_abort(SDIO_DMA_CH);
|
||||
dma_channel_abort(SDIO_DMA_CHB);
|
||||
pio_sm_set_enabled(SDIO_PIO, SDIO_CMD_SM, false);
|
||||
pio_sm_set_enabled(SDIO_PIO, SDIO_DATA_SM, false);
|
||||
|
||||
// Load PIO programs
|
||||
pio_clear_instruction_memory(SDIO_PIO);
|
||||
|
||||
// Command & clock state machine
|
||||
g_sdio.pio_cmd_clk_offset = pio_add_program(SDIO_PIO, &sdio_cmd_clk_program);
|
||||
pio_sm_config cfg = sdio_cmd_clk_program_get_default_config(g_sdio.pio_cmd_clk_offset);
|
||||
sm_config_set_out_pins(&cfg, SDIO_CMD, 1);
|
||||
sm_config_set_in_pins(&cfg, SDIO_CMD);
|
||||
sm_config_set_set_pins(&cfg, SDIO_CMD, 1);
|
||||
sm_config_set_jmp_pin(&cfg, SDIO_CMD);
|
||||
sm_config_set_sideset_pins(&cfg, SDIO_CLK);
|
||||
sm_config_set_out_shift(&cfg, false, true, 32);
|
||||
sm_config_set_in_shift(&cfg, false, true, 32);
|
||||
sm_config_set_clkdiv_int_frac(&cfg, clock_divider, 0);
|
||||
sm_config_set_mov_status(&cfg, STATUS_TX_LESSTHAN, 2);
|
||||
|
||||
pio_sm_init(SDIO_PIO, SDIO_CMD_SM, g_sdio.pio_cmd_clk_offset, &cfg);
|
||||
pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_CMD_SM, SDIO_CLK, 1, true);
|
||||
pio_sm_set_enabled(SDIO_PIO, SDIO_CMD_SM, true);
|
||||
|
||||
// Data reception program
|
||||
g_sdio.pio_data_rx_offset = pio_add_program(SDIO_PIO, &sdio_data_rx_program);
|
||||
g_sdio.pio_cfg_data_rx = sdio_data_rx_program_get_default_config(g_sdio.pio_data_rx_offset);
|
||||
sm_config_set_in_pins(&g_sdio.pio_cfg_data_rx, SDIO_D0);
|
||||
sm_config_set_in_shift(&g_sdio.pio_cfg_data_rx, false, true, 32);
|
||||
sm_config_set_out_shift(&g_sdio.pio_cfg_data_rx, false, true, 32);
|
||||
sm_config_set_clkdiv_int_frac(&g_sdio.pio_cfg_data_rx, clock_divider, 0);
|
||||
sm_config_set_sideset_pins(&g_sdio.pio_cfg_data_rx, SDIO_CLK);
|
||||
|
||||
// Data transmission program
|
||||
g_sdio.pio_data_tx_offset = pio_add_program(SDIO_PIO, &sdio_data_tx_program);
|
||||
g_sdio.pio_cfg_data_tx = sdio_data_tx_program_get_default_config(g_sdio.pio_data_tx_offset);
|
||||
sm_config_set_in_pins(&g_sdio.pio_cfg_data_tx, SDIO_D0);
|
||||
sm_config_set_set_pins(&g_sdio.pio_cfg_data_tx, SDIO_D0, 4);
|
||||
sm_config_set_out_pins(&g_sdio.pio_cfg_data_tx, SDIO_D0, 4);
|
||||
sm_config_set_in_shift(&g_sdio.pio_cfg_data_tx, false, false, 32);
|
||||
sm_config_set_out_shift(&g_sdio.pio_cfg_data_tx, false, true, 32);
|
||||
sm_config_set_clkdiv_int_frac(&g_sdio.pio_cfg_data_tx, clock_divider, 0);
|
||||
|
||||
// Disable SDIO pins input synchronizer.
|
||||
// This reduces input delay.
|
||||
// Because the CLK is driven synchronously to CPU clock,
|
||||
// there should be no metastability problems.
|
||||
SDIO_PIO->input_sync_bypass |= (1 << SDIO_CLK) | (1 << SDIO_CMD)
|
||||
| (1 << SDIO_D0) | (1 << SDIO_D1) | (1 << SDIO_D2) | (1 << SDIO_D3);
|
||||
|
||||
// Redirect GPIOs to PIO
|
||||
gpio_set_function(SDIO_CMD, GPIO_FUNC_PIO1);
|
||||
gpio_set_function(SDIO_CLK, GPIO_FUNC_PIO1);
|
||||
gpio_set_function(SDIO_D0, GPIO_FUNC_PIO1);
|
||||
gpio_set_function(SDIO_D1, GPIO_FUNC_PIO1);
|
||||
gpio_set_function(SDIO_D2, GPIO_FUNC_PIO1);
|
||||
gpio_set_function(SDIO_D3, GPIO_FUNC_PIO1);
|
||||
|
||||
gpio_set_slew_rate(SDIO_CMD, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(SDIO_CLK, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(SDIO_D0, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(SDIO_D1, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(SDIO_D2, GPIO_SLEW_RATE_FAST);
|
||||
gpio_set_slew_rate(SDIO_D3, GPIO_SLEW_RATE_FAST);
|
||||
|
||||
// Set up IRQ handler when DMA completes.
|
||||
irq_set_exclusive_handler(DMA_IRQ_1, rp2040_sdio_irq);
|
||||
irq_set_enabled(DMA_IRQ_1, true);
|
||||
}
|
||||
65
src/sd/rp2040_sdio.h
Normal file
65
src/sd/rp2040_sdio.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// SD card access using SDIO for RP2040 platform.
|
||||
// This module contains the low-level SDIO bus implementation using
|
||||
// the PIO peripheral. The high-level commands are in sd_card_sdio.cpp.
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
enum sdio_status_t {
|
||||
SDIO_OK = 0,
|
||||
SDIO_BUSY = 1,
|
||||
SDIO_ERR_RESPONSE_TIMEOUT = 2, // Timed out waiting for response from card
|
||||
SDIO_ERR_RESPONSE_CRC = 3, // Response CRC is wrong
|
||||
SDIO_ERR_RESPONSE_CODE = 4, // Response command code does not match what was sent
|
||||
SDIO_ERR_DATA_TIMEOUT = 5, // Timed out waiting for data block
|
||||
SDIO_ERR_DATA_CRC = 6, // CRC for data packet is wrong
|
||||
SDIO_ERR_WRITE_CRC = 7, // Card reports bad CRC for write
|
||||
SDIO_ERR_WRITE_FAIL = 8, // Card reports write failure
|
||||
};
|
||||
|
||||
enum sdio_block_poll_status_t
|
||||
{
|
||||
SDIO_BLOCK_NOT_READY,
|
||||
SDIO_BLOCK_OK,
|
||||
SDIO_BLOCK_CRC_FAIL,
|
||||
SDIO_BLOCK_ALL_DONE,
|
||||
SDIO_BLOCK_TIMEOUT
|
||||
};
|
||||
|
||||
#define SDIO_BLOCK_SIZE 512
|
||||
#define SDIO_WORDS_PER_BLOCK 128
|
||||
|
||||
// Execute a command that has 48-bit reply (response types R1, R6, R7)
|
||||
// If response is NULL, does not wait for reply.
|
||||
sdio_status_t rp2040_sdio_command_R1(uint8_t command, uint32_t arg, uint32_t *response);
|
||||
|
||||
// Execute a command that has 136-bit reply (response type R2)
|
||||
// Response buffer should have space for 16 bytes (the 128 bit payload)
|
||||
sdio_status_t rp2040_sdio_command_R2(uint8_t command, uint32_t arg, uint8_t *response);
|
||||
|
||||
// Execute a command that has 48-bit reply but without CRC (response R3)
|
||||
sdio_status_t rp2040_sdio_command_R3(uint8_t command, uint32_t arg, uint32_t *response);
|
||||
|
||||
// Start transferring data from SD card to memory buffer
|
||||
// Transfer block size is always 512 bytes.
|
||||
sdio_status_t rp2040_sdio_rx_start(uint8_t *buffer, uint32_t num_blocks);
|
||||
|
||||
sdio_status_t rp2040_sdio_rx_continue(uint8_t *buffer, uint32_t num_blocks);
|
||||
|
||||
// Check if reception is complete
|
||||
// Returns SDIO_BUSY while transferring, SDIO_OK when done and error on failure.
|
||||
sdio_status_t rp2040_sdio_rx_poll(uint32_t *bytes_complete = nullptr);
|
||||
|
||||
// Start transferring data from memory to SD card
|
||||
sdio_status_t rp2040_sdio_tx_start(const uint8_t *buffer, uint32_t num_blocks);
|
||||
|
||||
// Check if transmission is complete
|
||||
sdio_status_t rp2040_sdio_tx_poll(uint32_t *bytes_complete = nullptr);
|
||||
|
||||
// Force everything to idle state
|
||||
sdio_status_t rp2040_sdio_stop();
|
||||
|
||||
// (Re)initialize the SDIO interface
|
||||
void rp2040_sdio_init(int clock_divider = 1);
|
||||
|
||||
sdio_block_poll_status_t rp2040_sdio_rx_poll_one_block();
|
||||
149
src/sd/rp2040_sdio.pio
Normal file
149
src/sd/rp2040_sdio.pio
Normal file
@@ -0,0 +1,149 @@
|
||||
; RP2040 PIO program for implementing SD card access in SDIO mode
|
||||
; Run "pioasm rp2040_sdio.pio rp2040_sdio.pio.h" to regenerate the C header from this.
|
||||
|
||||
; The RP2040 official work-in-progress code at
|
||||
; https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
|
||||
; may be useful reference, but this is independent implementation.
|
||||
;
|
||||
; For official SDIO specifications, refer to:
|
||||
; https://www.sdcard.org/downloads/pls/
|
||||
; "SDIO Physical Layer Simplified Specification Version 8.00"
|
||||
|
||||
; Clock settings
|
||||
; For 3.3V communication the available speeds are:
|
||||
; - Default speed: max. 25 MHz clock
|
||||
; - High speed: max. 50 MHz clock
|
||||
;
|
||||
; From the default RP2040 clock speed of 125 MHz, the closest dividers
|
||||
; are 3 for 41.7 MHz and 5 for 25 MHz. The CPU can apply further divider
|
||||
; through state machine registers for the initial handshake.
|
||||
;
|
||||
; Because data is written on the falling edge and read on the rising
|
||||
; edge, it is preferrable to have a long 0 state and short 1 state.
|
||||
;.define CLKDIV 3
|
||||
; .define CLKDIV 5
|
||||
;for 200MHz base clock
|
||||
.define CLKDIV 8
|
||||
.define D0 ((CLKDIV + 1) / 2 - 1)
|
||||
.define D1 (CLKDIV/2 - 1)
|
||||
.define SDIO_CLK_GPIO 3
|
||||
|
||||
; State machine 0 is used to:
|
||||
; - generate continuous clock on SDIO_CLK
|
||||
; - send CMD packets
|
||||
; - receive response packets
|
||||
;
|
||||
; Pin mapping for this state machine:
|
||||
; - Sideset : CLK
|
||||
; - IN/OUT/SET : CMD
|
||||
; - JMP_PIN : CMD
|
||||
;
|
||||
; The commands to send are put on TX fifo and must have two words:
|
||||
; Word 0 bits 31-24: Number of bits in command minus one (usually 47)
|
||||
; Word 0 bits 23-00: First 24 bits of the command packet, shifted out MSB first
|
||||
; Word 1 bits 31-08: Last 24 bits of the command packet, shifted out MSB first
|
||||
; Word 1 bits 07-00: Number of bits in response minus one (usually 47), or 0 if no response
|
||||
;
|
||||
; The response is put on RX fifo, starting with the MSB.
|
||||
; Partial last word will be padded with zero bits at the top.
|
||||
;
|
||||
; The state machine EXECCTRL should be set so that STATUS indicates TX FIFO < 2
|
||||
; and that AUTOPULL and AUTOPUSH are enabled.
|
||||
|
||||
.program sdio_cmd_clk
|
||||
.side_set 1
|
||||
|
||||
mov OSR, NULL side 1 [D1] ; Make sure OSR is full of zeros to prevent autopull
|
||||
|
||||
wait_cmd:
|
||||
mov Y, !STATUS side 0 [D0] ; Check if TX FIFO has data
|
||||
jmp !Y wait_cmd side 1 [D1]
|
||||
|
||||
load_cmd:
|
||||
out NULL, 32 side 0 [D0] ; Load first word (trigger autopull)
|
||||
out X, 8 side 1 [D1] ; Number of bits to send
|
||||
set pins, 1 side 0 [D0] ; Initial state of CMD is high
|
||||
set pindirs, 1 side 1 [D1] ; Set SDIO_CMD as output
|
||||
|
||||
send_cmd:
|
||||
out pins, 1 side 0 [D0] ; Write output on falling edge of CLK
|
||||
jmp X-- send_cmd side 1 [D1]
|
||||
|
||||
prep_resp:
|
||||
set pindirs, 0 side 0 [D0] ; Set SDIO_CMD as input
|
||||
out X, 8 side 1 [D1] ; Get number of bits in response
|
||||
nop side 0 [D0] ; For clock alignment
|
||||
jmp !X resp_done side 1 [D1] ; Check if we expect a response
|
||||
|
||||
wait_resp:
|
||||
nop side 0 [D0]
|
||||
jmp PIN wait_resp side 1 [D1] ; Loop until SDIO_CMD = 0
|
||||
|
||||
; Note: input bits are read at the same time as we write CLK=0.
|
||||
; Because the host controls the clock, the read happens before
|
||||
; the card sees the falling clock edge. This gives maximum time
|
||||
; for the data bit to settle.
|
||||
read_resp:
|
||||
in PINS, 1 side 0 [D0] ; Read input data bit
|
||||
jmp X-- read_resp side 1 [D1] ; Loop to receive all data bits
|
||||
|
||||
resp_done:
|
||||
push side 0 [D0] ; Push the remaining part of response
|
||||
|
||||
; State machine 1 is used to send and receive data blocks.
|
||||
; Pin mapping for this state machine:
|
||||
; - IN / OUT: SDIO_D0-D3
|
||||
; - GPIO defined at beginning of this file: SDIO_CLK
|
||||
|
||||
; Data reception program
|
||||
; This program will wait for initial start of block token and then
|
||||
; receive a data block. The application must set number of nibbles
|
||||
; to receive minus 1 to Y register before running this program.
|
||||
.program sdio_data_rx
|
||||
.side_set 1 opt
|
||||
|
||||
wait_start:
|
||||
out X, 32 side 0
|
||||
; mov X, Y ; Reinitialize number of nibbles to receive
|
||||
wait 0 pin 0 ; Wait for zero state on D0
|
||||
wait 1 gpio SDIO_CLK_GPIO [CLKDIV-1] ; Wait for rising edge and then whole clock cycle
|
||||
|
||||
rx_data:
|
||||
in PINS, 4 [CLKDIV-2] ; Read nibble
|
||||
jmp X--, rx_data
|
||||
|
||||
; Data transmission program
|
||||
;
|
||||
; Before running this program, pindirs should be set as output
|
||||
; and register X should be initialized with the number of nibbles
|
||||
; to send minus 1 (typically 8 + 1024 + 16 + 1 - 1 = 1048)
|
||||
; and register Y with the number of response bits minus 1 (typically 31).
|
||||
;
|
||||
; Words written to TX FIFO must be:
|
||||
; - Word 0: start token 0xFFFFFFF0
|
||||
; - Word 1-128: transmitted data (512 bytes)
|
||||
; - Word 129-130: CRC checksum
|
||||
; - Word 131: end token 0xFFFFFFFF
|
||||
;
|
||||
; After the card reports idle status, RX FIFO will get a word that
|
||||
; contains the D0 line response from card.
|
||||
|
||||
.program sdio_data_tx
|
||||
wait 0 gpio SDIO_CLK_GPIO
|
||||
wait 1 gpio SDIO_CLK_GPIO [CLKDIV + D1 - 1]; Synchronize so that write occurs on falling edge
|
||||
|
||||
tx_loop:
|
||||
out PINS, 4 [D0] ; Write nibble and wait for whole clock cycle
|
||||
jmp X-- tx_loop [D1]
|
||||
|
||||
set pindirs, 0x00 [D0] ; Set data bus as input
|
||||
|
||||
.wrap_target
|
||||
response_loop:
|
||||
in PINS, 1 [D1] ; Read D0 on rising edge
|
||||
jmp Y--, response_loop [D0]
|
||||
|
||||
wait_idle:
|
||||
wait 1 pin 0 [D1] ; Wait for card to indicate idle condition
|
||||
push [D0] ; Push the response token
|
||||
.wrap
|
||||
244
src/tinyusb/dcd.h
Normal file
244
src/tinyusb/dcd.h
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_DCD_H_
|
||||
#define TUSB_DCD_H_
|
||||
|
||||
#include "tusb_common.h"
|
||||
#include "osal.h"
|
||||
// #include "common/tusb_fifo.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef enum {
|
||||
DCD_EVENT_INVALID = 0, // 0
|
||||
DCD_EVENT_BUS_RESET, // 1
|
||||
DCD_EVENT_UNPLUGGED, // 2
|
||||
DCD_EVENT_SOF, // 3
|
||||
DCD_EVENT_SUSPEND, // 4 TODO LPM Sleep L1 support
|
||||
DCD_EVENT_RESUME, // 5
|
||||
DCD_EVENT_SETUP_RECEIVED, // 6
|
||||
DCD_EVENT_XFER_COMPLETE, // 7
|
||||
USBD_EVENT_FUNC_CALL, // 8 Not an DCD event, just a convenient way to defer ISR function
|
||||
DCD_EVENT_COUNT
|
||||
} dcd_eventid_t;
|
||||
|
||||
typedef struct TU_ATTR_ALIGNED(4) {
|
||||
uint8_t rhport;
|
||||
uint8_t event_id;
|
||||
|
||||
union {
|
||||
// BUS RESET
|
||||
struct {
|
||||
tusb_speed_t speed;
|
||||
} bus_reset;
|
||||
|
||||
// SOF
|
||||
struct {
|
||||
uint32_t frame_count;
|
||||
}sof;
|
||||
|
||||
// SETUP_RECEIVED
|
||||
tusb_control_request_t setup_received;
|
||||
|
||||
// XFER_COMPLETE
|
||||
struct {
|
||||
uint8_t ep_addr;
|
||||
uint8_t result;
|
||||
uint32_t len;
|
||||
}xfer_complete;
|
||||
|
||||
// FUNC_CALL
|
||||
struct {
|
||||
void (*func) (void*);
|
||||
void* param;
|
||||
}func_call;
|
||||
};
|
||||
} dcd_event_t;
|
||||
|
||||
//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Memory API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// clean/flush data cache: write cache -> memory.
|
||||
// Required before an DMA TX transfer to make sure data is in memory
|
||||
void dcd_dcache_clean(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
// invalidate data cache: mark cache as invalid, next read will read from memory
|
||||
// Required BOTH before and after an DMA RX transfer
|
||||
void dcd_dcache_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
// clean and invalidate data cache
|
||||
// Required before an DMA transfer where memory is both read/write by DMA
|
||||
void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) TU_ATTR_WEAK;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Controller API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Initialize controller to device mode
|
||||
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init);
|
||||
|
||||
// Deinitialize controller, unset device mode.
|
||||
bool dcd_deinit(uint8_t rhport);
|
||||
|
||||
// Interrupt Handler
|
||||
void dcd_int_handler(uint8_t rhport);
|
||||
|
||||
// Enable device interrupt
|
||||
void dcd_int_enable (uint8_t rhport);
|
||||
|
||||
// Disable device interrupt
|
||||
void dcd_int_disable(uint8_t rhport);
|
||||
|
||||
// Receive Set Address request, mcu port must also include status IN response
|
||||
void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
|
||||
|
||||
// Wake up host
|
||||
void dcd_remote_wakeup(uint8_t rhport);
|
||||
|
||||
// Connect by enabling internal pull-up resistor on D+/D-
|
||||
void dcd_connect(uint8_t rhport);
|
||||
|
||||
// Disconnect by disabling internal pull-up resistor on D+/D-
|
||||
void dcd_disconnect(uint8_t rhport);
|
||||
|
||||
// Enable/Disable Start-of-frame interrupt. Default is disabled
|
||||
void dcd_sof_enable(uint8_t rhport, bool en);
|
||||
|
||||
#if CFG_TUD_TEST_MODE
|
||||
// Put device into a test mode (needs power cycle to quit)
|
||||
void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector);
|
||||
#endif
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when a control transfer's status stage is complete.
|
||||
// May help DCD to prepare for next control transfer, this API is optional.
|
||||
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request);
|
||||
|
||||
// Configure endpoint's registers according to descriptor
|
||||
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
|
||||
|
||||
// Close all non-control endpoints, cancel all pending transfers if any.
|
||||
// Invoked when switching from a non-zero Configuration by SET_CONFIGURE therefore
|
||||
// required for multiple configuration support.
|
||||
void dcd_edpt_close_all (uint8_t rhport);
|
||||
|
||||
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
|
||||
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
||||
|
||||
// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack
|
||||
// This API is optional, may be useful for register-based for transferring data.
|
||||
// bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK;
|
||||
|
||||
// Stall endpoint, any queuing transfer should be removed from endpoint
|
||||
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
// clear stall, data toggle is also reset to DATA0
|
||||
// This API never calls with control endpoints, since it is auto cleared when receiving setup packet
|
||||
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
#ifdef TUP_DCD_EDPT_ISO_ALLOC
|
||||
// Allocate packet buffer used by ISO endpoints
|
||||
// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering
|
||||
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size);
|
||||
|
||||
// Configure and enable an ISO endpoint according to descriptor
|
||||
bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
|
||||
|
||||
#else
|
||||
// Close an endpoint.
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Event API (implemented by stack)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Called by DCD to notify device stack
|
||||
extern void dcd_event_handler(dcd_event_t const * event, bool in_isr);
|
||||
|
||||
// helper to send bus signal event
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = eid;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// helper to send bus reset event
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_BUS_RESET;
|
||||
event.bus_reset.speed = speed;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// helper to send setup received
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_SETUP_RECEIVED;
|
||||
memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t));
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
// helper to send transfer complete event
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_XFER_COMPLETE;
|
||||
event.xfer_complete.ep_addr = ep_addr;
|
||||
event.xfer_complete.len = xferred_bytes;
|
||||
event.xfer_complete.result = result;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t frame_count, bool in_isr) {
|
||||
dcd_event_t event;
|
||||
event.rhport = rhport;
|
||||
event.event_id = DCD_EVENT_SOF;
|
||||
event.sof.frame_count = frame_count;
|
||||
dcd_event_handler(&event, in_isr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
567
src/tinyusb/dcd_rp2040.c
Normal file
567
src/tinyusb/dcd_rp2040.c
Normal file
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "../common.h"
|
||||
#include "../usbEventQueue.h"
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUD_ENABLED && (CFG_TUSB_MCU == OPT_MCU_RP2040) && !CFG_TUD_RPI_PIO_USB
|
||||
|
||||
#include "pico.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "rp2040_usb.h"
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
|
||||
#include "pico/fix/rp2040_usb_device_enumeration.h"
|
||||
#endif
|
||||
|
||||
#include "dcd.h"
|
||||
|
||||
// Current implementation force vbus detection as always present, causing device think it is always plugged into host.
|
||||
// Therefore it cannot detect disconnect event, mistaken it as suspend.
|
||||
// Note: won't work if change to 0 (for now)
|
||||
#define FORCE_VBUS_DETECT 1
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Low level controller
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
// Init these in dcd_init
|
||||
static uint8_t* next_buffer_ptr;
|
||||
|
||||
// USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in.
|
||||
static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2];
|
||||
|
||||
// SOF may be used by remote wakeup as RESUME, this indicate whether SOF is actually used by usbd
|
||||
static bool _sof_enable = false;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_num(uint8_t num, tusb_dir_t dir) {
|
||||
return &hw_endpoints[num][dir];
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr(uint8_t ep_addr) {
|
||||
uint8_t num = tu_edpt_number(ep_addr);
|
||||
tusb_dir_t dir = tu_edpt_dir(ep_addr);
|
||||
return hw_endpoint_get_by_num(num, dir);
|
||||
}
|
||||
|
||||
static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) {
|
||||
// size must be multiple of 64
|
||||
uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u;
|
||||
|
||||
// double buffered Bulk endpoint
|
||||
if (transfer_type == TUSB_XFER_BULK) {
|
||||
size *= 2u;
|
||||
}
|
||||
|
||||
ep->hw_data_buf = next_buffer_ptr;
|
||||
next_buffer_ptr += size;
|
||||
|
||||
assert(((uintptr_t) next_buffer_ptr & 0b111111u) == 0);
|
||||
uint dpram_offset = hw_data_offset(ep->hw_data_buf);
|
||||
hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX);
|
||||
|
||||
pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf);
|
||||
|
||||
// Fill in endpoint control register with buffer offset
|
||||
uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset;
|
||||
|
||||
*ep->endpoint_control = reg;
|
||||
}
|
||||
|
||||
static void _hw_endpoint_close(struct hw_endpoint* ep) {
|
||||
// Clear hardware registers and then zero the struct
|
||||
// Clears endpoint enable
|
||||
*ep->endpoint_control = 0;
|
||||
// Clears buffer available, etc
|
||||
*ep->buffer_control = 0;
|
||||
// Clear any endpoint state
|
||||
memset(ep, 0, sizeof(struct hw_endpoint));
|
||||
|
||||
// Reclaim buffer space if all endpoints are closed
|
||||
bool reclaim_buffers = true;
|
||||
for (uint8_t i = 1; i < USB_MAX_ENDPOINTS; i++) {
|
||||
if (hw_endpoint_get_by_num(i, TUSB_DIR_OUT)->hw_data_buf != NULL ||
|
||||
hw_endpoint_get_by_num(i, TUSB_DIR_IN)->hw_data_buf != NULL) {
|
||||
reclaim_buffers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reclaim_buffers) {
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_endpoint_close(uint8_t ep_addr) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
_hw_endpoint_close(ep);
|
||||
}
|
||||
|
||||
static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
|
||||
const uint8_t num = tu_edpt_number(ep_addr);
|
||||
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
|
||||
|
||||
ep->ep_addr = ep_addr;
|
||||
|
||||
// For device, IN is a tx transfer and OUT is an rx transfer
|
||||
ep->rx = (dir == TUSB_DIR_OUT);
|
||||
|
||||
ep->next_pid = 0u;
|
||||
ep->wMaxPacketSize = wMaxPacketSize;
|
||||
ep->transfer_type = transfer_type;
|
||||
|
||||
// Every endpoint has a buffer control register in dpram
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].in;
|
||||
} else {
|
||||
ep->buffer_control = &usb_dpram->ep_buf_ctrl[num].out;
|
||||
}
|
||||
|
||||
// Clear existing buffer control state
|
||||
*ep->buffer_control = 0;
|
||||
|
||||
if (num == 0) {
|
||||
// EP0 has no endpoint control register because the buffer offsets are fixed
|
||||
ep->endpoint_control = NULL;
|
||||
|
||||
// Buffer offset is fixed (also double buffered)
|
||||
ep->hw_data_buf = (uint8_t*) &usb_dpram->ep0_buf_a[0];
|
||||
} else {
|
||||
// Set the endpoint control register (starts at EP1, hence num-1)
|
||||
if (dir == TUSB_DIR_IN) {
|
||||
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].in;
|
||||
} else {
|
||||
ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out;
|
||||
}
|
||||
|
||||
// alloc a buffer and fill in endpoint control register
|
||||
_hw_endpoint_alloc(ep, transfer_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void hw_endpoint_xfer(uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
hw_endpoint_xfer_start(ep, buffer, total_bytes);
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(hw_handle_buff_status)(void) {
|
||||
uint32_t remaining_buffers = usb_hw->buf_status;
|
||||
pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
|
||||
uint bit = 1u;
|
||||
for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) {
|
||||
if (remaining_buffers & bit) {
|
||||
// clear this in advance
|
||||
usb_hw_clear->buf_status = bit;
|
||||
|
||||
// IN transfer for even i, OUT transfer for odd i
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(i >> 1u, (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN);
|
||||
|
||||
// Continue xfer
|
||||
bool done = hw_endpoint_xfer_continue(ep);
|
||||
if (done) {
|
||||
// Notify
|
||||
usb_tryEnqueueEvent32Bit(USB_EVENT_XFER_COMPLETE | ep->ep_addr | (ep->xferred_len << 8));
|
||||
// dcd_event_xfer_complete(0, ep->ep_addr, ep->xferred_len, XFER_RESULT_SUCCESS, true);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
}
|
||||
remaining_buffers &= ~bit;
|
||||
}
|
||||
bit <<= 1u;
|
||||
}
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void reset_ep0(void) {
|
||||
// If we have finished this transfer on EP0 set pid back to 1 for next
|
||||
// setup transfer. Also clear a stall in case
|
||||
for (uint8_t dir = 0; dir < 2; dir++) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(0, dir);
|
||||
if (ep->active) {
|
||||
// Abort any pending transfer from a prior control transfer per USB specs
|
||||
// Due to Errata RP2040-E2: ABORT flag is only applicable for B2 and later (unusable for B0, B1).
|
||||
// Which means we are not guaranteed to safely abort pending transfer on B0 and B1.
|
||||
uint32_t const abort_mask = (dir ? USB_EP_ABORT_EP0_IN_BITS : USB_EP_ABORT_EP0_OUT_BITS);
|
||||
if (rp2040_chip_version() >= 2) {
|
||||
usb_hw_set->abort = abort_mask;
|
||||
while ((usb_hw->abort_done & abort_mask) != abort_mask) {}
|
||||
}
|
||||
|
||||
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_DATA1_PID | USB_BUF_CTRL_SEL);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
|
||||
if (rp2040_chip_version() >= 2) {
|
||||
usb_hw_clear->abort_done = abort_mask;
|
||||
usb_hw_clear->abort = abort_mask;
|
||||
}
|
||||
}
|
||||
ep->next_pid = 1u;
|
||||
}
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(reset_non_control_endpoints)(void) {
|
||||
// Disable all non-control
|
||||
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS - 1; i++) {
|
||||
usb_dpram->ep_ctrl[i].in = 0;
|
||||
usb_dpram->ep_ctrl[i].out = 0;
|
||||
}
|
||||
|
||||
// clear non-control hw endpoints
|
||||
tu_memclr(hw_endpoints[1], sizeof(hw_endpoints) - 2 * sizeof(hw_endpoint_t));
|
||||
|
||||
// reclaim buffer space
|
||||
next_buffer_ptr = &usb_dpram->epx_data[0];
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
|
||||
uint32_t const status = usb_hw->ints;
|
||||
uint32_t handled = 0;
|
||||
|
||||
if (status & USB_INTF_DEV_SOF_BITS) {
|
||||
bool keep_sof_alive = false;
|
||||
|
||||
handled |= USB_INTF_DEV_SOF_BITS;
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
// Errata 15 workaround for Device Bulk-In endpoint
|
||||
e15_last_sof = time_us_32();
|
||||
|
||||
for (uint8_t i = 0; i < USB_MAX_ENDPOINTS; i++) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_num(i, TUSB_DIR_IN);
|
||||
|
||||
// Active Bulk IN endpoint requires SOF
|
||||
if ((ep->transfer_type == TUSB_XFER_BULK) && ep->active) {
|
||||
keep_sof_alive = true;
|
||||
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
// Deferred enable?
|
||||
if (ep->pending) {
|
||||
ep->pending = 0;
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
|
||||
hw_endpoint_lock_update(ep, -1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// disable SOF interrupt if it is used for RESUME in remote wakeup
|
||||
if (!keep_sof_alive && !_sof_enable) usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
|
||||
|
||||
usb_tryEnqueueEvent32Bit(USB_EVENT_SOF | (usb_hw->sof_rd & USB_SOF_RD_BITS));
|
||||
// dcd_event_sof(0, usb_hw->sof_rd & USB_SOF_RD_BITS, true);
|
||||
}
|
||||
|
||||
// xfer events are handled before setup req. So if a transfer completes immediately
|
||||
// before closing the EP, the events will be delivered in same order.
|
||||
if (status & USB_INTS_BUFF_STATUS_BITS) {
|
||||
handled |= USB_INTS_BUFF_STATUS_BITS;
|
||||
hw_handle_buff_status();
|
||||
}
|
||||
|
||||
if (status & USB_INTS_SETUP_REQ_BITS) {
|
||||
handled |= USB_INTS_SETUP_REQ_BITS;
|
||||
const tusb_control_request_t* setup = (const tusb_control_request_t*)remove_volatile_cast(uint8_t const*, &usb_dpram->setup_packet);
|
||||
|
||||
// reset pid to both 1 (data and ack)
|
||||
reset_ep0();
|
||||
|
||||
// Pass setup packet to tiny usb
|
||||
usb_tryEnqueueEvent64Bit(
|
||||
USB_EVENT_SETUP_RECEIVED | ((setup->wLength & 0x1FFF) << 16) | ((setup->bmRequestType_bit.direction) << 29) | setup->wIndex,
|
||||
setup->wValue | (setup->bRequest << 16) | ((setup->bmRequestType & 0x7F) << 24));
|
||||
// dcd_event_setup_received(0, setup, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
|
||||
}
|
||||
|
||||
#if FORCE_VBUS_DETECT == 0
|
||||
// Since we force VBUS detect On, device will always think it is connected and
|
||||
// couldn't distinguish between disconnect and suspend
|
||||
if (status & USB_INTS_DEV_CONN_DIS_BITS)
|
||||
{
|
||||
handled |= USB_INTS_DEV_CONN_DIS_BITS;
|
||||
|
||||
if ( usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS )
|
||||
{
|
||||
// Connected: nothing to do
|
||||
}else
|
||||
{
|
||||
// Disconnected
|
||||
usb_tryEnqueueEvent32Bit(USB_EVENT_UNPLUGGED);
|
||||
// dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, true);
|
||||
}
|
||||
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_CONNECTED_BITS;
|
||||
}
|
||||
#endif
|
||||
|
||||
// SE0 for 2.5 us or more (will last at least 10ms)
|
||||
if (status & USB_INTS_BUS_RESET_BITS) {
|
||||
pico_trace("BUS RESET\r\n");
|
||||
|
||||
handled |= USB_INTS_BUS_RESET_BITS;
|
||||
|
||||
usb_hw->dev_addr_ctrl = 0;
|
||||
reset_non_control_endpoints();
|
||||
usb_tryEnqueueEvent32Bit(USB_EVENT_BUS_RESET);
|
||||
// dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
|
||||
// Only run enumeration workaround if pull up is enabled
|
||||
if (usb_hw->sie_ctrl & USB_SIE_CTRL_PULLUP_EN_BITS) rp2040_usb_device_enumeration_fix();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Note from pico datasheet 4.1.2.6.4 (v1.2)
|
||||
* If you enable the suspend interrupt, it is likely you will see a suspend interrupt when
|
||||
* the device is first connected but the bus is idle. The bus can be idle for a few ms before
|
||||
* the host begins sending start of frame packets. You will also see a suspend interrupt
|
||||
* when the device is disconnected if you do not have a VBUS detect circuit connected. This is
|
||||
* because without VBUS detection, it is impossible to tell the difference between
|
||||
* being disconnected and suspended.
|
||||
*/
|
||||
if (status & USB_INTS_DEV_SUSPEND_BITS) {
|
||||
handled |= USB_INTS_DEV_SUSPEND_BITS;
|
||||
usb_tryEnqueueEvent32Bit(USB_EVENT_SUSPEND);
|
||||
// dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
|
||||
}
|
||||
|
||||
if (status & USB_INTS_DEV_RESUME_FROM_HOST_BITS) {
|
||||
handled |= USB_INTS_DEV_RESUME_FROM_HOST_BITS;
|
||||
usb_tryEnqueueEvent32Bit(USB_EVENT_RESUME);
|
||||
// dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
|
||||
usb_hw_clear->sie_status = USB_SIE_STATUS_RESUME_BITS;
|
||||
}
|
||||
|
||||
if (status ^ handled) {
|
||||
panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
||||
}
|
||||
}
|
||||
|
||||
#define USB_INTS_ERROR_BITS ( \
|
||||
USB_INTS_ERROR_DATA_SEQ_BITS | \
|
||||
USB_INTS_ERROR_BIT_STUFF_BITS | \
|
||||
USB_INTS_ERROR_CRC_BITS | \
|
||||
USB_INTS_ERROR_RX_OVERFLOW_BITS | \
|
||||
USB_INTS_ERROR_RX_TIMEOUT_BITS)
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Controller API
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
// older SDK
|
||||
#ifndef PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY
|
||||
#define PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY 0xff
|
||||
#endif
|
||||
|
||||
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
||||
(void) rh_init;
|
||||
assert(rhport == 0);
|
||||
|
||||
TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
|
||||
|
||||
// Reset hardware to default state
|
||||
rp2040_usb_init();
|
||||
|
||||
#if FORCE_VBUS_DETECT
|
||||
// Force VBUS detect so the device thinks it is plugged into a host
|
||||
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
|
||||
#endif
|
||||
|
||||
irq_add_shared_handler(USBCTRL_IRQ, dcd_rp2040_irq, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
|
||||
|
||||
// Init control endpoints
|
||||
tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t));
|
||||
hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL);
|
||||
hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL);
|
||||
|
||||
// Init non-control endpoints
|
||||
reset_non_control_endpoints();
|
||||
|
||||
// Initializes the USB peripheral for device mode and enables it.
|
||||
// Don't need to enable the pull up here. Force VBUS
|
||||
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
|
||||
|
||||
// Enable individual controller IRQS here. Processor interrupt enable will be used
|
||||
// for the global interrupt enable...
|
||||
// Note: Force VBUS detect cause disconnection not detectable
|
||||
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
|
||||
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS |
|
||||
USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS |
|
||||
(FORCE_VBUS_DETECT ? 0 : USB_INTS_DEV_CONN_DIS_BITS);
|
||||
|
||||
dcd_connect(rhport);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcd_deinit(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
reset_non_control_endpoints();
|
||||
irq_remove_handler(USBCTRL_IRQ, dcd_rp2040_irq);
|
||||
|
||||
// reset usb hardware into initial state
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_int_enable(__unused uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, true);
|
||||
}
|
||||
|
||||
void dcd_int_disable(__unused uint8_t rhport) {
|
||||
assert(rhport == 0);
|
||||
irq_set_enabled(USBCTRL_IRQ, false);
|
||||
}
|
||||
|
||||
void dcd_set_address(__unused uint8_t rhport, __unused uint8_t dev_addr) {
|
||||
assert(rhport == 0);
|
||||
|
||||
// Can't set device address in hardware until status xfer has complete
|
||||
// Send 0len complete response on EP0 IN
|
||||
hw_endpoint_xfer(0x80, NULL, 0);
|
||||
}
|
||||
|
||||
void dcd_remote_wakeup(__unused uint8_t rhport) {
|
||||
pico_info("dcd_remote_wakeup %d\n", rhport);
|
||||
assert(rhport == 0);
|
||||
|
||||
// since RESUME interrupt is not triggered if we are the one initiate
|
||||
// briefly enable SOF to notify usbd when bus is ready
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
|
||||
}
|
||||
|
||||
// disconnect by disabling internal pull-up resistor on D+/D-
|
||||
void dcd_disconnect(__unused uint8_t rhport) {
|
||||
(void) rhport;
|
||||
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
}
|
||||
|
||||
// connect by enabling internal pull-up resistor on D+/D-
|
||||
void dcd_connect(__unused uint8_t rhport) {
|
||||
(void) rhport;
|
||||
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
}
|
||||
|
||||
void dcd_sof_enable(uint8_t rhport, bool en) {
|
||||
(void) rhport;
|
||||
|
||||
_sof_enable = en;
|
||||
|
||||
if (en) {
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}
|
||||
#if !TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
else {
|
||||
// Don't clear immediately if the SOF workaround is in use.
|
||||
// The SOF handler will conditionally disable the interrupt.
|
||||
usb_hw_clear->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* DCD Endpoint port
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
|
||||
(void) rhport;
|
||||
|
||||
if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
|
||||
request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
|
||||
request->bRequest == TUSB_REQ_SET_ADDRESS) {
|
||||
usb_hw->dev_addr_ctrl = (uint8_t) request->wValue;
|
||||
}
|
||||
}
|
||||
|
||||
bool dcd_edpt_open(__unused uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
|
||||
assert(rhport == 0);
|
||||
hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_close_all(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
|
||||
// may need to use EP Abort
|
||||
reset_non_control_endpoints();
|
||||
}
|
||||
|
||||
bool dcd_edpt_xfer(__unused uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
|
||||
assert(rhport == 0);
|
||||
hw_endpoint_xfer(ep_addr, buffer, total_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
|
||||
if (tu_edpt_number(ep_addr) == 0) {
|
||||
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
|
||||
usb_hw_set->ep_stall_arm = (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) ? USB_EP_STALL_ARM_EP0_IN_BITS
|
||||
: USB_EP_STALL_ARM_EP0_OUT_BITS;
|
||||
}
|
||||
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
|
||||
// stall and clear current pending buffer
|
||||
// may need to use EP_ABORT
|
||||
_hw_endpoint_buffer_control_set_value32(ep, USB_BUF_CTRL_STALL);
|
||||
}
|
||||
|
||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
|
||||
if (tu_edpt_number(ep_addr)) {
|
||||
struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
|
||||
|
||||
// clear stall also reset toggle to DATA0, ready for next transfer
|
||||
ep->next_pid = 0;
|
||||
_hw_endpoint_buffer_control_clear_mask32(ep, USB_BUF_CTRL_STALL);
|
||||
}
|
||||
}
|
||||
|
||||
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
|
||||
(void) rhport;
|
||||
pico_trace("dcd_edpt_close %02x\r\n", ep_addr);
|
||||
hw_endpoint_close(ep_addr);
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(dcd_int_handler)(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
dcd_rp2040_irq();
|
||||
}
|
||||
|
||||
#endif
|
||||
99
src/tinyusb/osal.h
Normal file
99
src/tinyusb/osal.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_OSAL_H_
|
||||
#define _TUSB_OSAL_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "tusb_common.h"
|
||||
|
||||
typedef void (*osal_task_func_t)( void * );
|
||||
|
||||
// Timeout
|
||||
#define OSAL_TIMEOUT_NOTIMEOUT (0) // Return immediately
|
||||
#define OSAL_TIMEOUT_NORMAL (10) // Default timeout
|
||||
#define OSAL_TIMEOUT_WAIT_FOREVER (UINT32_MAX) // Wait forever
|
||||
#define OSAL_TIMEOUT_CONTROL_XFER OSAL_TIMEOUT_WAIT_FOREVER
|
||||
|
||||
// Mutex is required when using a preempted RTOS or MCU has multiple cores
|
||||
#if (CFG_TUSB_OS == OPT_OS_NONE) && !TUP_MCU_MULTIPLE_CORE
|
||||
#define OSAL_MUTEX_REQUIRED 0
|
||||
#define OSAL_MUTEX_DEF(_name) uint8_t :0
|
||||
#else
|
||||
#define OSAL_MUTEX_REQUIRED 1
|
||||
#define OSAL_MUTEX_DEF(_name) osal_mutex_def_t _name
|
||||
#endif
|
||||
|
||||
// OS thin implementation
|
||||
#if CFG_TUSB_OS == OPT_OS_NONE
|
||||
#include "osal_none.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_FREERTOS
|
||||
#include "osal_freertos.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_MYNEWT
|
||||
#include "osal_mynewt.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_PICO
|
||||
#include "osal_pico.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_RTTHREAD
|
||||
#include "osal_rtthread.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_RTX4
|
||||
#include "osal_rtx4.h"
|
||||
#elif CFG_TUSB_OS == OPT_OS_CUSTOM
|
||||
#include "tusb_os_custom.h" // implemented by application
|
||||
#else
|
||||
#error OS is not supported yet
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// OSAL Porting API
|
||||
// Should be implemented as static inline function in osal_port.h header
|
||||
/*
|
||||
osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
|
||||
bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
|
||||
bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);
|
||||
bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec);
|
||||
void osal_semaphore_reset(osal_semaphore_t sem_hdl); // TODO removed
|
||||
|
||||
osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef);
|
||||
bool osal_mutex_delete(osal_mutex_t mutex_hdl)
|
||||
bool osal_mutex_lock (osal_mutex_t sem_hdl, uint32_t msec);
|
||||
bool osal_mutex_unlock(osal_mutex_t mutex_hdl);
|
||||
|
||||
osal_queue_t osal_queue_create(osal_queue_def_t* qdef);
|
||||
bool osal_queue_delete(osal_queue_t qhdl);
|
||||
bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec);
|
||||
bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr);
|
||||
bool osal_queue_empty(osal_queue_t qhdl);
|
||||
*/
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_OSAL_H_ */
|
||||
196
src/tinyusb/osal_none.h
Normal file
196
src/tinyusb/osal_none.h
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_OSAL_NONE_H_
|
||||
#define TUSB_OSAL_NONE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TASK API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if CFG_TUH_ENABLED
|
||||
// currently only needed/available in host mode
|
||||
TU_ATTR_WEAK void osal_task_delay(uint32_t msec);
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Binary Semaphore API
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
volatile uint16_t count;
|
||||
} osal_semaphore_def_t;
|
||||
|
||||
typedef osal_semaphore_def_t* osal_semaphore_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef) {
|
||||
semdef->count = 0;
|
||||
return semdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t semd_hdl) {
|
||||
(void) semd_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
|
||||
(void) in_isr;
|
||||
sem_hdl->count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO blocking for now
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec) {
|
||||
(void) msec;
|
||||
|
||||
while (sem_hdl->count == 0) {}
|
||||
sem_hdl->count--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t sem_hdl) {
|
||||
sem_hdl->count = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MUTEX API
|
||||
// Within tinyusb, mutex is never used in ISR context
|
||||
//--------------------------------------------------------------------+
|
||||
typedef osal_semaphore_def_t osal_mutex_def_t;
|
||||
typedef osal_semaphore_t osal_mutex_t;
|
||||
|
||||
#if OSAL_MUTEX_REQUIRED
|
||||
// Note: multiple cores MCUs usually do provide IPC API for mutex
|
||||
// or we can use std atomic function
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef) {
|
||||
mdef->count = 1;
|
||||
return mdef;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_delete(osal_mutex_t mutex_hdl) {
|
||||
(void) mutex_hdl;
|
||||
return true; // nothing to do
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_lock (osal_mutex_t mutex_hdl, uint32_t msec) {
|
||||
return osal_semaphore_wait(mutex_hdl, msec);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl) {
|
||||
return osal_semaphore_post(mutex_hdl, false);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define osal_mutex_create(_mdef) (NULL)
|
||||
#define osal_mutex_lock(_mutex_hdl, _ms) (true)
|
||||
#define osal_mutex_unlock(_mutex_hdl) (true)
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// QUEUE API
|
||||
//--------------------------------------------------------------------+
|
||||
// #include "common/tusb_fifo.h"
|
||||
|
||||
// typedef struct {
|
||||
// void (* interrupt_set)(bool);
|
||||
// tu_fifo_t ff;
|
||||
// } osal_queue_def_t;
|
||||
|
||||
// typedef osal_queue_def_t* osal_queue_t;
|
||||
|
||||
// // _int_set is used as mutex in OS NONE (disable/enable USB ISR)
|
||||
// #define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
|
||||
// uint8_t _name##_buf[_depth*sizeof(_type)]; \
|
||||
// osal_queue_def_t _name = { \
|
||||
// .interrupt_set = _int_set, \
|
||||
// .ff = TU_FIFO_INIT(_name##_buf, _depth, _type, false) \
|
||||
// }
|
||||
|
||||
// // lock queue by disable USB interrupt
|
||||
// TU_ATTR_ALWAYS_INLINE static inline void _osal_q_lock(osal_queue_t qhdl) {
|
||||
// // disable dcd/hcd interrupt
|
||||
// qhdl->interrupt_set(false);
|
||||
// }
|
||||
|
||||
// // unlock queue
|
||||
// TU_ATTR_ALWAYS_INLINE static inline void _osal_q_unlock(osal_queue_t qhdl) {
|
||||
// // enable dcd/hcd interrupt
|
||||
// qhdl->interrupt_set(true);
|
||||
// }
|
||||
|
||||
// TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
|
||||
// tu_fifo_clear(&qdef->ff);
|
||||
// return (osal_queue_t) qdef;
|
||||
// }
|
||||
|
||||
// TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_delete(osal_queue_t qhdl) {
|
||||
// (void) qhdl;
|
||||
// return true; // nothing to do
|
||||
// }
|
||||
|
||||
// TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec) {
|
||||
// (void) msec; // not used, always behave as msec = 0
|
||||
|
||||
// _osal_q_lock(qhdl);
|
||||
// bool success = tu_fifo_read(&qhdl->ff, data);
|
||||
// _osal_q_unlock(qhdl);
|
||||
|
||||
// return success;
|
||||
// }
|
||||
|
||||
// TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const* data, bool in_isr) {
|
||||
// if (!in_isr) {
|
||||
// _osal_q_lock(qhdl);
|
||||
// }
|
||||
|
||||
// bool success = tu_fifo_write(&qhdl->ff, data);
|
||||
|
||||
// if (!in_isr) {
|
||||
// _osal_q_unlock(qhdl);
|
||||
// }
|
||||
|
||||
// return success;
|
||||
// }
|
||||
|
||||
// TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl) {
|
||||
// // Skip queue lock/unlock since this function is primarily called
|
||||
// // with interrupt disabled before going into low power mode
|
||||
// return tu_fifo_empty(&qhdl->ff);
|
||||
// }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
382
src/tinyusb/rp2040_usb.c
Normal file
382
src/tinyusb/rp2040_usb.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
* Copyright (c) 2021 Ha Thach (tinyusb.org) for Double Buffered
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#include "tusb_option.h"
|
||||
|
||||
#if CFG_TUSB_MCU == OPT_MCU_RP2040
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "rp2040_usb.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTOTYPE
|
||||
//--------------------------------------------------------------------+
|
||||
static void _hw_endpoint_xfer_sync(struct hw_endpoint* ep);
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
static bool e15_is_bulkin_ep(struct hw_endpoint* ep);
|
||||
static bool e15_is_critical_frame_period(struct hw_endpoint* ep);
|
||||
#else
|
||||
#define e15_is_bulkin_ep(x) (false)
|
||||
#define e15_is_critical_frame_period(x) (false)
|
||||
#endif
|
||||
|
||||
// if usb hardware is in host mode
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) {
|
||||
return (usb_hw->main_ctrl & USB_MAIN_CTRL_HOST_NDEVICE_BITS) ? true : false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Implementation
|
||||
//--------------------------------------------------------------------+
|
||||
// Provide own byte by byte memcpy as not all copies are aligned
|
||||
static void unaligned_memcpy(void *dst, const void *src, size_t n) {
|
||||
uint8_t *dst_byte = (uint8_t*)dst;
|
||||
const uint8_t *src_byte = (const uint8_t*)src;
|
||||
while (n--) {
|
||||
*dst_byte++ = *src_byte++;
|
||||
}
|
||||
}
|
||||
|
||||
void rp2040_usb_init(void) {
|
||||
// Reset usb controller
|
||||
reset_block(RESETS_RESET_USBCTRL_BITS);
|
||||
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
|
||||
|
||||
#ifdef __GNUC__
|
||||
// Clear any previous state just in case
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#if __GNUC__ > 6
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
#endif
|
||||
#endif
|
||||
memset(usb_dpram, 0, sizeof(*usb_dpram));
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// Mux the controller to the onboard usb phy
|
||||
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
|
||||
|
||||
TU_LOG2_INT(sizeof(hw_endpoint_t));
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(hw_endpoint_reset_transfer)(struct hw_endpoint* ep) {
|
||||
ep->active = false;
|
||||
ep->remaining_len = 0;
|
||||
ep->xferred_len = 0;
|
||||
ep->user_buf = 0;
|
||||
}
|
||||
|
||||
void __tusb_irq_path_func(_hw_endpoint_buffer_control_update32)(struct hw_endpoint* ep, uint32_t and_mask,
|
||||
uint32_t or_mask) {
|
||||
uint32_t value = 0;
|
||||
|
||||
if (and_mask) {
|
||||
value = *ep->buffer_control & and_mask;
|
||||
}
|
||||
|
||||
if (or_mask) {
|
||||
value |= or_mask;
|
||||
if (or_mask & USB_BUF_CTRL_AVAIL) {
|
||||
if (*ep->buffer_control & USB_BUF_CTRL_AVAIL) {
|
||||
panic("ep %02X was already available", ep->ep_addr);
|
||||
}
|
||||
*ep->buffer_control = value & ~USB_BUF_CTRL_AVAIL;
|
||||
// 4.1.2.5.1 Con-current access: 12 cycles (should be good for 48*12Mhz = 576Mhz) after write to buffer control
|
||||
// Don't need delay in host mode as host is in charge
|
||||
if ( !is_host_mode()) {
|
||||
busy_wait_at_least_cycles(12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ep->buffer_control = value;
|
||||
}
|
||||
|
||||
// prepare buffer, return buffer control
|
||||
static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
|
||||
uint16_t const buflen = tu_min16(ep->remaining_len, ep->wMaxPacketSize);
|
||||
ep->remaining_len = (uint16_t) (ep->remaining_len - buflen);
|
||||
|
||||
uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL;
|
||||
|
||||
// PID
|
||||
buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
|
||||
ep->next_pid ^= 1u;
|
||||
|
||||
if (!ep->rx) {
|
||||
// Copy data from user buffer to hw buffer
|
||||
unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
|
||||
ep->user_buf += buflen;
|
||||
|
||||
// Mark as full
|
||||
buf_ctrl |= USB_BUF_CTRL_FULL;
|
||||
}
|
||||
|
||||
// Is this the last buffer? Only really matters for host mode. Will trigger
|
||||
// the trans complete irq but also stop it polling. We only really care about
|
||||
// trans complete for setup packets being sent
|
||||
if (ep->remaining_len == 0) {
|
||||
buf_ctrl |= USB_BUF_CTRL_LAST;
|
||||
}
|
||||
|
||||
if (buf_id) buf_ctrl = buf_ctrl << 16;
|
||||
|
||||
return buf_ctrl;
|
||||
}
|
||||
|
||||
// Prepare buffer control register value
|
||||
void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint* ep) {
|
||||
uint32_t ep_ctrl = *ep->endpoint_control;
|
||||
|
||||
// always compute and start with buffer 0
|
||||
uint32_t buf_ctrl = prepare_ep_buffer(ep, 0) | USB_BUF_CTRL_SEL;
|
||||
|
||||
// For now: skip double buffered for OUT endpoint in Device mode, since
|
||||
// host could send < 64 bytes and cause short packet on buffer0
|
||||
// NOTE: this could happen to Host mode IN endpoint
|
||||
// Also, Host mode "interrupt" endpoint hardware is only single buffered,
|
||||
// NOTE2: Currently Host bulk is implemented using "interrupt" endpoint
|
||||
bool const is_host = is_host_mode();
|
||||
bool const force_single = (!is_host && !tu_edpt_dir(ep->ep_addr)) ||
|
||||
(is_host && tu_edpt_number(ep->ep_addr) != 0);
|
||||
|
||||
if (ep->remaining_len && !force_single) {
|
||||
// Use buffer 1 (double buffered) if there is still data
|
||||
// TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt)
|
||||
|
||||
buf_ctrl |= prepare_ep_buffer(ep, 1);
|
||||
|
||||
// Set endpoint control double buffered bit if needed
|
||||
ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER;
|
||||
ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER;
|
||||
} else {
|
||||
// Single buffered since 1 is enough
|
||||
ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
|
||||
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
|
||||
}
|
||||
|
||||
*ep->endpoint_control = ep_ctrl;
|
||||
|
||||
TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
|
||||
// Finally, write to buffer_control which will trigger the transfer
|
||||
// the next time the controller polls this dpram address
|
||||
_hw_endpoint_buffer_control_set_value32(ep, buf_ctrl);
|
||||
}
|
||||
|
||||
void hw_endpoint_xfer_start(struct hw_endpoint* ep, uint8_t* buffer, uint16_t total_len) {
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
if (ep->active) {
|
||||
// TODO: Is this acceptable for interrupt packets?
|
||||
TU_LOG(1, "WARN: starting new transfer on already active ep %02X\r\n", ep->ep_addr);
|
||||
hw_endpoint_reset_transfer(ep);
|
||||
}
|
||||
|
||||
// Fill in info now that we're kicking off the hw
|
||||
ep->remaining_len = total_len;
|
||||
ep->xferred_len = 0;
|
||||
ep->active = true;
|
||||
ep->user_buf = buffer;
|
||||
|
||||
if (e15_is_bulkin_ep(ep)) {
|
||||
usb_hw_set->inte = USB_INTS_DEV_SOF_BITS;
|
||||
}
|
||||
|
||||
if (e15_is_critical_frame_period(ep)) {
|
||||
ep->pending = 1;
|
||||
} else {
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
|
||||
hw_endpoint_lock_update(ep, -1);
|
||||
}
|
||||
|
||||
// sync endpoint buffer and return transferred bytes
|
||||
static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uint8_t buf_id) {
|
||||
uint32_t buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
|
||||
if (buf_id) buf_ctrl = buf_ctrl >> 16;
|
||||
|
||||
uint16_t xferred_bytes = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
|
||||
|
||||
if (!ep->rx) {
|
||||
// We are continuing a transfer here. If we are TX, we have successfully
|
||||
// sent some data can increase the length we have sent
|
||||
assert(!(buf_ctrl & USB_BUF_CTRL_FULL));
|
||||
|
||||
ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
|
||||
} else {
|
||||
// If we have received some data, so can increase the length
|
||||
// we have received AFTER we have copied it to the user buffer at the appropriate offset
|
||||
assert(buf_ctrl & USB_BUF_CTRL_FULL);
|
||||
|
||||
unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
|
||||
ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
|
||||
ep->user_buf += xferred_bytes;
|
||||
}
|
||||
|
||||
// Short packet
|
||||
if (xferred_bytes < ep->wMaxPacketSize) {
|
||||
pico_trace(" Short packet on buffer %d with %u bytes\r\n", buf_id, xferred_bytes);
|
||||
// Reduce total length as this is last packet
|
||||
ep->remaining_len = 0;
|
||||
}
|
||||
|
||||
return xferred_bytes;
|
||||
}
|
||||
|
||||
static void __tusb_irq_path_func(_hw_endpoint_xfer_sync)(struct hw_endpoint* ep) {
|
||||
// Update hw endpoint struct with info from hardware
|
||||
// after a buff status interrupt
|
||||
|
||||
uint32_t __unused buf_ctrl = _hw_endpoint_buffer_control_get_value32(ep);
|
||||
TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
|
||||
// always sync buffer 0
|
||||
uint16_t buf0_bytes = sync_ep_buffer(ep, 0);
|
||||
|
||||
// sync buffer 1 if double buffered
|
||||
if ((*ep->endpoint_control) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
|
||||
if (buf0_bytes == ep->wMaxPacketSize) {
|
||||
// sync buffer 1 if not short packet
|
||||
sync_ep_buffer(ep, 1);
|
||||
} else {
|
||||
// short packet on buffer 0
|
||||
// TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example
|
||||
// At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from
|
||||
// the next transfer (not current one). For now we disable double buffered for device OUT
|
||||
// NOTE this could happen to Host IN
|
||||
#if 0
|
||||
uint8_t const ep_num = tu_edpt_number(ep->ep_addr);
|
||||
uint8_t const dir = (uint8_t) tu_edpt_dir(ep->ep_addr);
|
||||
uint8_t const ep_id = 2*ep_num + (dir ? 0 : 1);
|
||||
|
||||
// abort queued transfer on buffer 1
|
||||
usb_hw->abort |= TU_BIT(ep_id);
|
||||
|
||||
while ( !(usb_hw->abort_done & TU_BIT(ep_id)) ) {}
|
||||
|
||||
uint32_t ep_ctrl = *ep->endpoint_control;
|
||||
ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
|
||||
ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
|
||||
|
||||
_hw_endpoint_buffer_control_set_value32(ep, 0);
|
||||
|
||||
usb_hw->abort &= ~TU_BIT(ep_id);
|
||||
|
||||
TU_LOG(3, "----SHORT PACKET buffer0 on EP %02X:\r\n", ep->ep_addr);
|
||||
TU_LOG(3, " BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if transfer is complete
|
||||
bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint* ep) {
|
||||
hw_endpoint_lock_update(ep, 1);
|
||||
|
||||
// Part way through a transfer
|
||||
if (!ep->active) {
|
||||
panic("Can't continue xfer on inactive ep %02X", ep->ep_addr);
|
||||
}
|
||||
|
||||
// Update EP struct from hardware state
|
||||
_hw_endpoint_xfer_sync(ep);
|
||||
|
||||
// Now we have synced our state with the hardware. Is there more data to transfer?
|
||||
// If we are done then notify tinyusb
|
||||
if (ep->remaining_len == 0) {
|
||||
pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr);
|
||||
// Notify caller we are done so it can notify the tinyusb stack
|
||||
hw_endpoint_lock_update(ep, -1);
|
||||
return true;
|
||||
} else {
|
||||
if (e15_is_critical_frame_period(ep)) {
|
||||
ep->pending = 1;
|
||||
} else {
|
||||
hw_endpoint_start_next_buffer(ep);
|
||||
}
|
||||
}
|
||||
|
||||
hw_endpoint_lock_update(ep, -1);
|
||||
// More work to do
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Errata 15
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
|
||||
/* Don't mark IN buffers as available during the last 200us of a full-speed
|
||||
frame. This avoids a situation seen with the USB2.0 hub on a Raspberry
|
||||
Pi 4 where a late IN token before the next full-speed SOF can cause port
|
||||
babble and a corrupt ACK packet. The nature of the data corruption has a
|
||||
chance to cause device lockup.
|
||||
|
||||
Use the next SOF to mark delayed buffers as available. This reduces
|
||||
available Bulk IN bandwidth by approximately 20%, and requires that the
|
||||
SOF interrupt is enabled while these transfers are ongoing.
|
||||
|
||||
Inherit the top-level enable from the corresponding Pico-SDK flag.
|
||||
Applications that will not use the device in a situation where it could
|
||||
be plugged into a Pi 4 or Pi 400 (for example, when directly connected
|
||||
to a commodity hub or other host) can turn off the flag in the SDK.
|
||||
*/
|
||||
|
||||
volatile uint32_t e15_last_sof = 0;
|
||||
|
||||
// check if Errata 15 is needed for this endpoint i.e device bulk-in
|
||||
static bool __tusb_irq_path_func(e15_is_bulkin_ep)(struct hw_endpoint* ep) {
|
||||
return (!is_host_mode() && tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN &&
|
||||
ep->transfer_type == TUSB_XFER_BULK);
|
||||
}
|
||||
|
||||
// check if we need to apply Errata 15 workaround : i.e
|
||||
// Endpoint is BULK IN and is currently in critical frame period i.e 20% of last usb frame
|
||||
static bool __tusb_irq_path_func(e15_is_critical_frame_period)(struct hw_endpoint* ep) {
|
||||
TU_VERIFY(e15_is_bulkin_ep(ep));
|
||||
|
||||
/* Avoid the last 200us (uframe 6.5-7) of a frame, up to the EOF2 point.
|
||||
* The device state machine cannot recover from receiving an incorrect PID
|
||||
* when it is expecting an ACK.
|
||||
*/
|
||||
uint32_t delta = time_us_32() - e15_last_sof;
|
||||
if (delta < 800 || delta > 998) {
|
||||
return false;
|
||||
}
|
||||
TU_LOG(3, "Avoiding sof %lu now %lu last %lu\r\n", (usb_hw->sof_rd + 1) & USB_SOF_RD_BITS, time_us_32(),
|
||||
e15_last_sof);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
#endif
|
||||
143
src/tinyusb/rp2040_usb.h
Normal file
143
src/tinyusb/rp2040_usb.h
Normal file
@@ -0,0 +1,143 @@
|
||||
#ifndef RP2040_COMMON_H_
|
||||
#define RP2040_COMMON_H_
|
||||
|
||||
#if defined(RP2040_USB_HOST_MODE) && defined(RP2040_USB_DEVICE_MODE)
|
||||
#error TinyUSB device and host mode not supported at the same time
|
||||
#endif
|
||||
|
||||
#include "tusb_common.h"
|
||||
|
||||
#include "pico.h"
|
||||
#include "hardware/structs/usb.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/resets.h"
|
||||
#include "hardware/timer.h"
|
||||
|
||||
#if defined(PICO_RP2040_USB_DEVICE_ENUMERATION_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX)
|
||||
#define TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
|
||||
#endif
|
||||
|
||||
#if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX)
|
||||
#define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
#endif
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
#undef PICO_RP2040_USB_FAST_IRQ
|
||||
#define PICO_RP2040_USB_FAST_IRQ 1
|
||||
#endif
|
||||
|
||||
#ifndef PICO_RP2040_USB_FAST_IRQ
|
||||
#define PICO_RP2040_USB_FAST_IRQ 0
|
||||
#endif
|
||||
|
||||
#if PICO_RP2040_USB_FAST_IRQ
|
||||
#define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x)
|
||||
#else
|
||||
#define __tusb_irq_path_func(x) x
|
||||
#endif
|
||||
|
||||
#define usb_hw_set ((usb_hw_t *) hw_set_alias_untyped(usb_hw))
|
||||
#define usb_hw_clear ((usb_hw_t *) hw_clear_alias_untyped(usb_hw))
|
||||
|
||||
#define pico_info(...) TU_LOG(2, __VA_ARGS__)
|
||||
#define pico_trace(...) TU_LOG(3, __VA_ARGS__)
|
||||
|
||||
// Hardware information per endpoint
|
||||
typedef struct hw_endpoint
|
||||
{
|
||||
// Is this a valid struct
|
||||
bool configured;
|
||||
|
||||
// Transfer direction (i.e. IN is rx for host but tx for device)
|
||||
// allows us to common up transfer functions
|
||||
bool rx;
|
||||
|
||||
uint8_t ep_addr;
|
||||
uint8_t next_pid;
|
||||
|
||||
// Endpoint control register
|
||||
io_rw_32 *endpoint_control;
|
||||
|
||||
// Buffer control register
|
||||
io_rw_32 *buffer_control;
|
||||
|
||||
// Buffer pointer in usb dpram
|
||||
uint8_t *hw_data_buf;
|
||||
|
||||
// User buffer in main memory
|
||||
uint8_t *user_buf;
|
||||
|
||||
// Current transfer information
|
||||
uint16_t remaining_len;
|
||||
uint16_t xferred_len;
|
||||
|
||||
// Data needed from EP descriptor
|
||||
uint16_t wMaxPacketSize;
|
||||
|
||||
// Endpoint is in use
|
||||
bool active;
|
||||
|
||||
// Interrupt, bulk, etc
|
||||
uint8_t transfer_type;
|
||||
|
||||
// Transfer scheduled but not active
|
||||
uint8_t pending;
|
||||
|
||||
#if CFG_TUH_ENABLED
|
||||
// Only needed for host
|
||||
uint8_t dev_addr;
|
||||
|
||||
// If interrupt endpoint
|
||||
uint8_t interrupt_num;
|
||||
#endif
|
||||
|
||||
} hw_endpoint_t;
|
||||
|
||||
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
|
||||
extern volatile uint32_t e15_last_sof;
|
||||
#endif
|
||||
|
||||
void rp2040_usb_init(void);
|
||||
|
||||
void hw_endpoint_xfer_start(struct hw_endpoint *ep, uint8_t *buffer, uint16_t total_len);
|
||||
bool hw_endpoint_xfer_continue(struct hw_endpoint *ep);
|
||||
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
|
||||
void hw_endpoint_start_next_buffer(struct hw_endpoint *ep);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) {
|
||||
// todo add critsec as necessary to prevent issues between worker and IRQ...
|
||||
// note that this is perhaps as simple as disabling IRQs because it would make
|
||||
// sense to have worker and IRQ on same core, however I think using critsec is about equivalent.
|
||||
}
|
||||
|
||||
void _hw_endpoint_buffer_control_update32(struct hw_endpoint *ep, uint32_t and_mask, uint32_t or_mask);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t _hw_endpoint_buffer_control_get_value32 (struct hw_endpoint *ep)
|
||||
{
|
||||
return *ep->buffer_control;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_value32 (struct hw_endpoint *ep, uint32_t value)
|
||||
{
|
||||
_hw_endpoint_buffer_control_update32(ep, 0, value);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_set_mask32 (struct hw_endpoint *ep, uint32_t value)
|
||||
{
|
||||
_hw_endpoint_buffer_control_update32(ep, ~value, value);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void _hw_endpoint_buffer_control_clear_mask32 (struct hw_endpoint *ep, uint32_t value)
|
||||
{
|
||||
_hw_endpoint_buffer_control_update32(ep, ~value, 0);
|
||||
}
|
||||
|
||||
static inline uintptr_t hw_data_offset (uint8_t *buf)
|
||||
{
|
||||
// Remove usb base from buffer pointer
|
||||
return (uintptr_t) buf ^ (uintptr_t) usb_dpram;
|
||||
}
|
||||
|
||||
extern const char *ep_dir_string[];
|
||||
|
||||
#endif
|
||||
316
src/tinyusb/tusb_common.h
Normal file
316
src/tinyusb/tusb_common.h
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_COMMON_H_
|
||||
#define _TUSB_COMMON_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Macros Helper
|
||||
//--------------------------------------------------------------------+
|
||||
#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
|
||||
#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
|
||||
#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) )
|
||||
#define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d))
|
||||
|
||||
#define TU_U16(_high, _low) ((uint16_t) (((_high) << 8) | (_low)))
|
||||
#define TU_U16_HIGH(_u16) ((uint8_t) (((_u16) >> 8) & 0x00ff))
|
||||
#define TU_U16_LOW(_u16) ((uint8_t) ((_u16) & 0x00ff))
|
||||
#define U16_TO_U8S_BE(_u16) TU_U16_HIGH(_u16), TU_U16_LOW(_u16)
|
||||
#define U16_TO_U8S_LE(_u16) TU_U16_LOW(_u16), TU_U16_HIGH(_u16)
|
||||
|
||||
#define TU_U32_BYTE3(_u32) ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB
|
||||
#define TU_U32_BYTE2(_u32) ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff))
|
||||
#define TU_U32_BYTE1(_u32) ((uint8_t) ((((uint32_t) _u32) >> 8) & 0x000000ff))
|
||||
#define TU_U32_BYTE0(_u32) ((uint8_t) (((uint32_t) _u32) & 0x000000ff)) // LSB
|
||||
|
||||
#define U32_TO_U8S_BE(_u32) TU_U32_BYTE3(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE0(_u32)
|
||||
#define U32_TO_U8S_LE(_u32) TU_U32_BYTE0(_u32), TU_U32_BYTE1(_u32), TU_U32_BYTE2(_u32), TU_U32_BYTE3(_u32)
|
||||
|
||||
#define TU_BIT(n) (1UL << (n))
|
||||
|
||||
// Generate a mask with bit from high (31) to low (0) set, e.g TU_GENMASK(3, 0) = 0b1111
|
||||
#define TU_GENMASK(h, l) ( (UINT32_MAX << (l)) & (UINT32_MAX >> (31 - (h))) )
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Includes
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Standard Headers
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Tinyusb Common Headers
|
||||
#include "tusb_option.h"
|
||||
#include "tusb_compiler.h"
|
||||
#include "tusb_verify.h"
|
||||
#include "tusb_types.h"
|
||||
#include "tusb_debug.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Optional API implemented by application if needed
|
||||
// TODO move to a more obvious place/file
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// flush data cache
|
||||
TU_ATTR_WEAK extern void tusb_app_dcache_flush(uintptr_t addr, uint32_t data_size);
|
||||
|
||||
// invalidate data cache
|
||||
TU_ATTR_WEAK extern void tusb_app_dcache_invalidate(uintptr_t addr, uint32_t data_size);
|
||||
|
||||
// Optional physical <-> virtual address translation
|
||||
TU_ATTR_WEAK extern void* tusb_app_virt_to_phys(void *virt_addr);
|
||||
TU_ATTR_WEAK extern void* tusb_app_phys_to_virt(void *phys_addr);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Inline Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Mem -------------//
|
||||
#define tu_memclr(buffer, size) memset((buffer), 0, (size))
|
||||
#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var)))
|
||||
|
||||
// This is a backport of memset_s from c11
|
||||
TU_ATTR_ALWAYS_INLINE static inline int tu_memset_s(void *dest, size_t destsz, int ch, size_t count) {
|
||||
// TODO may check if desst and src is not NULL
|
||||
if ( count > destsz ) {
|
||||
return -1;
|
||||
}
|
||||
memset(dest, ch, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is a backport of memcpy_s from c11
|
||||
TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, const void *src, size_t count) {
|
||||
// TODO may check if desst and src is not NULL
|
||||
if ( count > destsz ) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(dest, src, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//------------- Bytes -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) {
|
||||
return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) {
|
||||
return (uint16_t) ((((uint16_t) high) << 8) | low);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte2(uint32_t ui32) { return TU_U32_BYTE2(ui32); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte1(uint32_t ui32) { return TU_U32_BYTE1(ui32); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte0(uint32_t ui32) { return TU_U32_BYTE0(ui32); }
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_high16(uint32_t ui32) { return (uint16_t) (ui32 >> 16); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u32_low16 (uint32_t ui32) { return (uint16_t) (ui32 & 0x0000ffffu); }
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t ui16) { return TU_U16_HIGH(ui16); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_low (uint16_t ui16) { return TU_U16_LOW(ui16); }
|
||||
|
||||
//------------- Bits -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; }
|
||||
|
||||
//------------- Min -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; }
|
||||
|
||||
//------------- Max -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
|
||||
|
||||
//------------- Align -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment) {
|
||||
return value & ((uint32_t) ~(alignment-1));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4 (uint32_t value) { return (value & 0xFFFFFFFCUL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align8 (uint32_t value) { return (value & 0xFFFFFFF8UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); }
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned32(uint32_t value) { return (value & 0x1FUL) == 0; }
|
||||
TU_ATTR_ALWAYS_INLINE static inline bool tu_is_aligned64(uint64_t value) { return (value & 0x3FUL) == 0; }
|
||||
|
||||
//------------- Mathematics -------------//
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; }
|
||||
|
||||
// log2 of a value is its MSB's position
|
||||
// TODO use clz TODO remove
|
||||
static inline uint8_t tu_log2(uint32_t value)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
while (value >>= 1) { result++; }
|
||||
return result;
|
||||
}
|
||||
|
||||
//static inline uint8_t tu_log2(uint32_t value)
|
||||
//{
|
||||
// return sizeof(uint32_t) * CHAR_BIT - __builtin_clz(x) - 1;
|
||||
//}
|
||||
|
||||
static inline bool tu_is_power_of_two(uint32_t value)
|
||||
{
|
||||
return (value != 0) && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
//------------- Unaligned Access -------------//
|
||||
#if TUP_ARCH_STRICT_ALIGN
|
||||
|
||||
// Rely on compiler to generate correct code for unaligned access
|
||||
typedef struct { uint16_t val; } TU_ATTR_PACKED tu_unaligned_uint16_t;
|
||||
typedef struct { uint32_t val; } TU_ATTR_PACKED tu_unaligned_uint32_t;
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem)
|
||||
{
|
||||
tu_unaligned_uint32_t const* ua32 = (tu_unaligned_uint32_t const*) mem;
|
||||
return ua32->val;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value)
|
||||
{
|
||||
tu_unaligned_uint32_t* ua32 = (tu_unaligned_uint32_t*) mem;
|
||||
ua32->val = value;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem)
|
||||
{
|
||||
tu_unaligned_uint16_t const* ua16 = (tu_unaligned_uint16_t const*) mem;
|
||||
return ua16->val;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value)
|
||||
{
|
||||
tu_unaligned_uint16_t* ua16 = (tu_unaligned_uint16_t*) mem;
|
||||
ua16->val = value;
|
||||
}
|
||||
|
||||
#elif TUP_MCU_STRICT_ALIGN
|
||||
|
||||
// MCU such as LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM although it is ARM M4.
|
||||
// We have to manually pick up bytes since tu_unaligned_uint32_t will still generate unaligned code
|
||||
// NOTE: volatile cast to memory to prevent compiler to optimize and generate unaligned code
|
||||
// TODO Big Endian may need minor changes
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem)
|
||||
{
|
||||
volatile uint8_t const* buf8 = (uint8_t const*) mem;
|
||||
return tu_u32(buf8[3], buf8[2], buf8[1], buf8[0]);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value)
|
||||
{
|
||||
volatile uint8_t* buf8 = (uint8_t*) mem;
|
||||
buf8[0] = tu_u32_byte0(value);
|
||||
buf8[1] = tu_u32_byte1(value);
|
||||
buf8[2] = tu_u32_byte2(value);
|
||||
buf8[3] = tu_u32_byte3(value);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem)
|
||||
{
|
||||
volatile uint8_t const* buf8 = (uint8_t const*) mem;
|
||||
return tu_u16(buf8[1], buf8[0]);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value)
|
||||
{
|
||||
volatile uint8_t* buf8 = (uint8_t*) mem;
|
||||
buf8[0] = tu_u16_low(value);
|
||||
buf8[1] = tu_u16_high(value);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
// MCU that could access unaligned memory natively
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void *mem) {
|
||||
return *((uint32_t const *) mem);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void *mem) {
|
||||
return *((uint16_t const *) mem);
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void *mem, uint32_t value) {
|
||||
*((uint32_t *) mem) = value;
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void *mem, uint16_t value) {
|
||||
*((uint16_t *) mem) = value;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// To be removed
|
||||
//------------- Binary constant -------------//
|
||||
#if defined(__GNUC__) && !defined(__CC_ARM)
|
||||
|
||||
#define TU_BIN8(x) ((uint8_t) (0b##x))
|
||||
#define TU_BIN16(b1, b2) ((uint16_t) (0b##b1##b2))
|
||||
#define TU_BIN32(b1, b2, b3, b4) ((uint32_t) (0b##b1##b2##b3##b4))
|
||||
|
||||
#else
|
||||
|
||||
// internal macro of B8, B16, B32
|
||||
#define _B8__(x) (((x&0x0000000FUL)?1:0) \
|
||||
+((x&0x000000F0UL)?2:0) \
|
||||
+((x&0x00000F00UL)?4:0) \
|
||||
+((x&0x0000F000UL)?8:0) \
|
||||
+((x&0x000F0000UL)?16:0) \
|
||||
+((x&0x00F00000UL)?32:0) \
|
||||
+((x&0x0F000000UL)?64:0) \
|
||||
+((x&0xF0000000UL)?128:0))
|
||||
|
||||
#define TU_BIN8(d) ((uint8_t) _B8__(0x##d##UL))
|
||||
#define TU_BIN16(dmsb,dlsb) (((uint16_t)TU_BIN8(dmsb)<<8) + TU_BIN8(dlsb))
|
||||
#define TU_BIN32(dmsb,db2,db3,dlsb) \
|
||||
(((uint32_t)TU_BIN8(dmsb)<<24) \
|
||||
+ ((uint32_t)TU_BIN8(db2)<<16) \
|
||||
+ ((uint32_t)TU_BIN8(db3)<<8) \
|
||||
+ TU_BIN8(dlsb))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_COMMON_H_ */
|
||||
307
src/tinyusb/tusb_compiler.h
Normal file
307
src/tinyusb/tusb_compiler.h
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
/** \ingroup Group_Common
|
||||
* \defgroup Group_Compiler Compiler
|
||||
* \brief Group_Compiler brief
|
||||
* @{ */
|
||||
|
||||
#ifndef _TUSB_COMPILER_H_
|
||||
#define _TUSB_COMPILER_H_
|
||||
|
||||
#define TU_TOKEN(x) x
|
||||
#define TU_STRING(x) #x ///< stringify without expand
|
||||
#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify
|
||||
|
||||
#define TU_STRCAT(a, b) a##b ///< concat without expand
|
||||
#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand
|
||||
|
||||
#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
|
||||
#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens
|
||||
|
||||
#define TU_INCLUDE_PATH(_dir,_file) TU_XSTRING( TU_TOKEN(_dir)TU_TOKEN(_file) )
|
||||
|
||||
#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
|
||||
#define _TU_COUNTER_ __COUNTER__
|
||||
#else
|
||||
#define _TU_COUNTER_ __LINE__
|
||||
#endif
|
||||
|
||||
// Compile-time Assert
|
||||
#if defined (__cplusplus) && __cplusplus >= 201103L
|
||||
#define TU_VERIFY_STATIC static_assert
|
||||
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
#define TU_VERIFY_STATIC _Static_assert
|
||||
#elif defined(__CCRX__)
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0];
|
||||
#else
|
||||
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
|
||||
#endif
|
||||
|
||||
/* --------------------- Fuzzing types -------------------------------------- */
|
||||
#ifdef _FUZZ
|
||||
#define tu_static static __thread
|
||||
#else
|
||||
#define tu_static static
|
||||
#endif
|
||||
|
||||
// for declaration of reserved field, make use of _TU_COUNTER_
|
||||
#define TU_RESERVED TU_XSTRCAT(reserved, _TU_COUNTER_)
|
||||
|
||||
#define TU_LITTLE_ENDIAN (0x12u)
|
||||
#define TU_BIG_ENDIAN (0x21u)
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Count number of arguments of __VA_ARGS__
|
||||
* - reference https://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments
|
||||
* - _GET_NTH_ARG() takes args >= N (64) but only expand to Nth one (64th)
|
||||
* - _RSEQ_N() is reverse sequential to N to add padding to have
|
||||
* Nth position is the same as the number of arguments
|
||||
* - ##__VA_ARGS__ is used to deal with 0 paramerter (swallows comma)
|
||||
*------------------------------------------------------------------*/
|
||||
#if !defined(__CCRX__)
|
||||
#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__, _RSEQ_N())
|
||||
#else
|
||||
#define TU_ARGS_NUM(...) _TU_NARG(_0, __VA_ARGS__, _RSEQ_N())
|
||||
#endif
|
||||
|
||||
#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__)
|
||||
#define _GET_NTH_ARG( \
|
||||
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
|
||||
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
|
||||
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
|
||||
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
|
||||
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
|
||||
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
|
||||
_61,_62,_63,N,...) N
|
||||
#define _RSEQ_N() \
|
||||
62,61,60, \
|
||||
59,58,57,56,55,54,53,52,51,50, \
|
||||
49,48,47,46,45,44,43,42,41,40, \
|
||||
39,38,37,36,35,34,33,32,31,30, \
|
||||
29,28,27,26,25,24,23,22,21,20, \
|
||||
19,18,17,16,15,14,13,12,11,10, \
|
||||
9,8,7,6,5,4,3,2,1,0
|
||||
|
||||
// Apply an macro X to each of the arguments with an separated of choice
|
||||
#define TU_ARGS_APPLY(_X, _s, ...) TU_XSTRCAT(_TU_ARGS_APPLY_, TU_ARGS_NUM(__VA_ARGS__))(_X, _s, __VA_ARGS__)
|
||||
|
||||
#define _TU_ARGS_APPLY_1(_X, _s, _a1) _X(_a1)
|
||||
#define _TU_ARGS_APPLY_2(_X, _s, _a1, _a2) _X(_a1) _s _X(_a2)
|
||||
#define _TU_ARGS_APPLY_3(_X, _s, _a1, _a2, _a3) _X(_a1) _s _TU_ARGS_APPLY_2(_X, _s, _a2, _a3)
|
||||
#define _TU_ARGS_APPLY_4(_X, _s, _a1, _a2, _a3, _a4) _X(_a1) _s _TU_ARGS_APPLY_3(_X, _s, _a2, _a3, _a4)
|
||||
#define _TU_ARGS_APPLY_5(_X, _s, _a1, _a2, _a3, _a4, _a5) _X(_a1) _s _TU_ARGS_APPLY_4(_X, _s, _a2, _a3, _a4, _a5)
|
||||
#define _TU_ARGS_APPLY_6(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6) _X(_a1) _s _TU_ARGS_APPLY_5(_X, _s, _a2, _a3, _a4, _a5, _a6)
|
||||
#define _TU_ARGS_APPLY_7(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7) _X(_a1) _s _TU_ARGS_APPLY_6(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7)
|
||||
#define _TU_ARGS_APPLY_8(_X, _s, _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8) _X(_a1) _s _TU_ARGS_APPLY_7(_X, _s, _a2, _a3, _a4, _a5, _a6, _a7, _a8)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Macro for function default arguments
|
||||
//--------------------------------------------------------------------+
|
||||
#define TU_GET_3RD_ARG(arg1, arg2, arg3, ...) arg3
|
||||
|
||||
// function expand with number of arguments
|
||||
#define TU_FUNC_OPTIONAL_ARG(func, ...) TU_XSTRCAT(func##_arg, TU_ARGS_NUM(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Compiler porting with Attribute and Endian
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// TODO refactor since __attribute__ is supported across many compiler
|
||||
#if defined(__GNUC__)
|
||||
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define TU_ATTR_PACKED __attribute__ ((packed))
|
||||
#define TU_ATTR_WEAK __attribute__ ((weak))
|
||||
// #define TU_ATTR_WEAK_ALIAS(f) __attribute__ ((weak, alias(#f))
|
||||
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
|
||||
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
|
||||
#endif
|
||||
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN
|
||||
#define TU_ATTR_PACKED_END
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
#if __GNUC__ < 5
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
#else
|
||||
#if __has_attribute(__fallthrough__)
|
||||
#define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||
#else
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Endian conversion use well-known host to network (big endian) naming
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
// Unfortunately XC16 doesn't provide builtins for 32bit endian conversion
|
||||
#if defined(__XC16)
|
||||
#define TU_BSWAP16(u16) (__builtin_swap(u16))
|
||||
#define TU_BSWAP32(u32) ((((u32) & 0xff000000) >> 24) | \
|
||||
(((u32) & 0x00ff0000) >> 8) | \
|
||||
(((u32) & 0x0000ff00) << 8) | \
|
||||
(((u32) & 0x000000ff) << 24))
|
||||
#else
|
||||
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
|
||||
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
|
||||
#endif
|
||||
|
||||
#ifndef __ARMCC_VERSION
|
||||
// List of obsolete callback function that is renamed and should not be defined.
|
||||
// Put it here since only gcc support this pragma
|
||||
#pragma GCC poison tud_vendor_control_request_cb
|
||||
#endif
|
||||
|
||||
#elif defined(__TI_COMPILER_VERSION__)
|
||||
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define TU_ATTR_PACKED __attribute__ ((packed))
|
||||
#define TU_ATTR_WEAK __attribute__ ((weak))
|
||||
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
|
||||
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
#define TU_ATTR_USED __attribute__ ((used))
|
||||
#define TU_ATTR_FALLTHROUGH __attribute__((fallthrough))
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN
|
||||
#define TU_ATTR_PACKED_END
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
// __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian)
|
||||
#if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__)
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
|
||||
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
|
||||
|
||||
#elif defined(__ICCARM__)
|
||||
#include <intrinsics.h>
|
||||
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
|
||||
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
|
||||
#define TU_ATTR_PACKED __attribute__ ((packed))
|
||||
#define TU_ATTR_WEAK __attribute__ ((weak))
|
||||
#ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
|
||||
#define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
|
||||
#endif
|
||||
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
|
||||
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
|
||||
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN
|
||||
#define TU_ATTR_PACKED_END
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
// Endian conversion use well-known host to network (big endian) naming
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#define TU_BSWAP16(u16) (__iar_builtin_REV16(u16))
|
||||
#define TU_BSWAP32(u32) (__iar_builtin_REV(u32))
|
||||
|
||||
#elif defined(__CCRX__)
|
||||
#define TU_ATTR_ALIGNED(Bytes)
|
||||
#define TU_ATTR_SECTION(sec_name)
|
||||
#define TU_ATTR_PACKED
|
||||
#define TU_ATTR_WEAK
|
||||
#define TU_ATTR_ALWAYS_INLINE
|
||||
#define TU_ATTR_DEPRECATED(mess)
|
||||
#define TU_ATTR_UNUSED
|
||||
#define TU_ATTR_USED
|
||||
#define TU_ATTR_FALLTHROUGH do {} while (0) /* fallthrough */
|
||||
|
||||
#define TU_ATTR_PACKED_BEGIN _Pragma("pack")
|
||||
#define TU_ATTR_PACKED_END _Pragma("packoption")
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_BEGIN _Pragma("bit_order right")
|
||||
#define TU_ATTR_BIT_FIELD_ORDER_END _Pragma("bit_order")
|
||||
|
||||
// Endian conversion use well-known host to network (big endian) naming
|
||||
#if defined(__LIT)
|
||||
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
|
||||
#else
|
||||
#define TU_BYTE_ORDER TU_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16))
|
||||
#define TU_BSWAP32(u32) (_builtin_revl(u32))
|
||||
|
||||
#else
|
||||
#error "Compiler attribute porting is required"
|
||||
#endif
|
||||
|
||||
|
||||
#if (TU_BYTE_ORDER == TU_LITTLE_ENDIAN)
|
||||
|
||||
#define tu_htons(u16) (TU_BSWAP16(u16))
|
||||
#define tu_ntohs(u16) (TU_BSWAP16(u16))
|
||||
|
||||
#define tu_htonl(u32) (TU_BSWAP32(u32))
|
||||
#define tu_ntohl(u32) (TU_BSWAP32(u32))
|
||||
|
||||
#define tu_htole16(u16) (u16)
|
||||
#define tu_le16toh(u16) (u16)
|
||||
|
||||
#define tu_htole32(u32) (u32)
|
||||
#define tu_le32toh(u32) (u32)
|
||||
|
||||
#elif (TU_BYTE_ORDER == TU_BIG_ENDIAN)
|
||||
|
||||
#define tu_htons(u16) (u16)
|
||||
#define tu_ntohs(u16) (u16)
|
||||
|
||||
#define tu_htonl(u32) (u32)
|
||||
#define tu_ntohl(u32) (u32)
|
||||
|
||||
#define tu_htole16(u16) (TU_BSWAP16(u16))
|
||||
#define tu_le16toh(u16) (TU_BSWAP16(u16))
|
||||
|
||||
#define tu_htole32(u32) (TU_BSWAP32(u32))
|
||||
#define tu_le32toh(u32) (TU_BSWAP32(u32))
|
||||
|
||||
#else
|
||||
#error Byte order is undefined
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_COMPILER_H_ */
|
||||
|
||||
/// @}
|
||||
107
src/tinyusb/tusb_config.h
Normal file
107
src/tinyusb/tusb_config.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Board Specific Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_TUD_RHPORT
|
||||
#define BOARD_TUD_RHPORT 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
#ifndef BOARD_TUD_MAX_SPEED
|
||||
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by compiler flags for flexibility
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#define CFG_TUSB_MCU OPT_MCU_RP2040
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
// Enable Device stack
|
||||
#define CFG_TUD_ENABLED 1
|
||||
|
||||
// Default is max speed that hardware controller could support with on-chip PHY
|
||||
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_HID 0
|
||||
#define CFG_TUD_CDC 0
|
||||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 16
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
171
src/tinyusb/tusb_debug.h
Normal file
171
src/tinyusb/tusb_debug.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2022, Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_DEBUG_H_
|
||||
#define _TUSB_DEBUG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Debug
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// CFG_TUSB_DEBUG for debugging
|
||||
// 0 : no debug
|
||||
// 1 : print error
|
||||
// 2 : print warning
|
||||
// 3 : print info
|
||||
#if CFG_TUSB_DEBUG
|
||||
|
||||
// Enum to String for debugging purposes
|
||||
#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL || CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
|
||||
extern char const* const tu_str_speed[];
|
||||
extern char const* const tu_str_std_request[];
|
||||
extern char const* const tu_str_xfer_result[];
|
||||
#endif
|
||||
|
||||
void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
|
||||
|
||||
#ifdef CFG_TUSB_DEBUG_PRINTF
|
||||
extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...);
|
||||
#define tu_printf CFG_TUSB_DEBUG_PRINTF
|
||||
#else
|
||||
#define tu_printf printf
|
||||
#endif
|
||||
|
||||
static inline void tu_print_buf(uint8_t const* buf, uint32_t bufsize) {
|
||||
for(uint32_t i=0; i<bufsize; i++) tu_printf("%02X ", buf[i]);
|
||||
tu_printf("\r\n");
|
||||
}
|
||||
|
||||
// Log with Level
|
||||
#define TU_LOG(n, ...) TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__)
|
||||
#define TU_LOG_MEM(n, ...) TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__)
|
||||
#define TU_LOG_BUF(n, ...) TU_XSTRCAT3(TU_LOG, n, _BUF)(__VA_ARGS__)
|
||||
#define TU_LOG_INT(n, ...) TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__)
|
||||
#define TU_LOG_HEX(n, ...) TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__)
|
||||
#define TU_LOG_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__)
|
||||
#define TU_LOG_FAILED() tu_printf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__)
|
||||
|
||||
// Log Level 1: Error
|
||||
#define TU_LOG1 tu_printf
|
||||
#define TU_LOG1_MEM tu_print_mem
|
||||
#define TU_LOG1_BUF(_x, _n) tu_print_buf((uint8_t const*)(_x), _n)
|
||||
#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (unsigned long) (_x) )
|
||||
#define TU_LOG1_HEX(_x) tu_printf(#_x " = 0x%lX\r\n", (unsigned long) (_x) )
|
||||
|
||||
// Log Level 2: Warn
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
#define TU_LOG2 TU_LOG1
|
||||
#define TU_LOG2_MEM TU_LOG1_MEM
|
||||
#define TU_LOG2_BUF TU_LOG1_BUF
|
||||
#define TU_LOG2_INT TU_LOG1_INT
|
||||
#define TU_LOG2_HEX TU_LOG1_HEX
|
||||
#endif
|
||||
|
||||
// Log Level 3: Info
|
||||
#if CFG_TUSB_DEBUG >= 3
|
||||
#define TU_LOG3 TU_LOG1
|
||||
#define TU_LOG3_MEM TU_LOG1_MEM
|
||||
#define TU_LOG3_BUF TU_LOG1_BUF
|
||||
#define TU_LOG3_INT TU_LOG1_INT
|
||||
#define TU_LOG3_HEX TU_LOG1_HEX
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t key;
|
||||
const char* data;
|
||||
} tu_lookup_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t count;
|
||||
tu_lookup_entry_t const* items;
|
||||
} tu_lookup_table_t;
|
||||
|
||||
static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key) {
|
||||
tu_static char not_found[11];
|
||||
|
||||
for(uint16_t i=0; i<p_table->count; i++) {
|
||||
if (p_table->items[i].key == key) return p_table->items[i].data;
|
||||
}
|
||||
|
||||
// not found return the key value in hex
|
||||
snprintf(not_found, sizeof(not_found), "0x%08lX", (unsigned long) key);
|
||||
|
||||
return not_found;
|
||||
}
|
||||
|
||||
#endif // CFG_TUSB_DEBUG
|
||||
|
||||
#ifndef TU_LOG
|
||||
#define TU_LOG(n, ...)
|
||||
#define TU_LOG_MEM(n, ...)
|
||||
#define TU_LOG_BUF(n, ...)
|
||||
#define TU_LOG_INT(n, ...)
|
||||
#define TU_LOG_HEX(n, ...)
|
||||
#define TU_LOG_LOCATION()
|
||||
#define TU_LOG_FAILED()
|
||||
#endif
|
||||
|
||||
// TODO replace all TU_LOGn with TU_LOG(n)
|
||||
|
||||
#define TU_LOG0(...)
|
||||
#define TU_LOG0_MEM(...)
|
||||
#define TU_LOG0_BUF(...)
|
||||
#define TU_LOG0_INT(...)
|
||||
#define TU_LOG0_HEX(...)
|
||||
|
||||
#ifndef TU_LOG1
|
||||
#define TU_LOG1(...)
|
||||
#define TU_LOG1_MEM(...)
|
||||
#define TU_LOG1_BUF(...)
|
||||
#define TU_LOG1_INT(...)
|
||||
#define TU_LOG1_HEX(...)
|
||||
#endif
|
||||
|
||||
#ifndef TU_LOG2
|
||||
#define TU_LOG2(...)
|
||||
#define TU_LOG2_MEM(...)
|
||||
#define TU_LOG2_BUF(...)
|
||||
#define TU_LOG2_INT(...)
|
||||
#define TU_LOG2_HEX(...)
|
||||
#endif
|
||||
|
||||
#ifndef TU_LOG3
|
||||
#define TU_LOG3(...)
|
||||
#define TU_LOG3_MEM(...)
|
||||
#define TU_LOG3_BUF(...)
|
||||
#define TU_LOG3_INT(...)
|
||||
#define TU_LOG3_HEX(...)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_DEBUG_H_ */
|
||||
542
src/tinyusb/tusb_mcu.h
Normal file
542
src/tinyusb/tusb_mcu.h
Normal file
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021, Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_MCU_H_
|
||||
#define TUSB_MCU_H_
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Port/Platform Specific
|
||||
// TUP stand for TinyUSB Port/Platform (can be renamed)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
//------------- Unaligned Memory Access -------------//
|
||||
|
||||
#ifdef __ARM_ARCH
|
||||
// ARM Architecture set __ARM_FEATURE_UNALIGNED to 1 for mcu supports unaligned access
|
||||
#if defined(__ARM_FEATURE_UNALIGNED) && __ARM_FEATURE_UNALIGNED == 1
|
||||
#define TUP_ARCH_STRICT_ALIGN 0
|
||||
#else
|
||||
#define TUP_ARCH_STRICT_ALIGN 1
|
||||
#endif
|
||||
#else
|
||||
// TODO default to strict align for others
|
||||
// Should investigate other architecture such as risv, xtensa, mips for optimal setting
|
||||
#define TUP_ARCH_STRICT_ALIGN 1
|
||||
#endif
|
||||
|
||||
/* USB Controller Attributes for Device, Host or MCU (both)
|
||||
* - ENDPOINT_MAX: max (logical) number of endpoint
|
||||
* - ENDPOINT_EXCLUSIVE_NUMBER: endpoint number with different direction IN and OUT aren't allowed,
|
||||
* e.g EP1 OUT & EP1 IN cannot exist together
|
||||
* - RHPORT_HIGHSPEED: support highspeed with on-chip PHY
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// NXP
|
||||
//--------------------------------------------------------------------+
|
||||
#if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 5
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUP_USBIP_OHCI
|
||||
#define TUP_OHCI_RHPORTS 2
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC51UXX)
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 5
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC54)
|
||||
// TODO USB0 has 5, USB1 has 6
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC55)
|
||||
// TODO USB0 has 5, USB1 has 6
|
||||
#define TUP_USBIP_IP3511
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX)
|
||||
// USB0 has 6 with HS PHY, USB1 has 4 only FS
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXN9)
|
||||
// USB0 is chipidea FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_MCX
|
||||
|
||||
// USB1 is chipidea HS
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MCXA15)
|
||||
// USB0 is chipidea FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_MCX
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX)
|
||||
#define TUP_USBIP_CHIPIDEA_HS
|
||||
#define TUP_USBIP_EHCI
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_KINETIS_KL, OPT_MCU_KINETIS_K32L, OPT_MCU_KINETIS_K)
|
||||
#define TUP_USBIP_CHIPIDEA_FS
|
||||
#define TUP_USBIP_CHIPIDEA_FS_KINETIS
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MM32F327X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Nordic
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
|
||||
// 8 CBI + 1 ISO
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Microchip
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAMD51, OPT_MCU_SAME5X) || \
|
||||
TU_CHECK_MCU(OPT_MCU_SAMD11, OPT_MCU_SAML21, OPT_MCU_SAML22)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_SAMG)
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_SAMX7X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 10
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_PIC32MZ)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || \
|
||||
TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ST
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F1)
|
||||
// - F102, F103 use fsdev
|
||||
// - F105, F107 use dwc2
|
||||
#if defined (STM32F105x8) || defined (STM32F105xB) || defined (STM32F105xC) || \
|
||||
defined (STM32F107xB) || defined (STM32F107xC)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
#elif defined(STM32F102x6) || defined(STM32F102xB) || \
|
||||
defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#else
|
||||
#error "Unsupported STM32F1 mcu"
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F2)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// FS has 4 ep, HS has 5 ep
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F3)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F4)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// For most mcu, FS has 4, HS has 6. TODO 446/469/479 HS has 9
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32F7)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// FS has 6, HS has 9
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
|
||||
// MCU with on-chip HS Phy
|
||||
#if defined(STM32F723xx) || defined(STM32F730xx) || defined(STM32F733xx)
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // Port0: FS, Port1: HS
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32H7)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32H5)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32G4)
|
||||
// Device controller
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
|
||||
// TypeC controller
|
||||
#define TUP_USBIP_TYPEC_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_TYPEC_RHPORTS_NUM 1
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32G0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32L0, OPT_MCU_STM32L1)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32L4)
|
||||
// - L4x2, L4x3 use fsdev
|
||||
// - L4x4, L4x6, L4x7, L4x9 use dwc2
|
||||
#if defined (STM32L475xx) || defined (STM32L476xx) || \
|
||||
defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \
|
||||
defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \
|
||||
defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \
|
||||
defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#elif defined(STM32L412xx) || defined(STM32L422xx) || defined(STM32L432xx) || defined(STM32L433xx) || \
|
||||
defined(STM32L442xx) || defined(STM32L443xx) || defined(STM32L452xx) || defined(STM32L462xx)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#else
|
||||
#error "Unsupported STM32L4 mcu"
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32WB)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32U5)
|
||||
#if defined (STM32U535xx) || defined (STM32U545xx)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#else
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_STM32
|
||||
|
||||
// U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
|
||||
#if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \
|
||||
defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
|
||||
#define TUP_DCD_ENDPOINT_MAX 9
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#else
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32L5)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_STM32U0)
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_STM32
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Sony
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CXD56)
|
||||
#define TUP_DCD_ENDPOINT_MAX 7
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TI
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MSP430x5xx)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MSP432E4, OPT_MCU_TM4C123, OPT_MCU_TM4C129)
|
||||
#define TUP_USBIP_MUSB
|
||||
#define TUP_USBIP_MUSB_TI
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// ValentyUSB (Litex)
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_VALENTYUSB_EPTRI)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Nuvoton
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NUC121, OPT_MCU_NUC126)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NUC120)
|
||||
#define TUP_DCD_ENDPOINT_MAX 6
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_NUC505)
|
||||
#define TUP_DCD_ENDPOINT_MAX 12
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Espressif
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_ESP32
|
||||
#define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32P4)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_USBIP_DWC2_ESP32
|
||||
#define TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS
|
||||
#define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2)
|
||||
#if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421))
|
||||
#error "MCUs are only supported with CFG_TUH_MAX3421 enabled"
|
||||
#endif
|
||||
#define TUP_DCD_ENDPOINT_MAX 0
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Dialog
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_DA1469X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Raspberry Pi
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_RP2040)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb")))
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Silabs
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_EFM32GG)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 7
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Renesas
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_RX63X, OPT_MCU_RX65X, OPT_MCU_RX72N, OPT_MCU_RAXXX)
|
||||
#define TUP_USBIP_RUSB2
|
||||
#define TUP_DCD_ENDPOINT_MAX 10
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// GigaDevice
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_GD32VF103)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Broadcom
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Infineon
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_XMC4000)
|
||||
#define TUP_USBIP_DWC2
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// BridgeTek
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_FT90X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_FT93X)
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Allwinner
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_F1C100S)
|
||||
#define TUP_DCD_ENDPOINT_MAX 4
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// WCH
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
|
||||
#define TUP_USBIP_WCH_USBHS
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 0
|
||||
#endif
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBHS)
|
||||
#define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
|
||||
#endif
|
||||
|
||||
#define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
|
||||
#define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V103)
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 1
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
|
||||
// v20x support both FSDEV (USBD) and USBFS, default to FSDEV
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
#define TUP_USBIP_FSDEV
|
||||
#define TUP_USBIP_FSDEV_CH32
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 0
|
||||
#endif
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_FSDEV)
|
||||
#define CFG_TUD_WCH_USBIP_FSDEV (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
|
||||
#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
|
||||
// v307 support both FS and HS, default to HS
|
||||
#define TUP_USBIP_WCH_USBHS
|
||||
#define TUP_USBIP_WCH_USBFS
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBFS)
|
||||
#define CFG_TUD_WCH_USBIP_USBFS 0
|
||||
#endif
|
||||
|
||||
#if !defined(CFG_TUD_WCH_USBIP_USBHS)
|
||||
#define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
|
||||
#endif
|
||||
|
||||
#define TUP_RHPORT_HIGHSPEED CFG_TUD_WCH_USBIP_USBHS
|
||||
#define TUP_DCD_ENDPOINT_MAX (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Analog Devices
|
||||
//--------------------------------------------------------------------+
|
||||
#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
|
||||
#define TUP_USBIP_MUSB
|
||||
#define TUP_USBIP_MUSB_ADI
|
||||
#define TUP_DCD_ENDPOINT_MAX 12
|
||||
#define TUP_RHPORT_HIGHSPEED 1
|
||||
#define TUD_ENDPOINT_ONE_DIRECTION_ONLY
|
||||
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// External USB controller
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
|
||||
#ifndef CFG_TUH_MAX3421_ENDPOINT_TOTAL
|
||||
#define CFG_TUH_MAX3421_ENDPOINT_TOTAL (8 + 4*(CFG_TUH_DEVICE_MAX-1))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Default Values
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifndef TUP_MCU_MULTIPLE_CORE
|
||||
#define TUP_MCU_MULTIPLE_CORE 0
|
||||
#endif
|
||||
|
||||
#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED
|
||||
#warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8"
|
||||
#define TUP_DCD_ENDPOINT_MAX 8
|
||||
#endif
|
||||
|
||||
// Default to fullspeed if not defined
|
||||
#ifndef TUP_RHPORT_HIGHSPEED
|
||||
#define TUP_RHPORT_HIGHSPEED 0
|
||||
#endif
|
||||
|
||||
// fast function, normally mean placing function in SRAM
|
||||
#ifndef TU_ATTR_FAST_FUNC
|
||||
#define TU_ATTR_FAST_FUNC
|
||||
#endif
|
||||
|
||||
// USBIP that support ISO alloc & activate API
|
||||
#if defined(TUP_USBIP_DWC2) || defined(TUP_USBIP_FSDEV) || defined(TUP_USBIP_MUSB)
|
||||
#define TUP_DCD_EDPT_ISO_ALLOC
|
||||
#endif
|
||||
|
||||
#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA == 0
|
||||
#define TUP_MEM_CONST_ADDR
|
||||
#endif
|
||||
|
||||
#endif
|
||||
602
src/tinyusb/tusb_option.h
Normal file
602
src/tinyusb/tusb_option.h
Normal file
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_OPTION_H_
|
||||
#define _TUSB_OPTION_H_
|
||||
|
||||
#include "tusb_compiler.h"
|
||||
|
||||
// Version is release as major.minor.revision eg 1.0.0
|
||||
#define TUSB_VERSION_MAJOR 0
|
||||
#define TUSB_VERSION_MINOR 17
|
||||
#define TUSB_VERSION_REVISION 0
|
||||
|
||||
#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)
|
||||
#define TUSB_VERSION_STRING TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Supported MCUs
|
||||
// CFG_TUSB_MCU must be defined to one of following value
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define OPT_MCU_NONE 0
|
||||
|
||||
// LPC
|
||||
#define OPT_MCU_LPC11UXX 1 ///< NXP LPC11Uxx
|
||||
#define OPT_MCU_LPC13XX 2 ///< NXP LPC13xx
|
||||
#define OPT_MCU_LPC15XX 3 ///< NXP LPC15xx
|
||||
#define OPT_MCU_LPC175X_6X 4 ///< NXP LPC175x, LPC176x
|
||||
#define OPT_MCU_LPC177X_8X 5 ///< NXP LPC177x, LPC178x
|
||||
#define OPT_MCU_LPC18XX 6 ///< NXP LPC18xx
|
||||
#define OPT_MCU_LPC40XX 7 ///< NXP LPC40xx
|
||||
#define OPT_MCU_LPC43XX 8 ///< NXP LPC43xx
|
||||
#define OPT_MCU_LPC51 9 ///< NXP LPC51
|
||||
#define OPT_MCU_LPC51UXX OPT_MCU_LPC51 ///< NXP LPC51
|
||||
#define OPT_MCU_LPC54 10 ///< NXP LPC54
|
||||
#define OPT_MCU_LPC55 11 ///< NXP LPC55
|
||||
// legacy naming
|
||||
#define OPT_MCU_LPC54XXX OPT_MCU_LPC54
|
||||
#define OPT_MCU_LPC55XX OPT_MCU_LPC55
|
||||
|
||||
// NRF
|
||||
#define OPT_MCU_NRF5X 100 ///< Nordic nRF5x series
|
||||
|
||||
// SAM
|
||||
#define OPT_MCU_SAMD21 200 ///< MicroChip SAMD21
|
||||
#define OPT_MCU_SAMD51 201 ///< MicroChip SAMD51
|
||||
#define OPT_MCU_SAMG 202 ///< MicroChip SAMDG series
|
||||
#define OPT_MCU_SAME5X 203 ///< MicroChip SAM E5x
|
||||
#define OPT_MCU_SAMD11 204 ///< MicroChip SAMD11
|
||||
#define OPT_MCU_SAML22 205 ///< MicroChip SAML22
|
||||
#define OPT_MCU_SAML21 206 ///< MicroChip SAML21
|
||||
#define OPT_MCU_SAMX7X 207 ///< MicroChip SAME70, S70, V70, V71 family
|
||||
|
||||
// STM32
|
||||
#define OPT_MCU_STM32F0 300 ///< ST F0
|
||||
#define OPT_MCU_STM32F1 301 ///< ST F1
|
||||
#define OPT_MCU_STM32F2 302 ///< ST F2
|
||||
#define OPT_MCU_STM32F3 303 ///< ST F3
|
||||
#define OPT_MCU_STM32F4 304 ///< ST F4
|
||||
#define OPT_MCU_STM32F7 305 ///< ST F7
|
||||
#define OPT_MCU_STM32H7 306 ///< ST H7
|
||||
#define OPT_MCU_STM32L1 308 ///< ST L1
|
||||
#define OPT_MCU_STM32L0 307 ///< ST L0
|
||||
#define OPT_MCU_STM32L4 309 ///< ST L4
|
||||
#define OPT_MCU_STM32G0 310 ///< ST G0
|
||||
#define OPT_MCU_STM32G4 311 ///< ST G4
|
||||
#define OPT_MCU_STM32WB 312 ///< ST WB
|
||||
#define OPT_MCU_STM32U5 313 ///< ST U5
|
||||
#define OPT_MCU_STM32L5 314 ///< ST L5
|
||||
#define OPT_MCU_STM32H5 315 ///< ST H5
|
||||
#define OPT_MCU_STM32U0 316 ///< ST U0
|
||||
|
||||
// Sony
|
||||
#define OPT_MCU_CXD56 400 ///< SONY CXD56
|
||||
|
||||
// TI
|
||||
#define OPT_MCU_MSP430x5xx 500 ///< TI MSP430x5xx
|
||||
#define OPT_MCU_MSP432E4 510 ///< TI MSP432E4xx
|
||||
#define OPT_MCU_TM4C123 511 ///< TI Tiva-C 123x
|
||||
#define OPT_MCU_TM4C129 512 ///< TI Tiva-C 129x
|
||||
|
||||
// ValentyUSB eptri
|
||||
#define OPT_MCU_VALENTYUSB_EPTRI 600 ///< Fomu eptri config
|
||||
|
||||
// NXP iMX RT
|
||||
#define OPT_MCU_MIMXRT1XXX 700 ///< NXP iMX RT1xxx Series
|
||||
#define OPT_MCU_MIMXRT10XX OPT_MCU_MIMXRT1XXX ///< RT10xx
|
||||
#define OPT_MCU_MIMXRT11XX OPT_MCU_MIMXRT1XXX ///< RT11xx
|
||||
|
||||
// Nuvoton
|
||||
#define OPT_MCU_NUC121 800
|
||||
#define OPT_MCU_NUC126 801
|
||||
#define OPT_MCU_NUC120 802
|
||||
#define OPT_MCU_NUC505 803
|
||||
|
||||
// Espressif
|
||||
#define OPT_MCU_ESP32S2 900 ///< Espressif ESP32-S2
|
||||
#define OPT_MCU_ESP32S3 901 ///< Espressif ESP32-S3
|
||||
#define OPT_MCU_ESP32 902 ///< Espressif ESP32 (for host max3421e)
|
||||
#define OPT_MCU_ESP32C3 903 ///< Espressif ESP32-C3
|
||||
#define OPT_MCU_ESP32C6 904 ///< Espressif ESP32-C6
|
||||
#define OPT_MCU_ESP32C2 905 ///< Espressif ESP32-C2
|
||||
#define OPT_MCU_ESP32H2 906 ///< Espressif ESP32-H2
|
||||
#define OPT_MCU_ESP32P4 907 ///< Espressif ESP32-P4
|
||||
#define TUP_MCU_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU
|
||||
|
||||
// Dialog
|
||||
#define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x
|
||||
|
||||
// Raspberry Pi
|
||||
#define OPT_MCU_RP2040 1100 ///< Raspberry Pi RP2040
|
||||
|
||||
// NXP Kinetis
|
||||
#define OPT_MCU_KINETIS_KL 1200 ///< NXP KL series
|
||||
#define OPT_MCU_KINETIS_K32L 1201 ///< NXP K32L series
|
||||
#define OPT_MCU_KINETIS_K32 1201 ///< Alias to K32L
|
||||
#define OPT_MCU_KINETIS_K 1202 ///< NXP K series
|
||||
|
||||
#define OPT_MCU_MKL25ZXX 1200 ///< Alias to KL (obsolete)
|
||||
#define OPT_MCU_K32L2BXX 1201 ///< Alias to K32 (obsolete)
|
||||
|
||||
// Silabs
|
||||
#define OPT_MCU_EFM32GG 1300 ///< Silabs EFM32GG
|
||||
|
||||
// Renesas RX
|
||||
#define OPT_MCU_RX63X 1400 ///< Renesas RX63N/631
|
||||
#define OPT_MCU_RX65X 1401 ///< Renesas RX65N/RX651
|
||||
#define OPT_MCU_RX72N 1402 ///< Renesas RX72N
|
||||
#define OPT_MCU_RAXXX 1403 ///< Renesas RAxxx families
|
||||
|
||||
// Mind Motion
|
||||
#define OPT_MCU_MM32F327X 1500 ///< Mind Motion MM32F327
|
||||
|
||||
// GigaDevice
|
||||
#define OPT_MCU_GD32VF103 1600 ///< GigaDevice GD32VF103
|
||||
|
||||
// Broadcom
|
||||
#define OPT_MCU_BCM2711 1700 ///< Broadcom BCM2711
|
||||
#define OPT_MCU_BCM2835 1701 ///< Broadcom BCM2835
|
||||
#define OPT_MCU_BCM2837 1702 ///< Broadcom BCM2837
|
||||
|
||||
// Infineon
|
||||
#define OPT_MCU_XMC4000 1800 ///< Infineon XMC4000
|
||||
|
||||
// PIC
|
||||
#define OPT_MCU_PIC32MZ 1900 ///< MicroChip PIC32MZ family
|
||||
#define OPT_MCU_PIC32MM 1901 ///< MicroChip PIC32MM family
|
||||
#define OPT_MCU_PIC32MX 1902 ///< MicroChip PIC32MX family
|
||||
#define OPT_MCU_PIC32MK 1903 ///< MicroChip PIC32MK family
|
||||
#define OPT_MCU_PIC24 1910 ///< MicroChip PIC24 family
|
||||
#define OPT_MCU_DSPIC33 1911 ///< MicroChip DSPIC33 family
|
||||
|
||||
// BridgeTek
|
||||
#define OPT_MCU_FT90X 2000 ///< BridgeTek FT90x
|
||||
#define OPT_MCU_FT93X 2001 ///< BridgeTek FT93x
|
||||
|
||||
// Allwinner
|
||||
#define OPT_MCU_F1C100S 2100 ///< Allwinner F1C100s family
|
||||
|
||||
// WCH
|
||||
#define OPT_MCU_CH32V307 2200 ///< WCH CH32V307
|
||||
#define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x
|
||||
#define OPT_MCU_CH32V20X 2220 ///< WCH CH32V20X
|
||||
#define OPT_MCU_CH32V103 2230 ///< WCH CH32V103
|
||||
|
||||
// NXP LPC MCX
|
||||
#define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series
|
||||
#define OPT_MCU_MCXA15 2301 ///< NXP MCX A15 Series
|
||||
|
||||
// Analog Devices
|
||||
#define OPT_MCU_MAX32690 2400 ///< ADI MAX32690
|
||||
#define OPT_MCU_MAX32666 2401 ///< ADI MAX32666/5
|
||||
#define OPT_MCU_MAX32650 2402 ///< ADI MAX32650/1/2
|
||||
#define OPT_MCU_MAX78002 2403 ///< ADI MAX78002
|
||||
|
||||
// Check if configured MCU is one of listed
|
||||
// Apply _TU_CHECK_MCU with || as separator to list of input
|
||||
#define _TU_CHECK_MCU(_m) (CFG_TUSB_MCU == _m)
|
||||
#define TU_CHECK_MCU(...) (TU_ARGS_APPLY(_TU_CHECK_MCU, ||, __VA_ARGS__))
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Supported OS
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define OPT_OS_NONE 1 ///< No RTOS
|
||||
#define OPT_OS_FREERTOS 2 ///< FreeRTOS
|
||||
#define OPT_OS_MYNEWT 3 ///< Mynewt OS
|
||||
#define OPT_OS_CUSTOM 4 ///< Custom OS is implemented by application
|
||||
#define OPT_OS_PICO 5 ///< Raspberry Pi Pico SDK
|
||||
#define OPT_OS_RTTHREAD 6 ///< RT-Thread
|
||||
#define OPT_OS_RTX4 7 ///< Keil RTX 4
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Mode and Speed
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Low byte is operational mode
|
||||
#define OPT_MODE_NONE 0x0000 ///< Disabled
|
||||
#define OPT_MODE_DEVICE 0x0001 ///< Device Mode
|
||||
#define OPT_MODE_HOST 0x0002 ///< Host Mode
|
||||
|
||||
// High byte is max operational speed (corresponding to tusb_speed_t)
|
||||
#define OPT_MODE_DEFAULT_SPEED 0x0000 ///< Default (max) speed supported by MCU
|
||||
#define OPT_MODE_LOW_SPEED 0x0100 ///< Low Speed
|
||||
#define OPT_MODE_FULL_SPEED 0x0200 ///< Full Speed
|
||||
#define OPT_MODE_HIGH_SPEED 0x0400 ///< High Speed
|
||||
#define OPT_MODE_SPEED_MASK 0xff00
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Include tusb_config.h
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Allow to use command line to change the config name/location
|
||||
#ifdef CFG_TUSB_CONFIG_FILE
|
||||
#include CFG_TUSB_CONFIG_FILE
|
||||
#else
|
||||
#include "tusb_config.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBIP
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// DWC2 controller: use DMA for data transfer
|
||||
// For processors with data cache enabled, USB endpoint buffer region
|
||||
// (defined by CFG_TUSB_MEM_SECTION) must be declared as non-cacheable.
|
||||
// For example, on Cortex-M7 the MPU region can be configured as normal
|
||||
// non-cacheable, with RASR register value: TEX=1 C=0 B=0 S=0.
|
||||
#ifndef CFG_TUD_DWC2_DMA
|
||||
#define CFG_TUD_DWC2_DMA 0
|
||||
#endif
|
||||
|
||||
// Enable PIO-USB software host controller
|
||||
#ifndef CFG_TUH_RPI_PIO_USB
|
||||
#define CFG_TUH_RPI_PIO_USB 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_RPI_PIO_USB
|
||||
#define CFG_TUD_RPI_PIO_USB 0
|
||||
#endif
|
||||
|
||||
// MAX3421 Host controller option
|
||||
#ifndef CFG_TUH_MAX3421
|
||||
#define CFG_TUH_MAX3421 0
|
||||
#endif
|
||||
|
||||
#include "tusb_mcu.h"
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// RootHub Mode detection
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
//------------- Root hub as Device -------------//
|
||||
|
||||
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE)
|
||||
#define TUD_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
|
||||
#define TUD_OPT_RHPORT 0
|
||||
#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_DEVICE)
|
||||
#define TUD_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE)
|
||||
#define TUD_OPT_RHPORT 1
|
||||
#else
|
||||
#define TUD_RHPORT_MODE OPT_MODE_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_ENABLED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUD_ENABLED (TUD_RHPORT_MODE & OPT_MODE_DEVICE)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_MAX_SPEED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUD_MAX_SPEED (TUD_RHPORT_MODE & OPT_MODE_SPEED_MASK)
|
||||
#endif
|
||||
|
||||
// For backward compatible
|
||||
#define TUSB_OPT_DEVICE_ENABLED CFG_TUD_ENABLED
|
||||
|
||||
// highspeed support indicator
|
||||
#define TUD_OPT_HIGH_SPEED (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
|
||||
|
||||
//------------- Root hub as Host -------------//
|
||||
|
||||
#if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST)
|
||||
#define TUH_RHPORT_MODE (CFG_TUSB_RHPORT0_MODE)
|
||||
#define TUH_OPT_RHPORT 0
|
||||
#elif defined(CFG_TUSB_RHPORT1_MODE) && ((CFG_TUSB_RHPORT1_MODE) & OPT_MODE_HOST)
|
||||
#define TUH_RHPORT_MODE (CFG_TUSB_RHPORT1_MODE)
|
||||
#define TUH_OPT_RHPORT 1
|
||||
#else
|
||||
#define TUH_RHPORT_MODE OPT_MODE_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_ENABLED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUH_ENABLED (TUH_RHPORT_MODE & OPT_MODE_HOST)
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MAX_SPEED
|
||||
// fallback to use CFG_TUSB_RHPORTx_MODE
|
||||
#define CFG_TUH_MAX_SPEED (TUH_RHPORT_MODE & OPT_MODE_SPEED_MASK)
|
||||
#endif
|
||||
|
||||
// For backward compatible
|
||||
#define TUSB_OPT_HOST_ENABLED CFG_TUH_ENABLED
|
||||
|
||||
// highspeed support indicator
|
||||
#define TUH_OPT_HIGH_SPEED (CFG_TUH_MAX_SPEED ? (CFG_TUH_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TODO move later
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// TUP_MCU_STRICT_ALIGN will overwrite TUP_ARCH_STRICT_ALIGN.
|
||||
// In case TUP_MCU_STRICT_ALIGN = 1 and TUP_ARCH_STRICT_ALIGN =0, we will not reply on compiler
|
||||
// to generate unaligned access code.
|
||||
// LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM
|
||||
#if TUD_OPT_HIGH_SPEED && TU_CHECK_MCU(OPT_MCU_LPC54XXX, OPT_MCU_LPC55XX)
|
||||
#define TUP_MCU_STRICT_ALIGN 1
|
||||
#else
|
||||
#define TUP_MCU_STRICT_ALIGN 0
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Common Options (Default)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Debug enable to print out error message
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for USBH is logged
|
||||
#ifndef CFG_TUH_LOG_LEVEL
|
||||
#define CFG_TUH_LOG_LEVEL 2
|
||||
#endif
|
||||
|
||||
// Level where CFG_TUSB_DEBUG must be at least for USBD is logged
|
||||
#ifndef CFG_TUD_LOG_LEVEL
|
||||
#define CFG_TUD_LOG_LEVEL 2
|
||||
#endif
|
||||
|
||||
// Memory section for placing buffer used for usb transferring. If MEM_SECTION is different for
|
||||
// host and device use: CFG_TUD_MEM_SECTION, CFG_TUH_MEM_SECTION instead
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
// Alignment requirement of buffer used for usb transferring. if MEM_ALIGN is different for
|
||||
// host and device controller use: CFG_TUD_MEM_ALIGN, CFG_TUH_MEM_ALIGN instead
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
|
||||
#endif
|
||||
|
||||
// OS selection
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_OS_INC_PATH
|
||||
#define CFG_TUSB_OS_INC_PATH
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Device Options (Default)
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Attribute to place data in accessible RAM for device controller (default: CFG_TUSB_MEM_SECTION)
|
||||
#ifndef CFG_TUD_MEM_SECTION
|
||||
#define CFG_TUD_MEM_SECTION CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
// Attribute to align memory for device controller (default: CFG_TUSB_MEM_ALIGN)
|
||||
#ifndef CFG_TUD_MEM_ALIGN
|
||||
#define CFG_TUD_MEM_ALIGN CFG_TUSB_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_INTERFACE_MAX
|
||||
#define CFG_TUD_INTERFACE_MAX 16
|
||||
#endif
|
||||
|
||||
// default to max hardware endpoint, but can be smaller to save RAM
|
||||
#ifndef CFG_TUD_ENDPPOINT_MAX
|
||||
#define CFG_TUD_ENDPPOINT_MAX TUP_DCD_ENDPOINT_MAX
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_ENDPPOINT_MAX > TUP_DCD_ENDPOINT_MAX
|
||||
#error "CFG_TUD_ENDPPOINT_MAX must be less than or equal to TUP_DCD_ENDPOINT_MAX"
|
||||
#endif
|
||||
|
||||
// USB 2.0 7.1.20: compliance test mode support
|
||||
#ifndef CFG_TUD_TEST_MODE
|
||||
#define CFG_TUD_TEST_MODE 0
|
||||
#endif
|
||||
|
||||
//------------- Device Class Driver -------------//
|
||||
#ifndef CFG_TUD_BTH
|
||||
#define CFG_TUD_BTH 0
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_BTH && !defined(CFG_TUD_BTH_ISO_ALT_COUNT)
|
||||
#error CFG_TUD_BTH_ISO_ALT_COUNT must be defined to tell Bluetooth driver the number of ISO endpoints to use
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_CDC
|
||||
#define CFG_TUD_CDC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_MSC
|
||||
#define CFG_TUD_MSC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_HID
|
||||
#define CFG_TUD_HID 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_AUDIO
|
||||
#define CFG_TUD_AUDIO 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VIDEO
|
||||
#define CFG_TUD_VIDEO 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_MIDI
|
||||
#define CFG_TUD_MIDI 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_VENDOR
|
||||
#define CFG_TUD_VENDOR 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_USBTMC
|
||||
#define CFG_TUD_USBTMC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_DFU_RUNTIME
|
||||
#define CFG_TUD_DFU_RUNTIME 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_DFU
|
||||
#define CFG_TUD_DFU 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_ECM_RNDIS
|
||||
#ifdef CFG_TUD_NET
|
||||
#warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS"
|
||||
#define CFG_TUD_ECM_RNDIS CFG_TUD_NET
|
||||
#else
|
||||
#define CFG_TUD_ECM_RNDIS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_NCM
|
||||
#define CFG_TUD_NCM 0
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Host Options (Default)
|
||||
//--------------------------------------------------------------------
|
||||
#if CFG_TUH_ENABLED
|
||||
#ifndef CFG_TUH_DEVICE_MAX
|
||||
#define CFG_TUH_DEVICE_MAX 1
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_ENUMERATION_BUFSIZE
|
||||
#define CFG_TUH_ENUMERATION_BUFSIZE 256
|
||||
#endif
|
||||
#endif // CFG_TUH_ENABLED
|
||||
|
||||
// Attribute to place data in accessible RAM for host controller (default: CFG_TUSB_MEM_SECTION)
|
||||
#ifndef CFG_TUH_MEM_SECTION
|
||||
#define CFG_TUH_MEM_SECTION CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
// Attribute to align memory for host controller
|
||||
#ifndef CFG_TUH_MEM_ALIGN
|
||||
#define CFG_TUH_MEM_ALIGN CFG_TUSB_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
|
||||
#ifndef CFG_TUH_HUB
|
||||
#define CFG_TUH_HUB 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_CDC
|
||||
#define CFG_TUH_CDC 0
|
||||
#endif
|
||||
|
||||
// FTDI is not part of CDC class, only to re-use CDC driver API
|
||||
#ifndef CFG_TUH_CDC_FTDI
|
||||
#define CFG_TUH_CDC_FTDI 0
|
||||
#endif
|
||||
|
||||
// List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
|
||||
#ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST
|
||||
#define CFG_TUH_CDC_FTDI_VID_PID_LIST \
|
||||
{0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \
|
||||
{0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \
|
||||
{0x0403, 0xCD18}
|
||||
#endif
|
||||
|
||||
// CP210X is not part of CDC class, only to re-use CDC driver API
|
||||
#ifndef CFG_TUH_CDC_CP210X
|
||||
#define CFG_TUH_CDC_CP210X 0
|
||||
#endif
|
||||
|
||||
// List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
|
||||
#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
|
||||
#define CFG_TUH_CDC_CP210X_VID_PID_LIST \
|
||||
{0x10C4, 0xEA60}, {0x10C4, 0xEA70}
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_CDC_CH34X
|
||||
// CH34X is not part of CDC class, only to re-use CDC driver API
|
||||
#define CFG_TUH_CDC_CH34X 0
|
||||
#endif
|
||||
|
||||
// List of product IDs that can use the CH34X CDC driver
|
||||
#ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST
|
||||
#define CFG_TUH_CDC_CH34X_VID_PID_LIST \
|
||||
{ 0x1a86, 0x5523 }, /* ch341 chip */ \
|
||||
{ 0x1a86, 0x7522 }, /* ch340k chip */ \
|
||||
{ 0x1a86, 0x7523 }, /* ch340 chip */ \
|
||||
{ 0x1a86, 0xe523 }, /* ch330 chip */ \
|
||||
{ 0x4348, 0x5523 }, /* ch340 custom chip */ \
|
||||
{ 0x2184, 0x0057 }, /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ \
|
||||
{ 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_HID
|
||||
#define CFG_TUH_HID 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MIDI
|
||||
#define CFG_TUH_MIDI 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_MSC
|
||||
#define CFG_TUH_MSC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_VENDOR
|
||||
#define CFG_TUH_VENDOR 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_API_EDPT_XFER
|
||||
#define CFG_TUH_API_EDPT_XFER 0
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TypeC Options (Default)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#ifndef CFG_TUC_ENABLED
|
||||
#define CFG_TUC_ENABLED 0
|
||||
|
||||
#define tuc_int_handler(_p)
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Configuration Validation
|
||||
//------------------------------------------------------------------
|
||||
#if CFG_TUD_ENDPOINT0_SIZE > 64
|
||||
#error Control Endpoint Max Packet Size cannot be larger than 64
|
||||
#endif
|
||||
|
||||
// To avoid GCC compiler warnings when -pedantic option is used (strict ISO C)
|
||||
typedef int make_iso_compilers_happy;
|
||||
|
||||
#endif /* _TUSB_OPTION_H_ */
|
||||
|
||||
/** @} */
|
||||
559
src/tinyusb/tusb_types.h
Normal file
559
src/tinyusb/tusb_types.h
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
|
||||
#ifndef TUSB_TYPES_H_
|
||||
#define TUSB_TYPES_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "tusb_compiler.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* CONSTANTS
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
typedef enum {
|
||||
TUSB_ROLE_INVALID = 0,
|
||||
TUSB_ROLE_DEVICE,
|
||||
TUSB_ROLE_HOST,
|
||||
} tusb_role_t;
|
||||
|
||||
/// defined base on EHCI specs value for Endpoint Speed
|
||||
typedef enum {
|
||||
TUSB_SPEED_FULL = 0,
|
||||
TUSB_SPEED_LOW = 1,
|
||||
TUSB_SPEED_HIGH = 2,
|
||||
TUSB_SPEED_AUTO = 0xaa,
|
||||
TUSB_SPEED_INVALID = 0xff,
|
||||
} tusb_speed_t;
|
||||
|
||||
/// defined base on USB Specs Endpoint's bmAttributes
|
||||
typedef enum {
|
||||
TUSB_XFER_CONTROL = 0 ,
|
||||
TUSB_XFER_ISOCHRONOUS ,
|
||||
TUSB_XFER_BULK ,
|
||||
TUSB_XFER_INTERRUPT
|
||||
} tusb_xfer_type_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_DIR_OUT = 0,
|
||||
TUSB_DIR_IN = 1,
|
||||
|
||||
TUSB_DIR_IN_MASK = 0x80
|
||||
} tusb_dir_t;
|
||||
|
||||
enum {
|
||||
TUSB_EPSIZE_BULK_FS = 64,
|
||||
TUSB_EPSIZE_BULK_HS = 512,
|
||||
|
||||
TUSB_EPSIZE_ISO_FS_MAX = 1023,
|
||||
TUSB_EPSIZE_ISO_HS_MAX = 1024,
|
||||
};
|
||||
|
||||
/// Isochronous Endpoint Attributes
|
||||
typedef enum {
|
||||
TUSB_ISO_EP_ATT_NO_SYNC = 0x00,
|
||||
TUSB_ISO_EP_ATT_ASYNCHRONOUS = 0x04,
|
||||
TUSB_ISO_EP_ATT_ADAPTIVE = 0x08,
|
||||
TUSB_ISO_EP_ATT_SYNCHRONOUS = 0x0C,
|
||||
TUSB_ISO_EP_ATT_DATA = 0x00, ///< Data End Point
|
||||
TUSB_ISO_EP_ATT_EXPLICIT_FB = 0x10, ///< Feedback End Point
|
||||
TUSB_ISO_EP_ATT_IMPLICIT_FB = 0x20, ///< Data endpoint that also serves as an implicit feedback
|
||||
} tusb_iso_ep_attribute_t;
|
||||
|
||||
/// USB Descriptor Types
|
||||
typedef enum {
|
||||
TUSB_DESC_DEVICE = 0x01,
|
||||
TUSB_DESC_CONFIGURATION = 0x02,
|
||||
TUSB_DESC_STRING = 0x03,
|
||||
TUSB_DESC_INTERFACE = 0x04,
|
||||
TUSB_DESC_ENDPOINT = 0x05,
|
||||
TUSB_DESC_DEVICE_QUALIFIER = 0x06,
|
||||
TUSB_DESC_OTHER_SPEED_CONFIG = 0x07,
|
||||
TUSB_DESC_INTERFACE_POWER = 0x08,
|
||||
TUSB_DESC_OTG = 0x09,
|
||||
TUSB_DESC_DEBUG = 0x0A,
|
||||
TUSB_DESC_INTERFACE_ASSOCIATION = 0x0B,
|
||||
|
||||
TUSB_DESC_BOS = 0x0F,
|
||||
TUSB_DESC_DEVICE_CAPABILITY = 0x10,
|
||||
|
||||
TUSB_DESC_FUNCTIONAL = 0x21,
|
||||
|
||||
// Class Specific Descriptor
|
||||
TUSB_DESC_CS_DEVICE = 0x21,
|
||||
TUSB_DESC_CS_CONFIGURATION = 0x22,
|
||||
TUSB_DESC_CS_STRING = 0x23,
|
||||
TUSB_DESC_CS_INTERFACE = 0x24,
|
||||
TUSB_DESC_CS_ENDPOINT = 0x25,
|
||||
|
||||
TUSB_DESC_SUPERSPEED_ENDPOINT_COMPANION = 0x30,
|
||||
TUSB_DESC_SUPERSPEED_ISO_ENDPOINT_COMPANION = 0x31
|
||||
} tusb_desc_type_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_GET_STATUS = 0 ,
|
||||
TUSB_REQ_CLEAR_FEATURE = 1 ,
|
||||
TUSB_REQ_RESERVED = 2 ,
|
||||
TUSB_REQ_SET_FEATURE = 3 ,
|
||||
TUSB_REQ_RESERVED2 = 4 ,
|
||||
TUSB_REQ_SET_ADDRESS = 5 ,
|
||||
TUSB_REQ_GET_DESCRIPTOR = 6 ,
|
||||
TUSB_REQ_SET_DESCRIPTOR = 7 ,
|
||||
TUSB_REQ_GET_CONFIGURATION = 8 ,
|
||||
TUSB_REQ_SET_CONFIGURATION = 9 ,
|
||||
TUSB_REQ_GET_INTERFACE = 10 ,
|
||||
TUSB_REQ_SET_INTERFACE = 11 ,
|
||||
TUSB_REQ_SYNCH_FRAME = 12
|
||||
} tusb_request_code_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_FEATURE_EDPT_HALT = 0,
|
||||
TUSB_REQ_FEATURE_REMOTE_WAKEUP = 1,
|
||||
TUSB_REQ_FEATURE_TEST_MODE = 2
|
||||
} tusb_request_feature_selector_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_TYPE_STANDARD = 0,
|
||||
TUSB_REQ_TYPE_CLASS,
|
||||
TUSB_REQ_TYPE_VENDOR,
|
||||
TUSB_REQ_TYPE_INVALID
|
||||
} tusb_request_type_t;
|
||||
|
||||
typedef enum {
|
||||
TUSB_REQ_RCPT_DEVICE =0,
|
||||
TUSB_REQ_RCPT_INTERFACE,
|
||||
TUSB_REQ_RCPT_ENDPOINT,
|
||||
TUSB_REQ_RCPT_OTHER
|
||||
} tusb_request_recipient_t;
|
||||
|
||||
// https://www.usb.org/defined-class-codes
|
||||
typedef enum {
|
||||
TUSB_CLASS_UNSPECIFIED = 0 ,
|
||||
TUSB_CLASS_AUDIO = 1 ,
|
||||
TUSB_CLASS_CDC = 2 ,
|
||||
TUSB_CLASS_HID = 3 ,
|
||||
TUSB_CLASS_RESERVED_4 = 4 ,
|
||||
TUSB_CLASS_PHYSICAL = 5 ,
|
||||
TUSB_CLASS_IMAGE = 6 ,
|
||||
TUSB_CLASS_PRINTER = 7 ,
|
||||
TUSB_CLASS_MSC = 8 ,
|
||||
TUSB_CLASS_HUB = 9 ,
|
||||
TUSB_CLASS_CDC_DATA = 10 ,
|
||||
TUSB_CLASS_SMART_CARD = 11 ,
|
||||
TUSB_CLASS_RESERVED_12 = 12 ,
|
||||
TUSB_CLASS_CONTENT_SECURITY = 13 ,
|
||||
TUSB_CLASS_VIDEO = 14 ,
|
||||
TUSB_CLASS_PERSONAL_HEALTHCARE = 15 ,
|
||||
TUSB_CLASS_AUDIO_VIDEO = 16 ,
|
||||
|
||||
TUSB_CLASS_DIAGNOSTIC = 0xDC ,
|
||||
TUSB_CLASS_WIRELESS_CONTROLLER = 0xE0 ,
|
||||
TUSB_CLASS_MISC = 0xEF ,
|
||||
TUSB_CLASS_APPLICATION_SPECIFIC = 0xFE ,
|
||||
TUSB_CLASS_VENDOR_SPECIFIC = 0xFF
|
||||
} tusb_class_code_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MISC_SUBCLASS_COMMON = 2
|
||||
}misc_subclass_type_t;
|
||||
|
||||
typedef enum {
|
||||
MISC_PROTOCOL_IAD = 1
|
||||
} misc_protocol_type_t;
|
||||
|
||||
typedef enum {
|
||||
APP_SUBCLASS_USBTMC = 0x03,
|
||||
APP_SUBCLASS_DFU_RUNTIME = 0x01
|
||||
} app_subclass_type_t;
|
||||
|
||||
typedef enum {
|
||||
DEVICE_CAPABILITY_WIRELESS_USB = 0x01,
|
||||
DEVICE_CAPABILITY_USB20_EXTENSION = 0x02,
|
||||
DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03,
|
||||
DEVICE_CAPABILITY_CONTAINER_id = 0x04,
|
||||
DEVICE_CAPABILITY_PLATFORM = 0x05,
|
||||
DEVICE_CAPABILITY_POWER_DELIVERY = 0x06,
|
||||
DEVICE_CAPABILITY_BATTERY_INFO = 0x07,
|
||||
DEVICE_CAPABILITY_PD_CONSUMER_PORT = 0x08,
|
||||
DEVICE_CAPABILITY_PD_PROVIDER_PORT = 0x09,
|
||||
DEVICE_CAPABILITY_SUPERSPEED_PLUS = 0x0A,
|
||||
DEVICE_CAPABILITY_PRECESION_TIME_MEASUREMENT = 0x0B,
|
||||
DEVICE_CAPABILITY_WIRELESS_USB_EXT = 0x0C,
|
||||
DEVICE_CAPABILITY_BILLBOARD = 0x0D,
|
||||
DEVICE_CAPABILITY_AUTHENTICATION = 0x0E,
|
||||
DEVICE_CAPABILITY_BILLBOARD_EX = 0x0F,
|
||||
DEVICE_CAPABILITY_CONFIGURATION_SUMMARY = 0x10
|
||||
} device_capability_type_t;
|
||||
|
||||
enum {
|
||||
TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5,
|
||||
TUSB_DESC_CONFIG_ATT_SELF_POWERED = 1u << 6,
|
||||
};
|
||||
|
||||
#define TUSB_DESC_CONFIG_POWER_MA(x) ((x)/2)
|
||||
|
||||
// USB 2.0 Spec Table 9-7: Test Mode Selectors
|
||||
typedef enum {
|
||||
TUSB_FEATURE_TEST_J = 1,
|
||||
TUSB_FEATURE_TEST_K,
|
||||
TUSB_FEATURE_TEST_SE0_NAK,
|
||||
TUSB_FEATURE_TEST_PACKET,
|
||||
TUSB_FEATURE_TEST_FORCE_ENABLE,
|
||||
} tusb_feature_test_mode_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
typedef enum {
|
||||
XFER_RESULT_SUCCESS = 0,
|
||||
XFER_RESULT_FAILED,
|
||||
XFER_RESULT_STALLED,
|
||||
XFER_RESULT_TIMEOUT,
|
||||
XFER_RESULT_INVALID
|
||||
} xfer_result_t;
|
||||
|
||||
// TODO remove
|
||||
enum {
|
||||
DESC_OFFSET_LEN = 0,
|
||||
DESC_OFFSET_TYPE = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
INTERFACE_INVALID_NUMBER = 0xff
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MS_OS_20_SET_HEADER_DESCRIPTOR = 0x00,
|
||||
MS_OS_20_SUBSET_HEADER_CONFIGURATION = 0x01,
|
||||
MS_OS_20_SUBSET_HEADER_FUNCTION = 0x02,
|
||||
MS_OS_20_FEATURE_COMPATBLE_ID = 0x03,
|
||||
MS_OS_20_FEATURE_REG_PROPERTY = 0x04,
|
||||
MS_OS_20_FEATURE_MIN_RESUME_TIME = 0x05,
|
||||
MS_OS_20_FEATURE_MODEL_ID = 0x06,
|
||||
MS_OS_20_FEATURE_CCGP_DEVICE = 0x07,
|
||||
MS_OS_20_FEATURE_VENDOR_REVISION = 0x08
|
||||
} microsoft_os_20_type_t;
|
||||
|
||||
enum {
|
||||
CONTROL_STAGE_IDLE,
|
||||
CONTROL_STAGE_SETUP,
|
||||
CONTROL_STAGE_DATA,
|
||||
CONTROL_STAGE_ACK
|
||||
};
|
||||
|
||||
enum {
|
||||
TUSB_INDEX_INVALID_8 = 0xFFu
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
typedef struct {
|
||||
tusb_role_t role;
|
||||
tusb_speed_t speed;
|
||||
} tusb_rhport_init_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USB Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Start of all packed definitions for compiler without per-type packed
|
||||
TU_ATTR_PACKED_BEGIN
|
||||
TU_ATTR_BIT_FIELD_ORDER_BEGIN
|
||||
|
||||
/// USB Device Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||
uint8_t bDescriptorType ; ///< DEVICE Descriptor Type.
|
||||
uint16_t bcdUSB ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H).
|
||||
|
||||
uint8_t bDeviceClass ; ///< Class code (assigned by the USB-IF).
|
||||
uint8_t bDeviceSubClass ; ///< Subclass code (assigned by the USB-IF).
|
||||
uint8_t bDeviceProtocol ; ///< Protocol code (assigned by the USB-IF).
|
||||
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
|
||||
|
||||
uint16_t idVendor ; ///< Vendor ID (assigned by the USB-IF).
|
||||
uint16_t idProduct ; ///< Product ID (assigned by the manufacturer).
|
||||
uint16_t bcdDevice ; ///< Device release number in binary-coded decimal.
|
||||
uint8_t iManufacturer ; ///< Index of string descriptor describing manufacturer.
|
||||
uint8_t iProduct ; ///< Index of string descriptor describing product.
|
||||
uint8_t iSerialNumber ; ///< Index of string descriptor describing the device's serial number.
|
||||
|
||||
uint8_t bNumConfigurations ; ///< Number of possible configurations.
|
||||
} tusb_desc_device_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
|
||||
|
||||
// USB Binary Device Object Store (BOS) Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned for this descriptor
|
||||
uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS
|
||||
} tusb_desc_bos_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct");
|
||||
|
||||
/// USB Configuration Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< CONFIGURATION Descriptor Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned for this configuration. Includes the combined length of all descriptors (configuration, interface, endpoint, and class- or vendor-specific) returned for this configuration.
|
||||
|
||||
uint8_t bNumInterfaces ; ///< Number of interfaces supported by this configuration
|
||||
uint8_t bConfigurationValue ; ///< Value to use as an argument to the SetConfiguration() request to select this configuration.
|
||||
uint8_t iConfiguration ; ///< Index of string descriptor describing this configuration
|
||||
uint8_t bmAttributes ; ///< Configuration characteristics \n D7: Reserved (set to one)\n D6: Self-powered \n D5: Remote Wakeup \n D4...0: Reserved (reset to zero) \n D7 is reserved and must be set to one for historical reasons. \n A device configuration that uses power from the bus and a local source reports a non-zero value in bMaxPower to indicate the amount of bus power required and sets D6. The actual power source at runtime may be determined using the GetStatus(DEVICE) request (see USB 2.0 spec Section 9.4.5). \n If a device configuration supports remote wakeup, D5 is set to one.
|
||||
uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA).
|
||||
} tusb_desc_configuration_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct");
|
||||
|
||||
/// USB Interface Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< INTERFACE Descriptor Type
|
||||
|
||||
uint8_t bInterfaceNumber ; ///< Number of this interface. Zero-based value identifying the index in the array of concurrent interfaces supported by this configuration.
|
||||
uint8_t bAlternateSetting ; ///< Value used to select this alternate setting for the interface identified in the prior field
|
||||
uint8_t bNumEndpoints ; ///< Number of endpoints used by this interface (excluding endpoint zero). If this value is zero, this interface only uses the Default Control Pipe.
|
||||
uint8_t bInterfaceClass ; ///< Class code (assigned by the USB-IF). \li A value of zero is reserved for future standardization. \li If this field is set to FFH, the interface class is vendor-specific. \li All other values are reserved for assignment by the USB-IF.
|
||||
uint8_t bInterfaceSubClass ; ///< Subclass code (assigned by the USB-IF). \n These codes are qualified by the value of the bInterfaceClass field. \li If the bInterfaceClass field is reset to zero, this field must also be reset to zero. \li If the bInterfaceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
|
||||
uint8_t bInterfaceProtocol ; ///< Protocol code (assigned by the USB). \n These codes are qualified by the value of the bInterfaceClass and the bInterfaceSubClass fields. If an interface supports class-specific requests, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use a class-specific protocol on this interface. \li If this field is set to FFH, the device uses a vendor-specific protocol for this interface.
|
||||
uint8_t iInterface ; ///< Index of string descriptor describing this interface
|
||||
} tusb_desc_interface_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct");
|
||||
|
||||
/// USB Endpoint Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; // Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; // ENDPOINT Descriptor Type
|
||||
|
||||
uint8_t bEndpointAddress ; // The address of the endpoint
|
||||
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt
|
||||
uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous
|
||||
uint8_t usage : 2; // Data, Feedback, Implicit feedback
|
||||
uint8_t : 2;
|
||||
} bmAttributes;
|
||||
|
||||
uint16_t wMaxPacketSize ; // Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame
|
||||
uint8_t bInterval ; // Polling interval, in frames or microframes depending on the operating speed
|
||||
} tusb_desc_endpoint_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_endpoint_t) == 7, "size is not correct");
|
||||
|
||||
/// USB Other Speed Configuration Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
uint16_t wTotalLength ; ///< Total length of data returned
|
||||
|
||||
uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration
|
||||
uint8_t bConfigurationValue ; ///< Value to use to select configuration
|
||||
uint8_t iConfiguration ; ///< Index of string descriptor
|
||||
uint8_t bmAttributes ; ///< Same as Configuration descriptor
|
||||
uint8_t bMaxPower ; ///< Same as Configuration descriptor
|
||||
} tusb_desc_other_speed_t;
|
||||
|
||||
/// USB Device Qualifier Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Device Qualifier Type
|
||||
uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00)
|
||||
|
||||
uint8_t bDeviceClass ; ///< Class Code
|
||||
uint8_t bDeviceSubClass ; ///< SubClass Code
|
||||
uint8_t bDeviceProtocol ; ///< Protocol Code
|
||||
|
||||
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed
|
||||
uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations
|
||||
uint8_t bReserved ; ///< Reserved for future use, must be zero
|
||||
} tusb_desc_device_qualifier_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_device_qualifier_t) == 10, "size is not correct");
|
||||
|
||||
/// USB Interface Association Descriptor (IAD ECN)
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of descriptor
|
||||
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
|
||||
|
||||
uint8_t bFirstInterface ; ///< Index of the first associated interface.
|
||||
uint8_t bInterfaceCount ; ///< Total number of associated interfaces.
|
||||
|
||||
uint8_t bFunctionClass ; ///< Interface class ID.
|
||||
uint8_t bFunctionSubClass ; ///< Interface subclass ID.
|
||||
uint8_t bFunctionProtocol ; ///< Interface protocol ID.
|
||||
|
||||
uint8_t iFunction ; ///< Index of the string descriptor describing the interface association.
|
||||
} tusb_desc_interface_assoc_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_desc_interface_assoc_t) == 8, "size is not correct");
|
||||
|
||||
// USB String Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength ; ///< Size of this descriptor in bytes
|
||||
uint8_t bDescriptorType ; ///< Descriptor Type
|
||||
uint16_t unicode_string[];
|
||||
} tusb_desc_string_t;
|
||||
|
||||
// USB Binary Device Object Store (BOS)
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType ;
|
||||
uint8_t bDevCapabilityType;
|
||||
uint8_t bReserved;
|
||||
uint8_t PlatformCapabilityUUID[16];
|
||||
uint8_t CapabilityData[];
|
||||
} tusb_desc_bos_platform_t;
|
||||
|
||||
// USB WebUSB URL Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bScheme;
|
||||
char url[];
|
||||
} tusb_desc_webusb_url_t;
|
||||
|
||||
// DFU Functional Descriptor
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
|
||||
union {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t bitCanDnload : 1;
|
||||
uint8_t bitCanUpload : 1;
|
||||
uint8_t bitManifestationTolerant : 1;
|
||||
uint8_t bitWillDetach : 1;
|
||||
uint8_t reserved : 4;
|
||||
} bmAttributes;
|
||||
|
||||
uint8_t bAttributes;
|
||||
};
|
||||
|
||||
uint16_t wDetachTimeOut;
|
||||
uint16_t wTransferSize;
|
||||
uint16_t bcdDFUVersion;
|
||||
} tusb_desc_dfu_functional_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
typedef struct TU_ATTR_PACKED {
|
||||
union {
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
|
||||
uint8_t type : 2; ///< Request type tusb_request_type_t.
|
||||
uint8_t direction : 1; ///< Direction type. tusb_dir_t
|
||||
} bmRequestType_bit;
|
||||
|
||||
uint8_t bmRequestType;
|
||||
};
|
||||
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
} tusb_control_request_t;
|
||||
|
||||
TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
|
||||
|
||||
TU_ATTR_PACKED_END // End of all packed definitions
|
||||
TU_ATTR_BIT_FIELD_ORDER_END
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Endpoint helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Get direction from Endpoint address
|
||||
TU_ATTR_ALWAYS_INLINE static inline tusb_dir_t tu_edpt_dir(uint8_t addr) {
|
||||
return (addr & TUSB_DIR_IN_MASK) ? TUSB_DIR_IN : TUSB_DIR_OUT;
|
||||
}
|
||||
|
||||
// Get Endpoint number from address
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_number(uint8_t addr) {
|
||||
return (uint8_t)(addr & (~TUSB_DIR_IN_MASK));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t dir) {
|
||||
return (uint8_t)(num | (dir ? TUSB_DIR_IN_MASK : 0));
|
||||
}
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
|
||||
return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF;
|
||||
}
|
||||
|
||||
#if CFG_TUSB_DEBUG
|
||||
TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
|
||||
tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
|
||||
return str[t];
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptor helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// return next descriptor
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t const * tu_desc_next(void const* desc) {
|
||||
uint8_t const* desc8 = (uint8_t const*) desc;
|
||||
return desc8 + desc8[DESC_OFFSET_LEN];
|
||||
}
|
||||
|
||||
// get descriptor type
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_type(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_TYPE];
|
||||
}
|
||||
|
||||
// get descriptor length
|
||||
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_len(void const* desc) {
|
||||
return ((uint8_t const*) desc)[DESC_OFFSET_LEN];
|
||||
}
|
||||
|
||||
// find descriptor that match byte1 (type)
|
||||
uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1);
|
||||
|
||||
// find descriptor that match byte1 (type) and byte2
|
||||
uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2);
|
||||
|
||||
// find descriptor that match byte1 (type) and byte2
|
||||
uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TUSB_TYPES_H_
|
||||
133
src/tinyusb/tusb_verify.h
Normal file
133
src/tinyusb/tusb_verify.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* This file is part of the TinyUSB stack.
|
||||
*/
|
||||
#ifndef TUSB_VERIFY_H_
|
||||
#define TUSB_VERIFY_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "tusb_option.h"
|
||||
#include "tusb_compiler.h"
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* This file use an advanced macro technique to mimic the default parameter
|
||||
* as C++ for the sake of code simplicity. Beware of a headache macro
|
||||
* manipulation that you are told to stay away.
|
||||
*
|
||||
* This contains macros for both VERIFY and ASSERT:
|
||||
*
|
||||
* VERIFY: Used when there is an error condition which is not the
|
||||
* fault of the MCU. For example, bounds checking on data
|
||||
* sent to the micro over USB should use this function.
|
||||
* Another example is checking for buffer overflows, where
|
||||
* returning from the active function causes a NAK.
|
||||
*
|
||||
* ASSERT: Used for error conditions that are caused by MCU firmware
|
||||
* bugs. This is used to discover bugs in the code more
|
||||
* quickly. One example would be adding assertions in library
|
||||
* function calls to confirm a function's (untainted)
|
||||
* parameters are valid.
|
||||
*
|
||||
* The difference in behavior is that ASSERT triggers a breakpoint while
|
||||
* verify does not.
|
||||
*
|
||||
* #define TU_VERIFY(cond) if(cond) return false;
|
||||
* #define TU_VERIFY(cond,ret) if(cond) return ret;
|
||||
*
|
||||
* #define TU_ASSERT(cond) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return false;}
|
||||
* #define TU_ASSERT(cond,ret) if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
|
||||
*------------------------------------------------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TU_VERIFY Helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#if CFG_TUSB_DEBUG
|
||||
#include <stdio.h>
|
||||
#define TU_MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
|
||||
#else
|
||||
#define TU_MESS_FAILED() do {} while (0)
|
||||
#endif
|
||||
|
||||
// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
|
||||
#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \
|
||||
defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__)
|
||||
#define TU_BREAKPOINT() do { \
|
||||
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
|
||||
if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
|
||||
} while(0)
|
||||
|
||||
#elif defined(__riscv) && !TUP_MCU_ESPRESSIF
|
||||
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
|
||||
|
||||
#elif defined(_mips)
|
||||
#define TU_BREAKPOINT() do { __asm("sdbbp 0"); } while (0)
|
||||
|
||||
#else
|
||||
#define TU_BREAKPOINT() do {} while (0)
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* TU_VERIFY
|
||||
* - TU_VERIFY_1ARGS : return false if failed
|
||||
* - TU_VERIFY_2ARGS : return provided value if failed
|
||||
*------------------------------------------------------------------*/
|
||||
#define TU_VERIFY_DEFINE(_cond, _ret) \
|
||||
do { \
|
||||
if ( !(_cond) ) { return _ret; } \
|
||||
} while(0)
|
||||
|
||||
#define TU_VERIFY_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, false)
|
||||
#define TU_VERIFY_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _ret)
|
||||
|
||||
#define TU_VERIFY(...) TU_GET_3RD_ARG(__VA_ARGS__, TU_VERIFY_2ARGS, TU_VERIFY_1ARGS, _dummy)(__VA_ARGS__)
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* ASSERT
|
||||
* basically TU_VERIFY with TU_BREAKPOINT() as handler
|
||||
* - 1 arg : return false if failed
|
||||
* - 2 arg : return error if failed
|
||||
*------------------------------------------------------------------*/
|
||||
#define TU_ASSERT_DEFINE(_cond, _ret) \
|
||||
do { \
|
||||
if ( !(_cond) ) { TU_MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \
|
||||
} while(0)
|
||||
|
||||
#define TU_ASSERT_1ARGS(_cond) TU_ASSERT_DEFINE(_cond, false)
|
||||
#define TU_ASSERT_2ARGS(_cond, _ret) TU_ASSERT_DEFINE(_cond, _ret)
|
||||
|
||||
#ifndef TU_ASSERT
|
||||
#define TU_ASSERT(...) TU_GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_2ARGS, TU_ASSERT_1ARGS, _dummy)(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
88
src/usbEventQueue.c
Normal file
88
src/usbEventQueue.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "common.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "usbEventQueue.h"
|
||||
|
||||
static usb_event_queue_t sUsbEventQueue;
|
||||
|
||||
static void setCartridgeIrqState(bool asserted)
|
||||
{
|
||||
gpio_put(PIN_IRQ, asserted);
|
||||
}
|
||||
|
||||
void usb_clearEventQueue(void)
|
||||
{
|
||||
sUsbEventQueue.readPtr = 0;
|
||||
sUsbEventQueue.writePtr = 0;
|
||||
setCartridgeIrqState(false);
|
||||
}
|
||||
|
||||
void usb_prepareDequeueEvent(void)
|
||||
{
|
||||
u32 readPtr = sUsbEventQueue.readPtr;
|
||||
u32 writePtr = sUsbEventQueue.writePtr;
|
||||
u32 nextReadPtr = (readPtr + 1) % USB_EVENT_QUEUE_LENGTH;
|
||||
if (readPtr == writePtr || nextReadPtr == writePtr)
|
||||
{
|
||||
setCartridgeIrqState(false);
|
||||
}
|
||||
}
|
||||
|
||||
u32 usb_dequeueEvent(void)
|
||||
{
|
||||
u32 readPtr = sUsbEventQueue.readPtr;
|
||||
u32 writePtr = sUsbEventQueue.writePtr;
|
||||
if (readPtr == writePtr)
|
||||
{
|
||||
return USB_EVENT_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 result = sUsbEventQueue.queue[readPtr];
|
||||
u32 nextReadPtr = (readPtr + 1) % USB_EVENT_QUEUE_LENGTH;
|
||||
sUsbEventQueue.readPtr = nextReadPtr;
|
||||
if (nextReadPtr == writePtr)
|
||||
{
|
||||
setCartridgeIrqState(false);
|
||||
result |= 1u << 31;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool usb_tryEnqueueEvent32Bit(u32 event)
|
||||
{
|
||||
u32 readPtr = sUsbEventQueue.readPtr;
|
||||
u32 writePtr = sUsbEventQueue.writePtr;
|
||||
u32 nextWritePtr = (writePtr + 1) % USB_EVENT_QUEUE_LENGTH;
|
||||
if (readPtr == nextWritePtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sUsbEventQueue.queue[writePtr] = event;
|
||||
sUsbEventQueue.writePtr = nextWritePtr;
|
||||
setCartridgeIrqState(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool usb_tryEnqueueEvent64Bit(u32 event0, u32 event1)
|
||||
{
|
||||
u32 readPtr = sUsbEventQueue.readPtr;
|
||||
u32 writePtr = sUsbEventQueue.writePtr;
|
||||
u32 nextWritePtr = (writePtr + 1) % USB_EVENT_QUEUE_LENGTH;
|
||||
u32 nextNextWritePtr = (nextWritePtr + 1) % USB_EVENT_QUEUE_LENGTH;
|
||||
if (readPtr == nextWritePtr || readPtr == nextNextWritePtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sUsbEventQueue.queue[writePtr] = event0;
|
||||
sUsbEventQueue.queue[nextWritePtr] = event1;
|
||||
sUsbEventQueue.writePtr = nextNextWritePtr;
|
||||
setCartridgeIrqState(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
52
src/usbEventQueue.h
Normal file
52
src/usbEventQueue.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
enum
|
||||
{
|
||||
USB_EVENT_NONE = 0,
|
||||
USB_EVENT_BUS_RESET = 1,
|
||||
USB_EVENT_UNPLUGGED = 2,
|
||||
USB_EVENT_SOF = 0x20000000,
|
||||
USB_EVENT_SUSPEND = 3,
|
||||
USB_EVENT_RESUME = 4,
|
||||
USB_EVENT_SETUP_RECEIVED = 0x40000000,
|
||||
USB_EVENT_XFER_COMPLETE = 0x30000000
|
||||
};
|
||||
|
||||
#define USB_EVENT_QUEUE_LENGTH 64
|
||||
|
||||
/// @brief Struct representing a USB event queue.
|
||||
typedef struct
|
||||
{
|
||||
volatile u8 readPtr;
|
||||
volatile u8 writePtr;
|
||||
u32 queue[USB_EVENT_QUEUE_LENGTH];
|
||||
} usb_event_queue_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Clears the USB event queue.
|
||||
void usb_clearEventQueue(void);
|
||||
|
||||
/// @brief Prepares for dequeueing a USB event.
|
||||
void usb_prepareDequeueEvent(void);
|
||||
|
||||
/// @brief Dequeues a USB event and returns it.
|
||||
/// @return The dequeued USB event, or \see USB_EVENT_NONE when empty.
|
||||
u32 usb_dequeueEvent(void);
|
||||
|
||||
/// @brief Tries to enqueue a 32-bit USB \p event.
|
||||
/// @param event The event to enqueue.
|
||||
/// @return \c true when enqueueing was successful, or \c false otherwise.
|
||||
bool usb_tryEnqueueEvent32Bit(u32 event);
|
||||
|
||||
/// @brief Tries to enqueue a 64-bit USB \p event.
|
||||
/// @param event0 The first part of the event to enqueue.
|
||||
/// @param event1 The second part of the event to enqueue.
|
||||
/// @return \c true when enqueueing was successful, or \c false otherwise.
|
||||
bool usb_tryEnqueueEvent64Bit(u32 event0, u32 event1);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
12
src/wrfuxxed.S
Normal file
12
src/wrfuxxed.S
Normal file
@@ -0,0 +1,12 @@
|
||||
.section .flashdata,"a"
|
||||
|
||||
#if defined(DSPICO_ENABLE_WRFUXXED)
|
||||
.global gWrfuxxedPayload
|
||||
gWrfuxxedPayload:
|
||||
.incbin "../data/uartBufv060.bin"
|
||||
|
||||
gWrfuxxedPayloadDataEnd:
|
||||
.global gWrfuxxedPayloadSize
|
||||
.equ gWrfuxxedPayloadSize, gWrfuxxedPayloadDataEnd - gWrfuxxedPayload
|
||||
#endif
|
||||
.end
|
||||
9
src/wrfuxxed.h
Normal file
9
src/wrfuxxed.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
|
||||
#ifdef DSPICO_ENABLE_WRFUXXED
|
||||
|
||||
extern uint8_t gWrfuxxedPayload[];
|
||||
extern uint8_t gWrfuxxedPayloadSize[];
|
||||
|
||||
#endif
|
||||
44
src/xor.S
Normal file
44
src/xor.S
Normal file
@@ -0,0 +1,44 @@
|
||||
.syntax unified
|
||||
.section .data
|
||||
.thumb
|
||||
|
||||
#define PIO0_BASE 0x50200000
|
||||
#define PIO0_FSTAT (PIO0_BASE + 0x004)
|
||||
#define PIO0_TXF0 (PIO0_BASE + 0x010)
|
||||
|
||||
//r0 = srcA
|
||||
//r1 = srcB
|
||||
//r2 = dst
|
||||
//r3 = length (multiple of 8)
|
||||
.global applyXor
|
||||
.type applyXor, %function
|
||||
applyXor:
|
||||
push {r4-r7}
|
||||
|
||||
1:
|
||||
ldmia r0!, {r4,r5}
|
||||
ldmia r1!, {r6,r7}
|
||||
eors r4, r6
|
||||
eors r5, r7
|
||||
stmia r2!, {r4,r5}
|
||||
subs r3, #8
|
||||
bne 1b
|
||||
|
||||
pop {r4-r7}
|
||||
bx lr
|
||||
|
||||
//r0 = srcA, r1 = srcB
|
||||
@ .global xorToPio512
|
||||
@ xorToPio512:
|
||||
@ push {r4-r7}
|
||||
@ ldr r2,= PIO0_FSTAT
|
||||
@ ldr r3,= #(1 << 16)
|
||||
|
||||
@ ldr
|
||||
|
||||
@ pop {r4-r7}
|
||||
@ bx lr
|
||||
|
||||
|
||||
.pool
|
||||
.end
|
||||
16
src/xor.h
Normal file
16
src/xor.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Xors \p scrA with \p srcB and writes the result to \p dst. \p length bytes will be xorred.
|
||||
/// @param srcA The first source buffer.
|
||||
/// @param srcB The second source buffer.
|
||||
/// @param dst The destination buffer.
|
||||
/// @param length The number of bytes to xor. Must be a multiple of 8.
|
||||
extern void applyXor(const void* srcA, const void* srcB, void* dst, u32 length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user