From 84a14148ba50fa1afc725254b67abb9379a01f0a Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Wed, 24 Jan 2024 06:14:31 -0300 Subject: [PATCH 01/74] FIX: Allowing debug ROM to send bit 15 in game id --- lib/LinkRawWireless.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 8d18530..b0ff675 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -172,8 +172,7 @@ class LinkRawWireless { 0); auto broadcastData = - std::vector{buildU32(buildU16(gameName[1], gameName[0]), - gameId & LINK_RAW_WIRELESS_MAX_GAME_ID), + std::vector{buildU32(buildU16(gameName[1], gameName[0]), gameId), buildU32(buildU16(gameName[5], gameName[4]), buildU16(gameName[3], gameName[2])), buildU32(buildU16(gameName[9], gameName[8]), From ab11a896621498df749fe12f61dd9578bbe09f96 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Wed, 24 Jan 2024 06:15:24 -0300 Subject: [PATCH 02/74] Increasing version to v6.2.0 --- lib/LinkCable.hpp | 2 +- lib/LinkCableMultiboot.hpp | 2 +- lib/LinkGPIO.hpp | 2 +- lib/LinkRawCable.hpp | 2 +- lib/LinkRawWireless.hpp | 2 +- lib/LinkSPI.hpp | 2 +- lib/LinkUniversal.hpp | 2 +- lib/LinkWireless.hpp | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/LinkCable.hpp b/lib/LinkCable.hpp index a64813d..9b3c088 100644 --- a/lib/LinkCable.hpp +++ b/lib/LinkCable.hpp @@ -67,7 +67,7 @@ #define LINK_CABLE_BIT_GENERAL_PURPOSE_HIGH 15 #define LINK_CABLE_BARRIER asm volatile("" ::: "memory") -static volatile char LINK_CABLE_VERSION[] = "LinkCable/v6.1.1"; +static volatile char LINK_CABLE_VERSION[] = "LinkCable/v6.2.0"; void LINK_CABLE_ISR_VBLANK(); void LINK_CABLE_ISR_SERIAL(); diff --git a/lib/LinkCableMultiboot.hpp b/lib/LinkCableMultiboot.hpp index ff4cca3..19be90c 100644 --- a/lib/LinkCableMultiboot.hpp +++ b/lib/LinkCableMultiboot.hpp @@ -58,7 +58,7 @@ return error(FAILURE_DURING_HANDSHAKE); static volatile char LINK_CABLE_MULTIBOOT_VERSION[] = - "LinkCableMultiboot/v6.1.1"; + "LinkCableMultiboot/v6.2.0"; const u8 LINK_CABLE_MULTIBOOT_CLIENT_IDS[] = {0b0010, 0b0100, 0b1000}; diff --git a/lib/LinkGPIO.hpp b/lib/LinkGPIO.hpp index 3452efd..97a37cf 100644 --- a/lib/LinkGPIO.hpp +++ b/lib/LinkGPIO.hpp @@ -35,7 +35,7 @@ else \ REG &= ~(1 << BIT); -static volatile char LINK_GPIO_VERSION[] = "LinkGPIO/v6.1.1"; +static volatile char LINK_GPIO_VERSION[] = "LinkGPIO/v6.2.0"; const u8 LINK_GPIO_DATA_BITS[] = {2, 3, 1, 0}; const u8 LINK_GPIO_DIRECTION_BITS[] = {6, 7, 5, 4}; diff --git a/lib/LinkRawCable.hpp b/lib/LinkRawCable.hpp index 6675ce3..fdd4249 100644 --- a/lib/LinkRawCable.hpp +++ b/lib/LinkRawCable.hpp @@ -55,7 +55,7 @@ } \ } -static volatile char LINK_RAW_CABLE_VERSION[] = "LinkRawCable/v6.1.1"; +static volatile char LINK_RAW_CABLE_VERSION[] = "LinkRawCable/v6.2.0"; class LinkRawCable { public: diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index b0ff675..b599870 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -57,7 +57,7 @@ #define LINK_RAW_WIRELESS_COMMAND_WAIT 0x27 #define LINK_RAW_WIRELESS_COMMAND_BYE 0x3d -static volatile char LINK_RAW_WIRELESS_VERSION[] = "LinkRawWireless/v6.1.1"; +static volatile char LINK_RAW_WIRELESS_VERSION[] = "LinkRawWireless/v6.2.0"; const u16 LINK_RAW_WIRELESS_LOGIN_PARTS[] = { 0x494e, 0x494e, 0x544e, 0x544e, 0x4e45, 0x4e45, 0x4f44, 0x4f44, 0x8001}; diff --git a/lib/LinkSPI.hpp b/lib/LinkSPI.hpp index bfdd917..0fb79b6 100644 --- a/lib/LinkSPI.hpp +++ b/lib/LinkSPI.hpp @@ -53,7 +53,7 @@ #define LINK_SPI_BIT_GENERAL_PURPOSE_LOW 14 #define LINK_SPI_BIT_GENERAL_PURPOSE_HIGH 15 -static volatile char LINK_SPI_VERSION[] = "LinkSPI/v6.1.1"; +static volatile char LINK_SPI_VERSION[] = "LinkSPI/v6.2.0"; class LinkSPI { public: diff --git a/lib/LinkUniversal.hpp b/lib/LinkUniversal.hpp index bc61ebc..5a4534e 100644 --- a/lib/LinkUniversal.hpp +++ b/lib/LinkUniversal.hpp @@ -64,7 +64,7 @@ #define LINK_UNIVERSAL_SERVE_WAIT_FRAMES 60 #define LINK_UNIVERSAL_SERVE_WAIT_FRAMES_RANDOM 30 -static volatile char LINK_UNIVERSAL_VERSION[] = "LinkUniversal/v6.1.1"; +static volatile char LINK_UNIVERSAL_VERSION[] = "LinkUniversal/v6.2.0"; void LINK_UNIVERSAL_ISR_VBLANK(); void LINK_UNIVERSAL_ISR_SERIAL(); diff --git a/lib/LinkWireless.hpp b/lib/LinkWireless.hpp index de2fdf8..f04446a 100644 --- a/lib/LinkWireless.hpp +++ b/lib/LinkWireless.hpp @@ -139,7 +139,7 @@ if (!reset()) \ return false; -static volatile char LINK_WIRELESS_VERSION[] = "LinkWireless/v6.1.1"; +static volatile char LINK_WIRELESS_VERSION[] = "LinkWireless/v6.2.0"; void LINK_WIRELESS_ISR_VBLANK(); void LINK_WIRELESS_ISR_SERIAL(); From 2a52e88b8d4f3c8c93f7e6779e0adee7ef804d1b Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 25 Jan 2024 00:50:39 -0300 Subject: [PATCH 03/74] Adding LinkWirelessMultiboot_demo --- examples/LinkWirelessMultiboot_demo/Makefile | 289 ++++++++++++++++++ .../LinkWirelessMultiboot_demo/src/main.cpp | 45 +++ .../src/scenes/MultibootScene.cpp | 209 +++++++++++++ .../src/scenes/MultibootScene.h | 35 +++ .../src/utils/InputHandler.h | 39 +++ .../src/utils/SceneUtils.h | 52 ++++ examples/compile.sh | 5 + 7 files changed, 674 insertions(+) create mode 100644 examples/LinkWirelessMultiboot_demo/Makefile create mode 100644 examples/LinkWirelessMultiboot_demo/src/main.cpp create mode 100644 examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp create mode 100644 examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h create mode 100644 examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h create mode 100644 examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h diff --git a/examples/LinkWirelessMultiboot_demo/Makefile b/examples/LinkWirelessMultiboot_demo/Makefile new file mode 100644 index 0000000..9ad0b2f --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/Makefile @@ -0,0 +1,289 @@ +# === SETUP =========================================================== + +# --- No implicit rules --- +.SUFFIXES: + +# --- Paths --- +export TONCLIB := $(DEVKITPRO)/libtonc + +# === TONC RULES ====================================================== +# +# Yes, this is almost, but not quite, completely like to +# DKP's base_rules and gba_rules +# + +export PATH := $(DEVKITARM)/bin:$(PATH) + + +# --- Executable names --- + +PREFIX ?= arm-none-eabi- + +export CC := $(PREFIX)gcc +export CXX := $(PREFIX)g++ +export AS := $(PREFIX)as +export AR := $(PREFIX)ar +export NM := $(PREFIX)nm +export OBJCOPY := $(PREFIX)objcopy + +# LD defined in Makefile + + +# === LINK / TRANSLATE ================================================ + +%.gba : %.elf + @$(OBJCOPY) -O binary $< $@ + @echo built ... $(notdir $@) + @gbafix $@ -t$(TITLE) + +#---------------------------------------------------------------------- + +%.mb.elf : + @echo Linking multiboot + $(LD) -specs=gba_mb.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + $(NM) -Sn $@ > $(basename $(notdir $@)).map + +#---------------------------------------------------------------------- + +%.elf : + @echo Linking cartridge + $(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + $(NM) -Sn $@ > $(basename $(notdir $@)).map + +#---------------------------------------------------------------------- + +%.a : + @echo $(notdir $@) + @rm -f $@ + $(AR) -crs $@ $^ + + +# === OBJECTIFY ======================================================= + +%.iwram.o : %.iwram.cpp + @echo $(notdir $<) + $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(IARCH) -c $< -o $@ + +#---------------------------------------------------------------------- +%.iwram.o : %.iwram.c + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(IARCH) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.cpp + @echo $(notdir $<) + $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(RARCH) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.c + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(RARCH) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.s + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.S + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ + + +#---------------------------------------------------------------------- +# canned command sequence for binary data +#---------------------------------------------------------------------- + +define bin2o + bin2s $< | $(AS) -o $(@) + echo "extern const u8" `(echo $( `(echo $(> `(echo $(> `(echo $( $(BUILD)/$(TARGET).map + +all : $(BUILD) + +clean: + @echo clean ... + @rm -rf $(BUILD) $(TARGET).elf $(TARGET).gba $(TARGET).sav + +else # If we're here, we should be in the BUILD dir + +DEPENDS := $(OFILES:.o=.d) + +# --- Main targets ---- + +$(OUTPUT).gba : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +-include $(DEPENDS) + +endif # End BUILD switch + +# --- More targets ---------------------------------------------------- + +.PHONY: clean rebuild start + +rebuild: clean $(BUILD) + +start: + start "$(TARGET).gba" + +restart: rebuild start + +# EOF diff --git a/examples/LinkWirelessMultiboot_demo/src/main.cpp b/examples/LinkWirelessMultiboot_demo/src/main.cpp new file mode 100644 index 0000000..d5418d0 --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/main.cpp @@ -0,0 +1,45 @@ +#include +#include +#include "../../../lib/LinkRawWireless.hpp" +#include "../../_lib/interrupt.h" +#include "scenes/MultibootScene.h" +#include "utils/SceneUtils.h" + +void setUpInterrupts(); +void printTutorial(); +static std::shared_ptr engine{new GBAEngine()}; +static std::unique_ptr multibootScene{ + new MultibootScene(engine)}; + +LinkRawWireless* linkRawWireless = new LinkRawWireless(); + +int main() { + setUpInterrupts(); + + engine->setScene(multibootScene.get()); + + while (true) { + engine->update(); + + VBlankIntrWait(); + } + + return 0; +} + +inline void ISR_reset() { + RegisterRamReset(RESET_REG | RESET_VRAM); + SoftReset(); +} + +inline void setUpInterrupts() { + interrupt_init(); + + interrupt_set_handler(INTR_VBLANK, [] {}); + interrupt_enable(INTR_VBLANK); + + // A+B+START+SELECT + REG_KEYCNT = 0b1100000000001111; + interrupt_set_handler(INTR_KEYPAD, ISR_reset); + interrupt_enable(INTR_KEYPAD); +} diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp new file mode 100644 index 0000000..cccda8c --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -0,0 +1,209 @@ +#include "MultibootScene.h" + +#include +#include +#include +#include + +#include "../../../../lib/LinkRawWireless.hpp" +#include "utils/InputHandler.h" +#include "utils/SceneUtils.h" + +MultibootScene::MultibootScene(std::shared_ptr engine) + : Scene(engine) {} + +static std::unique_ptr aHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr bHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr upHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr downHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr lHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr rHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr selectHandler = + std::unique_ptr(new InputHandler()); + +static std::vector logLines; +static u32 currentLogLine = 0; +static bool useVerboseLog = true; + +#define MAX_LINES 20 +#define DRAW_LINE 0 + +void printScrollableText(u32 currentLine, + std::vector lines, + bool withCursor = false) { + for (u32 i = 0; i < MAX_LINES; i++) { + u32 lastLineIndex = MAX_LINES - 1; + u32 index = max(currentLine, lastLineIndex) - lastLineIndex + i; + if (index < lines.size()) { + std::string cursor = currentLine == index ? "> " : " "; + TextStream::instance().setText((withCursor ? cursor : "") + lines[index], + DRAW_LINE + i, -3); + } else { + TextStream::instance().setText(" ", + DRAW_LINE + i, -3); + } + } +} + +void print() { + printScrollableText(currentLogLine, logLines); +} + +void scrollBack() { + if (currentLogLine <= 0) + return; + currentLogLine--; + print(); +} + +void scrollForward() { + if (currentLogLine < MAX_LINES - 1) + currentLogLine = min(MAX_LINES - 1, logLines.size() - 1); + if (currentLogLine == logLines.size() - 1) + return; + currentLogLine++; + print(); +} + +void scrollPageUp() { + currentLogLine = max(currentLogLine - MAX_LINES, 0); + print(); +} + +void scrollPageDown() { + currentLogLine = min(currentLogLine + MAX_LINES, logLines.size() - 1); + print(); +} + +void scrollToTop() { + currentLogLine = 0; + print(); +} + +void scrollToBottom() { + currentLogLine = logLines.size() - 1; + print(); +} + +void clear() { + logLines.clear(); + currentLogLine = 0; + print(); +} + +void log(std::string string) { + logLines.push_back(string); + scrollPageDown(); +} + +std::vector MultibootScene::backgrounds() { + return {}; +} + +std::vector MultibootScene::sprites() { + std::vector sprites; + return sprites; +} + +void MultibootScene::load() { + SCENE_init(); + BACKGROUND_enable(true, false, false, false); + + linkRawWireless->logger = [](std::string string) { + if (useVerboseLog) + log(string); + }; + + log("---"); + log("LinkWirelessMultiboot demo"); + log(""); + log("A: send ROM"); + log("B: toggle log level"); + log("UP/DOWN: scroll up/down"); + log("L/R: scroll page up/down"); + log("UP+L/DOWN+R: scroll to top/bottom"); + log("SELECT: clear"); + log("---"); + log(""); + toggleLogLevel(); +} + +void MultibootScene::tick(u16 keys) { + if (engine->isTransitioning()) + return; + + processKeys(keys); + processButtons(); + + __qran_seed += keys; + __qran_seed += REG_RCNT; + __qran_seed += REG_SIOCNT; +} + +void MultibootScene::processKeys(u16 keys) { + aHandler->setIsPressed(keys & KEY_A); + bHandler->setIsPressed(keys & KEY_B); + upHandler->setIsPressed(keys & KEY_UP); + downHandler->setIsPressed(keys & KEY_DOWN); + lHandler->setIsPressed(keys & KEY_L); + rHandler->setIsPressed(keys & KEY_R); + selectHandler->setIsPressed(keys & KEY_SELECT); +} + +void MultibootScene::processButtons() { + if (bHandler->hasBeenPressedNow()) + toggleLogLevel(); + + if (aHandler->hasBeenPressedNow()) { + // TODO: IMPLEMENT + print(); + } + + if (lHandler->hasBeenPressedNow()) { + if (upHandler->getIsPressed()) + scrollToTop(); + else + scrollPageUp(); + } + + if (rHandler->hasBeenPressedNow()) { + if (downHandler->getIsPressed()) + scrollToBottom(); + else + scrollPageDown(); + } + + if (upHandler->getIsPressed()) + scrollBack(); + + if (downHandler->getIsPressed()) + scrollForward(); + + if (selectHandler->hasBeenPressedNow()) + clear(); +} + +void MultibootScene::toggleLogLevel() { + if (useVerboseLog) { + useVerboseLog = false; + log("! setting log level to NORMAL"); + } else { + useVerboseLog = true; + log("! setting log level to VERBOSE"); + } + log(""); +} + +void MultibootScene::logOperation(std::string name, + std::function operation) { + log("> " + name + "..."); + bool success = operation(); + log(success ? "< success :)" : "< failure :("); + log(""); +} diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h new file mode 100644 index 0000000..0c0cca4 --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h @@ -0,0 +1,35 @@ +#ifndef MULTIBOOT_SCENE_H +#define MULTIBOOT_SCENE_H + +#include +#include +#include +#include + +#include +#include + +class MultibootScene : public Scene { + public: + MultibootScene(std::shared_ptr engine); + + std::vector backgrounds() override; + std::vector sprites() override; + + void load() override; + void tick(u16 keys) override; + + private: + struct CommandMenuOption { + std::string name; + u8 command; + }; + int lastSelectedCommandIndex = 0; + + void processKeys(u16 keys); + void processButtons(); + void toggleLogLevel(); + void logOperation(std::string name, std::function operation); +}; + +#endif // MULTIBOOT_SCENE_H diff --git a/examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h b/examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h new file mode 100644 index 0000000..163e02e --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h @@ -0,0 +1,39 @@ +#ifndef INPUT_HANDLER_H +#define INPUT_HANDLER_H + +#include + +class InputHandler { + public: + InputHandler() { + this->isPressed = false; + this->isWaiting = true; + } + + inline bool getIsPressed() { return isPressed; } + + inline bool hasBeenPressedNow() { return isNewPressEvent; } + inline bool hasBeenReleasedNow() { return isNewReleaseEvent; } + + inline bool getHandledFlag() { return handledFlag; } + inline void setHandledFlag(bool value) { handledFlag = value; } + + inline void setIsPressed(bool isPressed) { + bool isNewPressEvent = !this->isWaiting && !this->isPressed && isPressed; + bool isNewReleaseEvent = !this->isWaiting && this->isPressed && !isPressed; + this->isPressed = isPressed; + this->isWaiting = this->isWaiting && isPressed; + + this->isNewPressEvent = isNewPressEvent; + this->isNewReleaseEvent = isNewReleaseEvent; + } + + protected: + bool isPressed = false; + bool isNewPressEvent = false; + bool isNewReleaseEvent = false; + bool handledFlag = false; + bool isWaiting = false; +}; + +#endif // INPUT_HANDLER_H \ No newline at end of file diff --git a/examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h b/examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h new file mode 100644 index 0000000..32258be --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h @@ -0,0 +1,52 @@ +#ifndef SCENE_UTILS_H +#define SCENE_UTILS_H + +#include +#include +#include +#include + +const u32 TEXT_MIDDLE_COL = 12; + +inline std::string asStr(u16 data) { + return std::to_string(data); +} + +inline void BACKGROUND_enable(bool bg0, bool bg1, bool bg2, bool bg3) { + REG_DISPCNT = bg0 ? REG_DISPCNT | DCNT_BG0 : REG_DISPCNT & ~DCNT_BG0; + REG_DISPCNT = bg1 ? REG_DISPCNT | DCNT_BG1 : REG_DISPCNT & ~DCNT_BG1; + REG_DISPCNT = bg2 ? REG_DISPCNT | DCNT_BG2 : REG_DISPCNT & ~DCNT_BG2; + REG_DISPCNT = bg3 ? REG_DISPCNT | DCNT_BG3 : REG_DISPCNT & ~DCNT_BG3; +} + +inline void SPRITE_disable() { + REG_DISPCNT = REG_DISPCNT & ~DCNT_OBJ; +} + +inline void SCENE_init() { + TextStream::instance().clear(); + TextStream::instance().scroll(0, 0); + TextStream::instance().setMosaic(false); + + BACKGROUND_enable(false, false, false, false); + SPRITE_disable(); +} + +inline void SCENE_write(std::string text, u32 row) { + TextStream::instance().setText(text, row, + TEXT_MIDDLE_COL - text.length() / 2); +} + +inline void SCENE_wait(u32 verticalLines) { + u32 count = 0; + u32 vCount = REG_VCOUNT; + + while (count < verticalLines) { + if (REG_VCOUNT != vCount) { + count++; + vCount = REG_VCOUNT; + } + }; +} + +#endif // SCENE_UTILS_H diff --git a/examples/compile.sh b/examples/compile.sh index 1877194..5e541b4 100644 --- a/examples/compile.sh +++ b/examples/compile.sh @@ -86,3 +86,8 @@ mv backup.gba LinkWireless_demo.gba sed -i -e "s/#define LINK_WIRELESS_PUT_ISR_IN_IWRAM/\/\/ #define LINK_WIRELESS_PUT_ISR_IN_IWRAM/g" ../../lib/LinkWireless.hpp sed -i -e "s/#define PROFILING_ENABLED/\/\/ #define PROFILING_ENABLED/g" ../../lib/LinkWireless.hpp cd .. + +cd LinkWirelessMultiboot_demo/ +make rebuild +cp LinkWirelessMultiboot_demo.gba ../ +cd .. From 4ed9ae21c57eeabf8ebecbf3c02f61512867acec Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 25 Jan 2024 01:08:14 -0300 Subject: [PATCH 04/74] Adding version number to all examples --- examples/LinkCableMultiboot_demo/src/main.cpp | 3 ++- examples/LinkCable_basic/src/main.cpp | 2 +- examples/LinkCable_full/src/main.cpp | 4 ++-- examples/LinkCable_stress/src/main.cpp | 4 ++-- examples/LinkGPIO_demo/src/main.cpp | 2 +- examples/LinkRawCable_demo/src/main.cpp | 2 +- examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp | 1 + examples/LinkSPI_demo/src/main.cpp | 2 +- examples/LinkUniversal_basic/src/main.cpp | 9 +++++---- .../src/scenes/MultibootScene.cpp | 1 + examples/LinkWireless_demo/src/main.cpp | 4 ++-- 11 files changed, 19 insertions(+), 15 deletions(-) diff --git a/examples/LinkCableMultiboot_demo/src/main.cpp b/examples/LinkCableMultiboot_demo/src/main.cpp index ee3f37b..691b951 100644 --- a/examples/LinkCableMultiboot_demo/src/main.cpp +++ b/examples/LinkCableMultiboot_demo/src/main.cpp @@ -49,7 +49,8 @@ int main() { // Sender options if (isSenderMode) { if (result != LinkCableMultiboot::Result::SUCCESS) - log("Press START to send the ROM...\nPress B to set client mode..."); + log("LinkCableMultiboot_demo\n (v6.2.0)\n\nPress START to send the " + "ROM...\nPress B to set client mode..."); if (keys & KEY_START) { log("Sending... (SELECT to cancel)"); diff --git a/examples/LinkCable_basic/src/main.cpp b/examples/LinkCable_basic/src/main.cpp index 187d368..09e4ec8 100644 --- a/examples/LinkCable_basic/src/main.cpp +++ b/examples/LinkCable_basic/src/main.cpp @@ -45,7 +45,7 @@ int main() { u16 keys = ~REG_KEYS & KEY_ANY; linkCable->send(keys + 1); // (avoid using 0) - std::string output = ""; + std::string output = "LinkCable_basic (v6.2.0)\n\n"; if (linkCable->isConnected()) { u8 playerCount = linkCable->playerCount(); u8 currentPlayerId = linkCable->currentPlayerId(); diff --git a/examples/LinkCable_full/src/main.cpp b/examples/LinkCable_full/src/main.cpp index 5a26bd8..be4d5ef 100644 --- a/examples/LinkCable_full/src/main.cpp +++ b/examples/LinkCable_full/src/main.cpp @@ -93,10 +93,10 @@ inline void setUpInterrupts() { void printTutorial() { #ifndef USE_LINK_UNIVERSAL - DEBULOG("LinkCable demo"); + DEBULOG("LinkCable_full (v6.2.0)"); #endif #ifdef USE_LINK_UNIVERSAL - DEBULOG("LinkUniversal demo"); + DEBULOG("LinkUniversal_full (v6.2.0)"); #endif DEBULOG(""); diff --git a/examples/LinkCable_stress/src/main.cpp b/examples/LinkCable_stress/src/main.cpp index 618d4c2..0af26d6 100644 --- a/examples/LinkCable_stress/src/main.cpp +++ b/examples/LinkCable_stress/src/main.cpp @@ -89,10 +89,10 @@ int main() { while (true) { #ifndef USE_LINK_UNIVERSAL - std::string output = "LinkCable\n\n"; + std::string output = "LinkCable_stress (v6.2.0)\n\n"; #endif #ifdef USE_LINK_UNIVERSAL - std::string output = "LinkUniversal\n\n"; + std::string output = "LinkUniversal_stress (v6.2.0)\n\n"; #endif link->deactivate(); diff --git a/examples/LinkGPIO_demo/src/main.cpp b/examples/LinkGPIO_demo/src/main.cpp index b6cfb34..ea017d3 100644 --- a/examples/LinkGPIO_demo/src/main.cpp +++ b/examples/LinkGPIO_demo/src/main.cpp @@ -27,7 +27,7 @@ int main() { while (true) { // (3) Use the pins - std::string output = ""; + std::string output = "LinkGPIO_demo (v6.2.0)\n\n"; // Commands u16 keys = ~REG_KEYS & KEY_ANY; diff --git a/examples/LinkRawCable_demo/src/main.cpp b/examples/LinkRawCable_demo/src/main.cpp index 32cf3a8..c21b2e5 100644 --- a/examples/LinkRawCable_demo/src/main.cpp +++ b/examples/LinkRawCable_demo/src/main.cpp @@ -33,7 +33,7 @@ int main() { u16 prevKeys = 0; while (true) { - std::string output = ""; + std::string output = "LinkRawCable_demo (v6.2.0)\n\n"; u16 keys = ~REG_KEYS & KEY_ANY; if (!linkRawCable->isActive()) { diff --git a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp index 1c7883c..c5e9e55 100644 --- a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp +++ b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp @@ -131,6 +131,7 @@ void DebugScene::load() { log("---"); log("LinkRawWireless demo"); + log(" (v6.2.0)"); log(""); log("START: reset wireless adapter"); log("A: send command"); diff --git a/examples/LinkSPI_demo/src/main.cpp b/examples/LinkSPI_demo/src/main.cpp index 00f8873..f5bb161 100644 --- a/examples/LinkSPI_demo/src/main.cpp +++ b/examples/LinkSPI_demo/src/main.cpp @@ -32,7 +32,7 @@ int main() { u32 counter = 0; while (true) { - std::string output = ""; + std::string output = "LinkSPI_demo (v6.2.0)\n\n"; u16 keys = ~REG_KEYS & KEY_ANY; if (!linkSPI->isActive()) { diff --git a/examples/LinkUniversal_basic/src/main.cpp b/examples/LinkUniversal_basic/src/main.cpp index 4c48408..9663030 100644 --- a/examples/LinkUniversal_basic/src/main.cpp +++ b/examples/LinkUniversal_basic/src/main.cpp @@ -21,10 +21,11 @@ void init() { int main() { init(); - log("Press A to start\n\n\n\n\nhold LEFT on start:\n -> force " - "cable\n\nhold RIGHT on start:\n -> force wireless\n\nhold UP on " - "start:\n -> force wireless server\n\nhold DOWN on start:\n -> force " - "wireless client\n\nhold B on start:\n -> set 2 players (wireless)"); + log("LinkUniversal_basic (v6.2.0)\n\n\nPress A to start\n\n\nhold LEFT on " + "start:\n -> force cable\n\nhold RIGHT on start:\n -> force " + "wireless\n\nhold UP on start:\n -> force wireless server\n\nhold DOWN " + "on start:\n -> force wireless client\n\nhold B on start:\n -> set 2 " + "players (wireless)"); waitFor(KEY_A); u16 initialKeys = ~REG_KEYS & KEY_ANY; bool forceCable = initialKeys & KEY_LEFT; diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index cccda8c..38e58bc 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -122,6 +122,7 @@ void MultibootScene::load() { log("---"); log("LinkWirelessMultiboot demo"); + log(" (v6.2.0)"); log(""); log("A: send ROM"); log("B: toggle log level"); diff --git a/examples/LinkWireless_demo/src/main.cpp b/examples/LinkWireless_demo/src/main.cpp index e222805..aedccbd 100644 --- a/examples/LinkWireless_demo/src/main.cpp +++ b/examples/LinkWireless_demo/src/main.cpp @@ -47,8 +47,8 @@ int main() { start: // Options - log("Press A to start\n\n\n\n\n\n\n\n\nhold LEFT on start:\n -> " - "disable forwarding\n\nhold UP on start:\n -> disable " + log("LinkWireless_demo (v6.2.0)\n\n\n\nPress A to start\n\n\n\n\nhold LEFT " + "on start:\n -> disable forwarding\n\nhold UP on start:\n -> disable " "retransmission\n\nhold B on start:\n -> set 2 players\n\nhold START on " "start:\n -> async ACK"); waitFor(KEY_A); From c5301dd96cc0da3779423321644f9667d00cded4 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 25 Jan 2024 06:22:05 -0300 Subject: [PATCH 05/74] Adding LinkWirelessMultiboot class --- README.md | 17 ++++++++++- lib/LinkWirelessMultiboot.hpp | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 lib/LinkWirelessMultiboot.hpp diff --git a/README.md b/README.md index 75d1d82..8549d15 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port. - [πŸ”—](#-LinkSPI) [LinkSPI.hpp](lib/LinkSPI.hpp): Connect with a PC (like a **Raspberry Pi**) or another GBA (with a GBC Link Cable) using this mode. Transfer up to 2Mbit/s! - [πŸ“»](#-LinkWireless) [LinkWireless.hpp](lib/LinkWireless.hpp): Connect up to 5 consoles with the **Wireless Adapter**! - [πŸ”§πŸ“»](#-LinkRawWireless) [LinkRawWireless.hpp](lib/LinkRawWireless.hpp): A **minimal** low-level API for the Wireless Adapter. -- [🌎](#-LinkUniversal) [LinkUniversal.hpp](lib/LinkUniversal.hpp): Add multiplayer support to your game, both with πŸ‘Ύ *Link Cables* and πŸ“» *Wireless Adapters*, using the **same API**. +- [πŸ“‘](#-LinkWirelessMultiboot) [LinkWirelessMultiboot.hpp](lib/LinkWirelessMultiboot.hpp): ‍Send **Multiboot software** (small 256KiB ROMs) to other GBAs **over the air**! +- [🌎](#-LinkUniversal) [LinkUniversal.hpp](lib/LinkUniversal.hpp): Add multiplayer support to your game, both with πŸ‘Ύ *Link Cables* and πŸ“» *Wireless Adapters*, using the **same API**! *(click on the emojis for documentation)* @@ -215,6 +216,20 @@ Name | Return type | Description ⚠️ `0xFFFF` is a reserved value, so don't send it! +# πŸ’» LinkWirelessMultiboot + +*(aka Multiboot through Wireless Adapter)* + +This tool allows sending Multiboot ROMs (small 256KiB programs that fit in EWRAM) from one GBA to up to 4 slaves, wirelessly, using a single cartridge. + +// TODO: Photo + +## Methods + +Name | Return type | Description +--- | --- | --- +`sendRom(rom, romSize, cancel)` | **LinkWirelessMultiboot::Result** | Sends the `rom`. During the handshake process, the library will continuously invoke `cancel`, and abort the transfer if it returns `true`. The `romSize` must be a number between `448` and `262144`, and a multiple of `16`. Once completed, the return value should be `LinkWirelessMultiboot::Result::SUCCESS`. + # 🌎 LinkUniversal A multiuse library that doesn't care whether you plug a Link Cable or a Wireless Adapter. It continuously switches between both and tries to connect to other peers, supporting the hot swapping of cables and adapters and all the features from [πŸ‘Ύ LinkCable](#-LinkCable) and [πŸ“» LinkWireless](#-LinkWireless). diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp new file mode 100644 index 0000000..f537be7 --- /dev/null +++ b/lib/LinkWirelessMultiboot.hpp @@ -0,0 +1,57 @@ +#ifndef LINK_WIRELESS_MULTIBOOT_H +#define LINK_WIRELESS_MULTIBOOT_H + +// -------------------------------------------------------------------------- +// A Wireless Multiboot tool to send small ROMs from a GBA to up to 4 slaves. +// -------------------------------------------------------------------------- +// Usage: +// - 1) Include this header in your main.cpp file and add: +// LinkWirelessMultiboot* linkWirelessMultiboot = +// new LinkWirelessMultiboot(); +// - 2) Send the ROM: +// LinkWirelessMultiboot::Result result = linkWirelessMultiboot->sendRom( +// romBytes, // for current ROM, use: ((const void*)MEM_EWRAM) +// romLength, // should be multiple of 0x10 +// []() { +// u16 keys = ~REG_KEYS & KEY_ANY; +// return keys & KEY_START; +// // (when this returns true, transfer will be canceled) +// } +// ); +// // `result` should be LinkWirelessMultiboot::Result::SUCCESS +// -------------------------------------------------------------------------- + +#include +#include "LinkRawWireless.hpp" + +#define LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE (0x100 + 0xc0) +#define LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE (256 * 1024) +#define LINK_WIRELESS_MULTIBOOT_HEADER_SIZE 0xC0 + +static volatile char LINK_WIRELESS_MULTIBOOT_VERSION[] = + "LinkWirelessMultiboot/v6.2.0"; + +class LinkWirelessMultiboot { + public: + enum Result { SUCCESS, INVALID_SIZE, CANCELED, FAILURE }; + + template + Result sendRom(const void* rom, u32 romSize, F cancel) { + // if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) + // return INVALID_SIZE; + // if (romSize > LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE) + // return INVALID_SIZE; + // if ((romSize % 0x10) != 0) + // return INVALID_SIZE; + + // TODO: IMPLEMENT + + return SUCCESS; + } + + private: +}; + +extern LinkWirelessMultiboot* linkWirelessMultiboot; + +#endif // LINK_WIRELESS_MULTIBOOT_H From b0911d782b5b0001436521bf1113aa5b3ff16432 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 26 Jan 2024 06:37:09 -0300 Subject: [PATCH 06/74] Adding GBFS to LinkWirelessMultiboot_demo --- examples/LinkWirelessMultiboot_demo/Makefile | 12 +- .../scripts/package.sh | 56 +++++++ .../src/scenes/MultibootScene.cpp | 18 ++ .../src/utils/gbfs/gbfs.h | 80 +++++++++ .../src/utils/gbfs/libgbfs.c | 155 ++++++++++++++++++ examples/compile.sh | 2 +- 6 files changed, 318 insertions(+), 5 deletions(-) create mode 100644 examples/LinkWirelessMultiboot_demo/scripts/package.sh create mode 100644 examples/LinkWirelessMultiboot_demo/src/utils/gbfs/gbfs.h create mode 100644 examples/LinkWirelessMultiboot_demo/src/utils/gbfs/libgbfs.c diff --git a/examples/LinkWirelessMultiboot_demo/Makefile b/examples/LinkWirelessMultiboot_demo/Makefile index 9ad0b2f..5b2ffc2 100644 --- a/examples/LinkWirelessMultiboot_demo/Makefile +++ b/examples/LinkWirelessMultiboot_demo/Makefile @@ -125,7 +125,8 @@ LIBS := -ltonc -lugba -lgba-sprite-engine BUILD := build SRCDIRS := src ../_lib ../../lib \ src/scenes \ - src/utils + src/utils \ + src/utils/gbfs DATADIRS := INCDIRS := src @@ -279,10 +280,13 @@ endif # End BUILD switch .PHONY: clean rebuild start -rebuild: clean $(BUILD) +rebuild: clean $(BUILD) package -start: - start "$(TARGET).gba" +package: $(BUILD) + ./scripts/package.sh + +start: package + start "$(TARGET).out.gba" restart: rebuild start diff --git a/examples/LinkWirelessMultiboot_demo/scripts/package.sh b/examples/LinkWirelessMultiboot_demo/scripts/package.sh new file mode 100644 index 0000000..cf6c2e6 --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/scripts/package.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +FILE_INPUT="LinkWirelessMultiboot_demo.gba" +FILE_TMP="LinkWirelessMultiboot_demo.tmp.gba" +FILE_OUTPUT="LinkWirelessMultiboot_demo.out.gba" +DATA="content.gbfs" + +if [ ! -f "$DATA" ]; then + echo "" + echo "The file $DATA does not exist. Run make import first!" + echo "" + exit 1 +fi + +KB=$((1024)) +MAX_ROM_SIZE_KB=$((32 * $KB - 1)) +INITIAL_REQUIRED_SIZE_KB=256 + +ROM_SIZE=$(wc -c < $FILE_INPUT) +if [ $? -ne 0 ]; then + exit 1 +fi +GBFS_SIZE=$(wc -c < $DATA) +if [ $? -ne 0 ]; then + exit 1 +fi +ROM_SIZE_KB=$(($ROM_SIZE / $KB)) +GBFS_SIZE_KB=$(($GBFS_SIZE / $KB)) +MAX_REQUIRED_SIZE_KB=$(($MAX_ROM_SIZE_KB - $GBFS_SIZE_KB)) +if (( $MAX_REQUIRED_SIZE_KB < $ROM_SIZE_KB )); then + echo "" + echo "[!] ERROR:" + echo "GBFS file too big." + echo "" + echo "GBFS_SIZE_KB=$GBFS_SIZE_KB" + echo "ROM_SIZE_KB=$ROM_SIZE_KB" + echo "(MAX_ROM_SIZE_KB=$MAX_ROM_SIZE_KB)" + echo "" + exit 1 +fi +REQUIRED_SIZE_KB=$(($INITIAL_REQUIRED_SIZE_KB > $MAX_REQUIRED_SIZE_KB ? $MAX_REQUIRED_SIZE_KB : $INITIAL_REQUIRED_SIZE_KB)) +PAD_NEEDED=$((($REQUIRED_SIZE_KB * $KB) - $ROM_SIZE)) + +cp $FILE_INPUT $FILE_TMP +if [ $? -ne 0 ]; then + exit 1 +fi +dd if=/dev/zero bs=1 count=$PAD_NEEDED >> $FILE_TMP +if [ $? -ne 0 ]; then + exit 1 +fi +cat $FILE_TMP $DATA > $FILE_OUTPUT +if [ $? -ne 0 ]; then + exit 1 +fi +rm $FILE_TMP diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index 38e58bc..f2ce6f4 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -9,6 +9,14 @@ #include "utils/InputHandler.h" #include "utils/SceneUtils.h" +extern "C" { +#include "utils/gbfs/gbfs.h" +} + +static const GBFS_FILE* fs = find_first_gbfs_file(0); + +const char* ROM_FILE_NAME = "rom-to-transfer.gba"; + MultibootScene::MultibootScene(std::shared_ptr engine) : Scene(engine) {} @@ -124,6 +132,16 @@ void MultibootScene::load() { log("LinkWirelessMultiboot demo"); log(" (v6.2.0)"); log(""); + if (fs == NULL) { + log("! GBFS file not found"); + while (true) + ; + } else if (gbfs_get_obj(fs, ROM_FILE_NAME, NULL) == NULL) { + log("! File not found in GBFS:"); + log(" " + std::string(ROM_FILE_NAME)); + while (true) + ; + } log("A: send ROM"); log("B: toggle log level"); log("UP/DOWN: scroll up/down"); diff --git a/examples/LinkWirelessMultiboot_demo/src/utils/gbfs/gbfs.h b/examples/LinkWirelessMultiboot_demo/src/utils/gbfs/gbfs.h new file mode 100644 index 0000000..645de5a --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/utils/gbfs/gbfs.h @@ -0,0 +1,80 @@ +/* gbfs.h + access object in a GBFS file + +Copyright 2002 Damian Yerrick + +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. + +*/ + +/* Dependency on prior include files + +Before you #include "gbfs.h", you should define the following types: + typedef (unsigned 16-bit integer) u16; + typedef (unsigned 32-bit integer) u32; +Your gba.h should do this for you. +*/ + +#ifndef INCLUDE_GBFS_H +#define INCLUDE_GBFS_H + +#pragma GCC system_header + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* to make a 300 KB space called samples do GBFS_SPACE(samples, 300) */ + +#define GBFS_SPACE(filename, kbytes) \ + const char filename[(kbytes) * 1024] __attribute__((aligned(16))) = \ + "PinEightGBFSSpace-" #filename "-" #kbytes; + +typedef struct GBFS_FILE { + char magic[16]; /* "PinEightGBFS\r\n\032\n" */ + u32 total_len; /* total length of archive */ + u16 dir_off; /* offset in bytes to directory */ + u16 dir_nmemb; /* number of files */ + char reserved[8]; /* for future use */ +} GBFS_FILE; + +typedef struct GBFS_ENTRY { + char name[24]; /* filename, nul-padded */ + u32 len; /* length of object in bytes */ + u32 data_offset; /* in bytes from beginning of file */ +} GBFS_ENTRY; + +const GBFS_FILE* find_first_gbfs_file(const void* start); +const void* skip_gbfs_file(const GBFS_FILE* file); +const void* gbfs_get_obj(const GBFS_FILE* file, const char* name, u32* len); +const void* gbfs_get_nth_obj(const GBFS_FILE* file, + size_t n, + char* name, + u32* len); +void* gbfs_copy_obj(void* dst, const GBFS_FILE* file, const char* name); +size_t gbfs_count_objs(const GBFS_FILE* file); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/examples/LinkWirelessMultiboot_demo/src/utils/gbfs/libgbfs.c b/examples/LinkWirelessMultiboot_demo/src/utils/gbfs/libgbfs.c new file mode 100644 index 0000000..a4c71d2 --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/utils/gbfs/libgbfs.c @@ -0,0 +1,155 @@ +#pragma GCC diagnostic ignored "-Wstringop-truncation" + +/* libgbfs.c + access object in a GBFS file + +Copyright 2002-2004 Damian Yerrick + +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 code assumes a LITTLE ENDIAN target. It'll need a boatload + of itohs and itohl calls if converted to run on Sega Genesis. It + also assumes that the target uses 16-bit short and 32-bit longs. +*/ + +typedef unsigned short u16; +typedef unsigned long u32; + +#include +#include + +#include "gbfs.h" + +/* change this to the end of your ROM, or to 0x02040000 for multiboot */ +#define GBFS_1ST_SEARCH_LIMIT ((const u32*)0x02040000) +#define GBFS_2ND_SEARCH_START ((const u32*)0x08000000) +#define GBFS_2ND_SEARCH_LIMIT ((const u32*)0x0a000000) + +/* a power of two, less than or equal to the argument passed to + padbin. Increasing the stride makes find_first_gbfs_file() + faster at the cost of a slightly larger binary. */ +#define GBFS_ALIGNMENT 256 + +const GBFS_FILE* find_first_gbfs_file(const void* start) { + /* align the pointer */ + const u32* here = (const u32*)((unsigned long)start & (-GBFS_ALIGNMENT)); + const char rest_of_magic[] = "ightGBFS\r\n\x1a\n"; + + /* Linear-search first in multiboot space. */ + while (here < GBFS_1ST_SEARCH_LIMIT) { + /* We have to keep the magic code in two pieces; otherwise, + this function may find itself and think it's a GBFS file. + This obviously won't work if your compiler stores this + numeric literal just before the literal string, but Devkit + Advance R4 and R5 seem to keep numeric constant pools + separate enough from string pools for this to work. + */ + if (*here == 0x456e6950) /* ASCII code for little endian "PinE" */ + { + /* We've matched the first four bytes. + If the rest of the magic matches, then we've found a file. */ + if (!memcmp(here + 1, rest_of_magic, 12)) + return (const GBFS_FILE*)here; + } + here += GBFS_ALIGNMENT / sizeof(*here); + } + + /* Now search in ROM space. */ + if (here < GBFS_2ND_SEARCH_START) + here = GBFS_2ND_SEARCH_START; + while (here < GBFS_2ND_SEARCH_LIMIT) { + /* Search loop same as above. */ + if (*here == 0x456e6950) /* ASCII code for little endian "PinE" */ + { + if (!memcmp(here + 1, rest_of_magic, 12)) + return (const GBFS_FILE*)here; + } + here += GBFS_ALIGNMENT / sizeof(*here); + } + return 0; +} + +const void* skip_gbfs_file(const GBFS_FILE* file) { + return ((char*)file + file->total_len); +} + +static int namecmp(const void* a, const void* b) { + return memcmp(a, b, 24); +} + +const void* gbfs_get_obj(const GBFS_FILE* file, const char* name, u32* len) { + char key[24] = {0}; + + const GBFS_ENTRY* dirbase = + (const GBFS_ENTRY*)((const char*)file + file->dir_off); + size_t n_entries = file->dir_nmemb; + const GBFS_ENTRY* here; + + strncpy(key, name, 24); + + here = bsearch(key, dirbase, n_entries, sizeof(GBFS_ENTRY), namecmp); + if (!here) + return NULL; + + if (len) + *len = here->len; + return (char*)file + here->data_offset; +} + +const void* gbfs_get_nth_obj(const GBFS_FILE* file, + size_t n, + char* name, + u32* len) { + const GBFS_ENTRY* dirbase = + (const GBFS_ENTRY*)((const char*)file + file->dir_off); + size_t n_entries = file->dir_nmemb; + const GBFS_ENTRY* here = dirbase + n; + + if (n >= n_entries) + return NULL; + + if (name) { + strncpy(name, here->name, 24); + name[24] = 0; + } + + if (len) + *len = here->len; + + return (char*)file + here->data_offset; +} + +void* gbfs_copy_obj(void* dst, const GBFS_FILE* file, const char* name) { + u32 len; + const void* src = gbfs_get_obj(file, name, &len); + + if (!src) + return NULL; + + memcpy(dst, src, len); + return dst; +} + +size_t gbfs_count_objs(const GBFS_FILE* file) { + return file ? file->dir_nmemb : 0; +} diff --git a/examples/compile.sh b/examples/compile.sh index 5e541b4..a997518 100644 --- a/examples/compile.sh +++ b/examples/compile.sh @@ -89,5 +89,5 @@ cd .. cd LinkWirelessMultiboot_demo/ make rebuild -cp LinkWirelessMultiboot_demo.gba ../ +cp LinkWirelessMultiboot_demo.out.gba ../LinkWirelessMultiboot_demo.gba cd .. From 2a998b1edfbc4cfabab393e12ff385870df7450b Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 26 Jan 2024 07:17:10 -0300 Subject: [PATCH 07/74] Starting wireless multiboot draft --- .../LinkWirelessMultiboot_demo/src/main.cpp | 4 +- .../src/scenes/MultibootScene.cpp | 14 +- lib/LinkSPI.hpp | 3 +- lib/LinkWirelessMultiboot.hpp | 189 +++++++++++++++++- 4 files changed, 202 insertions(+), 8 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/main.cpp b/examples/LinkWirelessMultiboot_demo/src/main.cpp index d5418d0..8097d3e 100644 --- a/examples/LinkWirelessMultiboot_demo/src/main.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/main.cpp @@ -1,6 +1,6 @@ #include #include -#include "../../../lib/LinkRawWireless.hpp" +#include "../../../lib/LinkWirelessMultiboot.hpp" #include "../../_lib/interrupt.h" #include "scenes/MultibootScene.h" #include "utils/SceneUtils.h" @@ -11,7 +11,7 @@ static std::shared_ptr engine{new GBAEngine()}; static std::unique_ptr multibootScene{ new MultibootScene(engine)}; -LinkRawWireless* linkRawWireless = new LinkRawWireless(); +LinkWirelessMultiboot* linkWirelessMultiboot = new LinkWirelessMultiboot(); int main() { setUpInterrupts(); diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index f2ce6f4..b5e586e 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -5,7 +5,7 @@ #include #include -#include "../../../../lib/LinkRawWireless.hpp" +#include "../../../../lib/LinkWirelessMultiboot.hpp" #include "utils/InputHandler.h" #include "utils/SceneUtils.h" @@ -123,7 +123,11 @@ void MultibootScene::load() { SCENE_init(); BACKGROUND_enable(true, false, false, false); - linkRawWireless->logger = [](std::string string) { + linkWirelessMultiboot->logger = [](std::string string) { + // if (useVerboseLog) // TODO: RESTORE + log(string); + }; + linkWirelessMultiboot->link->logger = [](std::string string) { if (useVerboseLog) log(string); }; @@ -180,7 +184,11 @@ void MultibootScene::processButtons() { toggleLogLevel(); if (aHandler->hasBeenPressedNow()) { - // TODO: IMPLEMENT + u32 fileLength; + const void* romToSend = gbfs_get_obj(fs, ROM_FILE_NAME, &fileLength); + + linkWirelessMultiboot->sendRom(romToSend, fileLength, + []() { return false; }); print(); } diff --git a/lib/LinkSPI.hpp b/lib/LinkSPI.hpp index 0fb79b6..f2b7ef5 100644 --- a/lib/LinkSPI.hpp +++ b/lib/LinkSPI.hpp @@ -116,8 +116,6 @@ class LinkSPI { setInterruptsOff(); } - enableTransfer(); - while (isMaster() && waitMode && !isSlaveReady()) if (cancel()) { disableTransfer(); @@ -126,6 +124,7 @@ class LinkSPI { return LINK_SPI_NO_DATA; } + enableTransfer(); // TODO: TEST THIS (moved) startTransfer(); if (_async) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index f537be7..bacc93a 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -32,9 +32,38 @@ static volatile char LINK_WIRELESS_MULTIBOOT_VERSION[] = "LinkWirelessMultiboot/v6.2.0"; class LinkWirelessMultiboot { + typedef void (*Logger)(std::string); + public: + Logger logger = [](std::string str) {}; enum Result { SUCCESS, INVALID_SIZE, CANCELED, FAILURE }; + struct ServerSDKHeader { + unsigned int payloadSize : 7; + unsigned int _unused_ : 2; + unsigned int phase : 2; + unsigned int n : 2; + unsigned int isACK : 1; + unsigned int slotState : 4; + unsigned int targetSlots : 4; + }; + union ServerSDKHeaderSerializer { + ServerSDKHeader asStruct; + u32 asInt; + }; + + struct ClientSDKHeader { + unsigned int payloadSize : 5; + unsigned int phase : 2; + unsigned int n : 2; + unsigned int isACK : 1; + unsigned int slotState : 4; + }; + union ClientSDKHeaderSerializer { + ClientSDKHeader asStruct; + u16 asInt; + }; + template Result sendRom(const void* rom, u32 romSize, F cancel) { // if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) @@ -44,12 +73,170 @@ class LinkWirelessMultiboot { // if ((romSize % 0x10) != 0) // return INVALID_SIZE; - // TODO: IMPLEMENT + bool success = true; + success = link->activate(); + if (!success) { + logger("cannot activate"); + return FAILURE; + } + logger("activated"); + + success = link->sendCommand(LINK_RAW_WIRELESS_COMMAND_SETUP, + std::vector{0x003F0120}) + .success; // TODO: IMPLEMENT SETUP + if (!success) { + logger("setup failed"); + return FAILURE; + } + logger("setup ok"); + + success = link->broadcast("Multi", "Test", 0b1111111111111111); + if (!success) { + logger("broadcast failed"); + return FAILURE; + } + logger("broadcast set"); + + success = link->startHost(); + if (!success) { + logger("start host failed"); + return FAILURE; + } + logger("host started"); + + LinkRawWireless::AcceptConnectionsResponse acceptResponse; + while (link->playerCount() == 1) { + link->acceptConnections(acceptResponse); + } + + logger("connected!"); + linkRawWireless->wait(228 * 20); // ~300ms + + bool hasData = false; + LinkRawWireless::ReceiveDataResponse response; + while (!hasData) { + if (!sendAndExpectData(std::vector{}, 1, response)) + return FAILURE; + hasData = response.data.size() > 0; + } + + logger("data received"); + ClientSDKHeader clientHeader = parseClientHeader(response.data[0]); + logger("client size: " + std::to_string(clientHeader.payloadSize)); + logger("n: " + std::to_string(clientHeader.n)); + logger("phase: " + std::to_string(clientHeader.phase)); + logger("ack: " + std::to_string(clientHeader.isACK)); + logger("slotState:" + std::to_string(clientHeader.slotState)); + + logger("sending ACK"); + firstack: + ServerSDKHeader serverHeader; + serverHeader = createACKFor(clientHeader); + u32 sndHeader = serializeServerHeader(serverHeader); + + if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + return FAILURE; + + if (response.data.size() == 0) { + goto firstack; + } + clientHeader = parseClientHeader(response.data[0]); + if (clientHeader.n == 1) + goto firstack; + + if (clientHeader.n == 2 && clientHeader.slotState == 1) { + logger("N IS NOW 2, slotstate = 1"); + } else { + logger("Error: weird packet"); + return FAILURE; + } + + secondack: + serverHeader = createACKFor(clientHeader); + sndHeader = serializeServerHeader(serverHeader); + if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + return FAILURE; + + if (response.data.size() == 0) { + goto secondack; + } + clientHeader = parseClientHeader(response.data[0]); + if (clientHeader.n == 2 && clientHeader.slotState == 1) + goto secondack; + + if (clientHeader.n == 1 && clientHeader.slotState == 2) { + logger("NI STARTED"); + } else { + logger("NI DIDN'T START"); + return FAILURE; + } + + while (clientHeader.slotState > 0) { + link->wait(228); + serverHeader = createACKFor(clientHeader); + sndHeader = serializeServerHeader(serverHeader); + if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + return FAILURE; + + clientHeader = parseClientHeader(response.data[0]); + } + + logger("slotState IS NOW 0"); return SUCCESS; } + LinkRawWireless* link = new LinkRawWireless(); + + ClientSDKHeader lastACK; + private: + bool sendAndExpectData(std::vector data, + u32 _bytes, + LinkRawWireless::ReceiveDataResponse& response) { + LinkRawWireless::RemoteCommand remoteCommand; + bool success = false; + success = link->sendDataAndWait(data, remoteCommand, _bytes); + if (!success) { + logger("senddatawait no"); + return false; + } + if (remoteCommand.commandId != 0x28) { + logger("expected response 0x28"); + logger("but got " + link->toHex(remoteCommand.commandId)); + return false; + } + success = link->receiveData(response); + if (!success) { + logger("receive data failed"); + return false; + } + return true; + } + + ServerSDKHeader createACKFor(ClientSDKHeader clientHeader) { + ServerSDKHeader serverHeader; + serverHeader.isACK = 1; + serverHeader.targetSlots = 0b0001; // TODO: Implement + serverHeader.payloadSize = 0; + serverHeader.n = clientHeader.n; + serverHeader.phase = clientHeader.phase; + serverHeader.slotState = clientHeader.slotState; + + return serverHeader; + } + + ClientSDKHeader parseClientHeader(u32 clientHeaderInt) { + ClientSDKHeaderSerializer clientSerializer; + clientSerializer.asInt = clientHeaderInt; + return clientSerializer.asStruct; + } + + u32 serializeServerHeader(ServerSDKHeader serverHeader) { + ServerSDKHeaderSerializer serverSerializer; + serverSerializer.asStruct = serverHeader; + return serverSerializer.asInt; + } }; extern LinkWirelessMultiboot* linkWirelessMultiboot; From 9b8c9388ff6aa690c9389195b1d07175159a25e2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 28 Jan 2024 08:30:06 -0300 Subject: [PATCH 08/74] Reaching the 'DOWNLOADING' point --- lib/LinkWirelessMultiboot.hpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index bacc93a..73ac954 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -112,6 +112,8 @@ class LinkWirelessMultiboot { logger("connected!"); linkRawWireless->wait(228 * 20); // ~300ms + // HANDSHAKE + bool hasData = false; LinkRawWireless::ReceiveDataResponse response; while (!hasData) { @@ -183,6 +185,30 @@ class LinkWirelessMultiboot { logger("slotState IS NOW 0"); + // ROM START COMMAND + bool didClientRespond = false; + while (!didClientRespond) { + link->wait(228); + + ServerSDKHeader serverHeader; + serverHeader.isACK = 0; + serverHeader.targetSlots = 0b0001; // TODO: Implement + serverHeader.payloadSize = 7; + serverHeader.n = 1; + serverHeader.phase = 0; + serverHeader.slotState = 1; + sndHeader = serializeServerHeader(serverHeader); + if (!sendAndExpectData(std::vector{sndHeader, 0x54, 0x02}, 10, + response)) + return FAILURE; + clientHeader = parseClientHeader(response.data[0]); + if (clientHeader.isACK == 1 && clientHeader.n == 1 && + clientHeader.phase == 0 && clientHeader.slotState == 1) + didClientRespond = true; + } + + logger("READY TO SEND ROM!"); + return SUCCESS; } From 845de04dc1c5404a9056c820421d500a350020a0 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 28 Jan 2024 09:08:09 -0300 Subject: [PATCH 09/74] Starting ROM send code --- .../src/scenes/MultibootScene.cpp | 3 +- lib/LinkWirelessMultiboot.hpp | 90 ++++++++++++++++++- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index b5e586e..ae3a9b8 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -185,7 +185,8 @@ void MultibootScene::processButtons() { if (aHandler->hasBeenPressedNow()) { u32 fileLength; - const void* romToSend = gbfs_get_obj(fs, ROM_FILE_NAME, &fileLength); + const u8* romToSend = + (const u8*)gbfs_get_obj(fs, ROM_FILE_NAME, &fileLength); linkWirelessMultiboot->sendRom(romToSend, fileLength, []() { return false; }); diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 73ac954..df4184d 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -65,7 +65,7 @@ class LinkWirelessMultiboot { }; template - Result sendRom(const void* rom, u32 romSize, F cancel) { + Result sendRom(const u8* rom, u32 romSize, F cancel) { // if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) // return INVALID_SIZE; // if (romSize > LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE) @@ -190,7 +190,6 @@ class LinkWirelessMultiboot { while (!didClientRespond) { link->wait(228); - ServerSDKHeader serverHeader; serverHeader.isACK = 0; serverHeader.targetSlots = 0b0001; // TODO: Implement serverHeader.payloadSize = 7; @@ -209,6 +208,93 @@ class LinkWirelessMultiboot { logger("READY TO SEND ROM!"); + // ROM START + u32 transferredBytes = 0; + u32 n = 1; + u32 phase = 0; + while (transferredBytes < romSize) { + retry: + link->wait(228); + + serverHeader.isACK = 0; + serverHeader.targetSlots = 0b0001; // TODO: Implement + serverHeader.payloadSize = 84; // 87 - 3 (serversdkheader) + serverHeader.n = n; + serverHeader.phase = phase; + serverHeader.slotState = 2; + sndHeader = serializeServerHeader(serverHeader); + std::vector data; + data.push_back(sndHeader | (rom[transferredBytes] << 24)); + for (u32 i = 1; i < 84; i += 4) { + data.push_back( + rom[transferredBytes + i] | (rom[transferredBytes + i + 1] << 8) | + (rom[transferredBytes + i + 2] << 16) | + (rom[transferredBytes + i + 3] << 24)); // TODO: CHECK BOUNDS + } + if (!sendAndExpectData(data, 87, response)) + return FAILURE; + clientHeader = parseClientHeader(response.data[0]); + if (clientHeader.isACK && clientHeader.n == n && + clientHeader.phase == phase) { + phase++; + if (phase == 4) { + phase = 0; + n++; + if (n == 4) + n = 0; + } + logger("-> " + std::to_string(transferredBytes * 100 / romSize)); + transferredBytes += 84; + } else + goto retry; + } + + logger("SEND FINISHED! Confirming..."); + + // ROM END COMMAND + didClientRespond = false; + while (!didClientRespond) { + link->wait(228); + + serverHeader.isACK = 0; + serverHeader.targetSlots = 0b0001; // TODO: Implement + serverHeader.payloadSize = 0; + serverHeader.n = 0; + serverHeader.phase = 0; + serverHeader.slotState = 3; + sndHeader = serializeServerHeader(serverHeader); + if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + return FAILURE; + clientHeader = parseClientHeader(response.data[0]); + if (clientHeader.isACK == 1 && clientHeader.n == 0 && + clientHeader.phase == 0 && clientHeader.slotState == 3) + didClientRespond = true; + } + + logger("Reconfirming..."); + + // ROM END 2 COMMAND + didClientRespond = false; + while (!didClientRespond) { + link->wait(228); + + serverHeader.isACK = 0; + serverHeader.targetSlots = 0b0001; // TODO: Implement + serverHeader.payloadSize = 0; + serverHeader.n = 1; + serverHeader.phase = 0; + serverHeader.slotState = 0; + sndHeader = serializeServerHeader(serverHeader); + if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + return FAILURE; + clientHeader = parseClientHeader(response.data[0]); + if (clientHeader.isACK == 1 && clientHeader.n == 1 && + clientHeader.phase == 0 && clientHeader.slotState == 0) + didClientRespond = true; + } + + logger("SUCCESS!"); + return SUCCESS; } From eff513e700d054b53ddd932813be950ea0e1f80a Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 28 Jan 2024 09:27:22 -0300 Subject: [PATCH 10/74] Using SendData, which is more stable than SendDataWait --- lib/LinkWirelessMultiboot.hpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index df4184d..b763bf5 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -214,8 +214,6 @@ class LinkWirelessMultiboot { u32 phase = 0; while (transferredBytes < romSize) { retry: - link->wait(228); - serverHeader.isACK = 0; serverHeader.targetSlots = 0b0001; // TODO: Implement serverHeader.payloadSize = 84; // 87 - 3 (serversdkheader) @@ -231,8 +229,18 @@ class LinkWirelessMultiboot { (rom[transferredBytes + i + 2] << 16) | (rom[transferredBytes + i + 3] << 24)); // TODO: CHECK BOUNDS } - if (!sendAndExpectData(data, 87, response)) + if (!link->sendData(data, 87)) { + logger("SendData failed!"); + return FAILURE; + } + LinkRawWireless::ReceiveDataResponse response; + if (!link->receiveData(response)) { + logger("ReceiveData failed!"); return FAILURE; + } + if (response.data.size() == 0) + goto retry; + clientHeader = parseClientHeader(response.data[0]); if (clientHeader.isACK && clientHeader.n == n && clientHeader.phase == phase) { From 55a904c5a58b519ab1b70e6851f9e44c6bbb578d Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 29 Jan 2024 04:43:45 -0300 Subject: [PATCH 11/74] Checking bounds, adding test multiboot rom to gbfs --- .../LinkWirelessMultiboot_demo/content.gbfs | Bin 0 -> 2512 bytes lib/LinkWirelessMultiboot.hpp | 33 +++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 examples/LinkWirelessMultiboot_demo/content.gbfs diff --git a/examples/LinkWirelessMultiboot_demo/content.gbfs b/examples/LinkWirelessMultiboot_demo/content.gbfs new file mode 100644 index 0000000000000000000000000000000000000000..667955ed4db652d311a02cf88bb0a9fce986c192 GIT binary patch literal 2512 zcmeH|e{2(F7{{OcvF^Hd+ihc5Zr#>jo7=iV*(ORD+1YLH+Fb{8lbC>i6o_(*3JorX zKQJ5&^#l?@`G)|~An^~;WKm0qj99@Cr;=PD{)hxvH=8jH$(evFh^fzYYeex6|JO@C zd7pdld7tNbzxUjG{X?6(hc?N>JpnRUTqG1}W&j6JkiWKWdBHcl#W%cl{pJ^+eP-+8 zO&iwd9_>Rv5iJ_vrvb9Y*B4m5`pNvF|HuD~0u&RQ;%FsZ4zVfF8&jvXW9iy?dlYR7 z-k9XGOH$sTNLemzBO+CNnRb<%8l=O*Li+&#y*=aa5uIk{3kq~^#vSy8%mV23Xt{Hi zGvf-md)?vO99kZab{^y2V0)p_ z{=gVM_dm+a%g!3KmLyd?dhXCJDTh26|D3AEU+q z>o8&(7>#7ZdoE~3HTLr3rQxK1&(Xm0sMiV1b1$1U z_RgTd4pYdYa*8R$$hV?NDbh`@kv&9lPQ*pYuAU&^ zq3Xw3mDrV);a5ckm~{?YXO~?SRY&Izk4;$~WYpHqDz?hXD340Z*h8dBULY>B#)u!l zyH@)suvC)>IJj1Nf@_Tjnzd?~^4kG?vq@N7r-|L=SNlkSG)m`fSAbjj9Y|d_7ui4n z&d;qV|FS%BG%%ZNhj7;**+3%l#)u%*M+BvPY(F_fj+2u_AEU>(9&m(-*j=Rs?3cj~ zT)Wg2?s7sk946{T^)sgojpN!n0uxkAHE@sEVb{f7=lPXBna4_QCC^OfqZ_Jf(_sRO zFC@Rs>GPQJ&gQkvX0x@?xKc-Xz;%;-pX?SFN;RsWRQ4(`vX-}P>=IBJx>Hcfdn;6T zPL~RWmjv0vdaTe?t{n{U+ME^#1B@of)c{SIBrD1CP^39hqeOGNsfODR$xxQ%_cgIx7@MpH~KNo7Ta0SkDA1wPpY(*TzgN0IuzHpiXnh z)KUw$02*#52jwHAOp1^iWlqcIWN;oKO#tq&Jq1HHRIQC_y#hbmc7C<hzTh~IK+$;Ar{1n6eA@_DZ(RVNI6o0EI=xeg~&sQ4OxT;NEKp7s*xI`7O6uT zkVYgLFX<~;^-|R--lTztyGXSeXYsp%J9ysg{oUMW{@b(S+_O1#qS%vphS4_mcK_MP z0tYPchy!R!1QSw(lpqZhh@Sw67m!QHHKYaSj5F#3?DUt7ZpX{`H^I5lbaR7s;zLIw z#kKHh+CPFLq_^|O(wzUmk2F-ID8P>u2B+?3vjWSPRRTOwQIlOlasK^j4iahF@pWnx z*Mnnq%G&Rn0e@-gy8rxCt$$>S!G9hl`esTeVyQCx&r#yp8^)Tv&zti;*8a0MXniO( zn!q~zN*d#*jI*vVK~Elcj;1UCMLOe~q5Lt7f%GF0B#J1={#fcR6T3k<1}5>m+oLmQ TZ%~P+CuxW^823gzW6ys8uCQ{m literal 0 HcmV?d00001 diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index b763bf5..4286862 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -73,6 +73,18 @@ class LinkWirelessMultiboot { // if ((romSize % 0x10) != 0) // return INVALID_SIZE; + u32 asd = 0; + ServerSDKHeader serverHeaderr; + serverHeaderr.isACK = 0; + serverHeaderr.targetSlots = 0b0001; // TODO: Implement + serverHeaderr.payloadSize = 84; // 87 - 3 (serversdkheader) + serverHeaderr.n = 1; + serverHeaderr.phase = 0; + serverHeaderr.slotState = 2; + u32 sndHeaderr = serializeServerHeader(serverHeaderr); + asd = sndHeaderr | (rom[0] << 24); + logger("first byte is " + link->toHex(asd)); + bool success = true; success = link->activate(); if (!success) { @@ -224,10 +236,16 @@ class LinkWirelessMultiboot { std::vector data; data.push_back(sndHeader | (rom[transferredBytes] << 24)); for (u32 i = 1; i < 84; i += 4) { - data.push_back( - rom[transferredBytes + i] | (rom[transferredBytes + i + 1] << 8) | - (rom[transferredBytes + i + 2] << 16) | - (rom[transferredBytes + i + 3] << 24)); // TODO: CHECK BOUNDS + u32 d = 0; + if (transferredBytes + i < romSize) + d |= rom[transferredBytes + i]; + if (transferredBytes + i + 1 < romSize) + d |= rom[transferredBytes + i + 1] << 8; + if (transferredBytes + i + 2 < romSize) + d |= rom[transferredBytes + i + 2] << 16; + if (transferredBytes + i + 3 < romSize) + d |= rom[transferredBytes + i + 3] << 24; + data.push_back(d); } if (!link->sendData(data, 87)) { logger("SendData failed!"); @@ -251,8 +269,8 @@ class LinkWirelessMultiboot { if (n == 4) n = 0; } - logger("-> " + std::to_string(transferredBytes * 100 / romSize)); transferredBytes += 84; + logger("-> " + std::to_string(transferredBytes * 100 / romSize)); } else goto retry; } @@ -296,9 +314,8 @@ class LinkWirelessMultiboot { if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); - if (clientHeader.isACK == 1 && clientHeader.n == 1 && - clientHeader.phase == 0 && clientHeader.slotState == 0) - didClientRespond = true; + // if (clientHeader.slotState == 0) + didClientRespond = true; } logger("SUCCESS!"); From 31088b9b86b8eaddde6f9ec8cc42368f5d615066 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 29 Jan 2024 07:56:13 -0300 Subject: [PATCH 12/74] Fixing off by one error (works!) --- .../LinkWirelessMultiboot_demo/content.gbfs | Bin 2512 -> 11232 bytes lib/LinkWirelessMultiboot.hpp | 56 +++++++++++------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/content.gbfs b/examples/LinkWirelessMultiboot_demo/content.gbfs index 667955ed4db652d311a02cf88bb0a9fce986c192..0bfec9ea48bf96def6f4e878afa42ed57671f08e 100644 GIT binary patch literal 11232 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 Oqrl{+YW^%BhXVk=Ia4zL diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 4286862..944aaad 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -73,17 +73,7 @@ class LinkWirelessMultiboot { // if ((romSize % 0x10) != 0) // return INVALID_SIZE; - u32 asd = 0; - ServerSDKHeader serverHeaderr; - serverHeaderr.isACK = 0; - serverHeaderr.targetSlots = 0b0001; // TODO: Implement - serverHeaderr.payloadSize = 84; // 87 - 3 (serversdkheader) - serverHeaderr.n = 1; - serverHeaderr.phase = 0; - serverHeaderr.slotState = 2; - u32 sndHeaderr = serializeServerHeader(serverHeaderr); - asd = sndHeaderr | (rom[0] << 24); - logger("first byte is " + link->toHex(asd)); + // std::vector bytes = {}; bool success = true; success = link->activate(); @@ -224,7 +214,9 @@ class LinkWirelessMultiboot { u32 transferredBytes = 0; u32 n = 1; u32 phase = 0; + bool isRetry = false; while (transferredBytes < romSize) { + isRetry = false; retry: serverHeader.isACK = 0; serverHeader.targetSlots = 0b0001; // TODO: Implement @@ -235,16 +227,18 @@ class LinkWirelessMultiboot { sndHeader = serializeServerHeader(serverHeader); std::vector data; data.push_back(sndHeader | (rom[transferredBytes] << 24)); + // if (!isRetry) + // bytes.push_back(rom[transferredBytes]); for (u32 i = 1; i < 84; i += 4) { u32 d = 0; - if (transferredBytes + i < romSize) - d |= rom[transferredBytes + i]; - if (transferredBytes + i + 1 < romSize) - d |= rom[transferredBytes + i + 1] << 8; - if (transferredBytes + i + 2 < romSize) - d |= rom[transferredBytes + i + 2] << 16; - if (transferredBytes + i + 3 < romSize) - d |= rom[transferredBytes + i + 3] << 24; + for (u32 j = 0; j < 4; j++) { + if (transferredBytes + i + j < romSize && i + j < 84) { + u8 byte = rom[transferredBytes + i + j]; + d |= byte << (j * 8); + // if (!isRetry) + // bytes.push_back(byte); + } + } data.push_back(d); } if (!link->sendData(data, 87)) { @@ -256,8 +250,10 @@ class LinkWirelessMultiboot { logger("ReceiveData failed!"); return FAILURE; } - if (response.data.size() == 0) + if (response.data.size() == 0) { + isRetry = true; goto retry; + } clientHeader = parseClientHeader(response.data[0]); if (clientHeader.isACK && clientHeader.n == n && @@ -271,8 +267,10 @@ class LinkWirelessMultiboot { } transferredBytes += 84; logger("-> " + std::to_string(transferredBytes * 100 / romSize)); - } else + } else { + isRetry = true; goto retry; + } } logger("SEND FINISHED! Confirming..."); @@ -320,6 +318,20 @@ class LinkWirelessMultiboot { logger("SUCCESS!"); + // u32 diffs = 0; + // for (u32 i = 0; i < romSize; i++) { + // if (rom[i] != bytes[i]) { + // logger("DIFF AT " + std::to_string(i) + ": " + link->toHex(bytes[i]) + // + + // " vs " + link->toHex(rom[i])); + // diffs++; + // } + // if (diffs > 100) + // break; + // } + + // logger("??"); + return SUCCESS; } @@ -372,7 +384,7 @@ class LinkWirelessMultiboot { u32 serializeServerHeader(ServerSDKHeader serverHeader) { ServerSDKHeaderSerializer serverSerializer; serverSerializer.asStruct = serverHeader; - return serverSerializer.asInt; + return serverSerializer.asInt & 0xffffff; } }; From 5c69549f6b37192696357b16f2bef4b14ecaff36 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 30 Jan 2024 01:26:02 -0300 Subject: [PATCH 13/74] Making LinkSPI more robust and using SendDataWait --- lib/LinkSPI.hpp | 21 +++++++++++++++++++-- lib/LinkWirelessMultiboot.hpp | 15 ++++++++------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/LinkSPI.hpp b/lib/LinkSPI.hpp index f2b7ef5..2dfb381 100644 --- a/lib/LinkSPI.hpp +++ b/lib/LinkSPI.hpp @@ -55,6 +55,9 @@ static volatile char LINK_SPI_VERSION[] = "LinkSPI/v6.2.0"; +const u32 LINK_SPI_MASK_CLEAR_SO_BIT = ~(1 << LINK_SPI_BIT_SO); +const u32 LINK_SPI_MASK_SET_START_BIT = (1 << LINK_SPI_BIT_START); + class LinkSPI { public: enum Mode { SLAVE, MASTER_256KBPS, MASTER_2MBPS }; @@ -124,8 +127,22 @@ class LinkSPI { return LINK_SPI_NO_DATA; } - enableTransfer(); // TODO: TEST THIS (moved) - startTransfer(); + asm volatile( + // enableTransfer(); + // startTransfer(); + "MOV R2, %[reg_siocnt]\n\t" // Move ®_SIOCNT to R2 + "LDR R0, [R2]\n\t" // Load SIOCNT into R0 + "LDR R1, %[clear_so_bit_mask]\n\t" // Load mask value to clear SO bit + "AND R0, R0, R1\n\t" // Clear SO bit + "STR R0, [R2]\n\t" // Store back to SIOCNT + "LDR R1, %[set_start_bit_mask]\n\t" // Load mask value to set START bit + "ORR R0, R0, R1\n\t" // Set START bit + "STR R0, [R2]\n\t" // Store back to SIOCNT + : + : [reg_siocnt] "r"(®_SIOCNT), + [clear_so_bit_mask] "m"(LINK_SPI_MASK_CLEAR_SO_BIT), + [set_start_bit_mask] "m"(LINK_SPI_MASK_SET_START_BIT) + : "r0", "r1", "r2"); if (_async) return LINK_SPI_NO_DATA; diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 944aaad..431ff85 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -215,6 +215,7 @@ class LinkWirelessMultiboot { u32 n = 1; u32 phase = 0; bool isRetry = false; + u32 progress = 0; while (transferredBytes < romSize) { isRetry = false; retry: @@ -241,13 +242,9 @@ class LinkWirelessMultiboot { } data.push_back(d); } - if (!link->sendData(data, 87)) { - logger("SendData failed!"); - return FAILURE; - } LinkRawWireless::ReceiveDataResponse response; - if (!link->receiveData(response)) { - logger("ReceiveData failed!"); + if (!sendAndExpectData(data, 87, response)) { + logger("SendData failed!"); return FAILURE; } if (response.data.size() == 0) { @@ -266,7 +263,11 @@ class LinkWirelessMultiboot { n = 0; } transferredBytes += 84; - logger("-> " + std::to_string(transferredBytes * 100 / romSize)); + u32 newProgress = transferredBytes * 100 / romSize; + if (newProgress != progress) { + progress = newProgress; + logger("-> " + std::to_string(transferredBytes * 100 / romSize)); + } } else { isRetry = true; goto retry; From 6f46c756e693b08ca409ddc48587df004e273ed7 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 30 Jan 2024 06:36:47 -0300 Subject: [PATCH 14/74] Remove all logging code from LinkRawWireless if logging is disabled --- examples/compile.sh | 2 + lib/LinkRawWireless.hpp | 143 +++++++++++++++++++++------------------- 2 files changed, 78 insertions(+), 67 deletions(-) diff --git a/examples/compile.sh b/examples/compile.sh index a997518..3e6f169 100644 --- a/examples/compile.sh +++ b/examples/compile.sh @@ -31,7 +31,9 @@ cp LinkRawCable_demo.gba ../ cd .. cd LinkRawWireless_demo/ +sed -i -e "s/\/\/ #define LINK_RAW_WIRELESS_ENABLE_LOGGING/#define LINK_RAW_WIRELESS_ENABLE_LOGGING/g" ../../lib/LinkRawWireless.hpp make rebuild +sed -i -e "s/#define LINK_RAW_WIRELESS_ENABLE_LOGGING/\/\/ #define LINK_RAW_WIRELESS_ENABLE_LOGGING/g" ../../lib/LinkRawWireless.hpp cp LinkRawWireless_demo.gba ../ cd .. diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index b599870..b6a713e 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -16,8 +16,14 @@ #include #include -// Enable logging (set `linkRawWireless->logger`) -#define LINK_RAW_WIRELESS_ENABLE_LOGGING true +// Enable logging (set `linkRawWireless->logger` and uncomment to enable) +// #define LINK_RAW_WIRELESS_ENABLE_LOGGING + +#ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING +#define LRWLOG(str) log(str) +#else +#define LRWLOG(str) +#endif #define LINK_RAW_WIRELESS_MAX_PLAYERS 5 #define LINK_RAW_WIRELESS_PING_WAIT 50 @@ -159,11 +165,11 @@ class LinkRawWireless { std::string userName = "", u16 gameId = LINK_RAW_WIRELESS_MAX_GAME_ID) { if (gameName.length() > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) { - log("! game name too long"); + LRWLOG("! game name too long"); return false; } if (userName.length() > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) { - log("! user name too long"); + LRWLOG("! user name too long"); return false; } gameName.append(LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH - gameName.length(), @@ -204,7 +210,7 @@ class LinkRawWireless { } wait(LINK_RAW_WIRELESS_TRANSFER_WAIT); - log("state = SERVING"); + LRWLOG("state = SERVING"); state = SERVING; return true; @@ -248,7 +254,7 @@ class LinkRawWireless { u8 oldPlayerCount = sessionState.playerCount; sessionState.playerCount = 1 + result.responses.size(); if (sessionState.playerCount != oldPlayerCount) - log("now: " + std::to_string(sessionState.playerCount) + " players"); + LRWLOG("now: " + std::to_string(sessionState.playerCount) + " players"); return true; } @@ -270,7 +276,7 @@ class LinkRawWireless { u8 oldPlayerCount = sessionState.playerCount; sessionState.playerCount = 1 + result.responses.size(); if (sessionState.playerCount != oldPlayerCount) - log("now: " + std::to_string(sessionState.playerCount) + " players"); + LRWLOG("now: " + std::to_string(sessionState.playerCount) + " players"); return true; } @@ -284,7 +290,7 @@ class LinkRawWireless { return false; } - log("state = SEARCHING"); + LRWLOG("state = SEARCHING"); state = SEARCHING; return true; @@ -323,7 +329,7 @@ class LinkRawWireless { servers.push_back(server); } - log("state = AUTHENTICATED"); + LRWLOG("state = AUTHENTICATED"); state = AUTHENTICATED; return true; @@ -351,7 +357,7 @@ class LinkRawWireless { return false; } - log("state = CONNECTING"); + LRWLOG("state = CONNECTING"); state = CONNECTING; return true; @@ -361,7 +367,7 @@ class LinkRawWireless { auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_IS_FINISHED_CONNECT); if (!result.success || result.responses.size() == 0) { if (result.responses.size() == 0) - log("! empty response"); + LRWLOG("! empty response"); reset(); return false; } @@ -373,7 +379,7 @@ class LinkRawWireless { u8 assignedPlayerId = 1 + (u8)msB32(result.responses[0]); if (assignedPlayerId >= LINK_RAW_WIRELESS_MAX_PLAYERS) { - log("! connection failed (1)"); + LRWLOG("! connection failed (1)"); reset(); response.phase = ERROR; return false; @@ -389,21 +395,21 @@ class LinkRawWireless { auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_FINISH_CONNECTION); if (!result.success || result.responses.size() == 0) { if (result.responses.size() == 0) - log("! empty response"); + LRWLOG("! empty response"); reset(); return false; } u16 status = msB32(result.responses[0]); if ((msB16(status) & 1) == 1) { - log("! connection failed (2)"); + LRWLOG("! connection failed (2)"); reset(); return false; } u8 assignedPlayerId = 1 + (u8)status; sessionState.currentPlayerId = assignedPlayerId; - log("state = CONNECTED"); + LRWLOG("state = CONNECTED"); state = CONNECTED; return true; @@ -415,7 +421,7 @@ class LinkRawWireless { ? bytes : (bytes << (3 + sessionState.currentPlayerId * 5)); data.insert(data.begin(), header); - log("using header " + toHex(header)); + LRWLOG("using header " + toHex(header)); bool success = sendCommand(LINK_RAW_WIRELESS_COMMAND_SEND_DATA, data).success; @@ -436,7 +442,7 @@ class LinkRawWireless { ? bytes : (bytes << (3 + sessionState.currentPlayerId * 5)); data.insert(data.begin(), header); - log("using header " + toHex(header)); + LRWLOG("using header " + toHex(header)); if (!sendCommand(LINK_RAW_WIRELESS_COMMAND_SEND_DATA_AND_WAIT, data) .success) { @@ -492,7 +498,7 @@ class LinkRawWireless { u32 command = buildCommand(type, length); u32 r; - log("sending command 0x" + toHex(command)); + LRWLOG("sending command 0x" + toHex(command)); if ((r = transfer(command)) != LINK_RAW_WIRELESS_DATA_REQUEST) { logExpectedButReceived(LINK_RAW_WIRELESS_DATA_REQUEST, r); return result; @@ -500,8 +506,8 @@ class LinkRawWireless { u32 parameterCount = 0; for (auto& param : params) { - log("sending param" + std::to_string(parameterCount) + ": 0x" + - toHex(param)); + LRWLOG("sending param" + std::to_string(parameterCount) + ": 0x" + + toHex(param)); if ((r = transfer(param)) != LINK_RAW_WIRELESS_DATA_REQUEST) { logExpectedButReceived(LINK_RAW_WIRELESS_DATA_REQUEST, r); return result; @@ -509,7 +515,7 @@ class LinkRawWireless { parameterCount++; } - log("sending response request"); + LRWLOG("sending response request"); u32 response = transfer(LINK_RAW_WIRELESS_DATA_REQUEST); u16 header = msB32(response); u16 data = lsB32(response); @@ -517,29 +523,31 @@ class LinkRawWireless { u8 ack = lsB16(data); if (header != LINK_RAW_WIRELESS_COMMAND_HEADER) { - log("! expected HEADER 0x9966"); - log("! but received 0x" + toHex(header)); + LRWLOG("! expected HEADER 0x9966"); + LRWLOG("! but received 0x" + toHex(header)); return result; } if (ack != type + LINK_RAW_WIRELESS_RESPONSE_ACK) { if (ack == 0xee && responses == 1) { - u8 code = (u8)transfer(LINK_RAW_WIRELESS_DATA_REQUEST); - log("! error received"); - log(code == 1 ? "! invalid state" : "! unknown cmd"); + u8 __attribute__((unused)) code = + (u8)transfer(LINK_RAW_WIRELESS_DATA_REQUEST); + LRWLOG("! error received"); + LRWLOG(code == 1 ? "! invalid state" : "! unknown cmd"); } else { - log("! expected ACK 0x" + toHex(type + LINK_RAW_WIRELESS_RESPONSE_ACK)); - log("! but received 0x" + toHex(ack)); + LRWLOG("! expected ACK 0x" + + toHex(type + LINK_RAW_WIRELESS_RESPONSE_ACK)); + LRWLOG("! but received 0x" + toHex(ack)); } return result; } - log("ack ok! " + std::to_string(responses) + " responses"); + LRWLOG("ack ok! " + std::to_string(responses) + " responses"); for (u32 i = 0; i < responses; i++) { - log("response " + std::to_string(i + 1) + "/" + - std::to_string(responses) + ":"); + LRWLOG("response " + std::to_string(i + 1) + "/" + + std::to_string(responses) + ":"); u32 responseData = transfer(LINK_RAW_WIRELESS_DATA_REQUEST); result.responses.push_back(responseData); - log("<< " + toHex(responseData)); + LRWLOG("<< " + toHex(responseData)); } result.success = true; @@ -549,10 +557,10 @@ class LinkRawWireless { RemoteCommand receiveCommandFromAdapter() { RemoteCommand remoteCommand; - log("setting SPI to SLAVE"); + LRWLOG("setting SPI to SLAVE"); linkSPI->activate(LinkSPI::Mode::SLAVE); - log("WAITING for adapter cmd"); + LRWLOG("WAITING for adapter cmd"); u32 command = linkSPI->transfer(LINK_RAW_WIRELESS_DATA_REQUEST); if (!reverseAcknowledge()) { linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); @@ -565,18 +573,18 @@ class LinkRawWireless { u8 params = msB16(data); u8 commandId = lsB16(data); if (header != LINK_RAW_WIRELESS_COMMAND_HEADER) { - log("! expected HEADER 0x9966"); - log("! but received 0x" + toHex(header)); + LRWLOG("! expected HEADER 0x9966"); + LRWLOG("! but received 0x" + toHex(header)); linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); reset(); return remoteCommand; } - log("received cmd: " + toHex(commandId) + " (" + std::to_string(params) + - " params)"); + LRWLOG("received cmd: " + toHex(commandId) + " (" + std::to_string(params) + + " params)"); for (u32 i = 0; i < params; i++) { - log("param " + std::to_string(i + 1) + "/" + std::to_string(params) + - ":"); + LRWLOG("param " + std::to_string(i + 1) + "/" + std::to_string(params) + + ":"); u32 paramData = linkSPI->transfer(LINK_RAW_WIRELESS_DATA_REQUEST); if (!reverseAcknowledge()) { linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); @@ -584,10 +592,10 @@ class LinkRawWireless { return remoteCommand; } remoteCommand.params.push_back(paramData); - log("<< " + toHex(paramData)); + LRWLOG("<< " + toHex(paramData)); } - log("sending ack"); + LRWLOG("sending ack"); command = linkSPI->transfer(0x99660000 | ((commandId + 0x80) & 0xff)); if (!reverseAcknowledge()) { linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); @@ -596,14 +604,14 @@ class LinkRawWireless { } if (command != LINK_RAW_WIRELESS_DATA_REQUEST) { - log("! expected cmd request"); - log("! but received 0x" + toHex(command)); + LRWLOG("! expected cmd request"); + LRWLOG("! but received 0x" + toHex(command)); linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); reset(); return remoteCommand; } - log("setting SPI to 2Mbps"); + LRWLOG("setting SPI to 2Mbps"); linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); remoteCommand.success = true; @@ -672,7 +680,7 @@ class LinkRawWireless { } void resetState() { - log("state = NEEDS_RESET"); + LRWLOG("state = NEEDS_RESET"); this->state = NEEDS_RESET; this->sessionState.playerCount = 1; this->sessionState.currentPlayerId = 0; @@ -682,7 +690,7 @@ class LinkRawWireless { bool start() { pingAdapter(); - log("setting SPI to 256Kbps"); + LRWLOG("setting SPI to 256Kbps"); linkSPI->activate(LinkSPI::Mode::MASTER_256KBPS); if (!login()) @@ -690,40 +698,40 @@ class LinkRawWireless { wait(LINK_RAW_WIRELESS_TRANSFER_WAIT); - log("sending HELLO command"); + LRWLOG("sending HELLO command"); if (!sendCommand(LINK_RAW_WIRELESS_COMMAND_HELLO).success) return false; - log("setting SPI to 2Mbps"); + LRWLOG("setting SPI to 2Mbps"); linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); - log("state = AUTHENTICATED"); + LRWLOG("state = AUTHENTICATED"); state = AUTHENTICATED; return true; } void pingAdapter() { - log("setting SO as OUTPUT"); + LRWLOG("setting SO as OUTPUT"); linkGPIO->setMode(LinkGPIO::Pin::SO, LinkGPIO::Direction::OUTPUT); - log("setting SD as OUTPUT"); + LRWLOG("setting SD as OUTPUT"); linkGPIO->setMode(LinkGPIO::Pin::SD, LinkGPIO::Direction::OUTPUT); - log("setting SD = HIGH"); + LRWLOG("setting SD = HIGH"); linkGPIO->writePin(LinkGPIO::SD, true); wait(LINK_RAW_WIRELESS_PING_WAIT); - log("setting SD = LOW"); + LRWLOG("setting SD = LOW"); linkGPIO->writePin(LinkGPIO::SD, false); } bool login() { LoginMemory memory; - log("sending initial login packet"); + LRWLOG("sending initial login packet"); if (!exchangeLoginPacket(LINK_RAW_WIRELESS_LOGIN_PARTS[0], 0, memory)) return false; for (u32 i = 0; i < LINK_RAW_WIRELESS_LOGIN_STEPS; i++) { - log("sending login packet " + std::to_string(i + 1) + "/" + - std::to_string(LINK_RAW_WIRELESS_LOGIN_STEPS)); + LRWLOG("sending login packet " + std::to_string(i + 1) + "/" + + std::to_string(LINK_RAW_WIRELESS_LOGIN_STEPS)); if (!exchangeLoginPacket(LINK_RAW_WIRELESS_LOGIN_PARTS[i], LINK_RAW_WIRELESS_LOGIN_PARTS[i], memory)) return false; @@ -778,16 +786,16 @@ class LinkRawWireless { linkSPI->_setSOLow(); while (!linkSPI->_isSIHigh()) { if (cmdTimeout(lines, vCount)) { - log("! ACK 1 failed. I put SO=LOW,"); - log("! but SI didn't become HIGH."); + LRWLOG("! ACK 1 failed. I put SO=LOW,"); + LRWLOG("! but SI didn't become HIGH."); return false; } } linkSPI->_setSOHigh(); while (linkSPI->_isSIHigh()) { if (cmdTimeout(lines, vCount)) { - log("! ACK 2 failed. I put SO=HIGH,"); - log("! but SI didn't become LOW."); + LRWLOG("! ACK 2 failed. I put SO=HIGH,"); + LRWLOG("! but SI didn't become LOW."); return false; } } @@ -802,8 +810,8 @@ class LinkRawWireless { while (!linkSPI->_isSIHigh()) { if (cmdTimeout(lines, vCount)) { - log("! REV_ACK failed. I put SO=HIGH,"); - log("! but SI didn't become HIGH."); + LRWLOG("! REV_ACK failed. I put SO=HIGH,"); + LRWLOG("! but SI didn't become HIGH."); return false; } } @@ -837,8 +845,8 @@ class LinkRawWireless { } void logExpectedButReceived(u32 expected, u32 received) { - log("! expected 0x" + toHex(expected)); - log("! but received 0x" + toHex(received)); + LRWLOG("! expected 0x" + toHex(expected)); + LRWLOG("! but received 0x" + toHex(received)); } template @@ -851,8 +859,7 @@ class LinkRawWireless { } inline __attribute__((always_inline)) void log(std::string str) { - if (LINK_RAW_WIRELESS_ENABLE_LOGGING) - logger(str); + logger(str); } u32 buildU32(u16 msB, u16 lsB) { return (msB << 16) | lsB; } @@ -865,4 +872,6 @@ class LinkRawWireless { extern LinkRawWireless* linkRawWireless; +#undef LRWLOG + #endif // LINK_RAW_WIRELESS_H From 18020521fd54460abcd7338860a1bd1ae28c9856 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 30 Jan 2024 06:54:46 -0300 Subject: [PATCH 15/74] Also making logger optional in LinkWirelessMultiboot --- examples/compile.sh | 2 + lib/LinkRawWireless.hpp | 6 +-- lib/LinkWirelessMultiboot.hpp | 88 +++++++++++++++++++---------------- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/examples/compile.sh b/examples/compile.sh index 3e6f169..465fd69 100644 --- a/examples/compile.sh +++ b/examples/compile.sh @@ -90,6 +90,8 @@ sed -i -e "s/#define PROFILING_ENABLED/\/\/ #define PROFILING_ENABLED/g" ../../l cd .. cd LinkWirelessMultiboot_demo/ +sed -i -e "s/\/\/ #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/#define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/g" ../../lib/LinkWirelessMultiboot.hpp make rebuild cp LinkWirelessMultiboot_demo.out.gba ../LinkWirelessMultiboot_demo.gba +sed -i -e "s/\/\/ #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/#define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/g" ../../lib/LinkWirelessMultiboot.hpp cd .. diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index b6a713e..f52948c 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -20,7 +20,7 @@ // #define LINK_RAW_WIRELESS_ENABLE_LOGGING #ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING -#define LRWLOG(str) log(str) +#define LRWLOG(str) logger(str) #else #define LRWLOG(str) #endif @@ -858,10 +858,6 @@ class LinkRawWireless { return rc; } - inline __attribute__((always_inline)) void log(std::string str) { - logger(str); - } - u32 buildU32(u16 msB, u16 lsB) { return (msB << 16) | lsB; } u16 buildU16(u8 msB, u8 lsB) { return (msB << 8) | lsB; } u16 msB32(u32 value) { return value >> 16; } diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 431ff85..e2e8bcc 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -24,6 +24,15 @@ #include #include "LinkRawWireless.hpp" +// Enable logging (set `linkWirelessMultiboot->logger` and uncomment to enable) +// #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING + +#ifdef LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING +#define LWMLOG(str) logger(str) +#else +#define LWMLOG(str) +#endif + #define LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE (0x100 + 0xc0) #define LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE (256 * 1024) #define LINK_WIRELESS_MULTIBOOT_HEADER_SIZE 0xC0 @@ -66,52 +75,51 @@ class LinkWirelessMultiboot { template Result sendRom(const u8* rom, u32 romSize, F cancel) { - // if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) - // return INVALID_SIZE; - // if (romSize > LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE) - // return INVALID_SIZE; - // if ((romSize % 0x10) != 0) - // return INVALID_SIZE; + if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) + return INVALID_SIZE; + if (romSize > LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE) + return INVALID_SIZE; + // TODO: Document no 0x10 boundary limit // std::vector bytes = {}; bool success = true; success = link->activate(); if (!success) { - logger("cannot activate"); + LWMLOG("cannot activate"); return FAILURE; } - logger("activated"); + LWMLOG("activated"); success = link->sendCommand(LINK_RAW_WIRELESS_COMMAND_SETUP, std::vector{0x003F0120}) .success; // TODO: IMPLEMENT SETUP if (!success) { - logger("setup failed"); + LWMLOG("setup failed"); return FAILURE; } - logger("setup ok"); + LWMLOG("setup ok"); success = link->broadcast("Multi", "Test", 0b1111111111111111); if (!success) { - logger("broadcast failed"); + LWMLOG("broadcast failed"); return FAILURE; } - logger("broadcast set"); + LWMLOG("broadcast set"); success = link->startHost(); if (!success) { - logger("start host failed"); + LWMLOG("start host failed"); return FAILURE; } - logger("host started"); + LWMLOG("host started"); LinkRawWireless::AcceptConnectionsResponse acceptResponse; while (link->playerCount() == 1) { link->acceptConnections(acceptResponse); } - logger("connected!"); + LWMLOG("connected!"); linkRawWireless->wait(228 * 20); // ~300ms // HANDSHAKE @@ -124,15 +132,15 @@ class LinkWirelessMultiboot { hasData = response.data.size() > 0; } - logger("data received"); + LWMLOG("data received"); ClientSDKHeader clientHeader = parseClientHeader(response.data[0]); - logger("client size: " + std::to_string(clientHeader.payloadSize)); - logger("n: " + std::to_string(clientHeader.n)); - logger("phase: " + std::to_string(clientHeader.phase)); - logger("ack: " + std::to_string(clientHeader.isACK)); - logger("slotState:" + std::to_string(clientHeader.slotState)); + LWMLOG("client size: " + std::to_string(clientHeader.payloadSize)); + LWMLOG("n: " + std::to_string(clientHeader.n)); + LWMLOG("phase: " + std::to_string(clientHeader.phase)); + LWMLOG("ack: " + std::to_string(clientHeader.isACK)); + LWMLOG("slotState:" + std::to_string(clientHeader.slotState)); - logger("sending ACK"); + LWMLOG("sending ACK"); firstack: ServerSDKHeader serverHeader; serverHeader = createACKFor(clientHeader); @@ -149,9 +157,9 @@ class LinkWirelessMultiboot { goto firstack; if (clientHeader.n == 2 && clientHeader.slotState == 1) { - logger("N IS NOW 2, slotstate = 1"); + LWMLOG("N IS NOW 2, slotstate = 1"); } else { - logger("Error: weird packet"); + LWMLOG("Error: weird packet"); return FAILURE; } @@ -169,9 +177,9 @@ class LinkWirelessMultiboot { goto secondack; if (clientHeader.n == 1 && clientHeader.slotState == 2) { - logger("NI STARTED"); + LWMLOG("NI STARTED"); } else { - logger("NI DIDN'T START"); + LWMLOG("NI DIDN'T START"); return FAILURE; } @@ -185,7 +193,7 @@ class LinkWirelessMultiboot { clientHeader = parseClientHeader(response.data[0]); } - logger("slotState IS NOW 0"); + LWMLOG("slotState IS NOW 0"); // ROM START COMMAND bool didClientRespond = false; @@ -208,7 +216,7 @@ class LinkWirelessMultiboot { didClientRespond = true; } - logger("READY TO SEND ROM!"); + LWMLOG("READY TO SEND ROM!"); // ROM START u32 transferredBytes = 0; @@ -244,7 +252,7 @@ class LinkWirelessMultiboot { } LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData(data, 87, response)) { - logger("SendData failed!"); + LWMLOG("SendData failed!"); return FAILURE; } if (response.data.size() == 0) { @@ -266,7 +274,7 @@ class LinkWirelessMultiboot { u32 newProgress = transferredBytes * 100 / romSize; if (newProgress != progress) { progress = newProgress; - logger("-> " + std::to_string(transferredBytes * 100 / romSize)); + LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); } } else { isRetry = true; @@ -274,7 +282,7 @@ class LinkWirelessMultiboot { } } - logger("SEND FINISHED! Confirming..."); + LWMLOG("SEND FINISHED! Confirming..."); // ROM END COMMAND didClientRespond = false; @@ -296,7 +304,7 @@ class LinkWirelessMultiboot { didClientRespond = true; } - logger("Reconfirming..."); + LWMLOG("Reconfirming..."); // ROM END 2 COMMAND didClientRespond = false; @@ -317,12 +325,12 @@ class LinkWirelessMultiboot { didClientRespond = true; } - logger("SUCCESS!"); + LWMLOG("SUCCESS!"); // u32 diffs = 0; // for (u32 i = 0; i < romSize; i++) { // if (rom[i] != bytes[i]) { - // logger("DIFF AT " + std::to_string(i) + ": " + link->toHex(bytes[i]) + // LWMLOG("DIFF AT " + std::to_string(i) + ": " + link->toHex(bytes[i]) // + // " vs " + link->toHex(rom[i])); // diffs++; @@ -331,7 +339,7 @@ class LinkWirelessMultiboot { // break; // } - // logger("??"); + // LWMLOG("??"); return SUCCESS; } @@ -348,17 +356,17 @@ class LinkWirelessMultiboot { bool success = false; success = link->sendDataAndWait(data, remoteCommand, _bytes); if (!success) { - logger("senddatawait no"); + LWMLOG("senddatawait no"); return false; } if (remoteCommand.commandId != 0x28) { - logger("expected response 0x28"); - logger("but got " + link->toHex(remoteCommand.commandId)); + LWMLOG("expected response 0x28"); + LWMLOG("but got " + link->toHex(remoteCommand.commandId)); return false; } success = link->receiveData(response); if (!success) { - logger("receive data failed"); + LWMLOG("receive data failed"); return false; } return true; @@ -391,4 +399,6 @@ class LinkWirelessMultiboot { extern LinkWirelessMultiboot* linkWirelessMultiboot; +#undef LWMLOG + #endif // LINK_WIRELESS_MULTIBOOT_H From ec67a2c7294dd44807f53a7937f7e032d9251b32 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 30 Jan 2024 07:26:46 -0300 Subject: [PATCH 16/74] Configurable magic in setup(...), start LinkWirelessMultiboot cleanup --- .../src/scenes/MultibootScene.cpp | 2 +- lib/LinkRawWireless.hpp | 5 +- lib/LinkWirelessMultiboot.hpp | 47 ++++++++++++------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index ae3a9b8..68ea64b 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -188,7 +188,7 @@ void MultibootScene::processButtons() { const u8* romToSend = (const u8*)gbfs_get_obj(fs, ROM_FILE_NAME, &fileLength); - linkWirelessMultiboot->sendRom(romToSend, fileLength, + linkWirelessMultiboot->sendRom(romToSend, fileLength, "Multi", "Test", 2, []() { return false; }); print(); } diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index f52948c..b507f42 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -151,11 +151,12 @@ class LinkRawWireless { return success; } - bool setup(u8 maxPlayers = LINK_RAW_WIRELESS_MAX_PLAYERS) { + bool setup(u8 maxPlayers = LINK_RAW_WIRELESS_MAX_PLAYERS, + u32 magic = LINK_RAW_WIRELESS_SETUP_MAGIC) { return sendCommand( LINK_RAW_WIRELESS_COMMAND_SETUP, std::vector{ - (u32)(LINK_RAW_WIRELESS_SETUP_MAGIC | + (u32)(magic | (((LINK_RAW_WIRELESS_MAX_PLAYERS - maxPlayers) & 0b11) << LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT))}) .success; diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index e2e8bcc..ea1140d 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -35,7 +35,10 @@ #define LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE (0x100 + 0xc0) #define LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE (256 * 1024) +#define LINK_WIRELESS_MULTIBOOT_MIN_PLAYERS 2 +#define LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS 5 #define LINK_WIRELESS_MULTIBOOT_HEADER_SIZE 0xC0 +#define LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC 0x003F0120 static volatile char LINK_WIRELESS_MULTIBOOT_VERSION[] = "LinkWirelessMultiboot/v6.2.0"; @@ -45,7 +48,14 @@ class LinkWirelessMultiboot { public: Logger logger = [](std::string str) {}; - enum Result { SUCCESS, INVALID_SIZE, CANCELED, FAILURE }; + enum Result { + SUCCESS, + INVALID_SIZE, + INVALID_PLAYERS, + CANCELED, + ADAPTER_NOT_DETECTED, + FAILURE + }; struct ServerSDKHeader { unsigned int payloadSize : 7; @@ -74,32 +84,33 @@ class LinkWirelessMultiboot { }; template - Result sendRom(const u8* rom, u32 romSize, F cancel) { + Result sendRom(const u8* rom, + u32 romSize, + const char* gameName, + const char* userName, + u8 players, + F cancel) { if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) return INVALID_SIZE; if (romSize > LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE) - return INVALID_SIZE; - // TODO: Document no 0x10 boundary limit - - // std::vector bytes = {}; - - bool success = true; - success = link->activate(); - if (!success) { - LWMLOG("cannot activate"); - return FAILURE; + return INVALID_SIZE; // TODO: Document no 0x10 boundary limit + if (players < LINK_WIRELESS_MULTIBOOT_MIN_PLAYERS || + players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) + return INVALID_PLAYERS; + + if (!link->activate()) { + LWMLOG("! adapter not detected"); + return ADAPTER_NOT_DETECTED; } LWMLOG("activated"); - success = link->sendCommand(LINK_RAW_WIRELESS_COMMAND_SETUP, - std::vector{0x003F0120}) - .success; // TODO: IMPLEMENT SETUP - if (!success) { - LWMLOG("setup failed"); + if (!link->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { + LWMLOG("! setup failed"); return FAILURE; } LWMLOG("setup ok"); + bool success; // TODO: REMOVE success = link->broadcast("Multi", "Test", 0b1111111111111111); if (!success) { LWMLOG("broadcast failed"); @@ -344,6 +355,8 @@ class LinkWirelessMultiboot { return SUCCESS; } + ~LinkWirelessMultiboot() { delete link; } + LinkRawWireless* link = new LinkRawWireless(); ClientSDKHeader lastACK; From 75aae7a279b78338fed2b45c00a775c3d7a008b0 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 30 Jan 2024 07:44:12 -0300 Subject: [PATCH 17/74] Migrating LinkRawWireless to C-style strings --- .../src/scenes/DebugScene.cpp | 4 +- lib/LinkRawWireless.hpp | 89 +++++++++++-------- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp index c5e9e55..a80443b 100644 --- a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp +++ b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp @@ -504,8 +504,8 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { log("[room.game] " + gameName); log("[room.user] " + userName); - bool success = - linkRawWireless->broadcast(gameName, userName, (u16)gameId); + bool success = linkRawWireless->broadcast( + gameName.c_str(), userName.c_str(), (u16)gameId); if (success) log("NOW CALL 0x19!"); diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index b507f42..1751ec0 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -10,16 +10,16 @@ #include #include +#include +#include // TODO: ARRAY #include "LinkGPIO.hpp" #include "LinkSPI.hpp" -#include -#include - // Enable logging (set `linkRawWireless->logger` and uncomment to enable) // #define LINK_RAW_WIRELESS_ENABLE_LOGGING #ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING +#include #define LRWLOG(str) logger(str) #else #define LRWLOG(str) @@ -97,8 +97,8 @@ class LinkRawWireless { struct Server { u16 id = 0; u16 gameId; - std::string gameName; - std::string userName; + char gameName[LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH + 1]; + char userName[LINK_RAW_WIRELESS_MAX_USER_NAME_LENGTH + 1]; u8 nextClientNumber; bool isFull() { return nextClientNumber == 0xff; } @@ -162,34 +162,35 @@ class LinkRawWireless { .success; } - bool broadcast(std::string gameName = "", - std::string userName = "", + bool broadcast(const char* gameName = "", + const char* userName = "", u16 gameId = LINK_RAW_WIRELESS_MAX_GAME_ID) { - if (gameName.length() > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) { + if (std::strlen(gameName) > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) { LRWLOG("! game name too long"); return false; } - if (userName.length() > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) { + if (std::strlen(userName) > LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH) { LRWLOG("! user name too long"); return false; } - gameName.append(LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH - gameName.length(), - 0); - userName.append(LINK_RAW_WIRELESS_MAX_USER_NAME_LENGTH - userName.length(), - 0); - - auto broadcastData = - std::vector{buildU32(buildU16(gameName[1], gameName[0]), gameId), - buildU32(buildU16(gameName[5], gameName[4]), - buildU16(gameName[3], gameName[2])), - buildU32(buildU16(gameName[9], gameName[8]), - buildU16(gameName[7], gameName[6])), - buildU32(buildU16(gameName[13], gameName[12]), - buildU16(gameName[11], gameName[10])), - buildU32(buildU16(userName[3], userName[2]), - buildU16(userName[1], userName[0])), - buildU32(buildU16(userName[7], userName[6]), - buildU16(userName[5], userName[4]))}; + + char finalGameName[LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH + 1]; + char finalUserName[LINK_RAW_WIRELESS_MAX_USER_NAME_LENGTH + 1]; + copyName(finalGameName, gameName, LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH); + copyName(finalUserName, userName, LINK_RAW_WIRELESS_MAX_USER_NAME_LENGTH); + + auto broadcastData = std::vector{ + buildU32(buildU16(finalGameName[1], finalGameName[0]), gameId), + buildU32(buildU16(finalGameName[5], finalGameName[4]), + buildU16(finalGameName[3], finalGameName[2])), + buildU32(buildU16(finalGameName[9], finalGameName[8]), + buildU16(finalGameName[7], finalGameName[6])), + buildU32(buildU16(finalGameName[13], finalGameName[12]), + buildU16(finalGameName[11], finalGameName[10])), + buildU32(buildU16(finalUserName[3], finalUserName[2]), + buildU16(finalUserName[1], finalUserName[0])), + buildU32(buildU16(finalUserName[7], finalUserName[6]), + buildU16(finalUserName[5], finalUserName[4]))}; bool success = sendCommand(LINK_RAW_WIRELESS_COMMAND_BROADCAST, broadcastData).success; @@ -319,12 +320,15 @@ class LinkRawWireless { server.id = (u16)result.responses[start]; server.gameId = result.responses[start + 1] & LINK_RAW_WIRELESS_MAX_GAME_ID; - recoverName(server.gameName, result.responses[start + 1], false); - recoverName(server.gameName, result.responses[start + 2]); - recoverName(server.gameName, result.responses[start + 3]); - recoverName(server.gameName, result.responses[start + 4]); - recoverName(server.userName, result.responses[start + 5]); - recoverName(server.userName, result.responses[start + 6]); + u32 gameI = 0, userI = 0; + recoverName(server.gameName, gameI, result.responses[start + 1], false); + recoverName(server.gameName, gameI, result.responses[start + 2]); + recoverName(server.gameName, gameI, result.responses[start + 3]); + recoverName(server.gameName, gameI, result.responses[start + 4]); + recoverName(server.userName, userI, result.responses[start + 5]); + recoverName(server.userName, userI, result.responses[start + 6]); + server.gameName[gameI] = '\0'; + server.userName[userI] = '\0'; server.nextClientNumber = (result.responses[start] >> 16) & 0xff; servers.push_back(server); @@ -653,24 +657,35 @@ class LinkRawWireless { State state = NEEDS_RESET; volatile bool isEnabled = false; - void recoverName(std::string& name, + void copyName(char* target, const char* source, u32 length) { + u32 len = std::strlen(source); + + for (u32 i = 0; i < length + 1; i++) + if (i < len) + target[i] = source[i]; + else + target[i] = '\0'; + } + + void recoverName(char* name, + u32& nameCursor, u32 word, bool includeFirstTwoBytes = true) { u32 character = 0; if (includeFirstTwoBytes) { character = lsB16(lsB32(word)); if (character > 0) - name.push_back(character); + name[nameCursor++] = character; character = msB16(lsB32(word)); if (character > 0) - name.push_back(character); + name[nameCursor++] = character; } character = lsB16(msB32(word)); if (character > 0) - name.push_back(character); + name[nameCursor++] = character; character = msB16(msB32(word)); if (character > 0) - name.push_back(character); + name[nameCursor++] = character; } bool reset(bool initialize = false) { From f10ad006ae07acc8222cb124ae7fbf6aa2b2dd16 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 30 Jan 2024 09:01:46 -0300 Subject: [PATCH 18/74] Migrating LinkRawWireless to static arrays --- .../src/scenes/DebugScene.cpp | 44 +++-- .../src/scenes/MultibootScene.cpp | 6 +- lib/LinkRawWireless.hpp | 170 +++++++++++------- lib/LinkWirelessMultiboot.hpp | 133 ++++++++------ 4 files changed, 216 insertions(+), 137 deletions(-) diff --git a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp index a80443b..76b75ea 100644 --- a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp +++ b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp @@ -111,6 +111,14 @@ void log(std::string string) { scrollPageDown(); } +std::array toArray( + std::vector vector) { + std::array array; + for (u32 i = 0; i < vector.size(); i++) + array[i] = vector[i]; + return array; +} + std::vector DebugScene::backgrounds() { return {}; } @@ -473,7 +481,7 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { if (success) { log("< [next slot] " + linkRawWireless->toHex(response.nextClientNumber, 2)); - for (u32 i = 0; i < response.connectedClients.size(); i++) { + for (u32 i = 0; i < response.connectedClientsSize; i++) { log("< [client" + std::to_string(response.connectedClients[i].clientNumber) + "] " + @@ -538,7 +546,7 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { bool success = linkRawWireless->acceptConnections(response); if (success) { - for (u32 i = 0; i < response.connectedClients.size(); i++) { + for (u32 i = 0; i < response.connectedClientsSize; i++) { log("< [client" + std::to_string(response.connectedClients[i].clientNumber) + "] " + @@ -556,7 +564,7 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { bool success = linkRawWireless->endHost(response); if (success) { - for (u32 i = 0; i < response.connectedClients.size(); i++) { + for (u32 i = 0; i < response.connectedClientsSize; i++) { log("< [client" + std::to_string(response.connectedClients[i].clientNumber) + "] " + @@ -580,11 +588,12 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { } case 0x1d: { return logOperation("sending " + name, [this]() { - std::vector servers; - bool success = linkRawWireless->broadcastReadPoll(servers); + LinkRawWireless::BroadcastReadPollResponse response; + bool success = linkRawWireless->broadcastReadPoll(response); if (success) { - for (u32 i = 0; i < servers.size(); i++) { + auto servers = response.servers; + for (u32 i = 0; i < response.serversSize; i++) { serverIds[i] = servers[i].id; log("< [room" + std::to_string(i) + ".id] " + @@ -661,7 +670,7 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { data.erase(data.begin()); return logOperation("sending " + name, [&data, bytes]() { - return linkRawWireless->sendData(data, bytes); + return linkRawWireless->sendData(toArray(data), data.size(), bytes); }); } case 0x25: { @@ -673,12 +682,12 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { return logOperation("sending " + name, [&data, bytes]() { LinkRawWireless::RemoteCommand remoteCommand; - bool success = - linkRawWireless->sendDataAndWait(data, remoteCommand, bytes); + bool success = linkRawWireless->sendDataAndWait( + toArray(data), data.size(), remoteCommand, bytes); if (success) { log("< [notif] " + linkRawWireless->toHex(remoteCommand.commandId)); - for (u32 i = 0; i < remoteCommand.params.size(); i++) { + for (u32 i = 0; i < remoteCommand.paramsSize; i++) { log("< [param" + std::to_string(i) + "] " + linkRawWireless->toHex(remoteCommand.params[i])); } @@ -699,7 +708,7 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { log("< [bytesC2] " + std::to_string(response.sentBytes[3])); log("< [bytesC3] " + std::to_string(response.sentBytes[4])); - for (u32 i = 0; i < response.data.size(); i++) + for (u32 i = 0; i < response.dataSize; i++) log("< [data" + std::to_string(i) + "] " + linkRawWireless->toHex(response.data[i])); } @@ -714,7 +723,7 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { if (success) { log("< [notif] " + linkRawWireless->toHex(remoteCommand.commandId)); - for (u32 i = 0; i < remoteCommand.params.size(); i++) { + for (u32 i = 0; i < remoteCommand.paramsSize; i++) { log("< [param" + std::to_string(i) + "] " + linkRawWireless->toHex(remoteCommand.params[i])); } @@ -873,8 +882,8 @@ std::string DebugScene::selectUserName() { void DebugScene::logGenericWaitCommand(std::string name, u32 id) { auto data = selectData(); return logOperation("sending " + name, [id, &data]() { - auto result = linkRawWireless->sendCommand(id, data); - for (u32 i = 0; i < result.responses.size(); i++) { + auto result = linkRawWireless->sendCommand(id, toArray(data), data.size()); + for (u32 i = 0; i < result.responsesSize; i++) { log("< [response" + std::to_string(i) + "] " + linkRawWireless->toHex(result.responses[i])); } @@ -889,7 +898,7 @@ void DebugScene::logGenericWaitCommand(std::string name, u32 id) { if (remoteCommand.success) { log("< [notif] " + linkRawWireless->toHex(remoteCommand.commandId)); - for (u32 i = 0; i < remoteCommand.params.size(); i++) { + for (u32 i = 0; i < remoteCommand.paramsSize; i++) { log("< [param" + std::to_string(i) + "] " + linkRawWireless->toHex(remoteCommand.params[i])); } @@ -908,8 +917,9 @@ void DebugScene::logSimpleCommand(std::string name, u32 id, std::vector params) { logOperation("sending " + name, [id, ¶ms]() { - auto result = linkRawWireless->sendCommand(id, params); - for (u32 i = 0; i < result.responses.size(); i++) { + auto result = + linkRawWireless->sendCommand(id, toArray(params), params.size()); + for (u32 i = 0; i < result.responsesSize; i++) { log("< [response" + std::to_string(i) + "] " + linkRawWireless->toHex(result.responses[i])); } diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index 68ea64b..7baeb6e 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include "../../../../lib/LinkWirelessMultiboot.hpp" @@ -188,8 +187,9 @@ void MultibootScene::processButtons() { const u8* romToSend = (const u8*)gbfs_get_obj(fs, ROM_FILE_NAME, &fileLength); - linkWirelessMultiboot->sendRom(romToSend, fileLength, "Multi", "Test", 2, - []() { return false; }); + linkWirelessMultiboot->sendRom(romToSend, fileLength, "Multi", "Test", + 0xffff, 2, + [](int connectedPlayers) { return false; }); print(); } diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 1751ec0..bc842a7 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -10,8 +10,8 @@ #include #include +#include #include -#include // TODO: ARRAY #include "LinkGPIO.hpp" #include "LinkSPI.hpp" @@ -29,6 +29,7 @@ #define LINK_RAW_WIRELESS_PING_WAIT 50 #define LINK_RAW_WIRELESS_TRANSFER_WAIT 15 #define LINK_RAW_WIRELESS_CMD_TIMEOUT 100 +#define LINK_RAW_WIRELESS_MAX_COMMAND_RESPONSE_LENGTH 30 #define LINK_RAW_WIRELESS_MAX_CLIENT_TRANSFER_LENGTH 4 #define LINK_RAW_WIRELESS_MAX_GAME_ID 0x7fff #define LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH 14 @@ -43,7 +44,10 @@ #define LINK_RAW_WIRELESS_BROADCAST_LENGTH 6 #define LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH \ (1 + LINK_RAW_WIRELESS_BROADCAST_LENGTH) -#define LINK_RAW_WIRELESS_MAX_COMMAND_TRANSFER_LENGTH 22 +#define LINK_RAW_WIRELESS_MAX_COMMAND_TRANSFER_LENGTH 23 +#define LINK_RAW_WIRELESS_MAX_SERVERS \ + (LINK_RAW_WIRELESS_MAX_COMMAND_RESPONSE_LENGTH / \ + LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH) #define LINK_RAW_WIRELESS_COMMAND_HELLO 0x10 #define LINK_RAW_WIRELESS_COMMAND_SETUP 0x17 #define LINK_RAW_WIRELESS_COMMAND_BROADCAST 0x16 @@ -85,13 +89,16 @@ class LinkRawWireless { struct CommandResult { bool success = false; - std::vector responses = std::vector{}; + std::array responses = + {}; + u32 responsesSize = 0; }; struct RemoteCommand { bool success = false; u8 commandId = 0; - std::vector params = std::vector{}; + std::array params = {}; + u32 paramsSize = 0; }; struct Server { @@ -111,13 +118,22 @@ class LinkRawWireless { typedef struct { u8 nextClientNumber = 0; - std::vector connectedClients = {}; + std::array + connectedClients = {}; + u32 connectedClientsSize = 0; } SlotStatusResponse; typedef struct { - std::vector connectedClients = {}; + std::array + connectedClients = {}; + u32 connectedClientsSize = 0; } AcceptConnectionsResponse; + typedef struct { + std::array servers; + u32 serversSize = 0; + } BroadcastReadPollResponse; + enum ConnectionPhase { STILL_CONNECTING, ERROR, SUCCESS }; typedef struct { @@ -127,7 +143,8 @@ class LinkRawWireless { typedef struct { u32 sentBytes[LINK_RAW_WIRELESS_MAX_PLAYERS]; - std::vector data = {}; + std::array data = {}; + u32 dataSize = 0; } ReceiveDataResponse; bool isActive() { return isEnabled; } @@ -155,10 +172,10 @@ class LinkRawWireless { u32 magic = LINK_RAW_WIRELESS_SETUP_MAGIC) { return sendCommand( LINK_RAW_WIRELESS_COMMAND_SETUP, - std::vector{ - (u32)(magic | - (((LINK_RAW_WIRELESS_MAX_PLAYERS - maxPlayers) & 0b11) - << LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT))}) + {(u32)(magic | + (((LINK_RAW_WIRELESS_MAX_PLAYERS - maxPlayers) & 0b11) + << LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT))}, + 1) .success; } @@ -179,21 +196,22 @@ class LinkRawWireless { copyName(finalGameName, gameName, LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH); copyName(finalUserName, userName, LINK_RAW_WIRELESS_MAX_USER_NAME_LENGTH); - auto broadcastData = std::vector{ - buildU32(buildU16(finalGameName[1], finalGameName[0]), gameId), - buildU32(buildU16(finalGameName[5], finalGameName[4]), - buildU16(finalGameName[3], finalGameName[2])), - buildU32(buildU16(finalGameName[9], finalGameName[8]), - buildU16(finalGameName[7], finalGameName[6])), - buildU32(buildU16(finalGameName[13], finalGameName[12]), - buildU16(finalGameName[11], finalGameName[10])), - buildU32(buildU16(finalUserName[3], finalUserName[2]), - buildU16(finalUserName[1], finalUserName[0])), - buildU32(buildU16(finalUserName[7], finalUserName[6]), - buildU16(finalUserName[5], finalUserName[4]))}; - bool success = - sendCommand(LINK_RAW_WIRELESS_COMMAND_BROADCAST, broadcastData).success; + sendCommand( + LINK_RAW_WIRELESS_COMMAND_BROADCAST, + {buildU32(buildU16(finalGameName[1], finalGameName[0]), gameId), + buildU32(buildU16(finalGameName[5], finalGameName[4]), + buildU16(finalGameName[3], finalGameName[2])), + buildU32(buildU16(finalGameName[9], finalGameName[8]), + buildU16(finalGameName[7], finalGameName[6])), + buildU32(buildU16(finalGameName[13], finalGameName[12]), + buildU16(finalGameName[11], finalGameName[10])), + buildU32(buildU16(finalUserName[3], finalUserName[2]), + buildU16(finalUserName[1], finalUserName[0])), + buildU32(buildU16(finalUserName[7], finalUserName[6]), + buildU16(finalUserName[5], finalUserName[4]))}, + LINK_RAW_WIRELESS_BROADCAST_LENGTH) + .success; if (!success) { reset(); @@ -226,13 +244,13 @@ class LinkRawWireless { return false; } - for (u32 i = 0; i < result.responses.size(); i++) { + for (u32 i = 0; i < result.responsesSize; i++) { if (i == 0) { response.nextClientNumber = (u8)lsB32(result.responses[i]); } else { - response.connectedClients.push_back( + response.connectedClients[response.connectedClientsSize++] = ConnectedClient{.deviceId = lsB32(result.responses[i]), - .clientNumber = (u8)msB32(result.responses[i])}); + .clientNumber = (u8)msB32(result.responses[i])}; } } @@ -247,14 +265,14 @@ class LinkRawWireless { return false; } - for (u32 i = 0; i < result.responses.size(); i++) { - response.connectedClients.push_back( + for (u32 i = 0; i < result.responsesSize; i++) { + response.connectedClients[response.connectedClientsSize++] = ConnectedClient{.deviceId = lsB32(result.responses[i]), - .clientNumber = (u8)msB32(result.responses[i])}); + .clientNumber = (u8)msB32(result.responses[i])}; } u8 oldPlayerCount = sessionState.playerCount; - sessionState.playerCount = 1 + result.responses.size(); + sessionState.playerCount = 1 + result.responsesSize; if (sessionState.playerCount != oldPlayerCount) LRWLOG("now: " + std::to_string(sessionState.playerCount) + " players"); @@ -269,14 +287,14 @@ class LinkRawWireless { return false; } - for (u32 i = 0; i < result.responses.size(); i++) { - response.connectedClients.push_back( + for (u32 i = 0; i < result.responsesSize; i++) { + response.connectedClients[response.connectedClientsSize++] = ConnectedClient{.deviceId = lsB32(result.responses[i]), - .clientNumber = (u8)msB32(result.responses[i])}); + .clientNumber = (u8)msB32(result.responses[i])}; } u8 oldPlayerCount = sessionState.playerCount; - sessionState.playerCount = 1 + result.responses.size(); + sessionState.playerCount = 1 + result.responsesSize; if (sessionState.playerCount != oldPlayerCount) LRWLOG("now: " + std::to_string(sessionState.playerCount) + " players"); @@ -298,12 +316,11 @@ class LinkRawWireless { return true; } - bool broadcastReadPoll(std::vector& servers) { + bool broadcastReadPoll(BroadcastReadPollResponse response) { auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_BROADCAST_READ_POLL); bool success = result.success && - result.responses.size() % LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH == - 0; + result.responsesSize % LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH == 0; if (!success) { reset(); @@ -311,7 +328,7 @@ class LinkRawWireless { } u32 totalBroadcasts = - result.responses.size() / LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH; + result.responsesSize / LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH; for (u32 i = 0; i < totalBroadcasts; i++) { u32 start = LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH * i; @@ -331,7 +348,7 @@ class LinkRawWireless { server.userName[userI] = '\0'; server.nextClientNumber = (result.responses[start] >> 16) & 0xff; - servers.push_back(server); + response.servers[response.serversSize++] = server; } LRWLOG("state = AUTHENTICATED"); @@ -353,9 +370,8 @@ class LinkRawWireless { } bool connect(u16 serverId) { - bool success = sendCommand(LINK_RAW_WIRELESS_COMMAND_CONNECT, - std::vector{serverId}) - .success; + bool success = + sendCommand(LINK_RAW_WIRELESS_COMMAND_CONNECT, {serverId}, 1).success; if (!success) { reset(); @@ -370,8 +386,8 @@ class LinkRawWireless { bool keepConnecting(ConnectionStatus& response) { auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_IS_FINISHED_CONNECT); - if (!result.success || result.responses.size() == 0) { - if (result.responses.size() == 0) + if (!result.success || result.responsesSize == 0) { + if (result.responsesSize == 0) LRWLOG("! empty response"); reset(); return false; @@ -398,8 +414,8 @@ class LinkRawWireless { bool finishConnection() { auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_FINISH_CONNECTION); - if (!result.success || result.responses.size() == 0) { - if (result.responses.size() == 0) + if (!result.success || result.responsesSize == 0) { + if (result.responsesSize == 0) LRWLOG("! empty response"); reset(); return false; @@ -420,16 +436,23 @@ class LinkRawWireless { return true; } - bool sendData(std::vector data, u32 _bytes = 0) { - u32 bytes = _bytes == 0 ? data.size() * 4 : _bytes; + bool sendData( + std::array data, + u32 dataSize, + u32 _bytes = 0) { + u32 bytes = _bytes == 0 ? dataSize * 4 : _bytes; u32 header = sessionState.currentPlayerId == 0 ? bytes : (bytes << (3 + sessionState.currentPlayerId * 5)); - data.insert(data.begin(), header); + for (u32 i = dataSize; i > 0; i--) + data[i] = data[i - 1]; + data[0] = header; + dataSize++; LRWLOG("using header " + toHex(header)); bool success = - sendCommand(LINK_RAW_WIRELESS_COMMAND_SEND_DATA, data).success; + sendCommand(LINK_RAW_WIRELESS_COMMAND_SEND_DATA, data, dataSize) + .success; if (!success) { reset(); @@ -439,17 +462,23 @@ class LinkRawWireless { return true; } - bool sendDataAndWait(std::vector data, - RemoteCommand& remoteCommand, - u32 _bytes = 0) { - u32 bytes = _bytes == 0 ? data.size() * 4 : _bytes; + bool sendDataAndWait( + std::array data, + u32 dataSize, + RemoteCommand& remoteCommand, + u32 _bytes = 0) { + u32 bytes = _bytes == 0 ? dataSize * 4 : _bytes; u32 header = sessionState.currentPlayerId == 0 ? bytes : (bytes << (3 + sessionState.currentPlayerId * 5)); - data.insert(data.begin(), header); + for (u32 i = dataSize; i > 0; i--) + data[i] = data[i - 1]; + data[0] = header; + dataSize++; LRWLOG("using header " + toHex(header)); - if (!sendCommand(LINK_RAW_WIRELESS_COMMAND_SEND_DATA_AND_WAIT, data) + if (!sendCommand(LINK_RAW_WIRELESS_COMMAND_SEND_DATA_AND_WAIT, data, + dataSize) .success) { reset(); return false; @@ -462,7 +491,9 @@ class LinkRawWireless { bool receiveData(ReceiveDataResponse& response) { auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_RECEIVE_DATA); - response.data = result.responses; + for (u32 i = 0; i < result.responsesSize; i++) + response.data[i] = result.responses[i]; + response.dataSize = result.responsesSize; if (!result.success) { reset(); @@ -472,9 +503,11 @@ class LinkRawWireless { for (u32 i = 0; i < LINK_RAW_WIRELESS_MAX_PLAYERS; i++) response.sentBytes[i] = 0; - if (response.data.size() > 0) { + if (response.dataSize > 0) { u32 header = response.data[0]; - response.data.erase(response.data.begin()); + for (u32 i = 1; i < response.dataSize; i++) + response.data[i - 1] = response.data[i]; + response.dataSize--; response.sentBytes[0] = header & 0b1111111; response.sentBytes[1] = (header >> 8) & 0b11111; response.sentBytes[2] = (header >> 13) & 0b11111; @@ -496,10 +529,12 @@ class LinkRawWireless { return remoteCommand.success; } - CommandResult sendCommand(u8 type, - std::vector params = std::vector{}) { + CommandResult sendCommand( + u8 type, + std::array params = + {}, + u16 length = 0) { CommandResult result; - u16 length = params.size(); u32 command = buildCommand(type, length); u32 r; @@ -510,7 +545,8 @@ class LinkRawWireless { } u32 parameterCount = 0; - for (auto& param : params) { + for (u32 i = 0; i < length; i++) { + u32 param = params[i]; LRWLOG("sending param" + std::to_string(parameterCount) + ": 0x" + toHex(param)); if ((r = transfer(param)) != LINK_RAW_WIRELESS_DATA_REQUEST) { @@ -551,7 +587,7 @@ class LinkRawWireless { LRWLOG("response " + std::to_string(i + 1) + "/" + std::to_string(responses) + ":"); u32 responseData = transfer(LINK_RAW_WIRELESS_DATA_REQUEST); - result.responses.push_back(responseData); + result.responses[result.responsesSize++] = responseData; LRWLOG("<< " + toHex(responseData)); } @@ -596,7 +632,7 @@ class LinkRawWireless { reset(); return remoteCommand; } - remoteCommand.params.push_back(paramData); + remoteCommand.params[remoteCommand.paramsSize++] = paramData; LRWLOG("<< " + toHex(paramData)); } diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index ea1140d..c4accae 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -39,6 +39,9 @@ #define LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS 5 #define LINK_WIRELESS_MULTIBOOT_HEADER_SIZE 0xC0 #define LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC 0x003F0120 +#define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG 0b1000000000000000 +#define LINK_WIRELESS_MULTIBOOT_FRAME_LINES 228 +#define LINK_WIRELESS_MULTIBOOT_FRAMES_BEFORE_HANDSHAKE 20 // ~300ms static volatile char LINK_WIRELESS_MULTIBOOT_VERSION[] = "LinkWirelessMultiboot/v6.2.0"; @@ -88,6 +91,7 @@ class LinkWirelessMultiboot { u32 romSize, const char* gameName, const char* userName, + const u16 gameId, u8 players, F cancel) { if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) @@ -98,49 +102,33 @@ class LinkWirelessMultiboot { players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) return INVALID_PLAYERS; - if (!link->activate()) { - LWMLOG("! adapter not detected"); + if (!activate()) return ADAPTER_NOT_DETECTED; - } - LWMLOG("activated"); - - if (!link->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { - LWMLOG("! setup failed"); - return FAILURE; - } - LWMLOG("setup ok"); - bool success; // TODO: REMOVE - success = link->broadcast("Multi", "Test", 0b1111111111111111); - if (!success) { - LWMLOG("broadcast failed"); + if (!initialize(gameName, userName, gameId, players)) return FAILURE; - } - LWMLOG("broadcast set"); - - success = link->startHost(); - if (!success) { - LWMLOG("start host failed"); - return FAILURE; - } - LWMLOG("host started"); LinkRawWireless::AcceptConnectionsResponse acceptResponse; - while (link->playerCount() == 1) { + while (link->playerCount() < players) { link->acceptConnections(acceptResponse); + if (cancel(players)) { + link->deactivate(); + return CANCELED; + } } - LWMLOG("connected!"); - linkRawWireless->wait(228 * 20); // ~300ms + LWMLOG("connected!"); // TODO: MOVE HANDSHAKE TO WHILE + linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES * + LINK_WIRELESS_MULTIBOOT_FRAMES_BEFORE_HANDSHAKE); // HANDSHAKE bool hasData = false; LinkRawWireless::ReceiveDataResponse response; while (!hasData) { - if (!sendAndExpectData(std::vector{}, 1, response)) + if (!sendAndExpectData(toArray(), 0, 1, response)) return FAILURE; - hasData = response.data.size() > 0; + hasData = response.dataSize > 0; } LWMLOG("data received"); @@ -157,10 +145,10 @@ class LinkWirelessMultiboot { serverHeader = createACKFor(clientHeader); u32 sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) return FAILURE; - if (response.data.size() == 0) { + if (response.dataSize == 0) { goto firstack; } clientHeader = parseClientHeader(response.data[0]); @@ -177,10 +165,10 @@ class LinkWirelessMultiboot { secondack: serverHeader = createACKFor(clientHeader); sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) return FAILURE; - if (response.data.size() == 0) { + if (response.dataSize == 0) { goto secondack; } clientHeader = parseClientHeader(response.data[0]); @@ -198,7 +186,7 @@ class LinkWirelessMultiboot { link->wait(228); serverHeader = createACKFor(clientHeader); sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); @@ -218,8 +206,7 @@ class LinkWirelessMultiboot { serverHeader.phase = 0; serverHeader.slotState = 1; sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(std::vector{sndHeader, 0x54, 0x02}, 10, - response)) + if (!sendAndExpectData(toArray(sndHeader, 0x54, 0x02), 3, 10, response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); if (clientHeader.isACK == 1 && clientHeader.n == 1 && @@ -233,10 +220,10 @@ class LinkWirelessMultiboot { u32 transferredBytes = 0; u32 n = 1; u32 phase = 0; - bool isRetry = false; + // bool isRetry = false; u32 progress = 0; while (transferredBytes < romSize) { - isRetry = false; + // isRetry = false; retry: serverHeader.isACK = 0; serverHeader.targetSlots = 0b0001; // TODO: Implement @@ -245,8 +232,9 @@ class LinkWirelessMultiboot { serverHeader.phase = phase; serverHeader.slotState = 2; sndHeader = serializeServerHeader(serverHeader); - std::vector data; - data.push_back(sndHeader | (rom[transferredBytes] << 24)); + std::array data; + u32 dataSize = 0; + data[dataSize++] = sndHeader | (rom[transferredBytes] << 24); // if (!isRetry) // bytes.push_back(rom[transferredBytes]); for (u32 i = 1; i < 84; i += 4) { @@ -259,15 +247,15 @@ class LinkWirelessMultiboot { // bytes.push_back(byte); } } - data.push_back(d); + data[dataSize++] = d; } LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData(data, 87, response)) { + if (!sendAndExpectData(data, 22, 87, response)) { LWMLOG("SendData failed!"); return FAILURE; } - if (response.data.size() == 0) { - isRetry = true; + if (response.dataSize == 0) { + // isRetry = true; goto retry; } @@ -288,7 +276,7 @@ class LinkWirelessMultiboot { LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); } } else { - isRetry = true; + // isRetry = true; goto retry; } } @@ -307,7 +295,7 @@ class LinkWirelessMultiboot { serverHeader.phase = 0; serverHeader.slotState = 3; sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); if (clientHeader.isACK == 1 && clientHeader.n == 0 && @@ -329,7 +317,7 @@ class LinkWirelessMultiboot { serverHeader.phase = 0; serverHeader.slotState = 0; sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(std::vector{sndHeader}, 3, response)) + if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); // if (clientHeader.slotState == 0) @@ -362,12 +350,51 @@ class LinkWirelessMultiboot { ClientSDKHeader lastACK; private: - bool sendAndExpectData(std::vector data, - u32 _bytes, - LinkRawWireless::ReceiveDataResponse& response) { + bool activate() { + if (!link->activate()) { + LWMLOG("! adapter not detected"); + return false; + } + LWMLOG("activated"); + + return true; + } + + bool initialize(const char* gameName, + const char* userName, + const u16 gameId, + u8 players) { + if (!link->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { + LWMLOG("! setup failed"); + return false; + } + LWMLOG("setup ok"); + + if (!link->broadcast( + gameName, userName, + gameId | LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG)) { + LWMLOG("! broadcast failed"); + return false; + } + LWMLOG("broadcast data set"); + + if (!link->startHost()) { + LWMLOG("! start host failed"); + return false; + } + LWMLOG("host started"); + + return true; + } + + bool sendAndExpectData( + std::array data, + u32 dataSize, + u32 _bytes, + LinkRawWireless::ReceiveDataResponse& response) { LinkRawWireless::RemoteCommand remoteCommand; bool success = false; - success = link->sendDataAndWait(data, remoteCommand, _bytes); + success = link->sendDataAndWait(data, dataSize, remoteCommand, _bytes); if (!success) { LWMLOG("senddatawait no"); return false; @@ -408,6 +435,12 @@ class LinkWirelessMultiboot { serverSerializer.asStruct = serverHeader; return serverSerializer.asInt & 0xffffff; } + + template + std::array toArray( + Args... args) { + return {static_cast(args)...}; + } }; extern LinkWirelessMultiboot* linkWirelessMultiboot; From ee39f8cda4b3163c39a7b692941326608d127cfc Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Wed, 31 Jan 2024 06:06:43 -0300 Subject: [PATCH 19/74] Starting refactor (moving SDK protocol to its own class) --- lib/LinkWirelessMultiboot.hpp | 90 +++++----------- lib/LinkWirelessOpenSDK.hpp | 186 ++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 67 deletions(-) create mode 100644 lib/LinkWirelessOpenSDK.hpp diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index c4accae..40984dc 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -23,9 +23,10 @@ #include #include "LinkRawWireless.hpp" +#include "LinkWirelessOpenSDK.hpp" // Enable logging (set `linkWirelessMultiboot->logger` and uncomment to enable) -// #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING +#define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING // TODO: DISABLE #ifdef LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING #define LWMLOG(str) logger(str) @@ -60,32 +61,6 @@ class LinkWirelessMultiboot { FAILURE }; - struct ServerSDKHeader { - unsigned int payloadSize : 7; - unsigned int _unused_ : 2; - unsigned int phase : 2; - unsigned int n : 2; - unsigned int isACK : 1; - unsigned int slotState : 4; - unsigned int targetSlots : 4; - }; - union ServerSDKHeaderSerializer { - ServerSDKHeader asStruct; - u32 asInt; - }; - - struct ClientSDKHeader { - unsigned int payloadSize : 5; - unsigned int phase : 2; - unsigned int n : 2; - unsigned int isACK : 1; - unsigned int slotState : 4; - }; - union ClientSDKHeaderSerializer { - ClientSDKHeader asStruct; - u16 asInt; - }; - template Result sendRom(const u8* rom, u32 romSize, @@ -132,7 +107,8 @@ class LinkWirelessMultiboot { } LWMLOG("data received"); - ClientSDKHeader clientHeader = parseClientHeader(response.data[0]); + LinkWirelessOpenSDK::ClientSDKHeader clientHeader = + parseClientHeader(response.data[0]); LWMLOG("client size: " + std::to_string(clientHeader.payloadSize)); LWMLOG("n: " + std::to_string(clientHeader.n)); LWMLOG("phase: " + std::to_string(clientHeader.phase)); @@ -141,7 +117,7 @@ class LinkWirelessMultiboot { LWMLOG("sending ACK"); firstack: - ServerSDKHeader serverHeader; + LinkWirelessOpenSDK::ServerSDKHeader serverHeader; serverHeader = createACKFor(clientHeader); u32 sndHeader = serializeServerHeader(serverHeader); @@ -225,32 +201,11 @@ class LinkWirelessMultiboot { while (transferredBytes < romSize) { // isRetry = false; retry: - serverHeader.isACK = 0; - serverHeader.targetSlots = 0b0001; // TODO: Implement - serverHeader.payloadSize = 84; // 87 - 3 (serversdkheader) - serverHeader.n = n; - serverHeader.phase = phase; - serverHeader.slotState = 2; - sndHeader = serializeServerHeader(serverHeader); - std::array data; - u32 dataSize = 0; - data[dataSize++] = sndHeader | (rom[transferredBytes] << 24); - // if (!isRetry) - // bytes.push_back(rom[transferredBytes]); - for (u32 i = 1; i < 84; i += 4) { - u32 d = 0; - for (u32 j = 0; j < 4; j++) { - if (transferredBytes + i + j < romSize && i + j < 84) { - u8 byte = rom[transferredBytes + i + j]; - d |= byte << (j * 8); - // if (!isRetry) - // bytes.push_back(byte); - } - } - data[dataSize++] = d; - } + auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( + rom, romSize, n, phase, 2, transferredBytes, 0b0001); LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData(data, 22, 87, response)) { + if (!sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, + sendBuffer.totalByteCount, response)) { LWMLOG("SendData failed!"); return FAILURE; } @@ -343,11 +298,15 @@ class LinkWirelessMultiboot { return SUCCESS; } - ~LinkWirelessMultiboot() { delete link; } + ~LinkWirelessMultiboot() { + delete link; + delete linkWirelessOpenSDK; + } LinkRawWireless* link = new LinkRawWireless(); + LinkWirelessOpenSDK* linkWirelessOpenSDK = new LinkWirelessOpenSDK(); - ClientSDKHeader lastACK; + LinkWirelessOpenSDK::ClientSDKHeader lastACK; private: bool activate() { @@ -412,8 +371,9 @@ class LinkWirelessMultiboot { return true; } - ServerSDKHeader createACKFor(ClientSDKHeader clientHeader) { - ServerSDKHeader serverHeader; + LinkWirelessOpenSDK::ServerSDKHeader createACKFor( + LinkWirelessOpenSDK::ClientSDKHeader clientHeader) { + LinkWirelessOpenSDK::ServerSDKHeader serverHeader; serverHeader.isACK = 1; serverHeader.targetSlots = 0b0001; // TODO: Implement serverHeader.payloadSize = 0; @@ -424,16 +384,12 @@ class LinkWirelessMultiboot { return serverHeader; } - ClientSDKHeader parseClientHeader(u32 clientHeaderInt) { - ClientSDKHeaderSerializer clientSerializer; - clientSerializer.asInt = clientHeaderInt; - return clientSerializer.asStruct; + LinkWirelessOpenSDK::ClientSDKHeader parseClientHeader(u32 clientHeaderInt) { + return linkWirelessOpenSDK->parseClientHeader(clientHeaderInt); } - u32 serializeServerHeader(ServerSDKHeader serverHeader) { - ServerSDKHeaderSerializer serverSerializer; - serverSerializer.asStruct = serverHeader; - return serverSerializer.asInt & 0xffffff; + u32 serializeServerHeader(LinkWirelessOpenSDK::ServerSDKHeader serverHeader) { + return linkWirelessOpenSDK->serializeServerHeader(serverHeader); } template @@ -447,4 +403,4 @@ extern LinkWirelessMultiboot* linkWirelessMultiboot; #undef LWMLOG -#endif // LINK_WIRELESS_MULTIBOOT_H +#endif // LINK_WIRELESS_MULTIBOOT_H \ No newline at end of file diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp new file mode 100644 index 0000000..f3c23b6 --- /dev/null +++ b/lib/LinkWirelessOpenSDK.hpp @@ -0,0 +1,186 @@ +#ifndef LINK_WIRELESS_OPEN_SDK_H +#define LINK_WIRELESS_OPEN_SDK_H + +// -------------------------------------------------------------------------- +// An open-source implementation of the "official" Wireless Adapter protocol. +// -------------------------------------------------------------------------- +// TODO: Document +// -------------------------------------------------------------------------- + +#include +#include + +#define LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_WORDS 23 +#define LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_BYTES_SERVER 87 +#define LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_BYTES_CLIENT 16 +#define LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER 3 +#define LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT 2 +#define LINK_WIRELESS_OPEN_SDK_HEADER_MASK_SERVER \ + ((1 << (LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER * 8)) - 1) +#define LINK_WIRELESS_OPEN_SDK_HEADER_MASK_CLIENT \ + ((1 << (LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT * 8)) - 1) +#define LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER \ + (LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_BYTES_SERVER - \ + LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER) +#define LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT \ + (LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_BYTES_CLIENT - \ + LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT) +#define LINK_WIRELESS_OPEN_SDK_MAX_PACKETS_SERVER \ + (LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_BYTES_SERVER / \ + LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER) +#define LINK_WIRELESS_OPEN_SDK_MAX_PACKETS_CLIENT \ + (LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_BYTES_CLIENT / \ + LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT) + +static volatile char LINK_WIRELESS_OPEN_SDK_VERSION[] = + "LinkWirelessOpenSDK/v6.2.0"; + +class LinkWirelessOpenSDK { + public: + template + struct SendBuffer { + T header; + std::array data; + u32 dataSize = 0; + u32 totalByteCount = 0; + }; + + struct ServerSDKHeader { + unsigned int payloadSize : 7; + unsigned int _unused_ : 2; + unsigned int phase : 2; + unsigned int n : 2; + unsigned int isACK : 1; + unsigned int slotState : 4; + unsigned int targetSlots : 4; + }; + union ServerSDKHeaderSerializer { + ServerSDKHeader asStruct; + u32 asInt; + }; + struct ServerPacket { + ServerSDKHeader header; + u8 payload[LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER]; + }; + struct ServerResponse { + ServerPacket packets[LINK_WIRELESS_OPEN_SDK_MAX_PACKETS_SERVER]; + u32 packetsSize = 0; + }; + struct ParentData { + ServerResponse response; + }; + + struct ClientSDKHeader { + unsigned int payloadSize : 5; + unsigned int phase : 2; + unsigned int n : 2; + unsigned int isACK : 1; + unsigned int slotState : 4; + }; + union ClientSDKHeaderSerializer { + ClientSDKHeader asStruct; + u16 asInt; + }; + struct ClientPacket { + ClientSDKHeader header; + u8 payload[LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT]; + }; + struct ClientResponse { + ClientPacket packets[LINK_WIRELESS_OPEN_SDK_MAX_PACKETS_CLIENT]; + u32 packetsSize = 0; + }; + struct ChildrenData { + ClientResponse responses[4]; + }; + + SendBuffer createServerBuffer(const u8* fullPayload, + u32 fullPayloadSize, + u8 n, + u8 phase, + u8 slotState, + u32 offset, + u8 targetSlots) { + SendBuffer buffer; + u32 payloadSize = + min(fullPayloadSize, LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER); + + buffer.header.isACK = 0; + buffer.header.targetSlots = targetSlots; + buffer.header.payloadSize = payloadSize; + buffer.header.n = n; + buffer.header.phase = phase; + buffer.header.slotState = slotState; + u32 sndHeader = serializeServerHeader(buffer.header); + + if (offset < fullPayloadSize) + buffer.data[buffer.dataSize++] = (fullPayload[offset] << 24) | sndHeader; + + for (u32 i = 1; i < payloadSize; i += 4) { + u32 d = 0; + for (u32 j = 0; j < 4; j++) { + if (offset + i + j < fullPayloadSize && + i + j < LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER) { + u8 byte = fullPayload[offset + i + j]; + d |= byte << (j * 8); + } + } + buffer.data[buffer.dataSize++] = d; + } + + buffer.totalByteCount = + LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER + payloadSize; + + return buffer; + } + + ServerSDKHeader createACKFor(ClientSDKHeader clientHeader, u8 clientNumber) { + ServerSDKHeader serverHeader; + serverHeader.isACK = 1; + serverHeader.targetSlots = (1 << clientNumber); + serverHeader.payloadSize = 0; + serverHeader.n = clientHeader.n; + serverHeader.phase = clientHeader.phase; + serverHeader.slotState = clientHeader.slotState; + + return serverHeader; + } + + ClientSDKHeader createACKFor(ServerSDKHeader serverHeader) { + ClientSDKHeader clientHeader; + clientHeader.isACK = 1; + clientHeader.payloadSize = 0; + clientHeader.n = serverHeader.n; + clientHeader.phase = serverHeader.phase; + clientHeader.slotState = serverHeader.slotState; + + return clientHeader; + } + + ClientSDKHeader parseClientHeader(u32 clientHeaderInt) { + ClientSDKHeaderSerializer clientSerializer; + clientSerializer.asInt = + clientHeaderInt & LINK_WIRELESS_OPEN_SDK_HEADER_MASK_CLIENT; + return clientSerializer.asStruct; + } + + u16 serializeClientHeader(ClientSDKHeader clientHeader) { + ClientSDKHeaderSerializer clientSerializer; + clientSerializer.asStruct = clientHeader; + return clientSerializer.asInt & LINK_WIRELESS_OPEN_SDK_HEADER_MASK_CLIENT; + } + + ServerSDKHeader parseServerHeader(u32 serverHeaderInt) { + ServerSDKHeaderSerializer serverSerializer; + serverSerializer.asInt = + serverHeaderInt & LINK_WIRELESS_OPEN_SDK_HEADER_MASK_SERVER; + return serverSerializer.asStruct; + } + + u32 serializeServerHeader(ServerSDKHeader serverHeader) { + ServerSDKHeaderSerializer serverSerializer; + serverSerializer.asStruct = serverHeader; + return serverSerializer.asInt & LINK_WIRELESS_OPEN_SDK_HEADER_MASK_SERVER; + } +}; + +#endif // LINK_WIRELESS_OPEN_SDK_H From d0a139d6334b57e1c3e9b3108f165d1b0d5c8c76 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Wed, 31 Jan 2024 23:58:48 -0300 Subject: [PATCH 20/74] Removed typedefs from LinkRawWireless --- lib/LinkRawWireless.hpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index bc842a7..3b035f8 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -111,41 +111,41 @@ class LinkRawWireless { bool isFull() { return nextClientNumber == 0xff; } }; - typedef struct { + struct ConnectedClient { u16 deviceId = 0; u8 clientNumber = 0; - } ConnectedClient; + }; - typedef struct { + struct SlotStatusResponse { u8 nextClientNumber = 0; std::array connectedClients = {}; u32 connectedClientsSize = 0; - } SlotStatusResponse; + }; - typedef struct { + struct AcceptConnectionsResponse { std::array connectedClients = {}; u32 connectedClientsSize = 0; - } AcceptConnectionsResponse; + }; - typedef struct { + struct BroadcastReadPollResponse { std::array servers; u32 serversSize = 0; - } BroadcastReadPollResponse; + }; enum ConnectionPhase { STILL_CONNECTING, ERROR, SUCCESS }; - typedef struct { + struct ConnectionStatus { ConnectionPhase phase = STILL_CONNECTING; u8 assignedClientNumber = 0; - } ConnectionStatus; + }; - typedef struct { + struct ReceiveDataResponse { u32 sentBytes[LINK_RAW_WIRELESS_MAX_PLAYERS]; std::array data = {}; u32 dataSize = 0; - } ReceiveDataResponse; + }; bool isActive() { return isEnabled; } From 19237ea76c8eb981b36967909862b3ab57dd17e3 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 01:01:09 -0300 Subject: [PATCH 21/74] Using createServerBuffer for all communication --- lib/LinkWirelessMultiboot.hpp | 81 ++++++++++++++--------------------- lib/LinkWirelessOpenSDK.hpp | 66 +++++++++++++++++++++++----- 2 files changed, 86 insertions(+), 61 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 40984dc..9210cf0 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -44,6 +44,10 @@ #define LINK_WIRELESS_MULTIBOOT_FRAME_LINES 228 #define LINK_WIRELESS_MULTIBOOT_FRAMES_BEFORE_HANDSHAKE 20 // ~300ms +const u8 LINK_WIRELESS_MULTIBOOT_CMD_START[] = {0x00, 0x54, 0x00, 0x00, + 0x00, 0x02, 0x00}; +const u8 LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE = 7; + static volatile char LINK_WIRELESS_MULTIBOOT_VERSION[] = "LinkWirelessMultiboot/v6.2.0"; @@ -92,7 +96,7 @@ class LinkWirelessMultiboot { } } - LWMLOG("connected!"); // TODO: MOVE HANDSHAKE TO WHILE + LWMLOG("new client"); // TODO: MOVE HANDSHAKE TO WHILE linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES * LINK_WIRELESS_MULTIBOOT_FRAMES_BEFORE_HANDSHAKE); @@ -106,7 +110,7 @@ class LinkWirelessMultiboot { hasData = response.dataSize > 0; } - LWMLOG("data received"); + LWMLOG("handshake received"); LinkWirelessOpenSDK::ClientSDKHeader clientHeader = parseClientHeader(response.data[0]); LWMLOG("client size: " + std::to_string(clientHeader.payloadSize)); @@ -117,11 +121,8 @@ class LinkWirelessMultiboot { LWMLOG("sending ACK"); firstack: - LinkWirelessOpenSDK::ServerSDKHeader serverHeader; - serverHeader = createACKFor(clientHeader); - u32 sndHeader = serializeServerHeader(serverHeader); - - if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerACKBuffer(clientHeader), response)) return FAILURE; if (response.dataSize == 0) { @@ -139,9 +140,8 @@ class LinkWirelessMultiboot { } secondack: - serverHeader = createACKFor(clientHeader); - sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerACKBuffer(clientHeader), response)) return FAILURE; if (response.dataSize == 0) { @@ -160,9 +160,9 @@ class LinkWirelessMultiboot { while (clientHeader.slotState > 0) { link->wait(228); - serverHeader = createACKFor(clientHeader); - sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerACKBuffer(clientHeader), + response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); @@ -175,14 +175,11 @@ class LinkWirelessMultiboot { while (!didClientRespond) { link->wait(228); - serverHeader.isACK = 0; - serverHeader.targetSlots = 0b0001; // TODO: Implement - serverHeader.payloadSize = 7; - serverHeader.n = 1; - serverHeader.phase = 0; - serverHeader.slotState = 1; - sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(toArray(sndHeader, 0x54, 0x02), 3, 10, response)) + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + LINK_WIRELESS_MULTIBOOT_CMD_START, + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, 1, 0, 1, 0, 0b0001), + response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); if (clientHeader.isACK == 1 && clientHeader.n == 1 && @@ -243,14 +240,9 @@ class LinkWirelessMultiboot { while (!didClientRespond) { link->wait(228); - serverHeader.isACK = 0; - serverHeader.targetSlots = 0b0001; // TODO: Implement - serverHeader.payloadSize = 0; - serverHeader.n = 0; - serverHeader.phase = 0; - serverHeader.slotState = 3; - sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) + if (!sendAndExpectData(linkWirelessOpenSDK->createServerBuffer( + {}, 0, 0, 0, 3, 0, 0b0001), + response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); if (clientHeader.isACK == 1 && clientHeader.n == 0 && @@ -265,14 +257,9 @@ class LinkWirelessMultiboot { while (!didClientRespond) { link->wait(228); - serverHeader.isACK = 0; - serverHeader.targetSlots = 0b0001; // TODO: Implement - serverHeader.payloadSize = 0; - serverHeader.n = 1; - serverHeader.phase = 0; - serverHeader.slotState = 0; - sndHeader = serializeServerHeader(serverHeader); - if (!sendAndExpectData(toArray(sndHeader), 1, 3, response)) + if (!sendAndExpectData(linkWirelessOpenSDK->createServerBuffer( + {}, 0, 1, 0, 0, 0, 0b0001), + response)) return FAILURE; clientHeader = parseClientHeader(response.data[0]); // if (clientHeader.slotState == 0) @@ -346,6 +333,13 @@ class LinkWirelessMultiboot { return true; } + bool sendAndExpectData(LinkWirelessOpenSDK::SendBuffer< + LinkWirelessOpenSDK::ServerSDKHeader> sendBuffer, + LinkRawWireless::ReceiveDataResponse& response) { + return sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, + sendBuffer.totalByteCount, response); + } + bool sendAndExpectData( std::array data, u32 dataSize, @@ -371,19 +365,6 @@ class LinkWirelessMultiboot { return true; } - LinkWirelessOpenSDK::ServerSDKHeader createACKFor( - LinkWirelessOpenSDK::ClientSDKHeader clientHeader) { - LinkWirelessOpenSDK::ServerSDKHeader serverHeader; - serverHeader.isACK = 1; - serverHeader.targetSlots = 0b0001; // TODO: Implement - serverHeader.payloadSize = 0; - serverHeader.n = clientHeader.n; - serverHeader.phase = clientHeader.phase; - serverHeader.slotState = clientHeader.slotState; - - return serverHeader; - } - LinkWirelessOpenSDK::ClientSDKHeader parseClientHeader(u32 clientHeaderInt) { return linkWirelessOpenSDK->parseClientHeader(clientHeaderInt); } diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index f3c23b6..6a25a89 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -9,6 +9,7 @@ #include #include +#include "LinkRawWireless.hpp" #define LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_WORDS 23 #define LINK_WIRELESS_OPEN_SDK_MAX_TRANSFER_BYTES_SERVER 87 @@ -36,7 +37,7 @@ static volatile char LINK_WIRELESS_OPEN_SDK_VERSION[] = "LinkWirelessOpenSDK/v6.2.0"; class LinkWirelessOpenSDK { - public: + public: // TODO: Build some private methods template struct SendBuffer { T header; @@ -93,13 +94,20 @@ class LinkWirelessOpenSDK { ClientResponse responses[4]; }; + // ChildrenData getChildrenData(LinkRawWireless::ReceiveDataResponse response) + // { + // for (u32 i = 1; i < LINK_RAW_WIRELESS_MAX_PLAYERS) { + // u32 packets = 0; + // } + // } + SendBuffer createServerBuffer(const u8* fullPayload, u32 fullPayloadSize, u8 n, u8 phase, u8 slotState, - u32 offset, - u8 targetSlots) { + u32 offset = 0, + u8 targetSlots = 0b1111) { SendBuffer buffer; u32 payloadSize = min(fullPayloadSize, LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER); @@ -110,21 +118,22 @@ class LinkWirelessOpenSDK { buffer.header.n = n; buffer.header.phase = phase; buffer.header.slotState = slotState; - u32 sndHeader = serializeServerHeader(buffer.header); + u32 headerInt = serializeServerHeader(buffer.header); - if (offset < fullPayloadSize) - buffer.data[buffer.dataSize++] = (fullPayload[offset] << 24) | sndHeader; + buffer.data[buffer.dataSize++] = + offset < fullPayloadSize ? (fullPayload[offset] << 24) | headerInt + : headerInt; for (u32 i = 1; i < payloadSize; i += 4) { - u32 d = 0; + u32 word = 0; for (u32 j = 0; j < 4; j++) { if (offset + i + j < fullPayloadSize && i + j < LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER) { u8 byte = fullPayload[offset + i + j]; - d |= byte << (j * 8); + word |= byte << (j * 8); } } - buffer.data[buffer.dataSize++] = d; + buffer.data[buffer.dataSize++] = word; } buffer.totalByteCount = @@ -133,7 +142,42 @@ class LinkWirelessOpenSDK { return buffer; } - ServerSDKHeader createACKFor(ClientSDKHeader clientHeader, u8 clientNumber) { + SendBuffer createServerACKBuffer( + ClientSDKHeader clientHeader) { + SendBuffer buffer; + + buffer.header = createACKHeaderFor(clientHeader, 0); + u32 headerInt = serializeServerHeader(buffer.header); + + buffer.data[buffer.dataSize++] = headerInt; + buffer.totalByteCount = LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER; + + return buffer; + } + + // SendBuffer createClientBuffer(const u8* fullPayload, + // u32 fullPayloadSize, + // u8 n, + // u8 phase, + // u8 slotState, + // u32 offset = 0) {} + // TODO: IMPLEMENT + + SendBuffer createClientACKBuffer( + ServerSDKHeader serverHeader) { + SendBuffer buffer; + + buffer.header = createACKHeaderFor(serverHeader); + u16 headerInt = serializeClientHeader(buffer.header); + + buffer.data[buffer.dataSize++] = headerInt; + buffer.totalByteCount = LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT; + + return buffer; + } + + ServerSDKHeader createACKHeaderFor(ClientSDKHeader clientHeader, + u8 clientNumber) { ServerSDKHeader serverHeader; serverHeader.isACK = 1; serverHeader.targetSlots = (1 << clientNumber); @@ -145,7 +189,7 @@ class LinkWirelessOpenSDK { return serverHeader; } - ClientSDKHeader createACKFor(ServerSDKHeader serverHeader) { + ClientSDKHeader createACKHeaderFor(ServerSDKHeader serverHeader) { ClientSDKHeader clientHeader; clientHeader.isACK = 1; clientHeader.payloadSize = 0; From 4c88c52889da8980966fbd70496accacaa2116a2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 01:31:24 -0300 Subject: [PATCH 22/74] Using C arrays in LinkRawWireless for easier cast between u32 and u8 streams --- lib/LinkRawWireless.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 3b035f8..cece6b1 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -89,15 +89,14 @@ class LinkRawWireless { struct CommandResult { bool success = false; - std::array responses = - {}; + u32 responses[LINK_RAW_WIRELESS_MAX_COMMAND_RESPONSE_LENGTH]; u32 responsesSize = 0; }; struct RemoteCommand { bool success = false; u8 commandId = 0; - std::array params = {}; + u32 params[LINK_RAW_WIRELESS_MAX_COMMAND_TRANSFER_LENGTH]; u32 paramsSize = 0; }; @@ -143,7 +142,7 @@ class LinkRawWireless { struct ReceiveDataResponse { u32 sentBytes[LINK_RAW_WIRELESS_MAX_PLAYERS]; - std::array data = {}; + u32 data[LINK_RAW_WIRELESS_MAX_COMMAND_TRANSFER_LENGTH]; u32 dataSize = 0; }; From d494c7cdb5ea0c7aeaeb1fb7fb624d2d322841b5 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 01:50:53 -0300 Subject: [PATCH 23/74] Adding getChildrenData parser method --- lib/LinkWirelessOpenSDK.hpp | 40 +++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 6a25a89..2dd0031 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -45,6 +45,7 @@ class LinkWirelessOpenSDK { u32 dataSize = 0; u32 totalByteCount = 0; }; + // TODO: SlotState enum struct ServerSDKHeader { unsigned int payloadSize : 7; @@ -94,12 +95,39 @@ class LinkWirelessOpenSDK { ClientResponse responses[4]; }; - // ChildrenData getChildrenData(LinkRawWireless::ReceiveDataResponse response) - // { - // for (u32 i = 1; i < LINK_RAW_WIRELESS_MAX_PLAYERS) { - // u32 packets = 0; - // } - // } + ChildrenData getChildrenData(LinkRawWireless::ReceiveDataResponse response) { + u8* buffer = (u8*)response.data; + u32 bufferSize = response.dataSize * 4; + + u32 cursor = 0; + ChildrenData childrenData; + + for (u32 i = 1; i < LINK_RAW_WIRELESS_MAX_PLAYERS) { + ClientResponse* clientResponse = childrenData.responses[i - 1]; + u32 remainingBytes = response.sentBytes[i]; + + while (remainingBytes >= LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT) { + ClientPacket* packet = + &clientResponse->packets[clientResponse->packetsSize]; + + packet->header = parseClientHeader(*((u16*)(buffer + cursor))); + cursor += LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT; + remainingBytes -= LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT; + + if (header.payloadSize > 0 && + header.payloadSize <= LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT && + remainingBytes >= packet->header.payloadSize) { + for (u32 j = 0; j < header.payloadSize; j++) + packet->payload[j] = buffer[cursor++]; + remainingBytes -= header.payloadSize; + } + + clientResponse->packetsSize++; + } + } + + return childrenData; + } SendBuffer createServerBuffer(const u8* fullPayload, u32 fullPayloadSize, From 9c664bcd70fb31de12896e225951286ef7ed348c Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 06:18:44 -0300 Subject: [PATCH 24/74] Using getChildrenData to parse all messages --- lib/LinkWirelessMultiboot.hpp | 227 ++++++++++++++++++---------------- lib/LinkWirelessOpenSDK.hpp | 14 +-- 2 files changed, 124 insertions(+), 117 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 9210cf0..112bb71 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -100,104 +100,122 @@ class LinkWirelessMultiboot { linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES * LINK_WIRELESS_MULTIBOOT_FRAMES_BEFORE_HANDSHAKE); - // HANDSHAKE - bool hasData = false; - LinkRawWireless::ReceiveDataResponse response; + LinkWirelessOpenSDK::ChildrenData childrenData; + LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; + + // HANDSHAKE while (!hasData) { + LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData(toArray(), 0, 1, response)) return FAILURE; - hasData = response.dataSize > 0; + childrenData = linkWirelessOpenSDK->getChildrenData(response); + hasData = childrenData.responses[0].packetsSize > 0; } - LWMLOG("handshake received"); - LinkWirelessOpenSDK::ClientSDKHeader clientHeader = - parseClientHeader(response.data[0]); - LWMLOG("client size: " + std::to_string(clientHeader.payloadSize)); - LWMLOG("n: " + std::to_string(clientHeader.n)); - LWMLOG("phase: " + std::to_string(clientHeader.phase)); - LWMLOG("ack: " + std::to_string(clientHeader.isACK)); - LWMLOG("slotState:" + std::to_string(clientHeader.slotState)); - LWMLOG("sending ACK"); - firstack: - if (!sendAndExpectData( - linkWirelessOpenSDK->createServerACKBuffer(clientHeader), response)) - return FAILURE; + lastValidHeader = childrenData.responses[0] + .packets[childrenData.responses[0].packetsSize - 1] + .header; - if (response.dataSize == 0) { - goto firstack; - } - clientHeader = parseClientHeader(response.data[0]); - if (clientHeader.n == 1) - goto firstack; - - if (clientHeader.n == 2 && clientHeader.slotState == 1) { - LWMLOG("N IS NOW 2, slotstate = 1"); - } else { - LWMLOG("Error: weird packet"); - return FAILURE; - } + // ACK 1 + hasData = false; + while (!hasData) { + LinkRawWireless::ReceiveDataResponse response; + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerACKBuffer(lastValidHeader), + response)) + return FAILURE; - secondack: - if (!sendAndExpectData( - linkWirelessOpenSDK->createServerACKBuffer(clientHeader), response)) - return FAILURE; + if (response.dataSize == 0) + continue; + childrenData = linkWirelessOpenSDK->getChildrenData(response); - if (response.dataSize == 0) { - goto secondack; - } - clientHeader = parseClientHeader(response.data[0]); - if (clientHeader.n == 2 && clientHeader.slotState == 1) - goto secondack; - - if (clientHeader.n == 1 && clientHeader.slotState == 2) { - LWMLOG("NI STARTED"); - } else { - LWMLOG("NI DIDN'T START"); - return FAILURE; + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + if (header.n == 2 && header.slotState == 1) { + hasData = true; + lastValidHeader = header; + break; + } + } } + LWMLOG("N IS NOW 2, slotState = 1"); - while (clientHeader.slotState > 0) { - link->wait(228); + // ACK 2 + hasData = false; + while (!hasData) { + LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( - linkWirelessOpenSDK->createServerACKBuffer(clientHeader), + linkWirelessOpenSDK->createServerACKBuffer(lastValidHeader), response)) return FAILURE; - clientHeader = parseClientHeader(response.data[0]); + if (response.dataSize == 0) + continue; + childrenData = linkWirelessOpenSDK->getChildrenData(response); + + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + if (header.n == 1 && header.slotState == 2) { + hasData = true; + lastValidHeader = header; + break; + } + } } + LWMLOG("NI STARTED"); + // RECEIVE NAME + while (lastValidHeader.slotState > 0) { + link->wait(228); + + LinkRawWireless::ReceiveDataResponse response; + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerACKBuffer(lastValidHeader), + response)) + return FAILURE; + childrenData = linkWirelessOpenSDK->getChildrenData(response); + lastValidHeader = childrenData.responses[0] + .packets[childrenData.responses[0].packetsSize - 1] + .header; + } LWMLOG("slotState IS NOW 0"); // ROM START COMMAND - bool didClientRespond = false; - while (!didClientRespond) { + hasData = false; + while (!hasData) { link->wait(228); + LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( LINK_WIRELESS_MULTIBOOT_CMD_START, LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, 1, 0, 1, 0, 0b0001), response)) return FAILURE; - clientHeader = parseClientHeader(response.data[0]); - if (clientHeader.isACK == 1 && clientHeader.n == 1 && - clientHeader.phase == 0 && clientHeader.slotState == 1) - didClientRespond = true; - } + if (response.dataSize == 0) + continue; + childrenData = linkWirelessOpenSDK->getChildrenData(response); + + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + if (header.isACK == 1 && header.n == 1 && header.phase == 0 && + header.slotState == 1) { + hasData = true; + break; + } + } + } LWMLOG("READY TO SEND ROM!"); // ROM START u32 transferredBytes = 0; u32 n = 1; u32 phase = 0; - // bool isRetry = false; u32 progress = 0; while (transferredBytes < romSize) { - // isRetry = false; - retry: auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( rom, romSize, n, phase, 2, transferredBytes, 0b0001); LinkRawWireless::ReceiveDataResponse response; @@ -206,82 +224,71 @@ class LinkWirelessMultiboot { LWMLOG("SendData failed!"); return FAILURE; } - if (response.dataSize == 0) { - // isRetry = true; - goto retry; - } - - clientHeader = parseClientHeader(response.data[0]); - if (clientHeader.isACK && clientHeader.n == n && - clientHeader.phase == phase) { - phase++; - if (phase == 4) { - phase = 0; - n++; - if (n == 4) - n = 0; - } - transferredBytes += 84; - u32 newProgress = transferredBytes * 100 / romSize; - if (newProgress != progress) { - progress = newProgress; - LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); + if (response.dataSize == 0) + continue; + childrenData = linkWirelessOpenSDK->getChildrenData(response); + + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + if (header.isACK && header.n == n && header.phase == phase) { + phase++; + if (phase == 4) { + phase = 0; + n++; + if (n == 4) + n = 0; + } + transferredBytes += 84; // TODO: Use sendBuffer + u32 newProgress = transferredBytes * 100 / romSize; + if (newProgress != progress) { + progress = newProgress; + LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); + } } - } else { - // isRetry = true; - goto retry; } } - LWMLOG("SEND FINISHED! Confirming..."); // ROM END COMMAND - didClientRespond = false; - while (!didClientRespond) { + hasData = false; + while (!hasData) { link->wait(228); + LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData(linkWirelessOpenSDK->createServerBuffer( {}, 0, 0, 0, 3, 0, 0b0001), response)) return FAILURE; - clientHeader = parseClientHeader(response.data[0]); - if (clientHeader.isACK == 1 && clientHeader.n == 0 && - clientHeader.phase == 0 && clientHeader.slotState == 3) - didClientRespond = true; + if (response.dataSize == 0) + continue; + childrenData = linkWirelessOpenSDK->getChildrenData(response); + + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + if (header.isACK == 1 && header.n == 0 && header.phase == 0 && + header.slotState == 3) { + hasData = true; + break; + } + } } - LWMLOG("Reconfirming..."); // ROM END 2 COMMAND - didClientRespond = false; - while (!didClientRespond) { + hasData = false; + while (!hasData) { link->wait(228); + LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData(linkWirelessOpenSDK->createServerBuffer( {}, 0, 1, 0, 0, 0, 0b0001), response)) return FAILURE; - clientHeader = parseClientHeader(response.data[0]); - // if (clientHeader.slotState == 0) - didClientRespond = true; + hasData = true; } LWMLOG("SUCCESS!"); - // u32 diffs = 0; - // for (u32 i = 0; i < romSize; i++) { - // if (rom[i] != bytes[i]) { - // LWMLOG("DIFF AT " + std::to_string(i) + ": " + link->toHex(bytes[i]) - // + - // " vs " + link->toHex(rom[i])); - // diffs++; - // } - // if (diffs > 100) - // break; - // } - - // LWMLOG("??"); - return SUCCESS; } diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 2dd0031..354eda0 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -97,13 +97,12 @@ class LinkWirelessOpenSDK { ChildrenData getChildrenData(LinkRawWireless::ReceiveDataResponse response) { u8* buffer = (u8*)response.data; - u32 bufferSize = response.dataSize * 4; u32 cursor = 0; ChildrenData childrenData; - for (u32 i = 1; i < LINK_RAW_WIRELESS_MAX_PLAYERS) { - ClientResponse* clientResponse = childrenData.responses[i - 1]; + for (u32 i = 1; i < LINK_RAW_WIRELESS_MAX_PLAYERS; i++) { + ClientResponse* clientResponse = &childrenData.responses[i - 1]; u32 remainingBytes = response.sentBytes[i]; while (remainingBytes >= LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT) { @@ -114,12 +113,13 @@ class LinkWirelessOpenSDK { cursor += LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT; remainingBytes -= LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT; - if (header.payloadSize > 0 && - header.payloadSize <= LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT && + if (packet->header.payloadSize > 0 && + packet->header.payloadSize <= + LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT && remainingBytes >= packet->header.payloadSize) { - for (u32 j = 0; j < header.payloadSize; j++) + for (u32 j = 0; j < packet->header.payloadSize; j++) packet->payload[j] = buffer[cursor++]; - remainingBytes -= header.payloadSize; + remainingBytes -= packet->header.payloadSize; } clientResponse->packetsSize++; From 74d02a2a344f96df739016ddbcccf8324f6196bf Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 06:43:38 -0300 Subject: [PATCH 25/74] Adding bounds check to getChildrenData and fixing other bugs --- lib/LinkWirelessMultiboot.hpp | 4 ++-- lib/LinkWirelessOpenSDK.hpp | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 112bb71..6ce8ebd 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -204,6 +204,7 @@ class LinkWirelessMultiboot { if (header.isACK == 1 && header.n == 1 && header.phase == 0 && header.slotState == 1) { hasData = true; + // TODO: LOG CHILDRENDATA AND DEBUG WITH LONGER DISTANCES break; } } @@ -224,8 +225,6 @@ class LinkWirelessMultiboot { LWMLOG("SendData failed!"); return FAILURE; } - if (response.dataSize == 0) - continue; childrenData = linkWirelessOpenSDK->getChildrenData(response); for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { @@ -244,6 +243,7 @@ class LinkWirelessMultiboot { progress = newProgress; LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); } + break; } } } diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 354eda0..b877d8f 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -97,10 +97,14 @@ class LinkWirelessOpenSDK { ChildrenData getChildrenData(LinkRawWireless::ReceiveDataResponse response) { u8* buffer = (u8*)response.data; - u32 cursor = 0; ChildrenData childrenData; + if (response.sentBytes[1] + response.sentBytes[2] + response.sentBytes[3] + + response.sentBytes[4] > + response.dataSize * 4) + return childrenData; + for (u32 i = 1; i < LINK_RAW_WIRELESS_MAX_PLAYERS; i++) { ClientResponse* clientResponse = &childrenData.responses[i - 1]; u32 remainingBytes = response.sentBytes[i]; From dd057313aed22c636bc1773050192bc765383f8f Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 22:08:04 -0300 Subject: [PATCH 26/74] FIX: Rom start handshake on retransmission --- lib/LinkWirelessMultiboot.hpp | 34 ++++++++++++++++++++++++++++++++-- lib/LinkWirelessOpenSDK.hpp | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 6ce8ebd..6574a3d 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -167,7 +167,8 @@ class LinkWirelessMultiboot { LWMLOG("NI STARTED"); // RECEIVE NAME - while (lastValidHeader.slotState > 0) { + hasData = false; + while (!hasData) { link->wait(228); LinkRawWireless::ReceiveDataResponse response; @@ -179,9 +180,29 @@ class LinkWirelessMultiboot { lastValidHeader = childrenData.responses[0] .packets[childrenData.responses[0].packetsSize - 1] .header; + + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + if (header.slotState == 0) { + hasData = true; + break; + } + } } LWMLOG("slotState IS NOW 0"); + // WAITING DATA STOP + hasData = false; + while (!hasData) { + link->wait(228); + LinkRawWireless::ReceiveDataResponse response; + if (!sendAndExpectData(toArray(), 0, 1, response)) + return FAILURE; + childrenData = linkWirelessOpenSDK->getChildrenData(response); + hasData = childrenData.responses[0].packetsSize == 0; + } + LWMLOG("ready to send start command"); + // ROM START COMMAND hasData = false; while (!hasData) { @@ -204,12 +225,21 @@ class LinkWirelessMultiboot { if (header.isACK == 1 && header.n == 1 && header.phase == 0 && header.slotState == 1) { hasData = true; - // TODO: LOG CHILDRENDATA AND DEBUG WITH LONGER DISTANCES break; } } } LWMLOG("READY TO SEND ROM!"); + LWMLOG("really?"); + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + LWMLOG("ack: " + std::to_string(header.isACK)); + LWMLOG("n: " + std::to_string(header.n)); + LWMLOG("phase: " + std::to_string(header.phase)); + LWMLOG("size: " + std::to_string(header.payloadSize)); + LWMLOG("slotState: " + std::to_string(header.slotState)); + LWMLOG("---"); + } // ROM START u32 transferredBytes = 0; diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index b877d8f..d2ae165 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -37,7 +37,7 @@ static volatile char LINK_WIRELESS_OPEN_SDK_VERSION[] = "LinkWirelessOpenSDK/v6.2.0"; class LinkWirelessOpenSDK { - public: // TODO: Build some private methods + public: template struct SendBuffer { T header; From 60ac840f52a18b5e1a5590c13c9b9dfc8f1ca858 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 22:50:01 -0300 Subject: [PATCH 27/74] Moving slotState (now commState) to an enum --- lib/LinkWirelessMultiboot.hpp | 41 ++++++++++++++++++++--------------- lib/LinkWirelessOpenSDK.hpp | 23 +++++++++++++------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 6574a3d..15b53eb 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -133,14 +133,15 @@ class LinkWirelessMultiboot { for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; - if (header.n == 2 && header.slotState == 1) { + if (header.n == 2 && + header.commState == LinkWirelessOpenSDK::CommState::STARTING) { hasData = true; lastValidHeader = header; break; } } } - LWMLOG("N IS NOW 2, slotState = 1"); + LWMLOG("N IS NOW 2, commState = 1"); // ACK 2 hasData = false; @@ -157,7 +158,8 @@ class LinkWirelessMultiboot { for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; - if (header.n == 1 && header.slotState == 2) { + if (header.n == 1 && + header.commState == LinkWirelessOpenSDK::CommState::COMMUNICATING) { hasData = true; lastValidHeader = header; break; @@ -183,13 +185,13 @@ class LinkWirelessMultiboot { for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; - if (header.slotState == 0) { + if (header.commState == LinkWirelessOpenSDK::CommState::OFF) { hasData = true; break; } } } - LWMLOG("slotState IS NOW 0"); + LWMLOG("commState IS NOW 0"); // WAITING DATA STOP hasData = false; @@ -212,7 +214,8 @@ class LinkWirelessMultiboot { if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( LINK_WIRELESS_MULTIBOOT_CMD_START, - LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, 1, 0, 1, 0, 0b0001), + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, 1, 0, + LinkWirelessOpenSDK::CommState::STARTING, 0, 0b0001), response)) return FAILURE; @@ -223,7 +226,7 @@ class LinkWirelessMultiboot { for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; if (header.isACK == 1 && header.n == 1 && header.phase == 0 && - header.slotState == 1) { + header.commState == LinkWirelessOpenSDK::CommState::STARTING) { hasData = true; break; } @@ -237,7 +240,7 @@ class LinkWirelessMultiboot { LWMLOG("n: " + std::to_string(header.n)); LWMLOG("phase: " + std::to_string(header.phase)); LWMLOG("size: " + std::to_string(header.payloadSize)); - LWMLOG("slotState: " + std::to_string(header.slotState)); + LWMLOG("commState: " + std::to_string(header.commState)); LWMLOG("---"); } @@ -248,7 +251,8 @@ class LinkWirelessMultiboot { u32 progress = 0; while (transferredBytes < romSize) { auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, n, phase, 2, transferredBytes, 0b0001); + rom, romSize, n, phase, LinkWirelessOpenSDK::CommState::COMMUNICATING, + transferredBytes, 0b0001); LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, sendBuffer.totalByteCount, response)) { @@ -267,7 +271,7 @@ class LinkWirelessMultiboot { if (n == 4) n = 0; } - transferredBytes += 84; // TODO: Use sendBuffer + transferredBytes += sendBuffer.header.payloadSize; u32 newProgress = transferredBytes * 100 / romSize; if (newProgress != progress) { progress = newProgress; @@ -285,9 +289,11 @@ class LinkWirelessMultiboot { link->wait(228); LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData(linkWirelessOpenSDK->createServerBuffer( - {}, 0, 0, 0, 3, 0, 0b0001), - response)) + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + {}, 0, 0, 0, LinkWirelessOpenSDK::CommState::ENDING, 0, + 0b0001), + response)) return FAILURE; if (response.dataSize == 0) continue; @@ -296,7 +302,7 @@ class LinkWirelessMultiboot { for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; if (header.isACK == 1 && header.n == 0 && header.phase == 0 && - header.slotState == 3) { + header.commState == LinkWirelessOpenSDK::CommState::ENDING) { hasData = true; break; } @@ -310,9 +316,10 @@ class LinkWirelessMultiboot { link->wait(228); LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData(linkWirelessOpenSDK->createServerBuffer( - {}, 0, 1, 0, 0, 0, 0b0001), - response)) + if (!sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + {}, 0, 1, 0, LinkWirelessOpenSDK::CommState::OFF, 0, 0b0001), + response)) return FAILURE; hasData = true; } diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index d2ae165..e627499 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -45,7 +45,14 @@ class LinkWirelessOpenSDK { u32 dataSize = 0; u32 totalByteCount = 0; }; - // TODO: SlotState enum + + enum CommState : unsigned int { + OFF = 0, + STARTING = 1, + COMMUNICATING = 2, + ENDING = 3, + DIRECT = 4 + }; struct ServerSDKHeader { unsigned int payloadSize : 7; @@ -53,7 +60,7 @@ class LinkWirelessOpenSDK { unsigned int phase : 2; unsigned int n : 2; unsigned int isACK : 1; - unsigned int slotState : 4; + CommState commState : 4; unsigned int targetSlots : 4; }; union ServerSDKHeaderSerializer { @@ -77,7 +84,7 @@ class LinkWirelessOpenSDK { unsigned int phase : 2; unsigned int n : 2; unsigned int isACK : 1; - unsigned int slotState : 4; + CommState commState : 4; }; union ClientSDKHeaderSerializer { ClientSDKHeader asStruct; @@ -137,7 +144,7 @@ class LinkWirelessOpenSDK { u32 fullPayloadSize, u8 n, u8 phase, - u8 slotState, + CommState commState, u32 offset = 0, u8 targetSlots = 0b1111) { SendBuffer buffer; @@ -149,7 +156,7 @@ class LinkWirelessOpenSDK { buffer.header.payloadSize = payloadSize; buffer.header.n = n; buffer.header.phase = phase; - buffer.header.slotState = slotState; + buffer.header.commState = commState; u32 headerInt = serializeServerHeader(buffer.header); buffer.data[buffer.dataSize++] = @@ -191,7 +198,7 @@ class LinkWirelessOpenSDK { // u32 fullPayloadSize, // u8 n, // u8 phase, - // u8 slotState, + // u8 commState, // u32 offset = 0) {} // TODO: IMPLEMENT @@ -216,7 +223,7 @@ class LinkWirelessOpenSDK { serverHeader.payloadSize = 0; serverHeader.n = clientHeader.n; serverHeader.phase = clientHeader.phase; - serverHeader.slotState = clientHeader.slotState; + serverHeader.commState = clientHeader.commState; return serverHeader; } @@ -227,7 +234,7 @@ class LinkWirelessOpenSDK { clientHeader.payloadSize = 0; clientHeader.n = serverHeader.n; clientHeader.phase = serverHeader.phase; - clientHeader.slotState = serverHeader.slotState; + clientHeader.commState = serverHeader.commState; return clientHeader; } From c55d2683ab0f7e1e57384badb075e8e4747b0223 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 23:09:09 -0300 Subject: [PATCH 28/74] The sender now detects timeouts --- lib/LinkWirelessMultiboot.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 15b53eb..c315dfc 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -401,6 +401,13 @@ class LinkWirelessMultiboot { LWMLOG("but got " + link->toHex(remoteCommand.commandId)); return false; } + if (remoteCommand.paramsSize > 0) { + if (((remoteCommand.params[0] >> 8) & 0b0001) == 0) { + // TODO: MUPLTIPLE CHILDREN + LWMLOG("timeout, children disconnected"); + return false; + } + } success = link->receiveData(response); if (!success) { LWMLOG("receive data failed"); From 01b5cfaacddbdd4e015da4371a8273862991b67c Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 1 Feb 2024 23:32:47 -0300 Subject: [PATCH 29/74] Fixing clock inversion issues by enabling prefetch --- .../LinkWirelessMultiboot_demo/src/main.cpp | 2 ++ lib/LinkWirelessMultiboot.hpp | 25 +++---------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/main.cpp b/examples/LinkWirelessMultiboot_demo/src/main.cpp index 8097d3e..89ba6e9 100644 --- a/examples/LinkWirelessMultiboot_demo/src/main.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/main.cpp @@ -16,6 +16,8 @@ LinkWirelessMultiboot* linkWirelessMultiboot = new LinkWirelessMultiboot(); int main() { setUpInterrupts(); + REG_WAITCNT = 1 << 14; // (prefetch ON) + engine->setScene(multibootScene.get()); while (true) { diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index c315dfc..23ff8f9 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -97,8 +97,6 @@ class LinkWirelessMultiboot { } LWMLOG("new client"); // TODO: MOVE HANDSHAKE TO WHILE - linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES * - LINK_WIRELESS_MULTIBOOT_FRAMES_BEFORE_HANDSHAKE); bool hasData = false; LinkWirelessOpenSDK::ChildrenData childrenData; @@ -171,8 +169,6 @@ class LinkWirelessMultiboot { // RECEIVE NAME hasData = false; while (!hasData) { - link->wait(228); - LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( linkWirelessOpenSDK->createServerACKBuffer(lastValidHeader), @@ -196,7 +192,6 @@ class LinkWirelessMultiboot { // WAITING DATA STOP hasData = false; while (!hasData) { - link->wait(228); LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData(toArray(), 0, 1, response)) return FAILURE; @@ -205,11 +200,11 @@ class LinkWirelessMultiboot { } LWMLOG("ready to send start command"); + wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); + // ROM START COMMAND hasData = false; while (!hasData) { - link->wait(228); - LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( @@ -233,16 +228,6 @@ class LinkWirelessMultiboot { } } LWMLOG("READY TO SEND ROM!"); - LWMLOG("really?"); - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - LWMLOG("ack: " + std::to_string(header.isACK)); - LWMLOG("n: " + std::to_string(header.n)); - LWMLOG("phase: " + std::to_string(header.phase)); - LWMLOG("size: " + std::to_string(header.payloadSize)); - LWMLOG("commState: " + std::to_string(header.commState)); - LWMLOG("---"); - } // ROM START u32 transferredBytes = 0; @@ -286,8 +271,6 @@ class LinkWirelessMultiboot { // ROM END COMMAND hasData = false; while (!hasData) { - link->wait(228); - LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( @@ -313,8 +296,6 @@ class LinkWirelessMultiboot { // ROM END 2 COMMAND hasData = false; while (!hasData) { - link->wait(228); - LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( @@ -403,7 +384,7 @@ class LinkWirelessMultiboot { } if (remoteCommand.paramsSize > 0) { if (((remoteCommand.params[0] >> 8) & 0b0001) == 0) { - // TODO: MUPLTIPLE CHILDREN + // TODO: MULTIPLE CHILDREN LWMLOG("timeout, children disconnected"); return false; } From d531167638956cd01e5d1726e194b2cc15746bd7 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 01:16:24 -0300 Subject: [PATCH 30/74] Updating docs with 0x99660128 and Setup parameters --- docs/wireless_adapter.md | 11 ++++++++++- lib/LinkRawWireless.hpp | 2 +- lib/LinkWirelessMultiboot.hpp | 10 +++++----- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/wireless_adapter.md b/docs/wireless_adapter.md index 83817c7..24195a8 100644 --- a/docs/wireless_adapter.md +++ b/docs/wireless_adapter.md @@ -241,6 +241,10 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg ⚠️ Clients must always set `maxPlayers` to `00`. +πŸ›°οΈ Bits `8-15` specify the number of times the adapter would perform a transmission. The default is `0`, which means infinite retransmissions. Setting a value of `3` means: transmit once, and only retry two times if the other console didn't receive data. After the maximum number of transmissions is reached, the client is marked as _inactive_ and will appear on the extra parameter that the adapter sends (`0x99660128`) in the [waiting commands](#waiting). + +⏲️ Bits `0-7` represent the timeout of the [waiting commands](#waiting)(#waiting). The default is _no timeout_ (`0`), but if this is set, the adapter will issue a `0x99660027` command after the timeout is reached. It's expressed in _frames_ (units of 16.6 ms). + #### Broadcast - `0x16` [![Image without alt text or caption](img/0x16.png)](img/0x16.png) @@ -523,10 +527,15 @@ Waiting * The GBA then sends the response back (e.g. `0x996600A8` as `0x28`Β +Β `0x80`Β =Β `0xA8`). * After this, control of the clock returns to the GBA, and it can start sending commands back again. For example this might be receiving the command sent by the other device using [ReceiveData](#receivedata---0x26). -⌚ This timeouts after 500ms of the adapter not having anything to tell the GBA about. In this case, the adapter sends `0x99660027`. **This is only true if the console has used the Setup command before**. The value that most games use (`0x003C0420`) seems to contain this timeout value, but the default is zero (no timeout). +⌚ This timeouts after 500ms of the adapter not having anything to tell the GBA about. In this case, the adapter sends `0x99660027`. **This is only true if the console has used the [Setup](#setup---0x17) command before**. The value that most games use (`0x003C0420`) contains this timeout value, but the default is zero (no timeout). βœ… When there's new data available, the adapter sends to the GBA a `0x99660028`. +⚠️ If some children didn't receive the data, the adapter sends to the GBA a `0x99660128`. + - The extra parameter has two bitarrays: + * Bits `0-4`: The clients that _received_ data. + * Bits `8-11`: The clients marked as _inactive_. This depends on the # of maximum transmissions configured with the [Setup](#setup---0x17) command. + πŸ”— When the adapter is disconnected from the parent, it sends a `0x99660029`. - Bit 8 of the response indicates the reason: * `0` = manual disconnect (aka the host used [DisconnectClient](#disconnectclient---0x30)) diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index cece6b1..3885b0e 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -644,7 +644,7 @@ class LinkRawWireless { } if (command != LINK_RAW_WIRELESS_DATA_REQUEST) { - LRWLOG("! expected cmd request"); + LRWLOG("! expected CMD request"); LRWLOG("! but received 0x" + toHex(command)); linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); reset(); diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 23ff8f9..f770707 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -374,24 +374,24 @@ class LinkWirelessMultiboot { bool success = false; success = link->sendDataAndWait(data, dataSize, remoteCommand, _bytes); if (!success) { - LWMLOG("senddatawait no"); + LWMLOG("! sendDataAndWait failed"); return false; } if (remoteCommand.commandId != 0x28) { - LWMLOG("expected response 0x28"); - LWMLOG("but got " + link->toHex(remoteCommand.commandId)); + LWMLOG("! expected EVENT 0x28"); + LWMLOG("! but got " + link->toHex(remoteCommand.commandId)); return false; } if (remoteCommand.paramsSize > 0) { if (((remoteCommand.params[0] >> 8) & 0b0001) == 0) { // TODO: MULTIPLE CHILDREN - LWMLOG("timeout, children disconnected"); + LWMLOG("! child timeout"); return false; } } success = link->receiveData(response); if (!success) { - LWMLOG("receive data failed"); + LWMLOG("! receiveData failed"); return false; } return true; From ca823cae719d4a3c47e86e769786a250bc233700 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 01:36:13 -0300 Subject: [PATCH 31/74] Adding tx and wait timeout parameters to LinkRawWireless::setup(...) --- .../src/scenes/DebugScene.cpp | 18 ++++++++++++++---- lib/LinkRawWireless.hpp | 16 ++++++++-------- lib/LinkWirelessMultiboot.hpp | 11 +++++++---- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp index 76b75ea..b4b5e52 100644 --- a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp +++ b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp @@ -527,12 +527,22 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { "Max players?", std::vector{"5", "4", "3", "2"})) == -1) ; + int maxTransmissions = -1; + while ((maxTransmissions = selectU8("Max transmissions?")) == -1) + ; + int waitTimeout = -1; + while ((waitTimeout = selectU8("Wait timeout?")) == -1) + ; - return logOperation("sending " + name, [maxPlayers]() { - log("maxPlayers = " + std::to_string(maxPlayers)); + return logOperation( + "sending " + name, [maxPlayers, maxTransmissions, waitTimeout]() { + log("maxPlayers = " + std::to_string(maxPlayers)); + log("maxTransmissions = " + std::to_string(maxTransmissions)); + log("waitTimeout = " + std::to_string(waitTimeout)); - return linkRawWireless->setup(5 - maxPlayers); - }); + return linkRawWireless->setup(5 - maxPlayers, maxTransmissions, + waitTimeout); + }); } case 0x18: goto generic; diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 3885b0e..33907e5 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -38,7 +38,7 @@ #define LINK_RAW_WIRELESS_COMMAND_HEADER 0x9966 #define LINK_RAW_WIRELESS_RESPONSE_ACK 0x80 #define LINK_RAW_WIRELESS_DATA_REQUEST 0x80000000 -#define LINK_RAW_WIRELESS_SETUP_MAGIC 0x003c0420 +#define LINK_RAW_WIRELESS_SETUP_MAGIC 0x003c0000 #define LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT 16 #define LINK_RAW_WIRELESS_STILL_CONNECTING 0x01000000 #define LINK_RAW_WIRELESS_BROADCAST_LENGTH 6 @@ -168,14 +168,14 @@ class LinkRawWireless { } bool setup(u8 maxPlayers = LINK_RAW_WIRELESS_MAX_PLAYERS, + u8 maxTransmissions = 4, + u8 waitTimeout = 32, u32 magic = LINK_RAW_WIRELESS_SETUP_MAGIC) { - return sendCommand( - LINK_RAW_WIRELESS_COMMAND_SETUP, - {(u32)(magic | - (((LINK_RAW_WIRELESS_MAX_PLAYERS - maxPlayers) & 0b11) - << LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT))}, - 1) - .success; + u32 config = (u32)(magic | + (((LINK_RAW_WIRELESS_MAX_PLAYERS - maxPlayers) & 0b11) + << LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT) | + (maxTransmissions << 8) | waitTimeout); + return sendCommand(LINK_RAW_WIRELESS_COMMAND_SETUP, {config}, 1).success; } bool broadcast(const char* gameName = "", diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index f770707..c5a1e5a 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -39,10 +39,11 @@ #define LINK_WIRELESS_MULTIBOOT_MIN_PLAYERS 2 #define LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS 5 #define LINK_WIRELESS_MULTIBOOT_HEADER_SIZE 0xC0 -#define LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC 0x003F0120 +#define LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC 0x003F0000 +#define LINK_WIRELESS_MULTIBOOT_SETUP_TX 1 +#define LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT 32 #define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG 0b1000000000000000 #define LINK_WIRELESS_MULTIBOOT_FRAME_LINES 228 -#define LINK_WIRELESS_MULTIBOOT_FRAMES_BEFORE_HANDSHAKE 20 // ~300ms const u8 LINK_WIRELESS_MULTIBOOT_CMD_START[] = {0x00, 0x54, 0x00, 0x00, 0x00, 0x02, 0x00}; @@ -200,7 +201,7 @@ class LinkWirelessMultiboot { } LWMLOG("ready to send start command"); - wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); + linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); // ROM START COMMAND hasData = false; @@ -335,7 +336,9 @@ class LinkWirelessMultiboot { const char* userName, const u16 gameId, u8 players) { - if (!link->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { + if (!link->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_TX, + LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT, + LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { LWMLOG("! setup failed"); return false; } From 706e2b3e2127788416e6f600d918beb9e73d40e8 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 02:01:52 -0300 Subject: [PATCH 32/74] Moving n and phase to its own SequenceNumber struct --- lib/LinkWirelessMultiboot.hpp | 22 ++++++++-------------- lib/LinkWirelessOpenSDK.hpp | 30 ++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index c5a1e5a..d74e705 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -210,7 +210,7 @@ class LinkWirelessMultiboot { if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( LINK_WIRELESS_MULTIBOOT_CMD_START, - LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, 1, 0, + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, {1, 0}, LinkWirelessOpenSDK::CommState::STARTING, 0, 0b0001), response)) return FAILURE; @@ -232,12 +232,11 @@ class LinkWirelessMultiboot { // ROM START u32 transferredBytes = 0; - u32 n = 1; - u32 phase = 0; + LinkWirelessOpenSDK::SequenceNumber sequence = {.n = 1, .phase = 0}; u32 progress = 0; while (transferredBytes < romSize) { auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, n, phase, LinkWirelessOpenSDK::CommState::COMMUNICATING, + rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, transferredBytes, 0b0001); LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, @@ -249,14 +248,8 @@ class LinkWirelessMultiboot { for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; - if (header.isACK && header.n == n && header.phase == phase) { - phase++; - if (phase == 4) { - phase = 0; - n++; - if (n == 4) - n = 0; - } + if (header.isACK && header.sequence() == sequence) { + sequence.inc(); transferredBytes += sendBuffer.header.payloadSize; u32 newProgress = transferredBytes * 100 / romSize; if (newProgress != progress) { @@ -275,7 +268,7 @@ class LinkWirelessMultiboot { LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( - {}, 0, 0, 0, LinkWirelessOpenSDK::CommState::ENDING, 0, + {}, 0, {0, 0}, LinkWirelessOpenSDK::CommState::ENDING, 0, 0b0001), response)) return FAILURE; @@ -300,7 +293,8 @@ class LinkWirelessMultiboot { LinkRawWireless::ReceiveDataResponse response; if (!sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( - {}, 0, 1, 0, LinkWirelessOpenSDK::CommState::OFF, 0, 0b0001), + {}, 0, {1, 0}, LinkWirelessOpenSDK::CommState::OFF, 0, + 0b0001), response)) return FAILURE; hasData = true; diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index e627499..730fe15 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -54,6 +54,25 @@ class LinkWirelessOpenSDK { DIRECT = 4 }; + struct SequenceNumber { + u32 n = 0; + u32 phase = 0; + + void inc() { + phase++; + if (phase == 4) { + phase = 0; + n++; + if (n == 4) + n = 0; + } + } + + bool operator==(const SequenceNumber& other) { + return n == other.n && phase == other.phase; + } + }; + struct ServerSDKHeader { unsigned int payloadSize : 7; unsigned int _unused_ : 2; @@ -62,6 +81,8 @@ class LinkWirelessOpenSDK { unsigned int isACK : 1; CommState commState : 4; unsigned int targetSlots : 4; + + SequenceNumber sequence() { return SequenceNumber{.n = n, .phase = phase}; } }; union ServerSDKHeaderSerializer { ServerSDKHeader asStruct; @@ -85,6 +106,8 @@ class LinkWirelessOpenSDK { unsigned int n : 2; unsigned int isACK : 1; CommState commState : 4; + + SequenceNumber sequence() { return SequenceNumber{.n = n, .phase = phase}; } }; union ClientSDKHeaderSerializer { ClientSDKHeader asStruct; @@ -142,8 +165,7 @@ class LinkWirelessOpenSDK { SendBuffer createServerBuffer(const u8* fullPayload, u32 fullPayloadSize, - u8 n, - u8 phase, + SequenceNumber sequence, CommState commState, u32 offset = 0, u8 targetSlots = 0b1111) { @@ -154,8 +176,8 @@ class LinkWirelessOpenSDK { buffer.header.isACK = 0; buffer.header.targetSlots = targetSlots; buffer.header.payloadSize = payloadSize; - buffer.header.n = n; - buffer.header.phase = phase; + buffer.header.n = sequence.n; + buffer.header.phase = sequence.phase; buffer.header.commState = commState; u32 headerInt = serializeServerHeader(buffer.header); From f78cf257ea6e6529642a88c44c52e8813ecc9fdc Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 02:07:48 -0300 Subject: [PATCH 33/74] Making a simple interface for createServerBuffer --- lib/LinkWirelessOpenSDK.hpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 730fe15..847ef6b 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -163,12 +163,13 @@ class LinkWirelessOpenSDK { return childrenData; } - SendBuffer createServerBuffer(const u8* fullPayload, - u32 fullPayloadSize, - SequenceNumber sequence, - CommState commState, - u32 offset = 0, - u8 targetSlots = 0b1111) { + SendBuffer createServerBuffer( + const u8* fullPayload, + u32 fullPayloadSize, + SequenceNumber sequence, + CommState commState = CommState::COMMUNICATING, + u32 offset = 0, + u8 targetSlots = 0b1111) { SendBuffer buffer; u32 payloadSize = min(fullPayloadSize, LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER); @@ -181,9 +182,9 @@ class LinkWirelessOpenSDK { buffer.header.commState = commState; u32 headerInt = serializeServerHeader(buffer.header); - buffer.data[buffer.dataSize++] = - offset < fullPayloadSize ? (fullPayload[offset] << 24) | headerInt - : headerInt; + buffer.data[buffer.dataSize++] = headerInt; + if (offset < fullPayloadSize) + offset |= fullPayload[offset] << 24; for (u32 i = 1; i < payloadSize; i += 4) { u32 word = 0; From 6d5bb80d974a2dbef420e8143daeccc460c70c07 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 02:18:45 -0300 Subject: [PATCH 34/74] Adding missing client methods to LinkWirelessOpenSDK --- lib/LinkWirelessOpenSDK.hpp | 86 +++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 847ef6b..789a342 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -143,7 +143,8 @@ class LinkWirelessOpenSDK { ClientPacket* packet = &clientResponse->packets[clientResponse->packetsSize]; - packet->header = parseClientHeader(*((u16*)(buffer + cursor))); + u32 headerInt = *((u16*)(buffer + cursor)); + packet->header = parseClientHeader(headerInt); cursor += LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT; remainingBytes -= LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT; @@ -163,6 +164,42 @@ class LinkWirelessOpenSDK { return childrenData; } + ParentData getParentData(LinkRawWireless::ReceiveDataResponse response) { + u8* buffer = (u8*)response.data; + u32 cursor = 0; + ParentData parentData; + + if (response.sentBytes[0] > response.dataSize * 4) + return parentData; + + ServerResponse* serverResponse = &parentData.response; + u32 remainingBytes = response.sentBytes[0]; + + while (remainingBytes >= LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER) { + ServerPacket* packet = + &serverResponse->packets[serverResponse->packetsSize]; + + u32 headerInt = (*((u16*)(buffer + cursor))) | + (((*((u8*)(buffer + cursor + 2)))) << 16); + packet->header = parseServerHeader(headerInt); + cursor += LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER; + remainingBytes -= LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_SERVER; + + if (packet->header.payloadSize > 0 && + packet->header.payloadSize <= + LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER && + remainingBytes >= packet->header.payloadSize) { + for (u32 j = 0; j < packet->header.payloadSize; j++) + packet->payload[j] = buffer[cursor++]; + remainingBytes -= packet->header.payloadSize; + } + + serverResponse->packetsSize++; + } + + return parentData; + } + SendBuffer createServerBuffer( const u8* fullPayload, u32 fullPayloadSize, @@ -217,13 +254,46 @@ class LinkWirelessOpenSDK { return buffer; } - // SendBuffer createClientBuffer(const u8* fullPayload, - // u32 fullPayloadSize, - // u8 n, - // u8 phase, - // u8 commState, - // u32 offset = 0) {} - // TODO: IMPLEMENT + SendBuffer createClientBuffer( + const u8* fullPayload, + u32 fullPayloadSize, + SequenceNumber sequence, + CommState commState = CommState::COMMUNICATING, + u32 offset = 0) { + SendBuffer buffer; + u32 payloadSize = + min(fullPayloadSize, LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT); + + buffer.header.isACK = 0; + buffer.header.payloadSize = payloadSize; + buffer.header.n = sequence.n; + buffer.header.phase = sequence.phase; + buffer.header.commState = commState; + u16 headerInt = serializeClientHeader(buffer.header); + + buffer.data[buffer.dataSize++] = headerInt; + if (offset < fullPayloadSize) + offset |= fullPayload[offset] << 16; + if (offset + 1 < fullPayloadSize) + offset |= fullPayload[offset + 1] << 24; + + for (u32 i = 2; i < payloadSize; i += 4) { + u32 word = 0; + for (u32 j = 0; j < 4; j++) { + if (offset + i + j < fullPayloadSize && + i + j < LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT) { + u8 byte = fullPayload[offset + i + j]; + word |= byte << (j * 8); + } + } + buffer.data[buffer.dataSize++] = word; + } + + buffer.totalByteCount = + LINK_WIRELESS_OPEN_SDK_HEADER_SIZE_CLIENT + payloadSize; + + return buffer; + } SendBuffer createClientACKBuffer( ServerSDKHeader serverHeader) { From db4fbddda6dc62c6d95f5a71c8b4d81253bfbb18 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 05:30:08 -0300 Subject: [PATCH 35/74] Starting WirelessMultiboot refactor --- lib/LinkWirelessMultiboot.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index d74e705..cc1c4c3 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -394,14 +394,6 @@ class LinkWirelessMultiboot { return true; } - LinkWirelessOpenSDK::ClientSDKHeader parseClientHeader(u32 clientHeaderInt) { - return linkWirelessOpenSDK->parseClientHeader(clientHeaderInt); - } - - u32 serializeServerHeader(LinkWirelessOpenSDK::ServerSDKHeader serverHeader) { - return linkWirelessOpenSDK->serializeServerHeader(serverHeader); - } - template std::array toArray( Args... args) { From fa6670892c74c3fb4f4a0370b637155f44326f45 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 07:59:39 -0300 Subject: [PATCH 36/74] Extracting a higher order functions for handshake --- .../src/scenes/MultibootScene.cpp | 7 +- lib/LinkWirelessMultiboot.hpp | 353 ++++++++++++------ lib/LinkWirelessOpenSDK.hpp | 5 +- 3 files changed, 255 insertions(+), 110 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index 7baeb6e..6549758 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -187,9 +187,10 @@ void MultibootScene::processButtons() { const u8* romToSend = (const u8*)gbfs_get_obj(fs, ROM_FILE_NAME, &fileLength); - linkWirelessMultiboot->sendRom(romToSend, fileLength, "Multi", "Test", - 0xffff, 2, - [](int connectedPlayers) { return false; }); + auto result = linkWirelessMultiboot->sendRom( + romToSend, fileLength, "Multiboot", "Test", 0xffff, 2, + [](int connectedPlayers) { return false; }); + log("-> result: " + std::to_string(result)); print(); } diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index cc1c4c3..50a27b6 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -44,6 +44,11 @@ #define LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT 32 #define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG 0b1000000000000000 #define LINK_WIRELESS_MULTIBOOT_FRAME_LINES 228 +#define LINK_WIRELESS_MULTIBOOT_TRY(CALL) \ + if ((lastResult = CALL) != SUCCESS) { \ + link->deactivate(); \ + return lastResult; \ + } const u8 LINK_WIRELESS_MULTIBOOT_CMD_START[] = {0x00, 0x54, 0x00, 0x00, 0x00, 0x02, 0x00}; @@ -66,14 +71,14 @@ class LinkWirelessMultiboot { FAILURE }; - template + template Result sendRom(const u8* rom, u32 romSize, const char* gameName, const char* userName, const u16 gameId, u8 players, - F cancel) { + C cancel) { if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) return INVALID_SIZE; if (romSize > LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE) @@ -82,132 +87,107 @@ class LinkWirelessMultiboot { players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) return INVALID_PLAYERS; - if (!activate()) - return ADAPTER_NOT_DETECTED; - - if (!initialize(gameName, userName, gameId, players)) - return FAILURE; - - LinkRawWireless::AcceptConnectionsResponse acceptResponse; - while (link->playerCount() < players) { - link->acceptConnections(acceptResponse); - if (cancel(players)) { - link->deactivate(); - return CANCELED; - } - } - - LWMLOG("new client"); // TODO: MOVE HANDSHAKE TO WHILE + LINK_WIRELESS_MULTIBOOT_TRY(activate()) + LINK_WIRELESS_MULTIBOOT_TRY(initialize(gameName, userName, gameId, players)) + LINK_WIRELESS_MULTIBOOT_TRY(waitForClients(players, cancel)) bool hasData = false; LinkWirelessOpenSDK::ChildrenData childrenData; - LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; - // HANDSHAKE - while (!hasData) { - LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData(toArray(), 0, 1, response)) - return FAILURE; - childrenData = linkWirelessOpenSDK->getChildrenData(response); - hasData = childrenData.responses[0].packetsSize > 0; - } - LWMLOG("handshake received"); - LWMLOG("sending ACK"); - lastValidHeader = childrenData.responses[0] - .packets[childrenData.responses[0].packetsSize - 1] - .header; + LWMLOG("ready to send start command"); + + linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); - // ACK 1 + /* + // ROM START COMMAND hasData = false; while (!hasData) { LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData( - linkWirelessOpenSDK->createServerACKBuffer(lastValidHeader), - response)) - return FAILURE; - - if (response.dataSize == 0) - continue; + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + LINK_WIRELESS_MULTIBOOT_CMD_START, + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, {1, 0}, + LinkWirelessOpenSDK::CommState::STARTING, 0, 0b0001), + response)) childrenData = linkWirelessOpenSDK->getChildrenData(response); for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; - if (header.n == 2 && + if (header.isACK == 1 && header.n == 1 && header.phase == 0 && header.commState == LinkWirelessOpenSDK::CommState::STARTING) { hasData = true; - lastValidHeader = header; break; } } } - LWMLOG("N IS NOW 2, commState = 1"); + LWMLOG("READY TO SEND ROM!"); - // ACK 2 - hasData = false; - while (!hasData) { + // ROM START + u32 transferredBytes = 0; + LinkWirelessOpenSDK::SequenceNumber sequence = {.n = 1, .phase = 0}; + u32 progress = 0; + while (transferredBytes < romSize) { + auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( + rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, + transferredBytes, 0b0001); LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData( - linkWirelessOpenSDK->createServerACKBuffer(lastValidHeader), - response)) - return FAILURE; - - if (response.dataSize == 0) - continue; + LINK_WIRELESS_MULTIBOOT_TRY( + sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, + sendBuffer.totalByteCount, response)) childrenData = linkWirelessOpenSDK->getChildrenData(response); for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; - if (header.n == 1 && - header.commState == LinkWirelessOpenSDK::CommState::COMMUNICATING) { - hasData = true; - lastValidHeader = header; + if (header.isACK && header.sequence() == sequence) { + sequence.inc(); + transferredBytes += sendBuffer.header.payloadSize; + u32 newProgress = transferredBytes * 100 / romSize; + if (newProgress != progress) { + progress = newProgress; + LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); + } break; } } } - LWMLOG("NI STARTED"); + LWMLOG("SEND FINISHED! Confirming..."); - // RECEIVE NAME + // ROM END COMMAND hasData = false; while (!hasData) { LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData( - linkWirelessOpenSDK->createServerACKBuffer(lastValidHeader), - response)) - return FAILURE; + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {0, 0}, LinkWirelessOpenSDK::CommState::ENDING, 0, 0b0001), + response)) childrenData = linkWirelessOpenSDK->getChildrenData(response); - lastValidHeader = childrenData.responses[0] - .packets[childrenData.responses[0].packetsSize - 1] - .header; for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { auto header = childrenData.responses[0].packets[i].header; - if (header.commState == LinkWirelessOpenSDK::CommState::OFF) { + if (header.isACK == 1 && header.n == 0 && header.phase == 0 && + header.commState == LinkWirelessOpenSDK::CommState::ENDING) { hasData = true; break; } } } - LWMLOG("commState IS NOW 0"); + LWMLOG("Reconfirming..."); - // WAITING DATA STOP - hasData = false; - while (!hasData) { - LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData(toArray(), 0, 1, response)) - return FAILURE; - childrenData = linkWirelessOpenSDK->getChildrenData(response); - hasData = childrenData.responses[0].packetsSize == 0; - } - LWMLOG("ready to send start command"); + // ROM END 2 COMMAND + LinkRawWireless::ReceiveDataResponse response; + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {1, 0}, LinkWirelessOpenSDK::CommState::OFF, 0, 0b0001), + response)) - linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); + LWMLOG("SUCCESS!"); + */ // ROM START COMMAND hasData = false; while (!hasData) { LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData( + if (!sendAndExpectData2( linkWirelessOpenSDK->createServerBuffer( LINK_WIRELESS_MULTIBOOT_CMD_START, LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, {1, 0}, @@ -239,8 +219,8 @@ class LinkWirelessMultiboot { rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, transferredBytes, 0b0001); LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, - sendBuffer.totalByteCount, response)) { + if (!sendAndExpectData2(sendBuffer.data, sendBuffer.dataSize, + sendBuffer.totalByteCount, response)) { LWMLOG("SendData failed!"); return FAILURE; } @@ -266,7 +246,7 @@ class LinkWirelessMultiboot { hasData = false; while (!hasData) { LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData( + if (!sendAndExpectData2( linkWirelessOpenSDK->createServerBuffer( {}, 0, {0, 0}, LinkWirelessOpenSDK::CommState::ENDING, 0, 0b0001), @@ -291,7 +271,7 @@ class LinkWirelessMultiboot { hasData = false; while (!hasData) { LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData( + if (!sendAndExpectData2( linkWirelessOpenSDK->createServerBuffer( {}, 0, {1, 0}, LinkWirelessOpenSDK::CommState::OFF, 0, 0b0001), @@ -312,29 +292,70 @@ class LinkWirelessMultiboot { LinkRawWireless* link = new LinkRawWireless(); LinkWirelessOpenSDK* linkWirelessOpenSDK = new LinkWirelessOpenSDK(); + LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; - LinkWirelessOpenSDK::ClientSDKHeader lastACK; + Result lastResult; private: - bool activate() { + // TODO: CLEAN UP + bool sendAndExpectData2(LinkWirelessOpenSDK::SendBuffer< + LinkWirelessOpenSDK::ServerSDKHeader> sendBuffer, + LinkRawWireless::ReceiveDataResponse& response) { + return sendAndExpectData2(sendBuffer.data, sendBuffer.dataSize, + sendBuffer.totalByteCount, response); + } + + bool sendAndExpectData2( + std::array data, + u32 dataSize, + u32 _bytes, + LinkRawWireless::ReceiveDataResponse& response) { + LinkRawWireless::RemoteCommand remoteCommand; + bool success = false; + success = link->sendDataAndWait(data, dataSize, remoteCommand, _bytes); + if (!success) { + LWMLOG("! sendDataAndWait failed"); + return false; + } + if (remoteCommand.commandId != 0x28) { + LWMLOG("! expected EVENT 0x28"); + LWMLOG("! but got " + link->toHex(remoteCommand.commandId)); + return false; + } + if (remoteCommand.paramsSize > 0) { + if (((remoteCommand.params[0] >> 8) & 0b0001) == 0) { + // TODO: MULTIPLE CHILDREN + LWMLOG("! child timeout"); + return false; + } + } + success = link->receiveData(response); + if (!success) { + LWMLOG("! receiveData failed"); + return false; + } + return true; + } + + Result activate() { if (!link->activate()) { LWMLOG("! adapter not detected"); - return false; + return ADAPTER_NOT_DETECTED; } LWMLOG("activated"); - return true; + return SUCCESS; } - bool initialize(const char* gameName, - const char* userName, - const u16 gameId, - u8 players) { + Result initialize(const char* gameName, + const char* userName, + const u16 gameId, + u8 players) { if (!link->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_TX, LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT, LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { LWMLOG("! setup failed"); - return false; + return FAILURE; } LWMLOG("setup ok"); @@ -342,56 +363,178 @@ class LinkWirelessMultiboot { gameName, userName, gameId | LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG)) { LWMLOG("! broadcast failed"); - return false; + return FAILURE; } LWMLOG("broadcast data set"); if (!link->startHost()) { LWMLOG("! start host failed"); - return false; + return FAILURE; } LWMLOG("host started"); - return true; + return SUCCESS; } - bool sendAndExpectData(LinkWirelessOpenSDK::SendBuffer< - LinkWirelessOpenSDK::ServerSDKHeader> sendBuffer, - LinkRawWireless::ReceiveDataResponse& response) { + template + Result waitForClients(u8 players, C cancel) { + LinkRawWireless::AcceptConnectionsResponse acceptResponse; + + u32 currentPlayers = 1; + while (link->playerCount() < players) { + if (cancel(link->playerCount())) + return CANCELED; + + link->acceptConnections(acceptResponse); + + if (link->playerCount() > currentPlayers) { + currentPlayers = link->playerCount(); + + u8 lastClientNumber = acceptResponse.connectedClientsSize - 1; + LINK_WIRELESS_MULTIBOOT_TRY(handshakeClient(lastClientNumber, cancel)) + } + } + + return SUCCESS; + } + + template + Result handshakeClient(u8 clientNumber, C cancel) { + LWMLOG("new client: " + std::to_string(clientNumber)); + LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + [this](LinkRawWireless::ReceiveDataResponse& response) { + return sendAndExpectData(toArray(), 0, 1, response); + }, + [](LinkWirelessOpenSDK::ClientPacket packet) { return true; }, cancel)) + // initial packet received + + LWMLOG("handshake (1/2)..."); + LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( + clientNumber, + [](LinkWirelessOpenSDK::ClientPacket packet) { + return packet.header.n == 2 && + packet.header.commState == + LinkWirelessOpenSDK::CommState::STARTING; + }, + cancel)) + // n = 2, commState = 1 + + LWMLOG("handshake (2/2)..."); + LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( + clientNumber, + [](LinkWirelessOpenSDK::ClientPacket packet) { + return packet.header.n == 1 && + packet.header.commState == + LinkWirelessOpenSDK::CommState::COMMUNICATING; + }, + cancel)) + // n = 1, commState = 2 + + LWMLOG("receiving name..."); + LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( + clientNumber, + [this](LinkWirelessOpenSDK::ClientPacket packet) { + this->lastValidHeader = packet.header; + return packet.header.commState == LinkWirelessOpenSDK::CommState::OFF; + }, + cancel)) + // commState = 0 + + LWMLOG("draining queue..."); + bool hasFinished = false; + while (!hasFinished) { + if (cancel(link->playerCount())) + return CANCELED; + + LinkRawWireless::ReceiveDataResponse response; + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(toArray(), 0, 1, response)) + hasFinished = childrenData.responses[0].packetsSize == 0; + } + LWMLOG("client " + std::to_string(clientNumber) + " accepted"); + + return SUCCESS; + } + + template + Result exchangeACK(u8 clientNumber, V validatePacket, C cancel) { + LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + [this, clientNumber](LinkRawWireless::ReceiveDataResponse& response) { + return sendAndExpectData(linkWirelessOpenSDK->createServerACKBuffer( + lastValidHeader, clientNumber), + response); + }, + validatePacket, cancel)) + + return SUCCESS; + } + + template + Result exchangeData(F sendAction, V validatePacket, C cancel) { + bool hasFinished = false; + while (!hasFinished) { + if (cancel(link->playerCount())) + return CANCELED; + + LinkRawWireless::ReceiveDataResponse response; + LINK_WIRELESS_MULTIBOOT_TRY(sendAction(response)) + auto childrenData = linkWirelessOpenSDK->getChildrenData(response); + + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto packet = childrenData.responses[0].packets[i]; + auto header = packet.header; + if (validatePacket(packet)) { + hasFinished = true; + lastValidHeader = header; + break; + } + } + } + + return SUCCESS; + } + + Result sendAndExpectData(LinkWirelessOpenSDK::SendBuffer< + LinkWirelessOpenSDK::ServerSDKHeader> sendBuffer, + LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, sendBuffer.totalByteCount, response); } - bool sendAndExpectData( + Result sendAndExpectData( std::array data, u32 dataSize, u32 _bytes, LinkRawWireless::ReceiveDataResponse& response) { LinkRawWireless::RemoteCommand remoteCommand; bool success = false; + success = link->sendDataAndWait(data, dataSize, remoteCommand, _bytes); if (!success) { LWMLOG("! sendDataAndWait failed"); - return false; + return FAILURE; } + if (remoteCommand.commandId != 0x28) { LWMLOG("! expected EVENT 0x28"); LWMLOG("! but got " + link->toHex(remoteCommand.commandId)); - return false; + return FAILURE; } + if (remoteCommand.paramsSize > 0) { if (((remoteCommand.params[0] >> 8) & 0b0001) == 0) { // TODO: MULTIPLE CHILDREN LWMLOG("! child timeout"); - return false; + return FAILURE; } } + success = link->receiveData(response); if (!success) { LWMLOG("! receiveData failed"); - return false; + return FAILURE; } - return true; + + return SUCCESS; } template diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 789a342..0fc8859 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -242,10 +242,11 @@ class LinkWirelessOpenSDK { } SendBuffer createServerACKBuffer( - ClientSDKHeader clientHeader) { + ClientSDKHeader clientHeader, + u8 clientNumber) { SendBuffer buffer; - buffer.header = createACKHeaderFor(clientHeader, 0); + buffer.header = createACKHeaderFor(clientHeader, clientNumber); u32 headerInt = serializeServerHeader(buffer.header); buffer.data[buffer.dataSize++] = headerInt; From 323e24272d2130180f377f4b53b0bbae0e621fe3 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 08:12:02 -0300 Subject: [PATCH 37/74] FIX: First bytes + cleanup --- lib/LinkWirelessMultiboot.hpp | 143 +--------------------------------- lib/LinkWirelessOpenSDK.hpp | 6 +- 2 files changed, 5 insertions(+), 144 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 50a27b6..247c08f 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -98,7 +98,6 @@ class LinkWirelessMultiboot { linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); - /* // ROM START COMMAND hasData = false; while (!hasData) { @@ -180,106 +179,6 @@ class LinkWirelessMultiboot { {}, 0, {1, 0}, LinkWirelessOpenSDK::CommState::OFF, 0, 0b0001), response)) - LWMLOG("SUCCESS!"); - */ - - // ROM START COMMAND - hasData = false; - while (!hasData) { - LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData2( - linkWirelessOpenSDK->createServerBuffer( - LINK_WIRELESS_MULTIBOOT_CMD_START, - LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, {1, 0}, - LinkWirelessOpenSDK::CommState::STARTING, 0, 0b0001), - response)) - return FAILURE; - - if (response.dataSize == 0) - continue; - childrenData = linkWirelessOpenSDK->getChildrenData(response); - - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - if (header.isACK == 1 && header.n == 1 && header.phase == 0 && - header.commState == LinkWirelessOpenSDK::CommState::STARTING) { - hasData = true; - break; - } - } - } - LWMLOG("READY TO SEND ROM!"); - - // ROM START - u32 transferredBytes = 0; - LinkWirelessOpenSDK::SequenceNumber sequence = {.n = 1, .phase = 0}; - u32 progress = 0; - while (transferredBytes < romSize) { - auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, - transferredBytes, 0b0001); - LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData2(sendBuffer.data, sendBuffer.dataSize, - sendBuffer.totalByteCount, response)) { - LWMLOG("SendData failed!"); - return FAILURE; - } - childrenData = linkWirelessOpenSDK->getChildrenData(response); - - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - if (header.isACK && header.sequence() == sequence) { - sequence.inc(); - transferredBytes += sendBuffer.header.payloadSize; - u32 newProgress = transferredBytes * 100 / romSize; - if (newProgress != progress) { - progress = newProgress; - LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); - } - break; - } - } - } - LWMLOG("SEND FINISHED! Confirming..."); - - // ROM END COMMAND - hasData = false; - while (!hasData) { - LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData2( - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {0, 0}, LinkWirelessOpenSDK::CommState::ENDING, 0, - 0b0001), - response)) - return FAILURE; - if (response.dataSize == 0) - continue; - childrenData = linkWirelessOpenSDK->getChildrenData(response); - - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - if (header.isACK == 1 && header.n == 0 && header.phase == 0 && - header.commState == LinkWirelessOpenSDK::CommState::ENDING) { - hasData = true; - break; - } - } - } - LWMLOG("Reconfirming..."); - - // ROM END 2 COMMAND - hasData = false; - while (!hasData) { - LinkRawWireless::ReceiveDataResponse response; - if (!sendAndExpectData2( - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {1, 0}, LinkWirelessOpenSDK::CommState::OFF, 0, - 0b0001), - response)) - return FAILURE; - hasData = true; - } - LWMLOG("SUCCESS!"); return SUCCESS; @@ -290,6 +189,7 @@ class LinkWirelessMultiboot { delete linkWirelessOpenSDK; } + // TODO: CLEANUP LinkRawWireless* link = new LinkRawWireless(); LinkWirelessOpenSDK* linkWirelessOpenSDK = new LinkWirelessOpenSDK(); LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; @@ -297,46 +197,6 @@ class LinkWirelessMultiboot { Result lastResult; private: - // TODO: CLEAN UP - bool sendAndExpectData2(LinkWirelessOpenSDK::SendBuffer< - LinkWirelessOpenSDK::ServerSDKHeader> sendBuffer, - LinkRawWireless::ReceiveDataResponse& response) { - return sendAndExpectData2(sendBuffer.data, sendBuffer.dataSize, - sendBuffer.totalByteCount, response); - } - - bool sendAndExpectData2( - std::array data, - u32 dataSize, - u32 _bytes, - LinkRawWireless::ReceiveDataResponse& response) { - LinkRawWireless::RemoteCommand remoteCommand; - bool success = false; - success = link->sendDataAndWait(data, dataSize, remoteCommand, _bytes); - if (!success) { - LWMLOG("! sendDataAndWait failed"); - return false; - } - if (remoteCommand.commandId != 0x28) { - LWMLOG("! expected EVENT 0x28"); - LWMLOG("! but got " + link->toHex(remoteCommand.commandId)); - return false; - } - if (remoteCommand.paramsSize > 0) { - if (((remoteCommand.params[0] >> 8) & 0b0001) == 0) { - // TODO: MULTIPLE CHILDREN - LWMLOG("! child timeout"); - return false; - } - } - success = link->receiveData(response); - if (!success) { - LWMLOG("! receiveData failed"); - return false; - } - return true; - } - Result activate() { if (!link->activate()) { LWMLOG("! adapter not detected"); @@ -448,6 +308,7 @@ class LinkWirelessMultiboot { LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(toArray(), 0, 1, response)) + auto childrenData = linkWirelessOpenSDK->getChildrenData(response); hasFinished = childrenData.responses[0].packetsSize == 0; } LWMLOG("client " + std::to_string(clientNumber) + " accepted"); diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 0fc8859..0900298 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -221,7 +221,7 @@ class LinkWirelessOpenSDK { buffer.data[buffer.dataSize++] = headerInt; if (offset < fullPayloadSize) - offset |= fullPayload[offset] << 24; + buffer.data[0] |= fullPayload[offset] << 24; for (u32 i = 1; i < payloadSize; i += 4) { u32 word = 0; @@ -274,9 +274,9 @@ class LinkWirelessOpenSDK { buffer.data[buffer.dataSize++] = headerInt; if (offset < fullPayloadSize) - offset |= fullPayload[offset] << 16; + buffer.data[0] |= fullPayload[offset] << 16; if (offset + 1 < fullPayloadSize) - offset |= fullPayload[offset + 1] << 24; + buffer.data[0] |= fullPayload[offset + 1] << 24; for (u32 i = 2; i < payloadSize; i += 4) { u32 word = 0; From b3f40201e80eb8f2735587db4166fc44b5d66e7a Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 08:25:55 -0300 Subject: [PATCH 38/74] Using the new functions in the last part --- lib/LinkWirelessMultiboot.hpp | 99 +++++++++++++++-------------------- 1 file changed, 43 insertions(+), 56 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 247c08f..63f231e 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -91,41 +91,33 @@ class LinkWirelessMultiboot { LINK_WIRELESS_MULTIBOOT_TRY(initialize(gameName, userName, gameId, players)) LINK_WIRELESS_MULTIBOOT_TRY(waitForClients(players, cancel)) - bool hasData = false; - LinkWirelessOpenSDK::ChildrenData childrenData; - - LWMLOG("ready to send start command"); - + LWMLOG("all players are connected"); linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); - // ROM START COMMAND - hasData = false; - while (!hasData) { - LinkRawWireless::ReceiveDataResponse response; - LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( - linkWirelessOpenSDK->createServerBuffer( - LINK_WIRELESS_MULTIBOOT_CMD_START, - LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, {1, 0}, - LinkWirelessOpenSDK::CommState::STARTING, 0, 0b0001), - response)) - childrenData = linkWirelessOpenSDK->getChildrenData(response); - - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - if (header.isACK == 1 && header.n == 1 && header.phase == 0 && - header.commState == LinkWirelessOpenSDK::CommState::STARTING) { - hasData = true; - break; - } - } - } - LWMLOG("READY TO SEND ROM!"); + LWMLOG("rom start command..."); + LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + [this](LinkRawWireless::ReceiveDataResponse& response) { + return sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + LINK_WIRELESS_MULTIBOOT_CMD_START, + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, {1, 0}, + LinkWirelessOpenSDK::CommState::STARTING, 0, 0b0001), + response); + }, + [](LinkWirelessOpenSDK::ClientPacket packet) { + auto header = packet.header; + return header.isACK == 1 && header.n == 1 && header.phase == 0 && + header.commState == LinkWirelessOpenSDK::CommState::STARTING; + }, + cancel)) - // ROM START - u32 transferredBytes = 0; + LWMLOG("SENDING ROM!"); + LinkWirelessOpenSDK::ChildrenData childrenData; LinkWirelessOpenSDK::SequenceNumber sequence = {.n = 1, .phase = 0}; + u32 transferredBytes = 0; u32 progress = 0; while (transferredBytes < romSize) { + // TODO: Check cancel auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, transferredBytes, 0b0001); @@ -149,30 +141,24 @@ class LinkWirelessMultiboot { } } } - LWMLOG("SEND FINISHED! Confirming..."); - // ROM END COMMAND - hasData = false; - while (!hasData) { - LinkRawWireless::ReceiveDataResponse response; - LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {0, 0}, LinkWirelessOpenSDK::CommState::ENDING, 0, 0b0001), - response)) - childrenData = linkWirelessOpenSDK->getChildrenData(response); - - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - if (header.isACK == 1 && header.n == 0 && header.phase == 0 && - header.commState == LinkWirelessOpenSDK::CommState::ENDING) { - hasData = true; - break; - } - } - } - LWMLOG("Reconfirming..."); + LWMLOG("confirming (1/2)..."); + LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + [this](LinkRawWireless::ReceiveDataResponse& response) { + return sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {0, 0}, LinkWirelessOpenSDK::CommState::ENDING, 0, + 0b0001), + response); + }, + [](LinkWirelessOpenSDK::ClientPacket packet) { + auto header = packet.header; + return header.isACK == 1 && header.n == 0 && header.phase == 0 && + header.commState == LinkWirelessOpenSDK::CommState::ENDING; + }, + cancel)) - // ROM END 2 COMMAND + LWMLOG("confirming (2/2)..."); LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( @@ -272,9 +258,9 @@ class LinkWirelessMultiboot { LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( clientNumber, [](LinkWirelessOpenSDK::ClientPacket packet) { - return packet.header.n == 2 && - packet.header.commState == - LinkWirelessOpenSDK::CommState::STARTING; + auto header = packet.header; + return header.n == 2 && + header.commState == LinkWirelessOpenSDK::CommState::STARTING; }, cancel)) // n = 2, commState = 1 @@ -283,8 +269,9 @@ class LinkWirelessMultiboot { LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( clientNumber, [](LinkWirelessOpenSDK::ClientPacket packet) { - return packet.header.n == 1 && - packet.header.commState == + auto header = packet.header; + return header.n == 1 && + header.commState == LinkWirelessOpenSDK::CommState::COMMUNICATING; }, cancel)) From 7903a4468b78ebaee586fbd16a2dda166a7975cd Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 08:44:57 -0300 Subject: [PATCH 39/74] Using clientNumber on handshake --- lib/LinkWirelessMultiboot.hpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 63f231e..c246aee 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -96,6 +96,7 @@ class LinkWirelessMultiboot { LWMLOG("rom start command..."); LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + 0, [this](LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( @@ -118,6 +119,7 @@ class LinkWirelessMultiboot { u32 progress = 0; while (transferredBytes < romSize) { // TODO: Check cancel + // TODO: Multiple clients auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, transferredBytes, 0b0001); @@ -144,6 +146,7 @@ class LinkWirelessMultiboot { LWMLOG("confirming (1/2)..."); LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + 0, [this](LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( @@ -248,6 +251,7 @@ class LinkWirelessMultiboot { Result handshakeClient(u8 clientNumber, C cancel) { LWMLOG("new client: " + std::to_string(clientNumber)); LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + clientNumber, [this](LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData(toArray(), 0, 1, response); }, @@ -296,8 +300,9 @@ class LinkWirelessMultiboot { LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(toArray(), 0, 1, response)) auto childrenData = linkWirelessOpenSDK->getChildrenData(response); - hasFinished = childrenData.responses[0].packetsSize == 0; + hasFinished = childrenData.responses[clientNumber].packetsSize == 0; } + LWMLOG("client " + std::to_string(clientNumber) + " accepted"); return SUCCESS; @@ -306,6 +311,7 @@ class LinkWirelessMultiboot { template Result exchangeACK(u8 clientNumber, V validatePacket, C cancel) { LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + clientNumber, [this, clientNumber](LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData(linkWirelessOpenSDK->createServerACKBuffer( lastValidHeader, clientNumber), @@ -317,7 +323,10 @@ class LinkWirelessMultiboot { } template - Result exchangeData(F sendAction, V validatePacket, C cancel) { + Result exchangeData(u8 clientNumber, + F sendAction, + V validatePacket, + C cancel) { bool hasFinished = false; while (!hasFinished) { if (cancel(link->playerCount())) @@ -327,8 +336,9 @@ class LinkWirelessMultiboot { LINK_WIRELESS_MULTIBOOT_TRY(sendAction(response)) auto childrenData = linkWirelessOpenSDK->getChildrenData(response); - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto packet = childrenData.responses[0].packets[i]; + for (u32 i = 0; i < childrenData.responses[clientNumber].packetsSize; + i++) { + auto packet = childrenData.responses[clientNumber].packets[i]; auto header = packet.header; if (validatePacket(packet)) { hasFinished = true; @@ -369,9 +379,9 @@ class LinkWirelessMultiboot { } if (remoteCommand.paramsSize > 0) { - if (((remoteCommand.params[0] >> 8) & 0b0001) == 0) { - // TODO: MULTIPLE CHILDREN - LWMLOG("! child timeout"); + u8 activeChildren = (remoteCommand.params[0] >> 8) & 0b1111; + if (activeChildren == 0) { + LWMLOG("! timeout"); return FAILURE; } } From bd4e6ef1ce15b3f72039388a136ac123f5c29fa9 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 09:11:51 -0300 Subject: [PATCH 40/74] Adding progress reporting to cancel() --- .../src/scenes/MultibootScene.cpp | 6 +- lib/LinkWirelessMultiboot.hpp | 101 +++++++++++------- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index 6549758..f2c7303 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -126,7 +126,7 @@ void MultibootScene::load() { // if (useVerboseLog) // TODO: RESTORE log(string); }; - linkWirelessMultiboot->link->logger = [](std::string string) { + linkWirelessMultiboot->linkRawWireless->logger = [](std::string string) { if (useVerboseLog) log(string); }; @@ -189,7 +189,9 @@ void MultibootScene::processButtons() { auto result = linkWirelessMultiboot->sendRom( romToSend, fileLength, "Multiboot", "Test", 0xffff, 2, - [](int connectedPlayers) { return false; }); + [](LinkWirelessMultiboot::MultibootProgress progress) { + return false; + }); log("-> result: " + std::to_string(result)); print(); } diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index c246aee..aaa59ec 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -44,10 +44,9 @@ #define LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT 32 #define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG 0b1000000000000000 #define LINK_WIRELESS_MULTIBOOT_FRAME_LINES 228 -#define LINK_WIRELESS_MULTIBOOT_TRY(CALL) \ - if ((lastResult = CALL) != SUCCESS) { \ - link->deactivate(); \ - return lastResult; \ +#define LINK_WIRELESS_MULTIBOOT_TRY(CALL) \ + if ((progress.lastResult = CALL) != SUCCESS) { \ + return finish(progress.lastResult); \ } const u8 LINK_WIRELESS_MULTIBOOT_CMD_START[] = {0x00, 0x54, 0x00, 0x00, @@ -70,6 +69,15 @@ class LinkWirelessMultiboot { ADAPTER_NOT_DETECTED, FAILURE }; + enum State { STOPPED, INITIALIZING, WAITING, PREPARING, SENDING, CONFIRMING }; + + struct MultibootProgress { + State state = STOPPED; + u32 connectedPlayers = 1; + + Result lastResult; + LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; + }; template Result sendRom(const u8* rom, @@ -88,10 +96,13 @@ class LinkWirelessMultiboot { return INVALID_PLAYERS; LINK_WIRELESS_MULTIBOOT_TRY(activate()) + progress.state = INITIALIZING; LINK_WIRELESS_MULTIBOOT_TRY(initialize(gameName, userName, gameId, players)) + progress.state = WAITING; LINK_WIRELESS_MULTIBOOT_TRY(waitForClients(players, cancel)) LWMLOG("all players are connected"); + progress.state = PREPARING; linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); LWMLOG("rom start command..."); @@ -113,12 +124,15 @@ class LinkWirelessMultiboot { cancel)) LWMLOG("SENDING ROM!"); + progress.state = SENDING; LinkWirelessOpenSDK::ChildrenData childrenData; LinkWirelessOpenSDK::SequenceNumber sequence = {.n = 1, .phase = 0}; u32 transferredBytes = 0; - u32 progress = 0; + u32 percent = 0; while (transferredBytes < romSize) { - // TODO: Check cancel + if (cancel(progress)) + return finish(CANCELED); + // TODO: Multiple clients auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, @@ -134,10 +148,10 @@ class LinkWirelessMultiboot { if (header.isACK && header.sequence() == sequence) { sequence.inc(); transferredBytes += sendBuffer.header.payloadSize; - u32 newProgress = transferredBytes * 100 / romSize; - if (newProgress != progress) { - progress = newProgress; - LWMLOG("-> " + std::to_string(transferredBytes * 100 / romSize)); + u32 newPercent = transferredBytes * 100 / romSize; + if (newPercent != percent) { + percent = newPercent; + LWMLOG("-> " + std::to_string(percent)); } break; } @@ -145,6 +159,7 @@ class LinkWirelessMultiboot { } LWMLOG("confirming (1/2)..."); + progress.state = CONFIRMING; LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( 0, [this](LinkRawWireless::ReceiveDataResponse& response) { @@ -169,25 +184,22 @@ class LinkWirelessMultiboot { response)) LWMLOG("SUCCESS!"); - - return SUCCESS; + return finish(SUCCESS); } ~LinkWirelessMultiboot() { - delete link; + delete linkRawWireless; delete linkWirelessOpenSDK; } - // TODO: CLEANUP - LinkRawWireless* link = new LinkRawWireless(); + LinkRawWireless* linkRawWireless = new LinkRawWireless(); LinkWirelessOpenSDK* linkWirelessOpenSDK = new LinkWirelessOpenSDK(); - LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; - - Result lastResult; private: + MultibootProgress progress; + Result activate() { - if (!link->activate()) { + if (!linkRawWireless->activate()) { LWMLOG("! adapter not detected"); return ADAPTER_NOT_DETECTED; } @@ -200,15 +212,15 @@ class LinkWirelessMultiboot { const char* userName, const u16 gameId, u8 players) { - if (!link->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_TX, - LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT, - LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { + if (!linkRawWireless->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_TX, + LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT, + LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { LWMLOG("! setup failed"); return FAILURE; } LWMLOG("setup ok"); - if (!link->broadcast( + if (!linkRawWireless->broadcast( gameName, userName, gameId | LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG)) { LWMLOG("! broadcast failed"); @@ -216,7 +228,7 @@ class LinkWirelessMultiboot { } LWMLOG("broadcast data set"); - if (!link->startHost()) { + if (!linkRawWireless->startHost()) { LWMLOG("! start host failed"); return FAILURE; } @@ -229,15 +241,15 @@ class LinkWirelessMultiboot { Result waitForClients(u8 players, C cancel) { LinkRawWireless::AcceptConnectionsResponse acceptResponse; - u32 currentPlayers = 1; - while (link->playerCount() < players) { - if (cancel(link->playerCount())) - return CANCELED; + progress.connectedPlayers = 1; + while (linkRawWireless->playerCount() < players) { + if (cancel(progress)) + return finish(CANCELED); - link->acceptConnections(acceptResponse); + linkRawWireless->acceptConnections(acceptResponse); - if (link->playerCount() > currentPlayers) { - currentPlayers = link->playerCount(); + if (linkRawWireless->playerCount() > progress.connectedPlayers) { + progress.connectedPlayers = linkRawWireless->playerCount(); u8 lastClientNumber = acceptResponse.connectedClientsSize - 1; LINK_WIRELESS_MULTIBOOT_TRY(handshakeClient(lastClientNumber, cancel)) @@ -285,7 +297,7 @@ class LinkWirelessMultiboot { LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( clientNumber, [this](LinkWirelessOpenSDK::ClientPacket packet) { - this->lastValidHeader = packet.header; + progress.lastValidHeader = packet.header; return packet.header.commState == LinkWirelessOpenSDK::CommState::OFF; }, cancel)) @@ -294,8 +306,8 @@ class LinkWirelessMultiboot { LWMLOG("draining queue..."); bool hasFinished = false; while (!hasFinished) { - if (cancel(link->playerCount())) - return CANCELED; + if (cancel(progress)) + return finish(CANCELED); LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(toArray(), 0, 1, response)) @@ -314,7 +326,7 @@ class LinkWirelessMultiboot { clientNumber, [this, clientNumber](LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData(linkWirelessOpenSDK->createServerACKBuffer( - lastValidHeader, clientNumber), + progress.lastValidHeader, clientNumber), response); }, validatePacket, cancel)) @@ -329,8 +341,8 @@ class LinkWirelessMultiboot { C cancel) { bool hasFinished = false; while (!hasFinished) { - if (cancel(link->playerCount())) - return CANCELED; + if (cancel(progress)) + return finish(CANCELED); LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAction(response)) @@ -342,7 +354,7 @@ class LinkWirelessMultiboot { auto header = packet.header; if (validatePacket(packet)) { hasFinished = true; - lastValidHeader = header; + progress.lastValidHeader = header; break; } } @@ -366,7 +378,8 @@ class LinkWirelessMultiboot { LinkRawWireless::RemoteCommand remoteCommand; bool success = false; - success = link->sendDataAndWait(data, dataSize, remoteCommand, _bytes); + success = + linkRawWireless->sendDataAndWait(data, dataSize, remoteCommand, _bytes); if (!success) { LWMLOG("! sendDataAndWait failed"); return FAILURE; @@ -374,7 +387,7 @@ class LinkWirelessMultiboot { if (remoteCommand.commandId != 0x28) { LWMLOG("! expected EVENT 0x28"); - LWMLOG("! but got " + link->toHex(remoteCommand.commandId)); + LWMLOG("! but got " + linkRawWireless->toHex(remoteCommand.commandId)); return FAILURE; } @@ -386,7 +399,7 @@ class LinkWirelessMultiboot { } } - success = link->receiveData(response); + success = linkRawWireless->receiveData(response); if (!success) { LWMLOG("! receiveData failed"); return FAILURE; @@ -395,6 +408,12 @@ class LinkWirelessMultiboot { return SUCCESS; } + Result finish(Result result) { + linkRawWireless->deactivate(); + progress = MultibootProgress{}; + return result; + } + template std::array toArray( Args... args) { From 26a115792a0ae5b490e0f5c5c307a823b3d057a2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 2 Feb 2024 09:42:03 -0300 Subject: [PATCH 41/74] Fixes weird bug? --- lib/LinkWirelessMultiboot.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index aaa59ec..8ae8f23 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -410,7 +410,8 @@ class LinkWirelessMultiboot { Result finish(Result result) { linkRawWireless->deactivate(); - progress = MultibootProgress{}; + progress.state = STOPPED; + progress.connectedPlayers = 1; return result; } From 7de48bc7acbd3ebe653f0e7edf05608c0bec2074 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 02:50:10 -0300 Subject: [PATCH 42/74] Extracting ACK validation function --- lib/LinkWirelessMultiboot.hpp | 135 ++++++++++++++++++---------------- lib/LinkWirelessOpenSDK.hpp | 40 +++++----- 2 files changed, 93 insertions(+), 82 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 8ae8f23..51fd90f 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -42,11 +42,11 @@ #define LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC 0x003F0000 #define LINK_WIRELESS_MULTIBOOT_SETUP_TX 1 #define LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT 32 -#define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG 0b1000000000000000 +#define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG (1 << 15) #define LINK_WIRELESS_MULTIBOOT_FRAME_LINES 228 -#define LINK_WIRELESS_MULTIBOOT_TRY(CALL) \ - if ((progress.lastResult = CALL) != SUCCESS) { \ - return finish(progress.lastResult); \ +#define LINK_WIRELESS_MULTIBOOT_TRY(CALL) \ + if ((lastResult = CALL) != SUCCESS) { \ + return finish(lastResult); \ } const u8 LINK_WIRELESS_MULTIBOOT_CMD_START[] = {0x00, 0x54, 0x00, 0x00, @@ -74,9 +74,7 @@ class LinkWirelessMultiboot { struct MultibootProgress { State state = STOPPED; u32 connectedPlayers = 1; - - Result lastResult; - LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; + u32 percentage = 0; }; template @@ -106,41 +104,32 @@ class LinkWirelessMultiboot { linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); LWMLOG("rom start command..."); - LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( - 0, - [this](LinkRawWireless::ReceiveDataResponse& response) { - return sendAndExpectData( - linkWirelessOpenSDK->createServerBuffer( - LINK_WIRELESS_MULTIBOOT_CMD_START, - LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, {1, 0}, - LinkWirelessOpenSDK::CommState::STARTING, 0, 0b0001), - response); - }, - [](LinkWirelessOpenSDK::ClientPacket packet) { - auto header = packet.header; - return header.isACK == 1 && header.n == 1 && header.phase == 0 && - header.commState == LinkWirelessOpenSDK::CommState::STARTING; - }, + LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( + 0, // TODO: Multiple clients + linkWirelessOpenSDK->createServerBuffer( + LINK_WIRELESS_MULTIBOOT_CMD_START, + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, + {1, 0, LinkWirelessOpenSDK::CommState::STARTING}, 0, 0b0001), cancel)) LWMLOG("SENDING ROM!"); progress.state = SENDING; LinkWirelessOpenSDK::ChildrenData childrenData; - LinkWirelessOpenSDK::SequenceNumber sequence = {.n = 1, .phase = 0}; + LinkWirelessOpenSDK::SequenceNumber sequence = { + .n = 1, + .phase = 0, + .commState = LinkWirelessOpenSDK::CommState::COMMUNICATING}; u32 transferredBytes = 0; - u32 percent = 0; + progress.percentage = 0; while (transferredBytes < romSize) { if (cancel(progress)) return finish(CANCELED); // TODO: Multiple clients auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, sequence, LinkWirelessOpenSDK::CommState::COMMUNICATING, - transferredBytes, 0b0001); + rom, romSize, sequence, transferredBytes, 0b0001); LinkRawWireless::ReceiveDataResponse response; - LINK_WIRELESS_MULTIBOOT_TRY( - sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, - sendBuffer.totalByteCount, response)) + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(sendBuffer, response)) childrenData = linkWirelessOpenSDK->getChildrenData(response); for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { @@ -148,10 +137,10 @@ class LinkWirelessMultiboot { if (header.isACK && header.sequence() == sequence) { sequence.inc(); transferredBytes += sendBuffer.header.payloadSize; - u32 newPercent = transferredBytes * 100 / romSize; - if (newPercent != percent) { - percent = newPercent; - LWMLOG("-> " + std::to_string(percent)); + u32 newPercentage = transferredBytes * 100 / romSize; + if (newPercentage != progress.percentage) { + progress.percentage = newPercentage; + LWMLOG("-> " + std::to_string(newPercentage)); } break; } @@ -160,27 +149,18 @@ class LinkWirelessMultiboot { LWMLOG("confirming (1/2)..."); progress.state = CONFIRMING; - LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( - 0, - [this](LinkRawWireless::ReceiveDataResponse& response) { - return sendAndExpectData( - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {0, 0}, LinkWirelessOpenSDK::CommState::ENDING, 0, - 0b0001), - response); - }, - [](LinkWirelessOpenSDK::ClientPacket packet) { - auto header = packet.header; - return header.isACK == 1 && header.n == 0 && header.phase == 0 && - header.commState == LinkWirelessOpenSDK::CommState::ENDING; - }, + LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( + 0, // TODO: Multiple clients + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {0, 0, LinkWirelessOpenSDK::CommState::ENDING}, 0, 0b0001), cancel)) LWMLOG("confirming (2/2)..."); + // TODO: Multiple clients LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( - {}, 0, {1, 0}, LinkWirelessOpenSDK::CommState::OFF, 0, 0b0001), + {}, 0, {1, 0, LinkWirelessOpenSDK::CommState::OFF}, 0, 0b0001), response)) LWMLOG("SUCCESS!"); @@ -197,6 +177,8 @@ class LinkWirelessMultiboot { private: MultibootProgress progress; + Result lastResult; + LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; Result activate() { if (!linkRawWireless->activate()) { @@ -271,7 +253,7 @@ class LinkWirelessMultiboot { // initial packet received LWMLOG("handshake (1/2)..."); - LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( + LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( clientNumber, [](LinkWirelessOpenSDK::ClientPacket packet) { auto header = packet.header; @@ -282,7 +264,7 @@ class LinkWirelessMultiboot { // n = 2, commState = 1 LWMLOG("handshake (2/2)..."); - LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( + LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( clientNumber, [](LinkWirelessOpenSDK::ClientPacket packet) { auto header = packet.header; @@ -294,10 +276,10 @@ class LinkWirelessMultiboot { // n = 1, commState = 2 LWMLOG("receiving name..."); - LINK_WIRELESS_MULTIBOOT_TRY(exchangeACK( + LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( clientNumber, [this](LinkWirelessOpenSDK::ClientPacket packet) { - progress.lastValidHeader = packet.header; + lastValidHeader = packet.header; return packet.header.commState == LinkWirelessOpenSDK::CommState::OFF; }, cancel)) @@ -320,13 +302,36 @@ class LinkWirelessMultiboot { return SUCCESS; } + template + __attribute__((noinline)) Result exchangeNewData( + u8 clientNumber, + LinkWirelessOpenSDK::SendBuffer + sendBuffer, + C cancel) { + LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( + clientNumber, + [this, &sendBuffer](LinkRawWireless::ReceiveDataResponse& response) { + return sendAndExpectData(sendBuffer, response); + }, + [&sendBuffer](LinkWirelessOpenSDK::ClientPacket packet) { + auto header = packet.header; + return header.isACK == 1 && + header.sequence() == sendBuffer.header.sequence(); + }, + cancel)) + + return SUCCESS; + } + template - Result exchangeACK(u8 clientNumber, V validatePacket, C cancel) { + __attribute__((noinline)) Result exchangeACKData(u8 clientNumber, + V validatePacket, + C cancel) { LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( clientNumber, [this, clientNumber](LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData(linkWirelessOpenSDK->createServerACKBuffer( - progress.lastValidHeader, clientNumber), + lastValidHeader, clientNumber), response); }, validatePacket, cancel)) @@ -335,10 +340,10 @@ class LinkWirelessMultiboot { } template - Result exchangeData(u8 clientNumber, - F sendAction, - V validatePacket, - C cancel) { + __attribute__((noinline)) Result exchangeData(u8 clientNumber, + F sendAction, + V validatePacket, + C cancel) { bool hasFinished = false; while (!hasFinished) { if (cancel(progress)) @@ -354,7 +359,7 @@ class LinkWirelessMultiboot { auto header = packet.header; if (validatePacket(packet)) { hasFinished = true; - progress.lastValidHeader = header; + lastValidHeader = header; break; } } @@ -363,14 +368,15 @@ class LinkWirelessMultiboot { return SUCCESS; } - Result sendAndExpectData(LinkWirelessOpenSDK::SendBuffer< - LinkWirelessOpenSDK::ServerSDKHeader> sendBuffer, - LinkRawWireless::ReceiveDataResponse& response) { + __attribute__((noinline)) Result sendAndExpectData( + LinkWirelessOpenSDK::SendBuffer + sendBuffer, + LinkRawWireless::ReceiveDataResponse& response) { return sendAndExpectData(sendBuffer.data, sendBuffer.dataSize, sendBuffer.totalByteCount, response); } - Result sendAndExpectData( + __attribute__((noinline)) Result sendAndExpectData( std::array data, u32 dataSize, u32 _bytes, @@ -408,10 +414,11 @@ class LinkWirelessMultiboot { return SUCCESS; } - Result finish(Result result) { + __attribute__((noinline)) Result finish(Result result) { linkRawWireless->deactivate(); progress.state = STOPPED; progress.connectedPlayers = 1; + progress.percentage = 0; return result; } diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index 0900298..c0f221d 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -57,6 +57,7 @@ class LinkWirelessOpenSDK { struct SequenceNumber { u32 n = 0; u32 phase = 0; + CommState commState = OFF; void inc() { phase++; @@ -69,7 +70,8 @@ class LinkWirelessOpenSDK { } bool operator==(const SequenceNumber& other) { - return n == other.n && phase == other.phase; + return n == other.n && phase == other.phase && + commState == other.commState; } }; @@ -82,7 +84,9 @@ class LinkWirelessOpenSDK { CommState commState : 4; unsigned int targetSlots : 4; - SequenceNumber sequence() { return SequenceNumber{.n = n, .phase = phase}; } + SequenceNumber sequence() { + return SequenceNumber{.n = n, .phase = phase, .commState = commState}; + } }; union ServerSDKHeaderSerializer { ServerSDKHeader asStruct; @@ -107,7 +111,9 @@ class LinkWirelessOpenSDK { unsigned int isACK : 1; CommState commState : 4; - SequenceNumber sequence() { return SequenceNumber{.n = n, .phase = phase}; } + SequenceNumber sequence() { + return SequenceNumber{.n = n, .phase = phase, .commState = commState}; + } }; union ClientSDKHeaderSerializer { ClientSDKHeader asStruct; @@ -200,13 +206,12 @@ class LinkWirelessOpenSDK { return parentData; } - SendBuffer createServerBuffer( - const u8* fullPayload, - u32 fullPayloadSize, - SequenceNumber sequence, - CommState commState = CommState::COMMUNICATING, - u32 offset = 0, - u8 targetSlots = 0b1111) { + // TODO: MOVE OFFSET TO END + SendBuffer createServerBuffer(const u8* fullPayload, + u32 fullPayloadSize, + SequenceNumber sequence, + u32 offset = 0, + u8 targetSlots = 0b1111) { SendBuffer buffer; u32 payloadSize = min(fullPayloadSize, LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER); @@ -216,7 +221,7 @@ class LinkWirelessOpenSDK { buffer.header.payloadSize = payloadSize; buffer.header.n = sequence.n; buffer.header.phase = sequence.phase; - buffer.header.commState = commState; + buffer.header.commState = sequence.commState; u32 headerInt = serializeServerHeader(buffer.header); buffer.data[buffer.dataSize++] = headerInt; @@ -255,12 +260,11 @@ class LinkWirelessOpenSDK { return buffer; } - SendBuffer createClientBuffer( - const u8* fullPayload, - u32 fullPayloadSize, - SequenceNumber sequence, - CommState commState = CommState::COMMUNICATING, - u32 offset = 0) { + // TODO: MOVE OFFSET TO END + SendBuffer createClientBuffer(const u8* fullPayload, + u32 fullPayloadSize, + SequenceNumber sequence, + u32 offset = 0) { SendBuffer buffer; u32 payloadSize = min(fullPayloadSize, LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_CLIENT); @@ -269,7 +273,7 @@ class LinkWirelessOpenSDK { buffer.header.payloadSize = payloadSize; buffer.header.n = sequence.n; buffer.header.phase = sequence.phase; - buffer.header.commState = commState; + buffer.header.commState = sequence.commState; u16 headerInt = serializeClientHeader(buffer.header); buffer.data[buffer.dataSize++] = headerInt; From a2f2f3be2fbe615008b87fb590cbe02009f161fd Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 03:10:22 -0300 Subject: [PATCH 43/74] Moving offset parameter to the end of the list --- lib/LinkWirelessMultiboot.hpp | 17 +++++++++-------- lib/LinkWirelessOpenSDK.hpp | 6 ++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 51fd90f..e1fbc0a 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -109,7 +109,7 @@ class LinkWirelessMultiboot { linkWirelessOpenSDK->createServerBuffer( LINK_WIRELESS_MULTIBOOT_CMD_START, LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, - {1, 0, LinkWirelessOpenSDK::CommState::STARTING}, 0, 0b0001), + {1, 0, LinkWirelessOpenSDK::CommState::STARTING}, 0b0001), cancel)) LWMLOG("SENDING ROM!"); @@ -127,7 +127,7 @@ class LinkWirelessMultiboot { // TODO: Multiple clients auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, sequence, transferredBytes, 0b0001); + rom, romSize, sequence, 0b0001, transferredBytes); LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(sendBuffer, response)) childrenData = linkWirelessOpenSDK->getChildrenData(response); @@ -152,7 +152,7 @@ class LinkWirelessMultiboot { LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( 0, // TODO: Multiple clients linkWirelessOpenSDK->createServerBuffer( - {}, 0, {0, 0, LinkWirelessOpenSDK::CommState::ENDING}, 0, 0b0001), + {}, 0, {0, 0, LinkWirelessOpenSDK::CommState::ENDING}, 0b0001), cancel)) LWMLOG("confirming (2/2)..."); @@ -160,7 +160,7 @@ class LinkWirelessMultiboot { LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( linkWirelessOpenSDK->createServerBuffer( - {}, 0, {1, 0, LinkWirelessOpenSDK::CommState::OFF}, 0, 0b0001), + {}, 0, {1, 0, LinkWirelessOpenSDK::CommState::OFF}, 0b0001), response)) LWMLOG("SUCCESS!"); @@ -250,7 +250,7 @@ class LinkWirelessMultiboot { return sendAndExpectData(toArray(), 0, 1, response); }, [](LinkWirelessOpenSDK::ClientPacket packet) { return true; }, cancel)) - // initial packet received + // (initial client packet received) LWMLOG("handshake (1/2)..."); LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( @@ -261,7 +261,7 @@ class LinkWirelessMultiboot { header.commState == LinkWirelessOpenSDK::CommState::STARTING; }, cancel)) - // n = 2, commState = 1 + // (n = 2, commState = 1) LWMLOG("handshake (2/2)..."); LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( @@ -273,7 +273,7 @@ class LinkWirelessMultiboot { LinkWirelessOpenSDK::CommState::COMMUNICATING; }, cancel)) - // n = 1, commState = 2 + // (n = 1, commState = 2) LWMLOG("receiving name..."); LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( @@ -283,7 +283,7 @@ class LinkWirelessMultiboot { return packet.header.commState == LinkWirelessOpenSDK::CommState::OFF; }, cancel)) - // commState = 0 + // (commState = 0) LWMLOG("draining queue..."); bool hasFinished = false; @@ -296,6 +296,7 @@ class LinkWirelessMultiboot { auto childrenData = linkWirelessOpenSDK->getChildrenData(response); hasFinished = childrenData.responses[clientNumber].packetsSize == 0; } + // (no more client packets) LWMLOG("client " + std::to_string(clientNumber) + " accepted"); diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index c0f221d..a6b4d75 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -206,12 +206,11 @@ class LinkWirelessOpenSDK { return parentData; } - // TODO: MOVE OFFSET TO END SendBuffer createServerBuffer(const u8* fullPayload, u32 fullPayloadSize, SequenceNumber sequence, - u32 offset = 0, - u8 targetSlots = 0b1111) { + u8 targetSlots = 0b1111, + u32 offset = 0) { SendBuffer buffer; u32 payloadSize = min(fullPayloadSize, LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER); @@ -260,7 +259,6 @@ class LinkWirelessOpenSDK { return buffer; } - // TODO: MOVE OFFSET TO END SendBuffer createClientBuffer(const u8* fullPayload, u32 fullPayloadSize, SequenceNumber sequence, From a43d8a6f069f8619d10158b7e6dffd0fa52a2baf Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 04:56:05 -0300 Subject: [PATCH 44/74] FIX: After clock inversion, a small wait is needed before sending new commands to make it more stable --- lib/LinkRawWireless.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 33907e5..7f6c7a8 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -654,6 +654,8 @@ class LinkRawWireless { LRWLOG("setting SPI to 2Mbps"); linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); + wait(LINK_RAW_WIRELESS_TRANSFER_WAIT); + remoteCommand.success = true; remoteCommand.commandId = commandId; From 45c39a2fbd29245eb48d6a788c5096e06f66647d Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 05:19:37 -0300 Subject: [PATCH 45/74] Adding LINK_UNIVERSAL_GAME_ID_FILTER to LinkUniversal --- README.md | 1 + lib/LinkUniversal.hpp | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8549d15..432236a 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,7 @@ Name | Type | Default | Description You can also change these compile-time constants: - `LINK_UNIVERSAL_MAX_PLAYERS`: to set a maximum number of players. The default value is `4` (LinkCable's limit) but can be increased to `5` to support larger wireless rooms. +- `LINK_UNIVERSAL_GAME_ID_FILTER`: to restrict wireless connections to rooms with a specific game ID (`0x0000` - `0x7fff`). The default value (`0`) connects to any game ID and uses `0x7fff` when serving. ## Methods diff --git a/lib/LinkUniversal.hpp b/lib/LinkUniversal.hpp index 5a4534e..f3a50fe 100644 --- a/lib/LinkUniversal.hpp +++ b/lib/LinkUniversal.hpp @@ -54,6 +54,9 @@ // Max players. Default = 4 (LinkCable's limit), but can be increased to 5 #define LINK_UNIVERSAL_MAX_PLAYERS LINK_CABLE_MAX_PLAYERS +// Game ID Filter. Default = 0 (no filter) +#define LINK_UNIVERSAL_GAME_ID_FILTER 0 + #define LINK_UNIVERSAL_DISCONNECTED LINK_CABLE_DISCONNECTED #define LINK_UNIVERSAL_NO_DATA LINK_CABLE_NO_DATA #define LINK_UNIVERSAL_MAX_ROOM_NUMBER 32000 @@ -393,7 +396,9 @@ class LinkUniversal { break; if (!server.isFull() && - std::strcmp(server.gameName, config.gameName) == 0) { + std::strcmp(server.gameName, config.gameName) == 0 && + (LINK_UNIVERSAL_GAME_ID_FILTER == 0 || + server.gameId == LINK_UNIVERSAL_GAME_ID_FILTER)) { u32 randomNumber = safeStoi(server.userName); if (randomNumber > maxRandomNumber && randomNumber < LINK_UNIVERSAL_MAX_ROOM_NUMBER) { @@ -417,7 +422,10 @@ class LinkUniversal { char randomNumberStr[6]; std::snprintf(randomNumberStr, sizeof(randomNumberStr), "%d", randomNumber); - if (!linkWireless->serve(config.gameName, randomNumberStr)) + if (!linkWireless->serve(config.gameName, randomNumberStr, + LINK_UNIVERSAL_GAME_ID_FILTER > 0 + ? LINK_UNIVERSAL_GAME_ID_FILTER + : LINK_WIRELESS_MAX_GAME_ID)) return false; } From 76d4c4b4197dcf18ea6e1e52da686adb1c975485 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 05:40:46 -0300 Subject: [PATCH 46/74] Replacing log level function with player toggle --- .../src/scenes/MultibootScene.cpp | 35 ++++++++----------- .../src/scenes/MultibootScene.h | 2 +- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index f2c7303..fb5ed28 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -36,7 +36,7 @@ static std::unique_ptr selectHandler = static std::vector logLines; static u32 currentLogLine = 0; -static bool useVerboseLog = true; +static u32 players = 5; #define MAX_LINES 20 #define DRAW_LINE 0 @@ -122,14 +122,12 @@ void MultibootScene::load() { SCENE_init(); BACKGROUND_enable(true, false, false, false); - linkWirelessMultiboot->logger = [](std::string string) { - // if (useVerboseLog) // TODO: RESTORE - log(string); - }; + linkWirelessMultiboot->logger = [](std::string string) { log(string); }; +#ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING linkWirelessMultiboot->linkRawWireless->logger = [](std::string string) { - if (useVerboseLog) - log(string); + log(string); }; +#endif log("---"); log("LinkWirelessMultiboot demo"); @@ -146,14 +144,14 @@ void MultibootScene::load() { ; } log("A: send ROM"); - log("B: toggle log level"); + log("B: toggle players"); log("UP/DOWN: scroll up/down"); log("L/R: scroll page up/down"); log("UP+L/DOWN+R: scroll to top/bottom"); log("SELECT: clear"); log("---"); log(""); - toggleLogLevel(); + togglePlayers(); } void MultibootScene::tick(u16 keys) { @@ -180,7 +178,7 @@ void MultibootScene::processKeys(u16 keys) { void MultibootScene::processButtons() { if (bHandler->hasBeenPressedNow()) - toggleLogLevel(); + togglePlayers(); if (aHandler->hasBeenPressedNow()) { u32 fileLength; @@ -188,7 +186,7 @@ void MultibootScene::processButtons() { (const u8*)gbfs_get_obj(fs, ROM_FILE_NAME, &fileLength); auto result = linkWirelessMultiboot->sendRom( - romToSend, fileLength, "Multiboot", "Test", 0xffff, 2, + romToSend, fileLength, "Multiboot", "Test", 0xffff, players, [](LinkWirelessMultiboot::MultibootProgress progress) { return false; }); @@ -220,15 +218,12 @@ void MultibootScene::processButtons() { clear(); } -void MultibootScene::toggleLogLevel() { - if (useVerboseLog) { - useVerboseLog = false; - log("! setting log level to NORMAL"); - } else { - useVerboseLog = true; - log("! setting log level to VERBOSE"); - } - log(""); +void MultibootScene::togglePlayers() { + players++; + if (players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) + players = LINK_WIRELESS_MULTIBOOT_MIN_PLAYERS; + + log("! setting players: " + std::to_string(players)); } void MultibootScene::logOperation(std::string name, diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h index 0c0cca4..293c009 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h @@ -28,7 +28,7 @@ class MultibootScene : public Scene { void processKeys(u16 keys); void processButtons(); - void toggleLogLevel(); + void togglePlayers(); void logOperation(std::string name, std::function operation); }; From 36e258e90c58ca9a8aba0692e848082f5dd1e619 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 05:41:17 -0300 Subject: [PATCH 47/74] Removing logging code from LinkRawWireless --- lib/LinkRawWireless.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 7f6c7a8..0d809c0 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -76,7 +76,9 @@ class LinkRawWireless { typedef void (*Logger)(std::string); public: +#ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING Logger logger = [](std::string str) {}; +#endif enum State { NEEDS_RESET, @@ -897,6 +899,7 @@ class LinkRawWireless { }; } +#ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING void logExpectedButReceived(u32 expected, u32 received) { LRWLOG("! expected 0x" + toHex(expected)); LRWLOG("! but received 0x" + toHex(received)); @@ -910,6 +913,7 @@ class LinkRawWireless { rc[i] = digits[(w >> j) & 0x0f]; return rc; } +#endif u32 buildU32(u16 msB, u16 lsB) { return (msB << 16) | lsB; } u16 buildU16(u8 msB, u8 lsB) { return (msB << 8) | lsB; } From 66532ecd0c801574e0e758f4ec07d976442181d9 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 06:11:07 -0300 Subject: [PATCH 48/74] Validating adapter name and timing fix --- .../LinkWirelessMultiboot_demo/src/main.cpp | 2 +- .../src/scenes/MultibootScene.cpp | 2 + lib/LinkRawWireless.hpp | 5 +- lib/LinkWirelessMultiboot.hpp | 71 ++++++++++++++++--- 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/main.cpp b/examples/LinkWirelessMultiboot_demo/src/main.cpp index 89ba6e9..a326e8f 100644 --- a/examples/LinkWirelessMultiboot_demo/src/main.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/main.cpp @@ -16,7 +16,7 @@ LinkWirelessMultiboot* linkWirelessMultiboot = new LinkWirelessMultiboot(); int main() { setUpInterrupts(); - REG_WAITCNT = 1 << 14; // (prefetch ON) + REG_WAITCNT = 0x4317; // (3,1 waitstates, prefetch ON) engine->setScene(multibootScene.get()); diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp index fb5ed28..de21b4a 100644 --- a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -122,7 +122,9 @@ void MultibootScene::load() { SCENE_init(); BACKGROUND_enable(true, false, false, false); +#ifdef LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING linkWirelessMultiboot->logger = [](std::string string) { log(string); }; +#endif #ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING linkWirelessMultiboot->linkRawWireless->logger = [](std::string string) { log(string); diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 0d809c0..73d0dbb 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -73,10 +73,9 @@ const u16 LINK_RAW_WIRELESS_LOGIN_PARTS[] = { 0x494e, 0x494e, 0x544e, 0x544e, 0x4e45, 0x4e45, 0x4f44, 0x4f44, 0x8001}; class LinkRawWireless { - typedef void (*Logger)(std::string); - public: #ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING + typedef void (*Logger)(std::string); Logger logger = [](std::string str) {}; #endif @@ -899,12 +898,12 @@ class LinkRawWireless { }; } -#ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING void logExpectedButReceived(u32 expected, u32 received) { LRWLOG("! expected 0x" + toHex(expected)); LRWLOG("! but received 0x" + toHex(received)); } +#ifdef LINK_RAW_WIRELESS_ENABLE_LOGGING template std::string toHex(I w, size_t hex_len = sizeof(I) << 1) { static const char* digits = "0123456789ABCDEF"; diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index e1fbc0a..3a9064c 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -52,21 +52,28 @@ const u8 LINK_WIRELESS_MULTIBOOT_CMD_START[] = {0x00, 0x54, 0x00, 0x00, 0x00, 0x02, 0x00}; const u8 LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE = 7; +const u8 LINK_WIRELESS_MULTIBOOT_BOOTLOADER_HANDSHAKE[][6] = { + {0x00, 0x00, 0x52, 0x46, 0x55, 0x2d}, + {0x4d, 0x42, 0x2d, 0x44, 0x4c, 0x00}}; +const u8 LINK_WIRELESS_MULTIBOOT_BOOTLOADER_HANDSHAKE_SIZE = 6; static volatile char LINK_WIRELESS_MULTIBOOT_VERSION[] = "LinkWirelessMultiboot/v6.2.0"; class LinkWirelessMultiboot { - typedef void (*Logger)(std::string); - public: +#ifdef LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING + typedef void (*Logger)(std::string); Logger logger = [](std::string str) {}; +#endif + enum Result { SUCCESS, INVALID_SIZE, INVALID_PLAYERS, CANCELED, ADAPTER_NOT_DETECTED, + BAD_HANDSHAKE, FAILURE }; enum State { STOPPED, INITIALIZING, WAITING, PREPARING, SENDING, CONFIRMING }; @@ -93,6 +100,9 @@ class LinkWirelessMultiboot { players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) return INVALID_PLAYERS; + lastWaitCNT = REG_WAITCNT; + REG_WAITCNT = 1 << 14; + LINK_WIRELESS_MULTIBOOT_TRY(activate()) progress.state = INITIALIZING; LINK_WIRELESS_MULTIBOOT_TRY(initialize(gameName, userName, gameId, players)) @@ -179,6 +189,7 @@ class LinkWirelessMultiboot { MultibootProgress progress; Result lastResult; LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; + u16 lastWaitCNT; Result activate() { if (!linkRawWireless->activate()) { @@ -243,6 +254,9 @@ class LinkWirelessMultiboot { template Result handshakeClient(u8 clientNumber, C cancel) { + LinkWirelessOpenSDK::ClientPacket handshakePackets[2]; + bool hasReceivedName = false; + LWMLOG("new client: " + std::to_string(clientNumber)); LINK_WIRELESS_MULTIBOOT_TRY(exchangeData( clientNumber, @@ -266,11 +280,14 @@ class LinkWirelessMultiboot { LWMLOG("handshake (2/2)..."); LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( clientNumber, - [](LinkWirelessOpenSDK::ClientPacket packet) { + [&handshakePackets](LinkWirelessOpenSDK::ClientPacket packet) { auto header = packet.header; - return header.n == 1 && - header.commState == - LinkWirelessOpenSDK::CommState::COMMUNICATING; + bool isValid = + header.n == 1 && header.phase == 0 && + header.commState == LinkWirelessOpenSDK::CommState::COMMUNICATING; + if (isValid) + handshakePackets[0] = packet; + return isValid; }, cancel)) // (n = 1, commState = 2) @@ -278,13 +295,35 @@ class LinkWirelessMultiboot { LWMLOG("receiving name..."); LINK_WIRELESS_MULTIBOOT_TRY(exchangeACKData( clientNumber, - [this](LinkWirelessOpenSDK::ClientPacket packet) { - lastValidHeader = packet.header; - return packet.header.commState == LinkWirelessOpenSDK::CommState::OFF; + [this, &handshakePackets, + &hasReceivedName](LinkWirelessOpenSDK::ClientPacket packet) { + auto header = packet.header; + lastValidHeader = header; + if (header.n == 1 && header.phase == 1 && + header.commState == + LinkWirelessOpenSDK::CommState::COMMUNICATING) { + handshakePackets[1] = packet; + hasReceivedName = true; + } + return header.commState == LinkWirelessOpenSDK::CommState::OFF; }, cancel)) // (commState = 0) + LWMLOG("validating name..."); + for (u32 i = 0; i < 2; i++) { + auto receivedPayload = handshakePackets[i].payload; + auto expectedPayload = LINK_WIRELESS_MULTIBOOT_BOOTLOADER_HANDSHAKE[i]; + + for (u32 j = 0; j < LINK_WIRELESS_MULTIBOOT_BOOTLOADER_HANDSHAKE_SIZE; + j++) { + if (!hasReceivedName || receivedPayload[j] != expectedPayload[j]) { + LWMLOG("! bad payload"); + return finish(BAD_HANDSHAKE); + } + } + } + LWMLOG("draining queue..."); bool hasFinished = false; while (!hasFinished) { @@ -394,7 +433,7 @@ class LinkWirelessMultiboot { if (remoteCommand.commandId != 0x28) { LWMLOG("! expected EVENT 0x28"); - LWMLOG("! but got " + linkRawWireless->toHex(remoteCommand.commandId)); + LWMLOG("! but got " + toHex(remoteCommand.commandId)); return FAILURE; } @@ -420,9 +459,21 @@ class LinkWirelessMultiboot { progress.state = STOPPED; progress.connectedPlayers = 1; progress.percentage = 0; + REG_WAITCNT = lastWaitCNT; return result; } +#ifdef LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING + template + std::string toHex(I w, size_t hex_len = sizeof(I) << 1) { + static const char* digits = "0123456789ABCDEF"; + std::string rc(hex_len, '0'); + for (size_t i = 0, j = (hex_len - 1) * 4; i < hex_len; ++i, j -= 4) + rc[i] = digits[(w >> j) & 0x0f]; + return rc; + } +#endif + template std::array toArray( Args... args) { From dded460b97bd456a042a2288c886a625b9632bbc Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 06:55:13 -0300 Subject: [PATCH 49/74] Extracting more functions --- lib/LinkWirelessMultiboot.hpp | 127 ++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 3a9064c..2320605 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -114,64 +114,14 @@ class LinkWirelessMultiboot { linkRawWireless->wait(LINK_WIRELESS_MULTIBOOT_FRAME_LINES); LWMLOG("rom start command..."); - LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( - 0, // TODO: Multiple clients - linkWirelessOpenSDK->createServerBuffer( - LINK_WIRELESS_MULTIBOOT_CMD_START, - LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, - {1, 0, LinkWirelessOpenSDK::CommState::STARTING}, 0b0001), - cancel)) + LINK_WIRELESS_MULTIBOOT_TRY(sendRomStartCommand(cancel)) LWMLOG("SENDING ROM!"); progress.state = SENDING; - LinkWirelessOpenSDK::ChildrenData childrenData; - LinkWirelessOpenSDK::SequenceNumber sequence = { - .n = 1, - .phase = 0, - .commState = LinkWirelessOpenSDK::CommState::COMMUNICATING}; - u32 transferredBytes = 0; - progress.percentage = 0; - while (transferredBytes < romSize) { - if (cancel(progress)) - return finish(CANCELED); - - // TODO: Multiple clients - auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, sequence, 0b0001, transferredBytes); - LinkRawWireless::ReceiveDataResponse response; - LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(sendBuffer, response)) - childrenData = linkWirelessOpenSDK->getChildrenData(response); + LINK_WIRELESS_MULTIBOOT_TRY(sendRomBytes(rom, romSize, cancel)) - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - if (header.isACK && header.sequence() == sequence) { - sequence.inc(); - transferredBytes += sendBuffer.header.payloadSize; - u32 newPercentage = transferredBytes * 100 / romSize; - if (newPercentage != progress.percentage) { - progress.percentage = newPercentage; - LWMLOG("-> " + std::to_string(newPercentage)); - } - break; - } - } - } - - LWMLOG("confirming (1/2)..."); progress.state = CONFIRMING; - LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( - 0, // TODO: Multiple clients - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {0, 0, LinkWirelessOpenSDK::CommState::ENDING}, 0b0001), - cancel)) - - LWMLOG("confirming (2/2)..."); - // TODO: Multiple clients - LinkRawWireless::ReceiveDataResponse response; - LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {1, 0, LinkWirelessOpenSDK::CommState::OFF}, 0b0001), - response)) + LINK_WIRELESS_MULTIBOOT_TRY(confirm(cancel)) LWMLOG("SUCCESS!"); return finish(SUCCESS); @@ -342,6 +292,77 @@ class LinkWirelessMultiboot { return SUCCESS; } + template + Result sendRomStartCommand(C cancel) { + LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( + 0, // TODO: Multiple clients + linkWirelessOpenSDK->createServerBuffer( + LINK_WIRELESS_MULTIBOOT_CMD_START, + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, + {1, 0, LinkWirelessOpenSDK::CommState::STARTING}, 0b0001), + cancel)) + + return SUCCESS; + } + + template + Result sendRomBytes(const u8* rom, u32 romSize, C cancel) { + LinkWirelessOpenSDK::ChildrenData childrenData; + LinkWirelessOpenSDK::SequenceNumber sequence = { + .n = 1, + .phase = 0, + .commState = LinkWirelessOpenSDK::CommState::COMMUNICATING}; + u32 transferredBytes = 0; + progress.percentage = 0; + while (transferredBytes < romSize) { + if (cancel(progress)) + return finish(CANCELED); + + // TODO: Multiple clients + auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( + rom, romSize, sequence, 0b0001, transferredBytes); + LinkRawWireless::ReceiveDataResponse response; + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(sendBuffer, response)) + childrenData = linkWirelessOpenSDK->getChildrenData(response); + + for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { + auto header = childrenData.responses[0].packets[i].header; + if (header.isACK && header.sequence() == sequence) { + sequence.inc(); + transferredBytes += sendBuffer.header.payloadSize; + u32 newPercentage = transferredBytes * 100 / romSize; + if (newPercentage != progress.percentage) { + progress.percentage = newPercentage; + LWMLOG("-> " + std::to_string(newPercentage)); + } + break; + } + } + } + + return SUCCESS; + } + + template + Result confirm(C cancel) { + LWMLOG("confirming (1/2)..."); + LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( + 0, // TODO: Multiple clients + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {0, 0, LinkWirelessOpenSDK::CommState::ENDING}, 0b0001), + cancel)) + + LWMLOG("confirming (2/2)..."); + // TODO: Multiple clients + LinkRawWireless::ReceiveDataResponse response; + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {1, 0, LinkWirelessOpenSDK::CommState::OFF}, 0b0001), + response)) + + return SUCCESS; + } + template __attribute__((noinline)) Result exchangeNewData( u8 clientNumber, From c6ec56b4faa4a439cf82ed5be82d3d9d7590f513 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 07:06:08 -0300 Subject: [PATCH 50/74] Fixing setup value's max player setting --- lib/LinkWirelessMultiboot.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 2320605..5d68d49 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -39,7 +39,7 @@ #define LINK_WIRELESS_MULTIBOOT_MIN_PLAYERS 2 #define LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS 5 #define LINK_WIRELESS_MULTIBOOT_HEADER_SIZE 0xC0 -#define LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC 0x003F0000 +#define LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC 0x003c0000 #define LINK_WIRELESS_MULTIBOOT_SETUP_TX 1 #define LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT 32 #define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG (1 << 15) @@ -103,9 +103,12 @@ class LinkWirelessMultiboot { lastWaitCNT = REG_WAITCNT; REG_WAITCNT = 1 << 14; + LWMLOG("starting..."); LINK_WIRELESS_MULTIBOOT_TRY(activate()) progress.state = INITIALIZING; LINK_WIRELESS_MULTIBOOT_TRY(initialize(gameName, userName, gameId, players)) + + LWMLOG("waiting for connections..."); progress.state = WAITING; LINK_WIRELESS_MULTIBOOT_TRY(waitForClients(players, cancel)) From db2f1a39282a8d69ee149155129c9512acefcad2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 07:21:06 -0300 Subject: [PATCH 51/74] FIX: Resetting size parameters in LinkRawWireless --- lib/LinkRawWireless.hpp | 13 ++++++++----- lib/LinkWirelessMultiboot.hpp | 5 ++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 73d0dbb..998f4c0 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -39,7 +39,6 @@ #define LINK_RAW_WIRELESS_RESPONSE_ACK 0x80 #define LINK_RAW_WIRELESS_DATA_REQUEST 0x80000000 #define LINK_RAW_WIRELESS_SETUP_MAGIC 0x003c0000 -#define LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT 16 #define LINK_RAW_WIRELESS_STILL_CONNECTING 0x01000000 #define LINK_RAW_WIRELESS_BROADCAST_LENGTH 6 #define LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH \ @@ -172,10 +171,10 @@ class LinkRawWireless { u8 maxTransmissions = 4, u8 waitTimeout = 32, u32 magic = LINK_RAW_WIRELESS_SETUP_MAGIC) { - u32 config = (u32)(magic | - (((LINK_RAW_WIRELESS_MAX_PLAYERS - maxPlayers) & 0b11) - << LINK_RAW_WIRELESS_SETUP_MAX_PLAYERS_BIT) | - (maxTransmissions << 8) | waitTimeout); + u32 config = + (u32)(magic | + (((LINK_RAW_WIRELESS_MAX_PLAYERS - maxPlayers) & 0b11) << 16) | + (maxTransmissions << 8) | waitTimeout); return sendCommand(LINK_RAW_WIRELESS_COMMAND_SETUP, {config}, 1).success; } @@ -244,6 +243,7 @@ class LinkRawWireless { return false; } + response.connectedClientsSize = 0; for (u32 i = 0; i < result.responsesSize; i++) { if (i == 0) { response.nextClientNumber = (u8)lsB32(result.responses[i]); @@ -265,6 +265,7 @@ class LinkRawWireless { return false; } + response.connectedClientsSize = 0; for (u32 i = 0; i < result.responsesSize; i++) { response.connectedClients[response.connectedClientsSize++] = ConnectedClient{.deviceId = lsB32(result.responses[i]), @@ -287,6 +288,7 @@ class LinkRawWireless { return false; } + response.connectedClientsSize = 0; for (u32 i = 0; i < result.responsesSize; i++) { response.connectedClients[response.connectedClientsSize++] = ConnectedClient{.deviceId = lsB32(result.responses[i]), @@ -330,6 +332,7 @@ class LinkRawWireless { u32 totalBroadcasts = result.responsesSize / LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH; + response.serversSize = 0; for (u32 i = 0; i < totalBroadcasts; i++) { u32 start = LINK_RAW_WIRELESS_BROADCAST_RESPONSE_LENGTH * i; diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 5d68d49..31cef2a 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -197,7 +197,10 @@ class LinkWirelessMultiboot { if (linkRawWireless->playerCount() > progress.connectedPlayers) { progress.connectedPlayers = linkRawWireless->playerCount(); - u8 lastClientNumber = acceptResponse.connectedClientsSize - 1; + u8 lastClientNumber = + acceptResponse + .connectedClients[acceptResponse.connectedClientsSize - 1] + .clientNumber; LINK_WIRELESS_MULTIBOOT_TRY(handshakeClient(lastClientNumber, cancel)) } } From 32150a212f951b119b7d70ef6db17268517d46ea Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 08:09:16 -0300 Subject: [PATCH 52/74] Detecting individual children timeout --- lib/LinkWirelessMultiboot.hpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 31cef2a..32f47f7 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -80,7 +80,7 @@ class LinkWirelessMultiboot { struct MultibootProgress { State state = STOPPED; - u32 connectedPlayers = 1; + u32 connectedClients = 0; u32 percentage = 0; }; @@ -187,15 +187,16 @@ class LinkWirelessMultiboot { Result waitForClients(u8 players, C cancel) { LinkRawWireless::AcceptConnectionsResponse acceptResponse; - progress.connectedPlayers = 1; + u32 currentPlayers = 1; while (linkRawWireless->playerCount() < players) { if (cancel(progress)) return finish(CANCELED); linkRawWireless->acceptConnections(acceptResponse); - if (linkRawWireless->playerCount() > progress.connectedPlayers) { - progress.connectedPlayers = linkRawWireless->playerCount(); + if (linkRawWireless->playerCount() > currentPlayers) { + currentPlayers = linkRawWireless->playerCount(); + progress.connectedClients = currentPlayers - 1; u8 lastClientNumber = acceptResponse @@ -466,8 +467,14 @@ class LinkWirelessMultiboot { if (remoteCommand.paramsSize > 0) { u8 activeChildren = (remoteCommand.params[0] >> 8) & 0b1111; - if (activeChildren == 0) { - LWMLOG("! timeout"); + u8 expectedActiveChildren = 0; + for (u32 i = 0; i < progress.connectedClients; i++) + expectedActiveChildren |= 1 << i; + + if (activeChildren != expectedActiveChildren) { + LWMLOG("! client timeout [" + std::to_string(activeChildren) + "]"); + LWMLOG("! vs expected: [" + std::to_string(expectedActiveChildren) + + "]"); return FAILURE; } } @@ -484,7 +491,7 @@ class LinkWirelessMultiboot { __attribute__((noinline)) Result finish(Result result) { linkRawWireless->deactivate(); progress.state = STOPPED; - progress.connectedPlayers = 1; + progress.connectedClients = 0; progress.percentage = 0; REG_WAITCNT = lastWaitCNT; return result; From 574e75f67c9b551e7de0483f3ec580e850f0a21c Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 08:26:07 -0300 Subject: [PATCH 53/74] Improving docs and removing outdated wireless documentation --- README.md | 2 +- docs/wireless.txt | 509 ------------------ examples/LinkCableMultiboot_demo/src/main.cpp | 2 +- lib/LinkCableMultiboot.hpp | 10 +- lib/LinkWirelessMultiboot.hpp | 16 +- 5 files changed, 18 insertions(+), 521 deletions(-) delete mode 100644 docs/wireless.txt diff --git a/README.md b/README.md index 432236a..210769a 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ This tool allows sending Multiboot ROMs (small 256KiB programs that fit in EWRAM Name | Return type | Description --- | --- | --- -`sendRom(rom, romSize, cancel)` | **LinkWirelessMultiboot::Result** | Sends the `rom`. During the handshake process, the library will continuously invoke `cancel`, and abort the transfer if it returns `true`. The `romSize` must be a number between `448` and `262144`, and a multiple of `16`. Once completed, the return value should be `LinkWirelessMultiboot::Result::SUCCESS`. +`sendRom(rom, romSize, gameName, userName, gameId, players, cancel)` | **LinkWirelessMultiboot::Result** | Sends the `rom`. The `players` must be the exact number of consoles that will download the ROM. Once this number of players is reached, the code will start transmitting the ROM bytes. During the process, the library will continuously invoke `cancel` (passing a `LinkWirelessMultiboot::MultibootProgress` object as argument), and abort the transfer if it returns `true`. The `romSize` must be a number between `448` and `262144`. It's recommended to use a ROM size that is a multiple of `16`, as this also ensures compatibility with Multiboot via Link Cable. Once completed, the return value should be `LinkWirelessMultiboot::Result::SUCCESS`. # 🌎 LinkUniversal diff --git a/docs/wireless.txt b/docs/wireless.txt deleted file mode 100644 index 1670e84..0000000 --- a/docs/wireless.txt +++ /dev/null @@ -1,509 +0,0 @@ -Nintendo Wireless Adapter Specification -v1.1 (23th June 2005) -by denopqrihg (denopqrihg@centrum.cz) - -This document describes how a GBA program can communicate with a plugged-in Nintendo wireless adapter. This info is not 100% accurate, nor am I responsible for any damage it may do. I am only sharing my partial knowledge. - -Some general info ------------------ -The Nintendo Wireless Adapter is a device that presents more multiplayer features than the link cable. It uses a kind of server/client approach (broadcasting, connections etc.). It allows to send quite large amounts of data at once (most I've seen was 80 bytes), has built-in synchronisation and much more. -This document is only about two player link. At the moment, I have no info about how it works with mre players. -If you don't understand something in this document or think something is unclear, contact me and I'll try to clarify. - -Which mode to use? ------------------- -32-bit Normal mode, all the time. No exceptions. -Mostly 2Mbit internal clock, but there are exceptions which will be pointed out. - -Initialization --------------- -Use: 32-bit Normal mode, 256Khz internal clock. -Send the text "NINTENDO/0x01/0x80" in the following manner: -Store two bytes of the text in the lower 16 bits of the send data. The upper 16 bits should be 1's complement of the previously received lower 16 bits. Send it. Received data should look like the one you sent, except that the upper and lower halfword are swapped. Repeat this process until you have sent the whole string (the last two bytes are 0x8001). - -Example: -Send Receive -0xgggg494e 0x494egggg -0xb6b1494e 0x494eb6b1 -0xb6b1544e 0x544eb6b1 -0xabb1544e 0x544eabb1 -0xabb14e45 0x4e45abb1 -0xb1ba4e45 0x4e45b1ba -0xb1ba4f44 0x4f44b1ba -0xb0bb4f44 0x4f44b0bb -0xb0bb8001 0x8001b0bb - -(gggg = garbage) - -Then send command 0x3d or 0x10, difference is unknown (what is a command and how to use it is in the next section). - -Communication -------------- -The adapter uses a form of communication that I call 'commands'. -It waits until you send a command to it, and then it does something according to what command you sent. -The format is: -0x9966wwcc -cc is the 1-byte command -ww is a number indicating how many 4-byte units of data will follow the command (I don't know what happens when you send data with a command that shouldn't send any). -When you send this, you always receive 0x80000000 (also when sending the additional data). -Then, you must send 0x80000000. You then receive a similar structure, 0x9966wwaa. -ww is again a number of words (4 byte) following this command -aa is the command you sent ORed with 0x80 (with some exceptions) -If there was a nonzero word count in the response, send 0x80000000 the apropriate number of times and store the data you receive (you requested it, so you should store it). -Remember to keep some delays between transfers. - -Example: -Send Receive -0x99660117 0x80000000 -0x003c0720 0x80000000 -0x80000000 0x99660097 - -Another example: -Send Receive -0x9966001a 0x80000000 -0x80000000 0x9966019a -0x80000000 0x00001547 - -Ackowledging ------------- -After each transfer (except initialization), a kind of 'acknowledge procedure' must be done (maybe it isn't necessary, but the games do it): - -Right after the transfer ends, output SO=low, wait until SI becomes high, then output SO=high and wait until SI=low. -!! Exception !! After commands 0xa5 and 0xa7, the polarity is 'swapped': output SO=low, wait until SI=low, then send SO=high and wait until SI=high - -Also, just before each transfer, set SO=low - -(This part is a bit unclear to me, maybe it doesn't work like this at all) - -Commands ------------------ -Many commands have completely unknown function, some of them must be sent but nobody knows why and what they mean. -This is not a complete list of commands, I only listed the commands that either are important or I know something about them. - -I will use the following notation: -Command number --------------- -S: how many words to send -R: how many words to receive -Description of what the command does (if known :-) - -0x10 ----- -S: 0 -R: 0 -Last step of initialization, difference between this and 0x3d is unknown, both are used -0x10 seems to be 'safer' - -0x11 ----- -S: 0 -R: 1 -This command always receives 0xff in 2 player link, maybe it has to do with 3+ players ?? - -0x13 ----- -S: 0 -R: 1 -Receives a random value, don't know what it's for - -0x16 ----- -S: usually 6 -R: 0 -Sends 'broadcast' data, which will be received by all adapters in range - -0x17 ----- -S: 1 -R: 0 -Setup?? The value sent should be 003d0620 (Digimon), 003c0420 (Pokemon) or something along this way. - -0x1a ----- -S: 0 -R: 0 or 1 -This command is used by the 'server' to check if someone wants to connect. If not, no data is received. If yes, you'll get a value (random? or ID?) - -0x1d -0x1e ----- -S: 0 -R: multiples of 7 (usually) -Read 'broadcast' data. Data from each adapter is prefixed by one word containing an 'ID' of the adapter , which is used when you want to connect. - -0x1f ----- -S: 1 -R: 0 -Connect to server. Value sent is the 'ID' of the adapter you want to connect to. - -0x20 ----- -S: 0 -R: 1 -Used after 0x1f, generally resend until highest byte is 0. - -0x21 ----- -S: 0 -R: 1 -Send once after 0x20 successfully finished (returned 0). - -0x24 ----- -S: anything -R: 0 -Send data to connected adapters (I've only seen this command used by servers, but maybe clients can use it as well) - -0x25 ----- -S: anything -R: 0 -Same as 0x24, with the exception that this command 'waits' until incoming data is available. -This keeps the games synchronized without any further in-game programming. -How to use: After you send all the data and get 0x996600a5 from the adapter, store 0x80000000 in the send data register and start a transfer with _EXTERNAL_ clock. Once the data arrives, you'll receive a 0x99660028 command (which you have to ackowledge just like the adapter - send back 0x996600a8). -Also note that after you receive the 0x996600a5 until you use the next command (usually 0x26), the polarity of the SI and SO conmfirmation procedure is swapped. - -0x26 ----- -S: 0 -R: anything -Receive data. - -0x27 ----- -S: 0 -R: 0 -Similar to 0x25, but without sending data. All strange behavior of 0x25 applies here, too. - -0x30 ----- -S: 1? -R: 0 -Most likely a 'Terminate connection' command. Data value is usually 1. - -0x3d ----- -S: 0 -R: 0 -Same as 0x10 - -0xa8 ----- -S: 0 -R: 0 -Ackowledge of 0x28 sent by adapter. - -0xee ----- -S: 1? -R: 0 -This is very unconfirmed, but is probably used when you want the adapter to resend the last command/whatever ?? - -Example -------------- -A complete example of a client/server communication -(all numbers are in hex) - -Server ------- - -value of -0x128 reg Send Receive -5084 00000000 garbage -5085 ffff494e 494egggg -5085 b6b1494e 494eb6b1 -5085 b6b1544e 544eb6b1 -5085 abb1544e abb1544e -5085 abb14e45 4e45abb1 -5085 b1ba4e45 4e45b1ba -5085 b1ba4f44 4f44b1ba -5085 b0bb4f44 4f44b0bb -5085 b0bb8001 8001b0bb -5003 -5083 99660010 80000000 -500b -5003 -5083 80000000 99660090 -500b -5003 -5083 99660117 80000000 -500b -5003 -5083 003c0420 80000000 -500b -5003 -5083 80000000 99660097 -500b -5003 -5003 -5083 99660616 80000000 -500b -5003 -5083 0c020002 80000000 -500b -5003 -5083 00005ce1 80000000 -500b -5003 -5083 00000000 80000000 -500b -5003 -5083 09000040 80000000 -500b -5003 -5083 c1cfc8cd 80000000 -500b -5003 -5083 00ffccbb 80000000 -500b -5003 -5083 80000000 99660096 -500b -5003 -5083 99660013 80000000 -500b -5003 -5083 80000000 99660193 -500b -5003 -5083 80000000 02001234 (example) -500b -5003 -5083 99660019 80000000 -500b -5003 -5083 80000000 99660099 -500b -|------------------------------------------------ -v | -5003 | -5083 9966001a 80000000 | -500b | -5003 | -5083 80000000 9966009a | -500b | -repeat ------------------------------------------ -until you get -|---------------------------------------------------------------- -v | -5003 | -5083 9966001a 80000000 | -500b | -5003 | -5083 80000000 9966019a | -5003 | -5083 80000000 00004875 (example) | -500b | -5003 | -5083 99660011 80000000 | -500b | -5003 | -5083 80000000 99660191 | -500b | -5003 | -5083 80000000 000000ff | -500b | -repeat a few times to ensure that it really is a connection ----- -5003 -5083 99660124 80000000 -500b -5003 -5083 12345678 80000000 -500b -5003 -5083 80000000 99660094 -500b -|------------------------------------------------ -v | -5003 | -5083 99660026 80000000 | -500b | -5003 80000000 996600a6 | -500b | -repeat until you get some data ----------------- - -send & receive etc. etc. -Most games switch to 0x25 for the server as well after a while. - -when you want to exit -5003 -5083 99660130 80000000 -500b -5003 -5083 00000001 80000000 -500b -5003 -5083 80000000 996600b0 -500b - -Client ------- - -value of -0x128 reg Send Receive -5084 00000000 garbage -5085 ffff494e 494egggg -5085 b6b1494e 494eb6b1 -5085 b6b1544e 544eb6b1 -5085 abb1544e abb1544e -5085 abb14e45 4e45abb1 -5085 b1ba4e45 4e45b1ba -5085 b1ba4f44 4f44b1ba -5085 b0bb4f44 4f44b0bb -5085 b0bb8001 8001b0bb -5003 -5083 99660010 80000000 -500b -5003 -5083 80000000 99660090 -500b -5003 -5083 99660117 80000000 -500b -5003 -5083 003c0420 80000000 -500b -5003 -5083 80000000 99660097 -500b -5003 -5003 -5083 99660616 80000000 -500b -5003 -5083 0c020002 80000000 -500b -5003 -5083 00005ce1 80000000 -500b -5003 -5083 00000000 80000000 -500b -5003 -5083 09000040 80000000 -500b -5003 -5083 c1cfc8cd 80000000 -500b -5003 -5083 00ffccbb 80000000 -500b -5003 -5083 80000000 99660096 -500b -|------------------------------------------------------------------------ -v | -5003 | -5083 99660017 80000000 | -500b | -5003 | -5083 80000000 99660797 | -500b | -5003 | -5083 80000000 00002154 adapter's ID | -500b | -5003 | -5083 80000000 0c020002 | -500b | -5003 | -5083 80000000 00005ce1 | -500b | -5003 | -5083 80000000 00000000 | -500b | -5003 | -5083 80000000 09000040 | -500b | -5003 | -5083 80000000 c1cfc8cd | -500b | -5003 | -5083 80000000 00ffccbb | -500b | -repeat, display available servers and wait until the player picks one --- -5003 -5083 9966011f 80000000 -500b -5003 -5083 00002154 80000000 pass the ID of the appropriate adapter -500b -5003 -5083 80000000 9966009f -500b -|------------------------------------------------ -v | -5003 | -5083 99660020 80000000 | -500b | -5003 | -5083 80000000 996601a0 | -500b | -5003 | -5083 80000000 00001567 | -500b if highest byte wasn't 0, repeat -------- -5003 -5083 99660021 80000000 -500b -5003 -5083 80000000 996600a1 -500b -5003 -5083 99660027 80000000 -500b -5003 -5083 80000000 996600a7 now you wait until SI = SO, not the other way around -500b -5002 -5082 80000000 99660028 !! EXTERNAL clock -500a -5002 -5082 996600a8 80000000 -500a -5003 -5083 99660026 80000000 up till now -500b -5003 -5083 80000000 996601a6 -500b -5003 -5083 80000000 12345678 -500b -5003 -5083 996602a5 80000000 -500b -5003 -5083 87654321 80000000 -500b -5003 -5083 45678123 80000000 -500b -5003 -5083 80000000 996600a5 here again, SI = SO -500b -5002 -5082 80000000 99660028 !! EXTERNAL clock -500a -5002 -5082 996600a8 80000000 -500a -5003 -5083 99660026 80000000 from here it's normal -etc. etc. - -when you want to exit -5003 -5083 99660130 80000000 -500b -5003 -5083 00000001 80000000 -500b -5003 -5083 80000000 996600b0 -500b - -Thanks to ---------- -Lizardon for trying out my 'pimped' ROMS on his adapter -Nintendo for not giving away ANY info on this thing :-( (at least it was nice practice to figure it out) -You for reading the whole example \ No newline at end of file diff --git a/examples/LinkCableMultiboot_demo/src/main.cpp b/examples/LinkCableMultiboot_demo/src/main.cpp index 691b951..1ef1fa3 100644 --- a/examples/LinkCableMultiboot_demo/src/main.cpp +++ b/examples/LinkCableMultiboot_demo/src/main.cpp @@ -58,7 +58,7 @@ int main() { // (3) Send the ROM result = - linkCableMultiboot->sendRom((const void*)MEM_EWRAM, romSize, []() { + linkCableMultiboot->sendRom((const u8*)MEM_EWRAM, romSize, []() { u16 keys = ~REG_KEYS & KEY_ANY; return keys & KEY_SELECT; }); diff --git a/lib/LinkCableMultiboot.hpp b/lib/LinkCableMultiboot.hpp index 19be90c..68cadc8 100644 --- a/lib/LinkCableMultiboot.hpp +++ b/lib/LinkCableMultiboot.hpp @@ -9,12 +9,12 @@ // LinkCableMultiboot* linkCableMultiboot = new LinkCableMultiboot(); // - 2) Send the ROM: // LinkCableMultiboot::Result result = linkCableMultiboot->sendRom( -// romBytes, // for current ROM, use: ((const void*)MEM_EWRAM) -// romLength, // should be multiple of 0x10 +// romBytes, // for current ROM, use: ((const u8*)MEM_EWRAM) +// romLength, // in bytes, should be multiple of 0x10 // []() { // u16 keys = ~REG_KEYS & KEY_ANY; // return keys & KEY_START; -// // (when this returns true, transfer will be canceled) +// // (when this returns true, the transfer will be canceled) // } // ); // // `result` should be LinkCableMultiboot::Result::SUCCESS @@ -73,7 +73,7 @@ class LinkCableMultiboot { }; template - Result sendRom(const void* rom, u32 romSize, F cancel) { + Result sendRom(const u8* rom, u32 romSize, F cancel) { if (romSize < LINK_CABLE_MULTIBOOT_MIN_ROM_SIZE) return INVALID_SIZE; if (romSize > LINK_CABLE_MULTIBOOT_MAX_ROM_SIZE) @@ -179,7 +179,7 @@ class LinkCableMultiboot { } template - PartialResult sendHeader(const void* rom, F cancel) { + PartialResult sendHeader(const u8* rom, F cancel) { u16* headerOut = (u16*)rom; for (int i = 0; i < LINK_CABLE_MULTIBOOT_HEADER_SIZE; i += 2) { diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 32f47f7..4b83977 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -10,12 +10,18 @@ // new LinkWirelessMultiboot(); // - 2) Send the ROM: // LinkWirelessMultiboot::Result result = linkWirelessMultiboot->sendRom( -// romBytes, // for current ROM, use: ((const void*)MEM_EWRAM) -// romLength, // should be multiple of 0x10 -// []() { +// romBytes, // for current ROM, use: ((const u8*)MEM_EWRAM) +// romLength, // in bytes +// Multiboot", // game name +// "Test", // user name +// 0xffff, // game id +// 2, // number of players +// [](LinkWirelessMultiboot::MultibootProgress progress) { +// // check progress.[state,connectedClients,percentage] +// // u16 keys = ~REG_KEYS & KEY_ANY; // return keys & KEY_START; -// // (when this returns true, transfer will be canceled) +// // (when this returns true, the transfer will be canceled) // } // ); // // `result` should be LinkWirelessMultiboot::Result::SUCCESS @@ -95,7 +101,7 @@ class LinkWirelessMultiboot { if (romSize < LINK_WIRELESS_MULTIBOOT_MIN_ROM_SIZE) return INVALID_SIZE; if (romSize > LINK_WIRELESS_MULTIBOOT_MAX_ROM_SIZE) - return INVALID_SIZE; // TODO: Document no 0x10 boundary limit + return INVALID_SIZE; if (players < LINK_WIRELESS_MULTIBOOT_MIN_PLAYERS || players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) return INVALID_PLAYERS; From 7294149ef48e86a613ec6f5fb9a256a48a3c9168 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sat, 3 Feb 2024 09:54:12 -0300 Subject: [PATCH 54/74] Adding support for multiple children --- lib/LinkWirelessMultiboot.hpp | 128 ++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 45 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 4b83977..7ba1cd4 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -145,12 +145,20 @@ class LinkWirelessMultiboot { LinkWirelessOpenSDK* linkWirelessOpenSDK = new LinkWirelessOpenSDK(); private: + struct Transfer { + LinkWirelessOpenSDK::SequenceNumber sequence = { + .n = 1, + .phase = 0, + .commState = LinkWirelessOpenSDK::CommState::COMMUNICATING}; + u32 transferred = 0; + }; + MultibootProgress progress; Result lastResult; LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; u16 lastWaitCNT; - Result activate() { + __attribute__((noinline)) Result activate() { if (!linkRawWireless->activate()) { LWMLOG("! adapter not detected"); return ADAPTER_NOT_DETECTED; @@ -160,10 +168,10 @@ class LinkWirelessMultiboot { return SUCCESS; } - Result initialize(const char* gameName, - const char* userName, - const u16 gameId, - u8 players) { + __attribute__((noinline)) Result initialize(const char* gameName, + const char* userName, + const u16 gameId, + u8 players) { if (!linkRawWireless->setup(players, LINK_WIRELESS_MULTIBOOT_SETUP_TX, LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT, LINK_WIRELESS_MULTIBOOT_SETUP_MAGIC)) { @@ -190,7 +198,7 @@ class LinkWirelessMultiboot { } template - Result waitForClients(u8 players, C cancel) { + __attribute__((noinline)) Result waitForClients(u8 players, C cancel) { LinkRawWireless::AcceptConnectionsResponse acceptResponse; u32 currentPlayers = 1; @@ -306,72 +314,102 @@ class LinkWirelessMultiboot { } template - Result sendRomStartCommand(C cancel) { - LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( - 0, // TODO: Multiple clients - linkWirelessOpenSDK->createServerBuffer( - LINK_WIRELESS_MULTIBOOT_CMD_START, - LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, - {1, 0, LinkWirelessOpenSDK::CommState::STARTING}, 0b0001), - cancel)) + __attribute__((noinline)) Result sendRomStartCommand(C cancel) { + for (u32 i = 0; i < progress.connectedClients; i++) { + LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( + i, + linkWirelessOpenSDK->createServerBuffer( + LINK_WIRELESS_MULTIBOOT_CMD_START, + LINK_WIRELESS_MULTIBOOT_CMD_START_SIZE, + {1, 0, LinkWirelessOpenSDK::CommState::STARTING}, 1 << i), + cancel)) + } return SUCCESS; } template - Result sendRomBytes(const u8* rom, u32 romSize, C cancel) { + __attribute__((noinline)) Result sendRomBytes(const u8* rom, + u32 romSize, + C cancel) { LinkWirelessOpenSDK::ChildrenData childrenData; - LinkWirelessOpenSDK::SequenceNumber sequence = { - .n = 1, - .phase = 0, - .commState = LinkWirelessOpenSDK::CommState::COMMUNICATING}; - u32 transferredBytes = 0; + std::array transfers; + progress.percentage = 0; - while (transferredBytes < romSize) { + u32 minClient = 0; + + while (transfers[minClient = findMinClient(transfers)].transferred < + romSize) { if (cancel(progress)) return finish(CANCELED); - // TODO: Multiple clients + auto minTransfer = transfers[minClient]; + auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, sequence, 0b0001, transferredBytes); + rom, romSize, minTransfer.sequence, 0b1111, minTransfer.transferred); LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(sendBuffer, response)) childrenData = linkWirelessOpenSDK->getChildrenData(response); - for (u32 i = 0; i < childrenData.responses[0].packetsSize; i++) { - auto header = childrenData.responses[0].packets[i].header; - if (header.isACK && header.sequence() == sequence) { - sequence.inc(); - transferredBytes += sendBuffer.header.payloadSize; - u32 newPercentage = transferredBytes * 100 / romSize; - if (newPercentage != progress.percentage) { - progress.percentage = newPercentage; - LWMLOG("-> " + std::to_string(newPercentage)); + for (u32 i = 0; i < progress.connectedClients; i++) { + for (u32 j = 0; j < childrenData.responses[i].packetsSize; j++) { + auto header = childrenData.responses[i].packets[j].header; + if (header.isACK && + transfers[i].transferred == minTransfer.transferred && + header.sequence() == minTransfer.sequence) { + transfers[i].sequence.inc(); + transfers[i].transferred += sendBuffer.header.payloadSize; + break; } - break; } } + + u32 newPercentage = + transfers[findMinClient(transfers)].transferred * 100 / romSize; + if (newPercentage != progress.percentage) { + progress.percentage = newPercentage; + LWMLOG("-> " + std::to_string(newPercentage)); + } } return SUCCESS; } + __attribute__((noinline)) u32 findMinClient( + std::array& + transfers) { + u32 minTransferredBytes = 0xffffffff; + u32 minClient = 0; + + for (u32 i = 0; i < progress.connectedClients; i++) { + if (transfers[i].transferred < minTransferredBytes) { + minTransferredBytes = transfers[i].transferred; + minClient = i; + } + } + + return minClient; + } + template - Result confirm(C cancel) { + __attribute__((noinline)) Result confirm(C cancel) { LWMLOG("confirming (1/2)..."); - LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( - 0, // TODO: Multiple clients - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {0, 0, LinkWirelessOpenSDK::CommState::ENDING}, 0b0001), - cancel)) + for (u32 i = 0; i < progress.connectedClients; i++) { + LINK_WIRELESS_MULTIBOOT_TRY(exchangeNewData( + i, + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {0, 0, LinkWirelessOpenSDK::CommState::ENDING}, 1 << i), + cancel)) + } LWMLOG("confirming (2/2)..."); - // TODO: Multiple clients - LinkRawWireless::ReceiveDataResponse response; - LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( - linkWirelessOpenSDK->createServerBuffer( - {}, 0, {1, 0, LinkWirelessOpenSDK::CommState::OFF}, 0b0001), - response)) + for (u32 i = 0; i < progress.connectedClients; i++) { + LinkRawWireless::ReceiveDataResponse response; + LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData( + linkWirelessOpenSDK->createServerBuffer( + {}, 0, {1, 0, LinkWirelessOpenSDK::CommState::OFF}, 1 << i), + response)) + } return SUCCESS; } From 30a449092c2bb3d60d83422e4cb4f29fa575bfba Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 05:55:23 -0300 Subject: [PATCH 55/74] Tracking packets individually --- lib/LinkWirelessMultiboot.hpp | 165 ++++++++++++++++++++++++++++++---- lib/LinkWirelessOpenSDK.hpp | 12 +-- 2 files changed, 151 insertions(+), 26 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 7ba1cd4..2821a34 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -50,6 +50,7 @@ #define LINK_WIRELESS_MULTIBOOT_SETUP_WAIT_TIMEOUT 32 #define LINK_WIRELESS_MULTIBOOT_GAME_ID_MULTIBOOT_FLAG (1 << 15) #define LINK_WIRELESS_MULTIBOOT_FRAME_LINES 228 +#define LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS 4 #define LINK_WIRELESS_MULTIBOOT_TRY(CALL) \ if ((lastResult = CALL) != SUCCESS) { \ return finish(lastResult); \ @@ -145,12 +146,134 @@ class LinkWirelessMultiboot { LinkWirelessOpenSDK* linkWirelessOpenSDK = new LinkWirelessOpenSDK(); private: + struct PendingTransfer { + u32 cursor; + bool ack; + bool isActive = false; + }; + + struct PendingTransferList { + std::array + transfers; + + PendingTransfer* max(bool ack = false) { + int maxCursor = -1; + int maxI = -1; + for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) { + if (transfers[i].isActive && (int)transfers[i].cursor > maxCursor && + (!ack || transfers[i].ack)) { + maxCursor = transfers[i].cursor; + maxI = i; + } + } + return maxI > -1 ? &transfers[maxI] : NULL; + } + + void addIfNeeded(u32 newCursor) { + auto maxTransfer = max(); + // linkWirelessMultiboot->logger("+> " + std::to_string(newCursor)); + // linkWirelessMultiboot->logger("max? " + + // std::to_string(maxTransfer != NULL)); + // linkWirelessMultiboot->logger( + // "cursor? " + + // std::to_string(maxTransfer != NULL ? maxTransfer->cursor : -123)); + if (maxTransfer != NULL && newCursor <= maxTransfer->cursor) + return; + + for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) { + if (!transfers[i].isActive) { + // linkWirelessMultiboot->logger("++ " + std::to_string(newCursor) + + // " pending"); + transfers[i].cursor = newCursor; + transfers[i].ack = false; + transfers[i].isActive = true; + break; + } + } + } + + int ack(LinkWirelessOpenSDK::SequenceNumber sequence) { + int index = findIndex(sequence); + if (index == -1) { + // linkWirelessMultiboot->logger( + // "can't find n=" + std::to_string(sequence.n) + + // ", ph=" + std::to_string(sequence.phase)); + return -1; + } + + // linkWirelessMultiboot->logger( + // "ack " + std::to_string(transfers[index].cursor) + " :D"); + + transfers[index].ack = true; + auto maxAckTransfer = max(true); + // linkWirelessMultiboot->logger( + // "max is " + + // std::to_string(maxAckTransfer != NULL ? maxAckTransfer->cursor : + // -2)); + bool canUpdateCursor = + maxAckTransfer != NULL && isAckCompleteUpTo(maxAckTransfer->cursor); + + if (canUpdateCursor) { + // linkWirelessMultiboot->logger( + // "NEW CURSOR " + std::to_string(maxAckTransfer->cursor + 1)); + cleanup(); + } + + return canUpdateCursor ? maxAckTransfer->cursor + 1 : -1; + } + + void cleanup() { + for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) { + if (transfers[i].isActive && transfers[i].ack) + transfers[i].isActive = false; + } + } + + bool isFull() { + for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) + if (!transfers[i].isActive) + return false; + return true; + } + + private: + bool isAckCompleteUpTo(u32 cursor) { + for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) + if (transfers[i].isActive && !transfers[i].ack && + transfers[i].cursor < cursor) + return false; + return true; + } + + int findIndex(LinkWirelessOpenSDK::SequenceNumber sequence) { + for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) { + if (transfers[i].isActive && + LinkWirelessOpenSDK::SequenceNumber::fromPacketId( + transfers[i].cursor) == sequence) { + return i; + } + } + + return -1; + } + }; + struct Transfer { - LinkWirelessOpenSDK::SequenceNumber sequence = { - .n = 1, - .phase = 0, - .commState = LinkWirelessOpenSDK::CommState::COMMUNICATING}; - u32 transferred = 0; + u32 cursor = 0; + PendingTransferList pendingTransferList; + + void addIfNeeded(u32 newCursor) { + if (newCursor >= cursor) + pendingTransferList.addIfNeeded(newCursor); + } + + u32 transferred() { + return cursor * LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER; + } + + LinkWirelessOpenSDK::SequenceNumber sequence() { + return LinkWirelessOpenSDK::SequenceNumber::fromPacketId(cursor); + } }; MultibootProgress progress; @@ -338,15 +461,21 @@ class LinkWirelessMultiboot { progress.percentage = 0; u32 minClient = 0; - while (transfers[minClient = findMinClient(transfers)].transferred < + while (transfers[minClient = findMinClient(transfers)].transferred() < romSize) { if (cancel(progress)) return finish(CANCELED); - auto minTransfer = transfers[minClient]; + u32 cursor = transfers[minClient].cursor; // TODO: INFLIGHT + u32 offset = cursor * LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER; + auto sequence = LinkWirelessOpenSDK::SequenceNumber::fromPacketId(cursor); auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, minTransfer.sequence, 0b1111, minTransfer.transferred); + rom, romSize, sequence, 0b1111, offset); + + for (u32 i = 0; i < progress.connectedClients; i++) + transfers[i].addIfNeeded(cursor); + LinkRawWireless::ReceiveDataResponse response; LINK_WIRELESS_MULTIBOOT_TRY(sendAndExpectData(sendBuffer, response)) childrenData = linkWirelessOpenSDK->getChildrenData(response); @@ -354,20 +483,20 @@ class LinkWirelessMultiboot { for (u32 i = 0; i < progress.connectedClients; i++) { for (u32 j = 0; j < childrenData.responses[i].packetsSize; j++) { auto header = childrenData.responses[i].packets[j].header; - if (header.isACK && - transfers[i].transferred == minTransfer.transferred && - header.sequence() == minTransfer.sequence) { - transfers[i].sequence.inc(); - transfers[i].transferred += sendBuffer.header.payloadSize; - break; + + if (header.isACK) { + int newAckCursor = + transfers[i].pendingTransferList.ack(header.sequence()); + if (newAckCursor > -1) + transfers[i].cursor = newAckCursor; } } } u32 newPercentage = - transfers[findMinClient(transfers)].transferred * 100 / romSize; + transfers[findMinClient(transfers)].transferred() * 100 / romSize; if (newPercentage != progress.percentage) { - progress.percentage = newPercentage; + progress.percentage = min(newPercentage, 100); LWMLOG("-> " + std::to_string(newPercentage)); } } @@ -382,8 +511,8 @@ class LinkWirelessMultiboot { u32 minClient = 0; for (u32 i = 0; i < progress.connectedClients; i++) { - if (transfers[i].transferred < minTransferredBytes) { - minTransferredBytes = transfers[i].transferred; + if (transfers[i].transferred() < minTransferredBytes) { + minTransferredBytes = transfers[i].transferred(); minClient = i; } } diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index a6b4d75..e5d4a9c 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -59,14 +59,10 @@ class LinkWirelessOpenSDK { u32 phase = 0; CommState commState = OFF; - void inc() { - phase++; - if (phase == 4) { - phase = 0; - n++; - if (n == 4) - n = 0; - } + static SequenceNumber fromPacketId(u32 packetId) { + return SequenceNumber{.n = ((packetId + 4) / 4) % 4, + packetId % 4, + .commState = COMMUNICATING}; } bool operator==(const SequenceNumber& other) { From 1c9adf3eb06f62900c73d734e876d8fd8121c3d9 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 07:01:46 -0300 Subject: [PATCH 56/74] Adding inflight packets (double speed!) --- lib/LinkWirelessMultiboot.hpp | 90 +++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 2821a34..7e05a74 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -169,21 +169,26 @@ class LinkWirelessMultiboot { return maxI > -1 ? &transfers[maxI] : NULL; } + PendingTransfer* minWithoutAck() { + u32 minCursor = 0xffffffff; + int minI = -1; + for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) { + if (transfers[i].isActive && transfers[i].cursor < minCursor && + !transfers[i].ack) { + minCursor = transfers[i].cursor; + minI = i; + } + } + return minI > -1 ? &transfers[minI] : NULL; + } + void addIfNeeded(u32 newCursor) { auto maxTransfer = max(); - // linkWirelessMultiboot->logger("+> " + std::to_string(newCursor)); - // linkWirelessMultiboot->logger("max? " + - // std::to_string(maxTransfer != NULL)); - // linkWirelessMultiboot->logger( - // "cursor? " + - // std::to_string(maxTransfer != NULL ? maxTransfer->cursor : -123)); if (maxTransfer != NULL && newCursor <= maxTransfer->cursor) return; for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) { if (!transfers[i].isActive) { - // linkWirelessMultiboot->logger("++ " + std::to_string(newCursor) + - // " pending"); transfers[i].cursor = newCursor; transfers[i].ack = false; transfers[i].isActive = true; @@ -194,30 +199,17 @@ class LinkWirelessMultiboot { int ack(LinkWirelessOpenSDK::SequenceNumber sequence) { int index = findIndex(sequence); - if (index == -1) { - // linkWirelessMultiboot->logger( - // "can't find n=" + std::to_string(sequence.n) + - // ", ph=" + std::to_string(sequence.phase)); + if (index == -1) return -1; - } - - // linkWirelessMultiboot->logger( - // "ack " + std::to_string(transfers[index].cursor) + " :D"); transfers[index].ack = true; + auto maxAckTransfer = max(true); - // linkWirelessMultiboot->logger( - // "max is " + - // std::to_string(maxAckTransfer != NULL ? maxAckTransfer->cursor : - // -2)); bool canUpdateCursor = maxAckTransfer != NULL && isAckCompleteUpTo(maxAckTransfer->cursor); - if (canUpdateCursor) { - // linkWirelessMultiboot->logger( - // "NEW CURSOR " + std::to_string(maxAckTransfer->cursor + 1)); + if (canUpdateCursor) cleanup(); - } return canUpdateCursor ? maxAckTransfer->cursor + 1 : -1; } @@ -230,10 +222,15 @@ class LinkWirelessMultiboot { } bool isFull() { + return size() == LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; + } + + u32 size() { + u32 size = 0; for (u32 i = 0; i < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS; i++) - if (!transfers[i].isActive) - return false; - return true; + if (transfers[i].isActive) + size++; + return size; } private: @@ -262,6 +259,18 @@ class LinkWirelessMultiboot { u32 cursor = 0; PendingTransferList pendingTransferList; + u32 nextCursor(bool canSendInflightPackets) { + u32 pendingCount = pendingTransferList.size(); + + if (canSendInflightPackets && pendingCount > 0 && + pendingCount < LINK_WIRELESS_MULTIBOOT_MAX_INFLIGHT_PACKETS) { + return pendingTransferList.max()->cursor + 1; + } else { + auto minWithoutAck = pendingTransferList.minWithoutAck(); + return minWithoutAck != NULL ? minWithoutAck->cursor : cursor; + } + } + void addIfNeeded(u32 newCursor) { if (newCursor >= cursor) pendingTransferList.addIfNeeded(newCursor); @@ -466,7 +475,7 @@ class LinkWirelessMultiboot { if (cancel(progress)) return finish(CANCELED); - u32 cursor = transfers[minClient].cursor; // TODO: INFLIGHT + u32 cursor = findMinCursor(transfers); u32 offset = cursor * LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER; auto sequence = LinkWirelessOpenSDK::SequenceNumber::fromPacketId(cursor); @@ -511,8 +520,9 @@ class LinkWirelessMultiboot { u32 minClient = 0; for (u32 i = 0; i < progress.connectedClients; i++) { - if (transfers[i].transferred() < minTransferredBytes) { - minTransferredBytes = transfers[i].transferred(); + u32 transferred = transfers[i].transferred(); + if (transferred < minTransferredBytes) { + minTransferredBytes = transferred; minClient = i; } } @@ -520,6 +530,26 @@ class LinkWirelessMultiboot { return minClient; } + __attribute__((noinline)) u32 findMinCursor( + std::array& + transfers) { + u32 minNextCursor = 0xffffffff; + + bool canSendInflightPackets = true; + for (u32 i = 0; i < progress.connectedClients; i++) { + if (transfers[i].pendingTransferList.isFull()) + canSendInflightPackets = false; + } + + for (u32 i = 0; i < progress.connectedClients; i++) { + u32 nextCursor = transfers[i].nextCursor(canSendInflightPackets); + if (nextCursor < minNextCursor) + minNextCursor = nextCursor; + } + + return minNextCursor; + } + template __attribute__((noinline)) Result confirm(C cancel) { LWMLOG("confirming (1/2)..."); From 324b180f5e998dfa99d3f507c362c1268449599a Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 09:05:51 -0300 Subject: [PATCH 57/74] Preventing race when reading activeChildren in 0x28 response --- lib/LinkWirelessMultiboot.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 7e05a74..3298b87 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -669,10 +669,11 @@ class LinkWirelessMultiboot { } if (remoteCommand.paramsSize > 0) { - u8 activeChildren = (remoteCommand.params[0] >> 8) & 0b1111; u8 expectedActiveChildren = 0; for (u32 i = 0; i < progress.connectedClients; i++) expectedActiveChildren |= 1 << i; + u8 activeChildren = + (remoteCommand.params[0] >> 8) & expectedActiveChildren; if (activeChildren != expectedActiveChildren) { LWMLOG("! client timeout [" + std::to_string(activeChildren) + "]"); From 311bd0a82274ac1fd803c993bb022f61e7db98f2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 10:18:07 -0300 Subject: [PATCH 58/74] Switching back to SendData (0x24) as it's way more stable on hw --- lib/LinkWirelessMultiboot.hpp | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 3298b87..f4d7a92 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -32,7 +32,7 @@ #include "LinkWirelessOpenSDK.hpp" // Enable logging (set `linkWirelessMultiboot->logger` and uncomment to enable) -#define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING // TODO: DISABLE +// #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING #ifdef LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING #define LWMLOG(str) logger(str) @@ -107,9 +107,6 @@ class LinkWirelessMultiboot { players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) return INVALID_PLAYERS; - lastWaitCNT = REG_WAITCNT; - REG_WAITCNT = 1 << 14; - LWMLOG("starting..."); LINK_WIRELESS_MULTIBOOT_TRY(activate()) progress.state = INITIALIZING; @@ -288,7 +285,6 @@ class LinkWirelessMultiboot { MultibootProgress progress; Result lastResult; LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; - u16 lastWaitCNT; __attribute__((noinline)) Result activate() { if (!linkRawWireless->activate()) { @@ -652,37 +648,14 @@ class LinkWirelessMultiboot { u32 dataSize, u32 _bytes, LinkRawWireless::ReceiveDataResponse& response) { - LinkRawWireless::RemoteCommand remoteCommand; bool success = false; - success = - linkRawWireless->sendDataAndWait(data, dataSize, remoteCommand, _bytes); + success = linkRawWireless->sendData(data, dataSize, _bytes); if (!success) { - LWMLOG("! sendDataAndWait failed"); - return FAILURE; - } - - if (remoteCommand.commandId != 0x28) { - LWMLOG("! expected EVENT 0x28"); - LWMLOG("! but got " + toHex(remoteCommand.commandId)); + LWMLOG("! sendData failed"); return FAILURE; } - if (remoteCommand.paramsSize > 0) { - u8 expectedActiveChildren = 0; - for (u32 i = 0; i < progress.connectedClients; i++) - expectedActiveChildren |= 1 << i; - u8 activeChildren = - (remoteCommand.params[0] >> 8) & expectedActiveChildren; - - if (activeChildren != expectedActiveChildren) { - LWMLOG("! client timeout [" + std::to_string(activeChildren) + "]"); - LWMLOG("! vs expected: [" + std::to_string(expectedActiveChildren) + - "]"); - return FAILURE; - } - } - success = linkRawWireless->receiveData(response); if (!success) { LWMLOG("! receiveData failed"); @@ -697,7 +670,6 @@ class LinkWirelessMultiboot { progress.state = STOPPED; progress.connectedClients = 0; progress.percentage = 0; - REG_WAITCNT = lastWaitCNT; return result; } From ed0affd5f5db9f1b3fa9f35e0a2347ad3d1038aa Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 10:46:42 -0300 Subject: [PATCH 59/74] Adding ROM header autopatching --- .../LinkWirelessMultiboot_demo/src/main.cpp | 2 -- lib/LinkWirelessMultiboot.hpp | 23 ++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/main.cpp b/examples/LinkWirelessMultiboot_demo/src/main.cpp index a326e8f..8097d3e 100644 --- a/examples/LinkWirelessMultiboot_demo/src/main.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/main.cpp @@ -16,8 +16,6 @@ LinkWirelessMultiboot* linkWirelessMultiboot = new LinkWirelessMultiboot(); int main() { setUpInterrupts(); - REG_WAITCNT = 0x4317; // (3,1 waitstates, prefetch ON) - engine->setScene(multibootScene.get()); while (true) { diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index f4d7a92..5691827 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -35,6 +35,7 @@ // #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING #ifdef LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING +#include #define LWMLOG(str) logger(str) #else #define LWMLOG(str) @@ -63,6 +64,10 @@ const u8 LINK_WIRELESS_MULTIBOOT_BOOTLOADER_HANDSHAKE[][6] = { {0x00, 0x00, 0x52, 0x46, 0x55, 0x2d}, {0x4d, 0x42, 0x2d, 0x44, 0x4c, 0x00}}; const u8 LINK_WIRELESS_MULTIBOOT_BOOTLOADER_HANDSHAKE_SIZE = 6; +const u8 LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH[] = { + 0x52, 0x46, 0x55, 0x2d, 0x4d, 0x42, 0x4f, 0x4f, 0x54, 0x00, 0x00, 0x00}; +const u8 LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH_OFFSET = 4; +const u8 LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH_SIZE = 12; static volatile char LINK_WIRELESS_MULTIBOOT_VERSION[] = "LinkWirelessMultiboot/v6.2.0"; @@ -462,6 +467,16 @@ class LinkWirelessMultiboot { C cancel) { LinkWirelessOpenSDK::ChildrenData childrenData; std::array transfers; + u8 firstPagePatch[LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER]; + for (u32 i = 0; i < LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER; i++) { + firstPagePatch[i] = + i >= LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH_OFFSET && + i < LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH_OFFSET + + LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH_SIZE + ? LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH + [i - LINK_WIRELESS_MULTIBOOT_ROM_HEADER_PATCH_OFFSET] + : rom[i]; + } progress.percentage = 0; u32 minClient = 0; @@ -474,9 +489,10 @@ class LinkWirelessMultiboot { u32 cursor = findMinCursor(transfers); u32 offset = cursor * LINK_WIRELESS_OPEN_SDK_MAX_PAYLOAD_SERVER; auto sequence = LinkWirelessOpenSDK::SequenceNumber::fromPacketId(cursor); + const u8* bufferToSend = cursor == 0 ? (const u8*)firstPagePatch : rom; auto sendBuffer = linkWirelessOpenSDK->createServerBuffer( - rom, romSize, sequence, 0b1111, offset); + bufferToSend, romSize, sequence, 0b1111, offset); for (u32 i = 0; i < progress.connectedClients; i++) transfers[i].addIfNeeded(cursor); @@ -499,9 +515,10 @@ class LinkWirelessMultiboot { } u32 newPercentage = - transfers[findMinClient(transfers)].transferred() * 100 / romSize; + min(transfers[findMinClient(transfers)].transferred() * 100 / romSize, + 100); if (newPercentage != progress.percentage) { - progress.percentage = min(newPercentage, 100); + progress.percentage = newPercentage; LWMLOG("-> " + std::to_string(newPercentage)); } } From b02061e11aeffad973d84389860d3e08e03fd498 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 11:09:54 -0300 Subject: [PATCH 60/74] Revert "Switching back to SendData (0x24) as it's way more stable on hw" (I spoke too sun, this has other timing problems) This reverts commit 311bd0a82274ac1fd803c993bb022f61e7db98f2. --- lib/LinkWirelessMultiboot.hpp | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index 5691827..be74321 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -112,6 +112,9 @@ class LinkWirelessMultiboot { players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) return INVALID_PLAYERS; + lastWaitCNT = REG_WAITCNT; + REG_WAITCNT = 1 << 14; + LWMLOG("starting..."); LINK_WIRELESS_MULTIBOOT_TRY(activate()) progress.state = INITIALIZING; @@ -290,6 +293,7 @@ class LinkWirelessMultiboot { MultibootProgress progress; Result lastResult; LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; + u16 lastWaitCNT; __attribute__((noinline)) Result activate() { if (!linkRawWireless->activate()) { @@ -665,14 +669,37 @@ class LinkWirelessMultiboot { u32 dataSize, u32 _bytes, LinkRawWireless::ReceiveDataResponse& response) { + LinkRawWireless::RemoteCommand remoteCommand; bool success = false; - success = linkRawWireless->sendData(data, dataSize, _bytes); + success = + linkRawWireless->sendDataAndWait(data, dataSize, remoteCommand, _bytes); if (!success) { - LWMLOG("! sendData failed"); + LWMLOG("! sendDataAndWait failed"); + return FAILURE; + } + + if (remoteCommand.commandId != 0x28) { + LWMLOG("! expected EVENT 0x28"); + LWMLOG("! but got " + toHex(remoteCommand.commandId)); return FAILURE; } + if (remoteCommand.paramsSize > 0) { + u8 expectedActiveChildren = 0; + for (u32 i = 0; i < progress.connectedClients; i++) + expectedActiveChildren |= 1 << i; + u8 activeChildren = + (remoteCommand.params[0] >> 8) & expectedActiveChildren; + + if (activeChildren != expectedActiveChildren) { + LWMLOG("! client timeout [" + std::to_string(activeChildren) + "]"); + LWMLOG("! vs expected: [" + std::to_string(expectedActiveChildren) + + "]"); + return FAILURE; + } + } + success = linkRawWireless->receiveData(response); if (!success) { LWMLOG("! receiveData failed"); @@ -687,6 +714,7 @@ class LinkWirelessMultiboot { progress.state = STOPPED; progress.connectedClients = 0; progress.percentage = 0; + REG_WAITCNT = lastWaitCNT; return result; } From c23b9d628c4da50ba4be66b1f2a2509431a7c284 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 11:31:31 -0300 Subject: [PATCH 61/74] gcc's -O3 was messing up the execution order for LinkWirelessMultiboot --- lib/LinkWirelessMultiboot.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index be74321..e4c10de 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -1,6 +1,9 @@ #ifndef LINK_WIRELESS_MULTIBOOT_H #define LINK_WIRELESS_MULTIBOOT_H +#pragma GCC push_options +#pragma GCC optimize("O2") + // -------------------------------------------------------------------------- // A Wireless Multiboot tool to send small ROMs from a GBA to up to 4 slaves. // -------------------------------------------------------------------------- @@ -740,4 +743,6 @@ extern LinkWirelessMultiboot* linkWirelessMultiboot; #undef LWMLOG +#pragma GCC pop_options + #endif // LINK_WIRELESS_MULTIBOOT_H \ No newline at end of file From cd221e63ab225485634f0739843449bdf773d772 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 19:42:42 -0300 Subject: [PATCH 62/74] Trying to make it work with the faster waitstates --- examples/LinkWirelessMultiboot_demo/src/main.cpp | 2 ++ lib/LinkWirelessMultiboot.hpp | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/LinkWirelessMultiboot_demo/src/main.cpp b/examples/LinkWirelessMultiboot_demo/src/main.cpp index 8097d3e..a326e8f 100644 --- a/examples/LinkWirelessMultiboot_demo/src/main.cpp +++ b/examples/LinkWirelessMultiboot_demo/src/main.cpp @@ -16,6 +16,8 @@ LinkWirelessMultiboot* linkWirelessMultiboot = new LinkWirelessMultiboot(); int main() { setUpInterrupts(); + REG_WAITCNT = 0x4317; // (3,1 waitstates, prefetch ON) + engine->setScene(multibootScene.get()); while (true) { diff --git a/lib/LinkWirelessMultiboot.hpp b/lib/LinkWirelessMultiboot.hpp index e4c10de..45c3db6 100644 --- a/lib/LinkWirelessMultiboot.hpp +++ b/lib/LinkWirelessMultiboot.hpp @@ -115,9 +115,6 @@ class LinkWirelessMultiboot { players > LINK_WIRELESS_MULTIBOOT_MAX_PLAYERS) return INVALID_PLAYERS; - lastWaitCNT = REG_WAITCNT; - REG_WAITCNT = 1 << 14; - LWMLOG("starting..."); LINK_WIRELESS_MULTIBOOT_TRY(activate()) progress.state = INITIALIZING; @@ -296,7 +293,6 @@ class LinkWirelessMultiboot { MultibootProgress progress; Result lastResult; LinkWirelessOpenSDK::ClientSDKHeader lastValidHeader; - u16 lastWaitCNT; __attribute__((noinline)) Result activate() { if (!linkRawWireless->activate()) { @@ -717,7 +713,6 @@ class LinkWirelessMultiboot { progress.state = STOPPED; progress.connectedClients = 0; progress.percentage = 0; - REG_WAITCNT = lastWaitCNT; return result; } From 9f21fc13f88ac1bfc025d5a428b667f450200a01 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 21:29:47 -0300 Subject: [PATCH 63/74] FIX: Clock inversion in LinkRawWireless sometimes failing --- docs/wireless_adapter.md | 9 +- .../src/scenes/DebugScene.cpp | 3 +- lib/LinkRawWireless.hpp | 97 +++++++++++++++---- 3 files changed, 87 insertions(+), 22 deletions(-) diff --git a/docs/wireless_adapter.md b/docs/wireless_adapter.md index 24195a8..98ded85 100644 --- a/docs/wireless_adapter.md +++ b/docs/wireless_adapter.md @@ -203,13 +203,14 @@ Commands are how you tell the adapter to do things. When in command mode the clo βŒ› If this acknowledge procedure doesn't complete, the adapter "gives up" after ~800ΞΌs and start listening again for commands. That means that if a game doesn't implement this logic, it has to wait almost 1 millisecond between transfers (vs ~40ΞΌs in normal scenarios). -πŸ”€ Also, the ACK protocol is different after a [Wait](#waiting) command: +πŸ”€ Also, the ACK protocol is reversed after a [Wait](#waiting) command (when the clock is reversed): - 1. The GBA goes high as soon as it can. + 1. The adapter goes low as soon as it can. + 1. The GBA goes high. 2. The adapter goes high. 3. The GBA goes low _when it’s ready_. - 4. The adapter goes low when it’s ready. - 5. The adapter starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value. + 3. The adapter goes low when it's ready. + 4. The adapter starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value. Whenever either side expects something to be sent from the other (as SPI is always dual direction, although one side is often not used), the value `0x80000000` is used. diff --git a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp index b4b5e52..15b85c0 100644 --- a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp +++ b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp @@ -892,7 +892,8 @@ std::string DebugScene::selectUserName() { void DebugScene::logGenericWaitCommand(std::string name, u32 id) { auto data = selectData(); return logOperation("sending " + name, [id, &data]() { - auto result = linkRawWireless->sendCommand(id, toArray(data), data.size()); + auto result = + linkRawWireless->sendCommand(id, toArray(data), data.size(), true); for (u32 i = 0; i < result.responsesSize; i++) { log("< [response" + std::to_string(i) + "] " + linkRawWireless->toHex(result.responses[i])); diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index 998f4c0..faccc56 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -481,7 +481,7 @@ class LinkRawWireless { LRWLOG("using header " + toHex(header)); if (!sendCommand(LINK_RAW_WIRELESS_COMMAND_SEND_DATA_AND_WAIT, data, - dataSize) + dataSize, true) .success) { reset(); return false; @@ -522,7 +522,7 @@ class LinkRawWireless { } bool wait(RemoteCommand& remoteCommand) { - if (!sendCommand(LINK_RAW_WIRELESS_COMMAND_WAIT).success) { + if (!sendCommand(LINK_RAW_WIRELESS_COMMAND_WAIT, {}, 0, true).success) { reset(); return false; } @@ -536,7 +536,8 @@ class LinkRawWireless { u8 type, std::array params = {}, - u16 length = 0) { + u16 length = 0, + bool invertsClock = false) { CommandResult result; u32 command = buildCommand(type, length); u32 r; @@ -560,7 +561,10 @@ class LinkRawWireless { } LRWLOG("sending response request"); - u32 response = transfer(LINK_RAW_WIRELESS_DATA_REQUEST); + u32 response = + invertsClock + ? transferAndStartClockInversionACK(LINK_RAW_WIRELESS_DATA_REQUEST) + : transfer(LINK_RAW_WIRELESS_DATA_REQUEST); u16 header = msB32(response); u16 data = lsB32(response); u8 responses = msB16(data); @@ -572,7 +576,7 @@ class LinkRawWireless { return result; } if (ack != type + LINK_RAW_WIRELESS_RESPONSE_ACK) { - if (ack == 0xee && responses == 1) { + if (ack == 0xee && responses == 1 && !invertsClock) { u8 __attribute__((unused)) code = (u8)transfer(LINK_RAW_WIRELESS_DATA_REQUEST); LRWLOG("! error received"); @@ -586,12 +590,14 @@ class LinkRawWireless { } LRWLOG("ack ok! " + std::to_string(responses) + " responses"); - for (u32 i = 0; i < responses; i++) { - LRWLOG("response " + std::to_string(i + 1) + "/" + - std::to_string(responses) + ":"); - u32 responseData = transfer(LINK_RAW_WIRELESS_DATA_REQUEST); - result.responses[result.responsesSize++] = responseData; - LRWLOG("<< " + toHex(responseData)); + if (!invertsClock) { + for (u32 i = 0; i < responses; i++) { + LRWLOG("response " + std::to_string(i + 1) + "/" + + std::to_string(responses) + ":"); + u32 responseData = transfer(LINK_RAW_WIRELESS_DATA_REQUEST); + result.responses[result.responsesSize++] = responseData; + LRWLOG("<< " + toHex(responseData)); + } } result.success = true; @@ -605,7 +611,8 @@ class LinkRawWireless { linkSPI->activate(LinkSPI::Mode::SLAVE); LRWLOG("WAITING for adapter cmd"); - u32 command = linkSPI->transfer(LINK_RAW_WIRELESS_DATA_REQUEST); + u32 command = linkSPI->transfer( + LINK_RAW_WIRELESS_DATA_REQUEST, []() { return false; }, false, true); if (!reverseAcknowledge()) { linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); reset(); @@ -629,7 +636,8 @@ class LinkRawWireless { for (u32 i = 0; i < params; i++) { LRWLOG("param " + std::to_string(i + 1) + "/" + std::to_string(params) + ":"); - u32 paramData = linkSPI->transfer(LINK_RAW_WIRELESS_DATA_REQUEST); + u32 paramData = linkSPI->transfer( + LINK_RAW_WIRELESS_DATA_REQUEST, []() { return false; }, false, true); if (!reverseAcknowledge()) { linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); reset(); @@ -640,8 +648,10 @@ class LinkRawWireless { } LRWLOG("sending ack"); - command = linkSPI->transfer(0x99660000 | ((commandId + 0x80) & 0xff)); - if (!reverseAcknowledge()) { + command = linkSPI->transfer( + 0x99660000 | ((commandId + 0x80) & 0xff), []() { return false; }, false, + true); + if (!reverseAcknowledge(true)) { linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); reset(); return remoteCommand; @@ -836,6 +846,19 @@ class LinkRawWireless { return receivedData; } + u32 transferAndStartClockInversionACK(u32 data) { + u32 lines = 0; + u32 vCount = REG_VCOUNT; + u32 receivedData = linkSPI->transfer( + data, [this, &lines, &vCount]() { return cmdTimeout(lines, vCount); }, + false, true); + + if (!reverseAcknowledgeStart()) + return LINK_SPI_NO_DATA; + + return receivedData; + } + bool acknowledge() { u32 lines = 0; u32 vCount = REG_VCOUNT; @@ -861,17 +884,57 @@ class LinkRawWireless { return true; } - bool reverseAcknowledge() { + bool reverseAcknowledgeStart() { u32 lines = 0; u32 vCount = REG_VCOUNT; + linkSPI->_setSOLow(); + wait(1); + linkSPI->_setSOHigh(); + while (linkSPI->_isSIHigh()) { + if (cmdTimeout(lines, vCount)) { + LRWLOG("! Rev0 failed. I put SO=HIGH,"); + LRWLOG("! but SI didn't become LOW."); + return false; + } + } + linkSPI->_setSOLow(); + + return true; + } + + bool reverseAcknowledge(bool isLastPart = false) { + u32 lines = 0; + u32 vCount = REG_VCOUNT; + + linkSPI->_setSOLow(); + while (linkSPI->_isSIHigh()) { + if (cmdTimeout(lines, vCount)) { + LRWLOG("! RevAck0 failed. I put SO=LOW,"); + LRWLOG("! but SI didn't become LOW."); + return false; + } + } + + linkSPI->_setSOHigh(); while (!linkSPI->_isSIHigh()) { if (cmdTimeout(lines, vCount)) { - LRWLOG("! REV_ACK failed. I put SO=HIGH,"); + LRWLOG("! RevAck1 failed. I put SO=HIGH,"); LRWLOG("! but SI didn't become HIGH."); return false; } } + // (normally, this occurs on the next linkSPI->transfer(...) call) + if (isLastPart) { + linkSPI->_setSOLow(); + while (linkSPI->_isSIHigh()) { + if (cmdTimeout(lines, vCount)) { + LRWLOG("! RevAck2 failed. I put SO=LOW,"); + LRWLOG("! but SI didn't become LOW."); + return false; + } + } + } return true; } From ca87a9f66c8df58819343686eef6dc946bbc8957 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 21:54:22 -0300 Subject: [PATCH 64/74] Adding clock inversion discoveries to docs --- docs/img/clock-inversion-ack-start.png | Bin 0 -> 18625 bytes docs/wireless_adapter.md | 31 ++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 docs/img/clock-inversion-ack-start.png diff --git a/docs/img/clock-inversion-ack-start.png b/docs/img/clock-inversion-ack-start.png new file mode 100644 index 0000000000000000000000000000000000000000..3c12d619563ae68fc108eb75cc3fc79219f2553c GIT binary patch literal 18625 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 J423Iq{~x7}0$l(A literal 0 HcmV?d00001 diff --git a/docs/wireless_adapter.md b/docs/wireless_adapter.md index 98ded85..a1d8be8 100644 --- a/docs/wireless_adapter.md +++ b/docs/wireless_adapter.md @@ -203,15 +203,6 @@ Commands are how you tell the adapter to do things. When in command mode the clo βŒ› If this acknowledge procedure doesn't complete, the adapter "gives up" after ~800ΞΌs and start listening again for commands. That means that if a game doesn't implement this logic, it has to wait almost 1 millisecond between transfers (vs ~40ΞΌs in normal scenarios). -πŸ”€ Also, the ACK protocol is reversed after a [Wait](#waiting) command (when the clock is reversed): - - 1. The adapter goes low as soon as it can. - 1. The GBA goes high. - 2. The adapter goes high. - 3. The GBA goes low _when it’s ready_. - 3. The adapter goes low when it's ready. - 4. The adapter starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value. - Whenever either side expects something to be sent from the other (as SPI is always dual direction, although one side is often not used), the value `0x80000000` is used. ### List of commands @@ -542,6 +533,28 @@ Waiting * `0` = manual disconnect (aka the host used [DisconnectClient](#disconnectclient---0x30)) * `1` = the connection was lost +β—€ **Inverted ACKs** + +The ACK protocol changes while the clock is inverted. + +Right after the adapter responds to a `0x9966xx25` with `0x996600A5`, it behaves like this: + + 1. The adapter stays high until the GBA goes high + 2. The adapter goes low + 3. The GBA goes low + +[![Image without alt text or caption](img/clock-inversion-ack-start.png)](img/clock-inversion-ack-start.png) + +Then, when the adapter issues commands to the GBA, the acknowledge procedure is 'standard', but with the inverted roles: + + 1. The adapter goes low as soon as it can. + 2. The GBA goes high. + 3. The adapter goes high. + 4. The GBA goes low _when it’s ready_. + 5. The adapter goes low when it's ready. + 6. The adapter starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value. + + SPI config ---------- From 96cc45832b7c5fa7471dc87e8407879ced5acc14 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 22:20:30 -0300 Subject: [PATCH 65/74] Adding LinkWirelessOpenSDK documentation --- README.md | 177 ++++++++++++++++++++---------------- lib/LinkWirelessOpenSDK.hpp | 2 +- 2 files changed, 98 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 210769a..fe476c1 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port. Its main purpose is to provide multiplayer support to homebrew games. - [πŸ‘Ύ](#-LinkCable) [LinkCable.hpp](lib/LinkCable.hpp): The classic 16-bit **Multi-Play mode** (up to 4 players) using a GBA Link Cable! + - [πŸ’»](#-LinkCableMultiboot) [LinkCableMultiboot.hpp](lib/LinkCableMultiboot.hpp): ‍Send **Multiboot software** (small 256KiB ROMs) to other GBAs with no cartridge! - [πŸ”§πŸ‘Ύ](#-LinkRawCable) [LinkRawCable.hpp](lib/LinkRawCable.hpp): A **minimal** low-level API for the 16-bit Multi-Play mode. -- [πŸ’»](#-LinkCableMultiboot) [LinkCableMultiboot.hpp](lib/LinkCableMultiboot.hpp): ‍Send **Multiboot software** (small 256KiB ROMs) to other GBAs with no cartridge! -- [πŸ”Œ](#-LinkGPIO) [LinkGPIO.hpp](lib/LinkGPIO.hpp): Use the Link Port however you want to control **any device** (like LEDs, rumble motors, and that kind of stuff)! -- [πŸ”—](#-LinkSPI) [LinkSPI.hpp](lib/LinkSPI.hpp): Connect with a PC (like a **Raspberry Pi**) or another GBA (with a GBC Link Cable) using this mode. Transfer up to 2Mbit/s! - [πŸ“»](#-LinkWireless) [LinkWireless.hpp](lib/LinkWireless.hpp): Connect up to 5 consoles with the **Wireless Adapter**! + - [πŸ“‘](#-LinkWirelessMultiboot) [LinkWirelessMultiboot.hpp](lib/LinkWirelessMultiboot.hpp): ‍Send **Multiboot software** (small 256KiB ROMs) to other GBAs **over the air**! - [πŸ”§πŸ“»](#-LinkRawWireless) [LinkRawWireless.hpp](lib/LinkRawWireless.hpp): A **minimal** low-level API for the Wireless Adapter. -- [πŸ“‘](#-LinkWirelessMultiboot) [LinkWirelessMultiboot.hpp](lib/LinkWirelessMultiboot.hpp): ‍Send **Multiboot software** (small 256KiB ROMs) to other GBAs **over the air**! + - [πŸ”§πŸ›οΈ](#-LinkWirelessOpenSDK) [LinkWirelessOpenSDK.hpp](lib/LinkWirelessOpenSDK.hpp): An abstraction of the **official** software level protocol of the Wireless Adapter. - [🌎](#-LinkUniversal) [LinkUniversal.hpp](lib/LinkUniversal.hpp): Add multiplayer support to your game, both with πŸ‘Ύ *Link Cables* and πŸ“» *Wireless Adapters*, using the **same API**! +- [πŸ”Œ](#-LinkGPIO) [LinkGPIO.hpp](lib/LinkGPIO.hpp): Use the Link Port however you want to control **any device** (like LEDs, rumble motors, and that kind of stuff)! +- [πŸ”—](#-LinkSPI) [LinkSPI.hpp](lib/LinkSPI.hpp): Connect with a PC (like a **Raspberry Pi**) or another GBA (with a GBC Link Cable) using this mode. Transfer up to 2Mbit/s! *(click on the emojis for documentation)* @@ -21,9 +22,11 @@ A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port. - Include the library you want (e.g. [LinkCable.hpp](lib/LinkCable.hpp)) in your game code, and refer to its comments for instructions. Most of these libraries are provided as single header files for simplicity. The only external dependency is **libtonc**, which comes preinstalled with *devkitPro*. - Check out the [examples](examples) folder. * Builds are available in [Releases](https://github.com/afska/gba-link-connection/releases). - * They can be tested on real GBAs or using emulators (*mGBA*, *NO$GBA*, or *VBA-M*). + * They can be tested on real GBAs or using emulators. * For `LinkCable`/`LinkWireless`/`LinkUniversal` there are stress tests that you can use to tweak your configuration. +To learn implementation details, you might also want to check out the [docs](docs) folder, which contains important documentation. + ### Makefile actions (for all examples) ```bash @@ -96,66 +99,38 @@ Name | Return type | Description ⚠️ for better results, turn on the GBAs **after** calling the `sendRom` method! -# πŸ”Œ LinkGPIO - -*(aka General Purpose Mode)* - -This is the default Link Port mode, and it allows users to manipulate pins `SI`, `SO`, `SD` and `SC` directly. - -![photo](https://user-images.githubusercontent.com/1631752/212867547-e0a795aa-da00-4b2c-8640-8db7ea857e19.jpg) - -## Methods - -Name | Return type | Description ---- | --- | --- -`reset()` | - | Resets communication mode to General Purpose. **Required to initialize the library!** -`setMode(pin, direction)` | - | Configures a `pin` to use a `direction` (input or output). -`getMode(pin)` | **LinkGPIO::Direction** | Returns the direction set at `pin`. -`readPin(pin)` | **bool** | Returns whether a `pin` is *HIGH* or not (when set as an input). -`writePin(pin, isHigh)` | - | Sets a `pin` to be high or not (when set as an output). -`setSIInterrupts(isEnabled)` | - | If it `isEnabled`, an IRQ will be generated when `SI` changes from *HIGH* to *LOW*. - -⚠️ always set the `SI` terminal to an input! - -# πŸ”— LinkSPI - -*(aka Normal Mode)* +# πŸ”§πŸ‘Ύ LinkRawCable -This is the GBA's implementation of SPI. In this library, packets are set to 32-bit, as there's no benefit to using the 8-bit version. You can use this to interact with other GBAs or computers that know SPI. +- This is a minimal hardware wrapper designed for the *Multi-Play mode*. +- It doesn't include any of the features of [πŸ‘Ύ LinkCable](#-LinkCable), so it's not well suited for games. +- Its demo (`LinkRawCable_demo`) can help emulator developers in enhancing accuracy. -![screenshot](https://user-images.githubusercontent.com/1631752/213068614-875049f6-bb01-41b6-9e30-98c73cc69b25.png) +![screenshot](https://github.com/afska/gba-link-connection/assets/1631752/29a25bf7-211e-49d6-a32a-566c72a44973) ## Methods Name | Return type | Description --- | --- | --- `isActive()` | **bool** | Returns whether the library is active or not. -`activate(mode)` | - | Activates the library in a specific `mode` (one of `LinkSPI::Mode::SLAVE`, `LinkSPI::Mode::MASTER_256KBPS`, or `LinkSPI::Mode::MASTER_2MBPS`). +`activate(baudRate = BAUD_RATE_1)` | - | Activates the library in a specific `baudRate` (`LinkRawCable::BaudRate`). `deactivate()` | - | Deactivates the library. -`transfer(data)` | **u32** | Exchanges `data` with the other end. Returns the received data. -`transfer(data, cancel)` | **u32** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. -`transferAsync(data, [cancel])` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`. Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! -`getAsyncState()` | **LinkSPI::AsyncState** | Returns the state of the last async transfer (one of `LinkSPI::AsyncState::IDLE`, `LinkSPI::AsyncState::WAITING`, or `LinkSPI::AsyncState::READY`). -`getAsyncData()` | **u32** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. -`getMode()` | **LinkSPI::Mode** | Returns the current `mode`. -`setWaitModeActive(isActive)` | - | Enables or disables `waitMode` (*). -`isWaitModeActive()` | **bool** | Returns whether `waitMode` (*) is active or not. - -> (*) `waitMode`: The GBA adds an extra feature over SPI. When working as master, it can check whether the other terminal is ready to receive, and wait if it's not. That makes the connection more reliable, but it's not always supported on other hardware units (e.g. the Wireless Adapter), so it must be disabled in those cases. -> -> `waitMode` is disabled by default. - -⚠️ when using Normal Mode between two GBAs, use a GBC Link Cable! - -⚠️ only use the 2Mbps mode with custom hardware (very short wires)! +`transfer(data)` | **LinkRawCable::Response** | Exchanges `data` with the connected consoles. Returns the received data, including the assigned player id. +`transfer(data, cancel)` | **LinkRawCable::Response** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. +`transferAsync(data)` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`. Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! +`getAsyncState()` | **LinkRawCable::AsyncState** | Returns the state of the last async transfer (one of `LinkRawCable::AsyncState::IDLE`, `LinkRawCable::AsyncState::WAITING`, or `LinkRawCable::AsyncState::READY`). +`getAsyncData()` | **LinkRawCable::Response** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. +`isMaster()` | **bool** | Returns whether the console is connected as master or not. Returns garbage when the cable is not properly connected. +`isReady()` | **bool** | Returns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected. +`getBaudRate()` | **LinkRawCable::BaudRate** | Returns the current `baudRate`. -⚠️ don't send `0xFFFFFFFF`, it's reserved for errors! +- don't send `0xFFFF`, it's a reserved value that means *disconnected client* +- only `transfer(...)` if `isReady()` # πŸ“» LinkWireless *(aka GBA Wireless Adapter)* -This is a driver for an accessory that enables wireless games up to 5 players. The inner workings of the adapter are highly unknown, but [this article](docs/wireless_adapter.md) is very helpful. I've updated the blog post to add more details about the things I learnt by the means of ~~reverse engineering~~ brute force and trial&error. +This is a driver for an accessory that enables wireless games up to 5 players. The inner workings of the adapter are highly unknown, but [this blog post](docs/wireless_adapter.md) is very helpful. I've updated it to add more details about the things I learnt by the means of ~~reverse engineering~~ brute force and trial&error. The library, by default, implements a lightweight protocol (on top of the adapter's message system) that sends packet IDs and checksums. This allows detecting disconnections, forwarding messages to all nodes, and retransmitting to prevent packet loss. @@ -222,7 +197,7 @@ Name | Return type | Description This tool allows sending Multiboot ROMs (small 256KiB programs that fit in EWRAM) from one GBA to up to 4 slaves, wirelessly, using a single cartridge. -// TODO: Photo +https://github.com/afska/gba-link-connection/assets/1631752/9a648bff-b14f-4a85-92d4-ccf366adce2d ## Methods @@ -230,6 +205,34 @@ Name | Return type | Description --- | --- | --- `sendRom(rom, romSize, gameName, userName, gameId, players, cancel)` | **LinkWirelessMultiboot::Result** | Sends the `rom`. The `players` must be the exact number of consoles that will download the ROM. Once this number of players is reached, the code will start transmitting the ROM bytes. During the process, the library will continuously invoke `cancel` (passing a `LinkWirelessMultiboot::MultibootProgress` object as argument), and abort the transfer if it returns `true`. The `romSize` must be a number between `448` and `262144`. It's recommended to use a ROM size that is a multiple of `16`, as this also ensures compatibility with Multiboot via Link Cable. Once completed, the return value should be `LinkWirelessMultiboot::Result::SUCCESS`. +# πŸ”§πŸ“» LinkRawWireless + +- This is a minimal hardware wrapper designed for the *Wireless Adapter*. +- It doesn't include any of the features of [πŸ“» LinkWireless](#-LinkWireless), so it's not well suited for games. +- Its demo (`LinkRawWireless_demo`) can help emulator developers in enhancing accuracy. + +![screenshot](https://github.com/afska/gba-link-connection/assets/1631752/bc7e5a7d-a1bd-46a5-8318-98160c1229ae) + +## Methods + +- There's one method for every supported wireless adapter command. +- Use `sendCommand(...)` to send arbitrary commands. + +# πŸ”§πŸ› LinkWirelessOpenSDK + +All first-party games, including the Multiboot 'bootloader' sent by the adapter, use an official software-level protocol. This class provides methods for creating and reading packets that adhere to this protocol. It's supposed to be used in conjunction with [πŸ”§πŸ“» LinkRawWireless](#-LinkRawWireless). + +## Methods + +Name | Return type | Description +--- | --- | --- +`getChildrenData(response)` | **LinkWirelessOpenSDK::ChildrenData** | Parses the `response` and returns a struct containing all the received packets from the connected clients. +`getParentData(response)` | **LinkWirelessOpenSDK::ParentData** | Parses the `response` and returns a struct containing all the received packets from the host. +`createServerBuffer(fullPayload, fullPayloadSize, sequence, [targetSlots], [offset])` | **LinkWirelessOpenSDK::SendBuffer** | Creates a buffer for the host to send a `fullPayload` with a valid header. If `fullPayloadSize` is higher than `84` (the maximum payload size), the buffer will only contain the **first** `84` bytes (unless an `offset` > 0 is used). A `sequence` number must be created by using `LinkWirelessOpenSDK::SequenceNumber::fromPacketId(...)`. Optionally, a `targetSlots` bit array can be used to exclude some clients from the transmissions (the default is `0b1111`). +`createServerACKBuffer(clientHeader, clientNumber)` | **LinkWirelessOpenSDK::SendBuffer** | Creates a buffer for the host to acknowledge a header received from a certain `clientNumber`. +`createClientBuffer(fullPayload, fullPayloadSize, sequence, [offset])` | **LinkWirelessOpenSDK::SendBuffer** | Creates a buffer for the client to send a `fullPayload` with a valid header. If `fullPayloadSize` is higher than `14` (the maximum payload size), the buffer will only contain the **first** `14` bytes (unless an `offset` > 0 is used). A `sequence` number must be created by using `LinkWirelessOpenSDK::SequenceNumber::fromPacketId(...)`. +`createClientACKBuffer(serverHeader)` | **LinkWirelessOpenSDK::SendBuffer** | Creates a buffer for the client to acknowledge a header received from the host. + # 🌎 LinkUniversal A multiuse library that doesn't care whether you plug a Link Cable or a Wireless Adapter. It continuously switches between both and tries to connect to other peers, supporting the hot swapping of cables and adapters and all the features from [πŸ‘Ύ LinkCable](#-LinkCable) and [πŸ“» LinkWireless](#-LinkWireless). @@ -244,8 +247,8 @@ Name | Type | Default | Description --- | --- | --- | --- `protocol` | **LinkUniversal::Protocol** | `AUTODETECT` | Specifies what protocol should be used (one of `LinkUniversal::Protocol::AUTODETECT`, `LinkUniversal::Protocol::CABLE`, `LinkUniversal::Protocol::WIRELESS_AUTO`, `LinkUniversal::Protocol::WIRELESS_SERVER`, or `LinkUniversal::Protocol::WIRELESS_CLIENT`). `gameName` | **const char\*** | `""` | The game name that will be broadcasted in wireless sessions (max `14` characters). The string must be a null-terminated character array. The library uses this to only connect to servers from the same game. -`cableOptions` | **LinkUniversal::CableOptions** | *same as LinkCable* | All the [πŸ‘Ύ LinkCable](#constructor) constructor parameters in one *struct*. -`wirelessOptions` | **LinkUniversal::WirelessOptions** | *same as LinkWireless* | All the [πŸ“» LinkWireless](#constructor-1) constructor parameters in one *struct*. +`cableOptions` | **LinkUniversal::CableOptions** | *same as LinkCable* | All the [πŸ‘Ύ LinkCable](#-LinkCable) constructor parameters in one *struct*. +`wirelessOptions` | **LinkUniversal::WirelessOptions** | *same as LinkWireless* | All the [πŸ“» LinkWireless](#-LinkWireless) constructor parameters in one *struct*. You can also change these compile-time constants: - `LINK_UNIVERSAL_MAX_PLAYERS`: to set a maximum number of players. The default value is `4` (LinkCable's limit) but can be increased to `5` to support larger wireless rooms. @@ -253,7 +256,7 @@ You can also change these compile-time constants: ## Methods -The interface is the same as [πŸ‘Ύ LinkCable](#methods). Additionally, it supports these methods: +The interface is the same as [πŸ‘Ύ LinkCable](#-LinkCable). Additionally, it supports these methods: Name | Return type | Description --- | --- | --- @@ -261,45 +264,59 @@ Name | Return type | Description `getMode()` | **LinkUniversal::Mode** | Returns the active mode (one of `LinkUniversal::Mode::LINK_CABLE`, or `LinkUniversal::Mode::LINK_WIRELESS`). `getProtocol()` | **LinkUniversal::Protocol** | Returns the active protocol (one of `LinkUniversal::Protocol::AUTODETECT`, `LinkUniversal::Protocol::CABLE`, `LinkUniversal::Protocol::WIRELESS_AUTO`, `LinkUniversal::Protocol::WIRELESS_SERVER`, or `LinkUniversal::Protocol::WIRELESS_CLIENT`). `setProtocol(protocol)` | - | Sets the active `protocol`. -`getWirelessState()` | **LinkWireless::State** | Returns the wireless state (same as [πŸ“» LinkWireless](#methods-4)'s `getState()`). +`getWirelessState()` | **LinkWireless::State** | Returns the wireless state (same as [πŸ“» LinkWireless](#-LinkWireless)'s `getState()`). -# πŸ”§πŸ‘Ύ LinkRawCable +# πŸ”Œ LinkGPIO -- This is a minimal hardware wrapper designed for the *Multi-Play mode*. -- It doesn't include any of the features of [πŸ‘Ύ LinkCable](#-LinkCable), so it's not well suited for games. -- Its demo (`LinkRawCable_demo`) can help emulator developers in enhancing accuracy. +*(aka General Purpose Mode)* -![screenshot](https://github.com/afska/gba-link-connection/assets/1631752/29a25bf7-211e-49d6-a32a-566c72a44973) +This is the default Link Port mode, and it allows users to manipulate pins `SI`, `SO`, `SD` and `SC` directly. + +![photo](https://user-images.githubusercontent.com/1631752/212867547-e0a795aa-da00-4b2c-8640-8db7ea857e19.jpg) ## Methods Name | Return type | Description --- | --- | --- -`isActive()` | **bool** | Returns whether the library is active or not. -`activate(baudRate = BAUD_RATE_1)` | - | Activates the library in a specific `baudRate` (`LinkRawCable::BaudRate`). -`deactivate()` | - | Deactivates the library. -`transfer(data)` | **LinkRawCable::Response** | Exchanges `data` with the connected consoles. Returns the received data, including the assigned player id. -`transfer(data, cancel)` | **LinkRawCable::Response** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. -`transferAsync(data)` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`. Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! -`getAsyncState()` | **LinkRawCable::AsyncState** | Returns the state of the last async transfer (one of `LinkRawCable::AsyncState::IDLE`, `LinkRawCable::AsyncState::WAITING`, or `LinkRawCable::AsyncState::READY`). -`getAsyncData()` | **LinkRawCable::Response** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. -`isMaster()` | **bool** | Returns whether the console is connected as master or not. Returns garbage when the cable is not properly connected. -`isReady()` | **bool** | Returns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected. -`getBaudRate()` | **LinkRawCable::BaudRate** | Returns the current `baudRate`. +`reset()` | - | Resets communication mode to General Purpose. **Required to initialize the library!** +`setMode(pin, direction)` | - | Configures a `pin` to use a `direction` (input or output). +`getMode(pin)` | **LinkGPIO::Direction** | Returns the direction set at `pin`. +`readPin(pin)` | **bool** | Returns whether a `pin` is *HIGH* or not (when set as an input). +`writePin(pin, isHigh)` | - | Sets a `pin` to be high or not (when set as an output). +`setSIInterrupts(isEnabled)` | - | If it `isEnabled`, an IRQ will be generated when `SI` changes from *HIGH* to *LOW*. -- don't send `0xFFFF`, it's a reserved value that means *disconnected client* -- only `transfer(...)` if `isReady()` +⚠️ always set the `SI` terminal to an input! -# πŸ”§πŸ“» LinkRawWireless +# πŸ”— LinkSPI -- This is a minimal hardware wrapper designed for the *Wireless Adapter*. -- It doesn't include any of the features of [πŸ“» LinkWireless](#-LinkWireless), so it's not well suited for games. -- Its demo (`LinkRawWireless_demo`) can help emulator developers in enhancing accuracy. +*(aka Normal Mode)* -![screenshot](https://github.com/afska/gba-link-connection/assets/1631752/bc7e5a7d-a1bd-46a5-8318-98160c1229ae) +This is the GBA's implementation of SPI. In this library, packets are set to 32-bit, as there's no benefit to using the 8-bit version. You can use this to interact with other GBAs or computers that know SPI. +![screenshot](https://user-images.githubusercontent.com/1631752/213068614-875049f6-bb01-41b6-9e30-98c73cc69b25.png) ## Methods -- There's one method for every supported wireless adapter command. -- Use `sendCommand(...)` to send arbitrary commands. \ No newline at end of file +Name | Return type | Description +--- | --- | --- +`isActive()` | **bool** | Returns whether the library is active or not. +`activate(mode)` | - | Activates the library in a specific `mode` (one of `LinkSPI::Mode::SLAVE`, `LinkSPI::Mode::MASTER_256KBPS`, or `LinkSPI::Mode::MASTER_2MBPS`). +`deactivate()` | - | Deactivates the library. +`transfer(data)` | **u32** | Exchanges `data` with the other end. Returns the received data. +`transfer(data, cancel)` | **u32** | Like `transfer(data)` but accepts a `cancel()` function. The library will continuously invoke it, and abort the transfer if it returns `true`. +`transferAsync(data, [cancel])` | - | Schedules a `data` transfer and returns. After this, call `getAsyncState()` and `getAsyncData()`. Note that until you retrieve the async data, normal `transfer(...)`s won't do anything! +`getAsyncState()` | **LinkSPI::AsyncState** | Returns the state of the last async transfer (one of `LinkSPI::AsyncState::IDLE`, `LinkSPI::AsyncState::WAITING`, or `LinkSPI::AsyncState::READY`). +`getAsyncData()` | **u32** | If the async state is `READY`, returns the remote data and switches the state back to `IDLE`. +`getMode()` | **LinkSPI::Mode** | Returns the current `mode`. +`setWaitModeActive(isActive)` | - | Enables or disables `waitMode` (*). +`isWaitModeActive()` | **bool** | Returns whether `waitMode` (*) is active or not. + +> (*) `waitMode`: The GBA adds an extra feature over SPI. When working as master, it can check whether the other terminal is ready to receive, and wait if it's not. That makes the connection more reliable, but it's not always supported on other hardware units (e.g. the Wireless Adapter), so it must be disabled in those cases. +> +> `waitMode` is disabled by default. + +⚠️ when using Normal Mode between two GBAs, use a GBC Link Cable! + +⚠️ only use the 2Mbps mode with custom hardware (very short wires)! + +⚠️ don't send `0xFFFFFFFF`, it's reserved for errors! diff --git a/lib/LinkWirelessOpenSDK.hpp b/lib/LinkWirelessOpenSDK.hpp index e5d4a9c..c0ca9d5 100644 --- a/lib/LinkWirelessOpenSDK.hpp +++ b/lib/LinkWirelessOpenSDK.hpp @@ -4,7 +4,7 @@ // -------------------------------------------------------------------------- // An open-source implementation of the "official" Wireless Adapter protocol. // -------------------------------------------------------------------------- -// TODO: Document +// - Check out README.md for documentation. // -------------------------------------------------------------------------- #include From 7c6191cebe0681eae78877cac35a962672ff2737 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 23:15:56 -0300 Subject: [PATCH 66/74] Cleaning LinkCableMultiboot code by making it use LinkRawCable under the hood --- lib/LinkCableMultiboot.hpp | 88 +++++++++++--------------------------- 1 file changed, 24 insertions(+), 64 deletions(-) diff --git a/lib/LinkCableMultiboot.hpp b/lib/LinkCableMultiboot.hpp index 68cadc8..df07b50 100644 --- a/lib/LinkCableMultiboot.hpp +++ b/lib/LinkCableMultiboot.hpp @@ -25,6 +25,7 @@ #include #include +#include "LinkRawCable.hpp" #define LINK_CABLE_MULTIBOOT_MIN_ROM_SIZE (0x100 + 0xc0) #define LINK_CABLE_MULTIBOOT_MAX_ROM_SIZE (256 * 1024) @@ -43,11 +44,7 @@ #define LINK_CABLE_MULTIBOOT_ACK_RESPONSE 0x73 #define LINK_CABLE_MULTIBOOT_HEADER_SIZE 0xC0 #define LINK_CABLE_MULTIBOOT_SWI_MULTIPLAYER_MODE 1 -#define LINK_CABLE_MULTIBOOT_SIOCNT_MAX_BAUD_RATE 3 -#define LINK_CABLE_MULTIBOOT_BIT_START 7 -#define LINK_CABLE_MULTIBOOT_BIT_MULTIPLAYER 13 -#define LINK_CABLE_MULTIBOOT_BIT_GENERAL_PURPOSE_LOW 14 -#define LINK_CABLE_MULTIBOOT_BIT_GENERAL_PURPOSE_HIGH 15 +#define LINK_CABLE_MULTIBOOT_MAX_BAUD_RATE LinkRawCable::BaudRate::BAUD_RATE_3 #define LINK_CABLE_MULTIBOOT_TRY(CALL) \ do { \ partialResult = CALL; \ @@ -109,12 +106,16 @@ class LinkCableMultiboot { int result = MultiBoot(&multiBootParameters, LINK_CABLE_MULTIBOOT_SWI_MULTIPLAYER_MODE); - setGeneralPurposeMode(); + linkRawCable->deactivate(); return result == 1 ? FAILURE_DURING_TRANSFER : SUCCESS; } + ~LinkCableMultiboot() { delete linkRawCable; } + private: + LinkRawCable* linkRawCable = new LinkRawCable(); + enum PartialResult { NEEDS_RETRY, FINISHED, ABORTED, ERROR }; struct Responses { @@ -123,17 +124,18 @@ class LinkCableMultiboot { template PartialResult detectClients(MultiBootParam& multiBootParameters, F cancel) { - setMultiPlayMode(); + linkRawCable->activate(LINK_CABLE_MULTIBOOT_MAX_BAUD_RATE); for (u32 t = 0; t < LINK_CABLE_MULTIBOOT_DETECTION_TRIES; t++) { - auto responses = exchange(LINK_CABLE_MULTIBOOT_HANDSHAKE, cancel); + auto response = + linkRawCable->transfer(LINK_CABLE_MULTIBOOT_HANDSHAKE, cancel); if (cancel()) return ABORTED; for (u32 i = 0; i < LINK_CABLE_MULTIBOOT_CLIENTS; i++) { - if ((responses.d[i] & 0xfff0) == + if ((response.data[1 + i] & 0xfff0) == LINK_CABLE_MULTIBOOT_HANDSHAKE_RESPONSE) { - auto clientId = responses.d[i] & 0xf; + auto clientId = response.data[1 + i] & 0xf; switch (clientId) { case 0b0010: @@ -150,7 +152,7 @@ class LinkCableMultiboot { } if (multiBootParameters.client_bit == 0) { - setGeneralPurposeMode(); + linkRawCable->deactivate(); wait(LINK_CABLE_MULTIBOOT_WAIT_BEFORE_RETRY); return NEEDS_RETRY; } @@ -183,7 +185,7 @@ class LinkCableMultiboot { u16* headerOut = (u16*)rom; for (int i = 0; i < LINK_CABLE_MULTIBOOT_HEADER_SIZE; i += 2) { - exchange(*(headerOut++), cancel); + linkRawCable->transfer(*(headerOut++), cancel); if (cancel()) return ABORTED; } @@ -196,13 +198,13 @@ class LinkCableMultiboot { auto data = LINK_CABLE_MULTIBOOT_SEND_PALETTE | LINK_CABLE_MULTIBOOT_PALETTE_DATA; - auto responses = exchange(data, cancel); + auto response = linkRawCable->transfer(data, cancel); if (cancel()) return ABORTED; for (u32 i = 0; i < LINK_CABLE_MULTIBOOT_CLIENTS; i++) { - if (responses.d[i] >> 8 == LINK_CABLE_MULTIBOOT_ACK_RESPONSE) - multiBootParameters.client_data[i] = responses.d[i] & 0xff; + if (response.data[1 + i] >> 8 == LINK_CABLE_MULTIBOOT_ACK_RESPONSE) + multiBootParameters.client_data[i] = response.data[1 + i] & 0xff; } for (u32 i = 0; i < LINK_CABLE_MULTIBOOT_CLIENTS; i++) { @@ -222,12 +224,13 @@ class LinkCableMultiboot { F cancel) { u16 data = LINK_CABLE_MULTIBOOT_CONFIRM_HANDSHAKE_DATA | multiBootParameters.handshake_data; - auto responses = exchange(data, cancel); + auto response = linkRawCable->transfer(data, cancel); if (cancel()) return ABORTED; - return (responses.d[0] >> 8) == LINK_CABLE_MULTIBOOT_ACK_RESPONSE ? FINISHED - : ERROR; + return (response.data[1] >> 8) == LINK_CABLE_MULTIBOOT_ACK_RESPONSE + ? FINISHED + : ERROR; } template @@ -235,7 +238,7 @@ class LinkCableMultiboot { u16 data, u16 expectedResponse, F cancel) { - auto responses = exchange(data, cancel); + auto response = linkRawCable->transfer(data, cancel); if (cancel()) return ABORTED; @@ -244,7 +247,7 @@ class LinkCableMultiboot { u16 expectedResponseWithId = expectedResponse | clientId; bool isClientConnected = multiBootParameters.client_bit & clientId; - if (isClientConnected && responses.d[i] != expectedResponseWithId) + if (isClientConnected && response.data[1 + i] != expectedResponseWithId) return ERROR; } @@ -252,49 +255,10 @@ class LinkCableMultiboot { } Result error(Result error) { - setGeneralPurposeMode(); + linkRawCable->deactivate(); return error; } - template - Responses exchange(u16 data, F cancel) { - Responses responses; - responses.d[0] = 0xffff; - responses.d[1] = 0xffff; - responses.d[2] = 0xffff; - - wait(LINK_CABLE_MULTIBOOT_WAIT_BEFORE_TRANSFER); - - while (isBitHigh(LINK_CABLE_MULTIBOOT_BIT_START)) - if (cancel()) - return responses; - - REG_SIOMLT_SEND = data; - setBitHigh(LINK_CABLE_MULTIBOOT_BIT_START); - - while (isBitHigh(LINK_CABLE_MULTIBOOT_BIT_START)) - if (cancel()) - return responses; - - for (u32 i = 0; i < 3; i++) - responses.d[i] = REG_SIOMULTI[1 + i]; - - return responses; - } - - void setMultiPlayMode() { - REG_RCNT = REG_RCNT & ~(1 << LINK_CABLE_MULTIBOOT_BIT_GENERAL_PURPOSE_HIGH); - REG_SIOCNT = (1 << LINK_CABLE_MULTIBOOT_BIT_MULTIPLAYER); - REG_SIOCNT |= LINK_CABLE_MULTIBOOT_SIOCNT_MAX_BAUD_RATE; - REG_SIOMLT_SEND = 0; - } - - void setGeneralPurposeMode() { - REG_RCNT = - (REG_RCNT & ~(1 << LINK_CABLE_MULTIBOOT_BIT_GENERAL_PURPOSE_LOW)) | - (1 << LINK_CABLE_MULTIBOOT_BIT_GENERAL_PURPOSE_HIGH); - } - void wait(u32 verticalLines) { u32 count = 0; u32 vCount = REG_VCOUNT; @@ -306,10 +270,6 @@ class LinkCableMultiboot { } }; } - - bool isBitHigh(u8 bit) { return (REG_SIOCNT >> bit) & 1; } - void setBitHigh(u8 bit) { REG_SIOCNT |= 1 << bit; } - void setBitLow(u8 bit) { REG_SIOCNT &= ~(1 << bit); } }; extern LinkCableMultiboot* linkCableMultiboot; From 4d03d0c729583b6b0284656cc8e1640db1d29ecb Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 23:17:02 -0300 Subject: [PATCH 67/74] Adjusting LinkCableMultiboot rom length --- examples/LinkCableMultiboot_demo/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/LinkCableMultiboot_demo/src/main.cpp b/examples/LinkCableMultiboot_demo/src/main.cpp index 1ef1fa3..28313f7 100644 --- a/examples/LinkCableMultiboot_demo/src/main.cpp +++ b/examples/LinkCableMultiboot_demo/src/main.cpp @@ -31,7 +31,7 @@ int main() { // Hardcoded ROM length // This is optional, you could also use `LINK_CABLE_MULTIBOOT_MAX_ROM_SIZE` // (but the transfer will be painfully slow) - u32 romSize = 64880; + u32 romSize = 39264; // Note that this project's Makefile pads the ROM to a 0x10 boundary // (as required for Multiboot). From 70d914dae3ac452de9470739f7911efcfffb5698 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Sun, 4 Feb 2024 23:33:23 -0300 Subject: [PATCH 68/74] Adding optional 8-bit support to LinkSPI --- README.md | 2 +- lib/LinkSPI.hpp | 52 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fe476c1..e940211 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ Name | Return type | Description *(aka Normal Mode)* -This is the GBA's implementation of SPI. In this library, packets are set to 32-bit, as there's no benefit to using the 8-bit version. You can use this to interact with other GBAs or computers that know SPI. +This is the GBA's implementation of SPI. You can use this to interact with other GBAs or computers that know SPI. By default, it uses 32-bit packets, but you can switch to 8-bit by enabling the compile-time constant `LINK_SPI_8BIT_MODE`. ![screenshot](https://user-images.githubusercontent.com/1631752/213068614-875049f6-bb01-41b6-9e30-98c73cc69b25.png) diff --git a/lib/LinkSPI.hpp b/lib/LinkSPI.hpp index 2dfb381..396c5c2 100644 --- a/lib/LinkSPI.hpp +++ b/lib/LinkSPI.hpp @@ -42,7 +42,30 @@ #include +// 8-bit mode (uncomment to enable) +// #define LINK_SPI_8BIT_MODE + +#ifdef LINK_SPI_8BIT_MODE +#define LINK_SPI_DATA_TYPE u8 +#endif +#ifndef LINK_SPI_8BIT_MODE +#define LINK_SPI_DATA_TYPE u32 +#endif + +#ifdef LINK_SPI_8BIT_MODE +#define LINK_SPI_DATA_REG REG_SIODATA8 +#endif +#ifndef LINK_SPI_8BIT_MODE +#define LINK_SPI_DATA_REG REG_SIODATA32 +#endif + +#ifdef LINK_SPI_8BIT_MODE +#define LINK_SPI_NO_DATA 0xff +#endif +#ifndef LINK_SPI_8BIT_MODE #define LINK_SPI_NO_DATA 0xffffffff +#endif + #define LINK_SPI_BIT_CLOCK 0 #define LINK_SPI_BIT_CLOCK_SPEED 1 #define LINK_SPI_BIT_SI 2 @@ -98,15 +121,15 @@ class LinkSPI { asyncData = 0; } - u32 transfer(u32 data) { + LINK_SPI_DATA_TYPE transfer(LINK_SPI_DATA_TYPE data) { return transfer(data, []() { return false; }); } template - u32 transfer(u32 data, - F cancel, - bool _async = false, - bool _customAck = false) { + LINK_SPI_DATA_TYPE transfer(LINK_SPI_DATA_TYPE data, + F cancel, + bool _async = false, + bool _customAck = false) { if (asyncState != IDLE) return LINK_SPI_NO_DATA; @@ -160,21 +183,21 @@ class LinkSPI { return getData(); } - void transferAsync(u32 data) { + void transferAsync(LINK_SPI_DATA_TYPE data) { transfer( data, []() { return false; }, true); } template - void transferAsync(u32 data, F cancel) { + void transferAsync(LINK_SPI_DATA_TYPE data, F cancel) { transfer(data, cancel, true); } - u32 getAsyncData() { + LINK_SPI_DATA_TYPE getAsyncData() { if (asyncState != READY) return LINK_SPI_NO_DATA; - u32 data = asyncData; + LINK_SPI_DATA_TYPE data = asyncData; asyncState = IDLE; return data; } @@ -204,12 +227,17 @@ class LinkSPI { Mode mode = Mode::SLAVE; bool waitMode = false; AsyncState asyncState = IDLE; - u32 asyncData = 0; + LINK_SPI_DATA_TYPE asyncData = 0; volatile bool isEnabled = false; void setNormalMode32Bit() { REG_RCNT = REG_RCNT & ~(1 << LINK_SPI_BIT_GENERAL_PURPOSE_HIGH); +#ifdef LINK_SPI_8BIT_MODE + REG_SIOCNT = 0; +#endif +#ifndef LINK_SPI_8BIT_MODE REG_SIOCNT = 1 << LINK_SPI_BIT_LENGTH; +#endif } void setGeneralPurposeMode() { @@ -217,8 +245,8 @@ class LinkSPI { (1 << LINK_SPI_BIT_GENERAL_PURPOSE_HIGH); } - void setData(u32 data) { REG_SIODATA32 = data; } - u32 getData() { return REG_SIODATA32; } + void setData(LINK_SPI_DATA_TYPE data) { LINK_SPI_DATA_REG = data; } + LINK_SPI_DATA_TYPE getData() { return LINK_SPI_DATA_REG; } void enableTransfer() { _setSOLow(); } void disableTransfer() { _setSOHigh(); } From 67afa0ca3cfa638f2a76e1ea826278714824653f Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 5 Feb 2024 00:26:17 -0300 Subject: [PATCH 69/74] Adding Wireless Multiboot documentation --- docs/wireless_adapter.md | 143 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 2 deletions(-) diff --git a/docs/wireless_adapter.md b/docs/wireless_adapter.md index a1d8be8..e1aa7b6 100644 --- a/docs/wireless_adapter.md +++ b/docs/wireless_adapter.md @@ -4,6 +4,8 @@ Game Boy Advance Wireless Adapter - 🌎 **Original post**: https://blog.kuiper.dev/gba-wireless-adapter 🌎 - ✏️ **Updates**: [@davidgfnet](https://github.com/davidgfnet) and I were discovering new things and we added them here! +> You can learn more details by reading [LinkRawWireless.hpp](../lib/LinkRawWireless.hpp)'s code. + The Wireless Adapter ==================== @@ -251,7 +253,7 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg (if you read from right to left, it says `ICE CLIMBER` - `NINTENDO`) -πŸ†” The **Game ID** is what games use to avoid listing servers from another game. This is done on the software layer (GBA), the adapter does not enforce this in any way, nor does gba-link-connection. +πŸ†” The **Game ID** is what games use to avoid listing servers from another game. This is done on the software layer (GBA), the adapter does not enforce this in any way, nor does gba-link-connection (unless `LINK_UNIVERSAL_GAME_ID_FILTER` is set). πŸ”₯ This command can be called to update the broadcast data even when the server has already started using `StartHost`. Some games include metadata in the game/user name fields, such as the player's gender or a busy flag. @@ -378,7 +380,7 @@ Both Pokemon games and the multiboot ROM that the adapter sends when no cartridg - **Host**: `ReceiveData` - Receives `{rcvHeader}`, 20 -πŸ” This command can also be used with one header and **no data**. In this case, it will resend the last N bytes (based on the header) of the last packet. +πŸ” This command can also be used with one header and **no data**. In this case, it will resend the last N bytes (based on the header) of the last packet. Until we have a better name, we'll call this **ghost sends**. #### SendDataWait - `0x25` @@ -554,6 +556,143 @@ Then, when the adapter issues commands to the GBA, the acknowledge procedure is 5. The adapter goes low when it's ready. 6. The adapter starts a transfer, clock starts pulsing, and both sides exchange the next 32 bit value. +Wireless Multiboot +------------------ + +> You can learn more details by reading [LinkWirelessMultiboot.hpp](../lib/LinkWirelessMultiboot.hpp)'s code. + +To host a 'multiboot' room, a host sets the **multiboot flag** (bit 15) in its game ID (inside broadcast data) and starts serving. + +- 1) For each new client that connects, it runs a small handshake where the client sends their 'game name' and 'player name'. The bootloader always sends `RFU-MB-DL` as game name and `PLAYER A` (or `B`, `C`, `D`) as player name. + +- 2) When the host player confirms that all players are ready, it sends a 'rom start' command. + +- 3) The host sends the rom bytes in 84-byte chunks. + +- 4) The host sends a 'rom end' command and the games boot. + +### Valid header + +The bootloader will only accept ROMs with valid headers: they must contain this in its bytes `4-15`: + +`0x52, 0x46, 0x55, 0x2d, 0x4d, 0x42, 0x4f, 0x4f, 0x54, 0x00, 0x00, 0x00` + +(this represents the string `RFU-MB-DL` and zeros) + +### Custom protocol + +> You can learn more details by reading [LinkWirelessOpenSDK.hpp](../lib/LinkWirelessOpenSDK.hpp)'s code. + +All this communication uses a custom software-layer protocol made by Nintendo, the same one used by first-party games. + +Server buffers use a 3-byte header: + +```c++ +struct ServerSDKHeader { + unsigned int payloadSize : 7; + unsigned int _unused_ : 2; + unsigned int phase : 2; + unsigned int n : 2; + unsigned int isACK : 1; + CommState commState : 4; + unsigned int targetSlots : 4; +} +``` + +Clients use a 2-byte header: + +```c++ +struct ClientSDKHeader { + unsigned int payloadSize : 5; + unsigned int phase : 2; + unsigned int n : 2; + unsigned int isACK : 1; + CommState commState : 4; +} +``` + +...and `CommState` is: + +```c++ +enum CommState : unsigned int { + OFF = 0, + STARTING = 1, + COMMUNICATING = 2, + ENDING = 3, + DIRECT = 4 +}; +``` + +- All transfers have sequence numbers (`n` and `phase`) unless `commState` is `DIRECT` (a sort of UDP). +- There's a short initialization ritual until reaching the `COMMUNICATING` state. +- Once the `COMMUNICATING` state is reached, the initial sequence is `n=1, phase=0`. +- After each packet, the other node responds with a packet containing the same `n`, `phase` and `commState`, but with the `isACK` bit set. +- The sequence continues: `n=1,ph=1` | `n=1,ph=2` | `n=1,ph=3` | `n=2,ph=0` | `n=2,ph=1` | `n=2,ph=2` | `n=2,ph=3` | `n=3,ph=0` | `n=3,ph=1` | `n=3,ph=2` | `n=3,ph=3` | `n=0,ph=0` | `n=0,ph=1` | `n=0,ph=2` | `n=0,ph=3` | `n=1,ph=0` | `n=0,ph=1` | etc. +- Repeated or old sequence numbers are ignored, that's how they handle retransmission. +- Transfers can contain more than one packet. +- As the maximum transfer lengths are `87` (server) and `16` (client), based on header sizes, the maximum payload lengths are `84` and `14`. +- The `targetSlots` field inside the server header is a bit array that indicates which clients the message is directed to. E.g. `0b0100` means 'client 2 only' and `0b1111` means 'all clients'. + +### (1) Client handshake + +- Server: repeatedly performs _ghost sends_ (see [SendData](#senddata---0x24)) until the client talks. +- Client: sends `0x06010486`, `0x00001A00`. + - Header: `0x0486` (`size=6, n=1, ph=0, ack=0, commState=1`) (`1 = STARTING`) + - Payload: `0x01`, `0x06`, `0x00`, `0x1A`, `0x00`, `0x00` +- Server: ACKs the packet (`size=0, n=1, ph=0, ack=1, commState=1`) +- Client: sends `0x00000501`. + - Header: `0x0501` (`size=1, n=2, ph=0, ack=0, commState=1`) + - Payload: `0x00` +- Server: ACKs the packet +- Client: sends `0x00000886`, `0x2D554652`. + - Header: `0x0886` (`size=6, n=1, ph=0, ack=0, commState=2`) (`2 = COMMUNICATING`) + - Payload: `0x00`, `0x00`, `0x52`, `0x46`, `0x55`, `0x2D` + - => `RFU-` +- Server: ACKs the packet +- Client: sends `0x424D08A6`, `0x004C442D`. + - Header: `0x08A6` (`size=6, n=1, ph=1, ack=0, commState=2`) + - Payload: `MB-DL` +- Server: ACKs the packet +- Client: sends `0x000008C6`, `0x50000000`. + - Header: `0x08C6` (`size=6, n=1, ph=2, ack=0, commState=2`) + - Payload: `P` +- Server: ACKs the packet +- Client: sends `0x414C08E6`, `0x20524559`. + - Header: `0x08E6` (`size=6, n=1, ph=3, ack=0, commState=2`) + - Payload: `LAYER` +- Server: ACKs the packet +- Client: sends `0x00410902`. + - Header: `0x0902` (`size=2, n=2, ph=0, ack=0, commState=2`) + - Payload: `A` +- Server: ACKs the packet +- Client: sends `0x00000C00`. + - Header: `0x0C00` (`size=0, n=0, ph=0, ack=0, commState=3`) (`3 = ENDING`) + - No payload +- Server: ACKs the packet +- Client: sends `0x00000080`. + - Header: `0x0080` (`size=0, n=1, ph=0, ack=0, commState=0`) (`0 = OFF`) + - No payload + +## (2) ROM start command + +- Server: sends `0x00044807`, `0x00000054`, `0x00000002`. + - Header: `0x044807` (`size=7, n=1, ph=0, ack=0, commState=1`) (`1 = STARTING`) + - Payload: `0x00`, `0x54`, `0x00`, `0x00`, `0x00`, `0x02`, `0x00` +- Client: ACKs the packet (`size=0, n=1, ph=0, ack=1, commState=1`) + +## (3) ROM bytes + +- At this stage, `commState` is already `2` (`COMMUNICATING`) and the ROM bytes are sent in 84-byte chunks. +- The last transfer is also `84` bytes, no matter the ROM size (it's padded with zeros). +- A number (2~4) of 'inflight packets' is allowed to speed up transfers. +- Packets without an ACK are retransmitted. + +## (4) ROM end command + +After all ROM chunks are ACK'd, the last transfers are: + +- `size=0, n=0, ph=0, ack=0, commState=3` (`3 = ENDING`) +- `size=0, n=1, ph=0, ack=0, commState=0` (`0 = OFF`) SPI config ---------- From 2d73fe69d4ef13964de59f791775cbc5a6f670bd Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 5 Feb 2024 00:38:01 -0300 Subject: [PATCH 70/74] Fixing name conflict with newer devkitPro --- examples/LinkCable_full/src/main.cpp | 16 ++-- examples/LinkCable_full/src/main.h | 4 +- .../LinkCable_full/src/scenes/TestScene.cpp | 24 +++--- examples/LinkCable_stress/src/main.cpp | 73 ++++++++++--------- examples/LinkCable_stress/src/main.h | 4 +- 5 files changed, 62 insertions(+), 59 deletions(-) diff --git a/examples/LinkCable_full/src/main.cpp b/examples/LinkCable_full/src/main.cpp index be4d5ef..ffb2b58 100644 --- a/examples/LinkCable_full/src/main.cpp +++ b/examples/LinkCable_full/src/main.cpp @@ -14,11 +14,11 @@ static std::unique_ptr testScene{new TestScene(engine)}; #ifndef USE_LINK_UNIVERSAL LinkCable* linkCable = new LinkCable(); -LinkCable* link = linkCable; +LinkCable* linkConnection = linkCable; #endif #ifdef USE_LINK_UNIVERSAL LinkUniversal* linkUniversal = new LinkUniversal(); -LinkUniversal* link = linkUniversal; +LinkUniversal* linkConnection = linkUniversal; #endif int main() { @@ -32,19 +32,19 @@ int main() { u16 keys = ~REG_KEYS & KEY_ANY; // enable and disable - if ((keys & KEY_DOWN) && link->isActive()) { - link->deactivate(); + if ((keys & KEY_DOWN) && linkConnection->isActive()) { + linkConnection->deactivate(); DEBULOG("! stopped"); } - if ((keys & KEY_START) && !link->isActive()) { - link->activate(); + if ((keys & KEY_START) && !linkConnection->isActive()) { + linkConnection->activate(); DEBULOG("! started"); } // log player id/count and important flags TextStream::instance().setText( - "P" + asStr(link->currentPlayerId()) + "/" + - asStr(link->playerCount()) + "-R" + + "P" + asStr(linkConnection->currentPlayerId()) + "/" + + asStr(linkConnection->playerCount()) + "-R" + asStr(isBitHigh(REG_SIOCNT, LINK_CABLE_BIT_READY)) + "-S" + asStr(isBitHigh(REG_SIOCNT, LINK_CABLE_BIT_START)) + "-E" + asStr(isBitHigh(REG_SIOCNT, LINK_CABLE_BIT_ERROR)), diff --git a/examples/LinkCable_full/src/main.h b/examples/LinkCable_full/src/main.h index 0f685d0..b7001b1 100644 --- a/examples/LinkCable_full/src/main.h +++ b/examples/LinkCable_full/src/main.h @@ -8,10 +8,10 @@ // #define USE_LINK_UNIVERSAL #ifndef USE_LINK_UNIVERSAL -extern LinkCable* link; +extern LinkCable* linkConnection; #endif #ifdef USE_LINK_UNIVERSAL -extern LinkUniversal* link; +extern LinkUniversal* linkConnection; #endif #endif // MAIN_H diff --git a/examples/LinkCable_full/src/scenes/TestScene.cpp b/examples/LinkCable_full/src/scenes/TestScene.cpp index 37ff10b..dcf73b5 100644 --- a/examples/LinkCable_full/src/scenes/TestScene.cpp +++ b/examples/LinkCable_full/src/scenes/TestScene.cpp @@ -21,7 +21,7 @@ static std::unique_ptr selectHandler = inline void send(u16 data) { DEBULOG("-> " + asStr(data)); - link->send(data); + linkConnection->send(data); } std::vector TestScene::backgrounds() { @@ -43,7 +43,7 @@ void TestScene::tick(u16 keys) { return; // sync - link->sync(); + linkConnection->sync(); frameCounter++; @@ -55,12 +55,13 @@ void TestScene::tick(u16 keys) { selectHandler->setIsPressed(keys & KEY_SELECT); // log events - if (!isConnected && link->isConnected()) { + if (!isConnected && linkConnection->isConnected()) { isConnected = true; initialized = false; - DEBULOG("! connected (" + asStr(link->playerCount()) + " players)"); + DEBULOG("! connected (" + asStr(linkConnection->playerCount()) + + " players)"); } - if (isConnected && !link->isConnected()) { + if (isConnected && !linkConnection->isConnected()) { isConnected = false; DEBULOG("! disconnected"); } @@ -71,7 +72,8 @@ void TestScene::tick(u16 keys) { // determine which value should be sent u16 value = LINK_CABLE_NO_DATA; - if (!initialized && link->isConnected() && link->currentPlayerId() == 1) { + if (!initialized && linkConnection->isConnected() && + linkConnection->currentPlayerId() == 1) { initialized = true; value = 999; } @@ -91,11 +93,11 @@ void TestScene::tick(u16 keys) { } // process received data - if (link->isConnected()) { - for (u32 i = 0; i < link->playerCount(); i++) { - while (link->canRead(i)) { - u16 message = link->read(i); - if (i != link->currentPlayerId()) + if (linkConnection->isConnected()) { + for (u32 i = 0; i < linkConnection->playerCount(); i++) { + while (linkConnection->canRead(i)) { + u16 message = linkConnection->read(i); + if (i != linkConnection->currentPlayerId()) DEBULOG("<-p" + asStr(i) + ": " + asStr(message) + " (frame " + asStr(frameCounter) + ")"); } diff --git a/examples/LinkCable_stress/src/main.cpp b/examples/LinkCable_stress/src/main.cpp index 0af26d6..18bd24c 100644 --- a/examples/LinkCable_stress/src/main.cpp +++ b/examples/LinkCable_stress/src/main.cpp @@ -33,7 +33,7 @@ u32 toMs(u32 cycles); #ifndef USE_LINK_UNIVERSAL LinkCable* linkCable = new LinkCable(); -LinkCable* link = linkCable; +LinkCable* linkConnection = linkCable; #endif #ifdef USE_LINK_UNIVERSAL LinkUniversal* linkUniversal = @@ -53,7 +53,7 @@ LinkUniversal* linkUniversal = .interval = LINK_WIRELESS_DEFAULT_INTERVAL, .sendTimerId = LINK_WIRELESS_DEFAULT_SEND_TIMER_ID, .asyncACKTimerId = 0}); -LinkUniversal* link = linkUniversal; +LinkUniversal* linkConnection = linkUniversal; #endif void init() { @@ -95,7 +95,7 @@ int main() { std::string output = "LinkUniversal_stress (v6.2.0)\n\n"; #endif - link->deactivate(); + linkConnection->deactivate(); output += "A: Test packet loss\nB: Test packet sync\nL: Measure ping latency\nR: " @@ -116,14 +116,14 @@ int main() { if (initialKeys & KEY_UP) interval = 10; #ifndef USE_LINK_UNIVERSAL - link->config.interval = interval; + linkConnection->config.interval = interval; #endif #ifdef USE_LINK_UNIVERSAL - link->linkCable->config.interval = interval; - link->linkWireless->config.interval = interval; + linkConnection->linkCable->config.interval = interval; + linkConnection->linkWireless->config.interval = interval; #endif - link->activate(); + linkConnection->activate(); if (initialKeys & KEY_A) test(false); @@ -150,37 +150,37 @@ void test(bool withSync) { if (needsReset()) return; - link->sync(); - auto playerCount = link->playerCount(); + linkConnection->sync(); + auto playerCount = linkConnection->playerCount(); std::string output = ""; - if (link->isConnected() && playerCount == 2) { + if (linkConnection->isConnected() && playerCount == 2) { u16 keys = ~REG_KEYS & KEY_ANY; if (keys & KEY_START) { log("Lagging..."); wait(1500); } - auto currentPlayerId = link->currentPlayerId(); + auto currentPlayerId = linkConnection->currentPlayerId(); auto remotePlayerId = !currentPlayerId; if (localCounter < FINAL_VALUE) { localCounter++; - link->send(localCounter); + linkConnection->send(localCounter); } if (localCounter == 1 || withSync) { - while (link->peek(remotePlayerId) != localCounter) { - if (!link->waitFor(remotePlayerId, needsReset)) + while (linkConnection->peek(remotePlayerId) != localCounter) { + if (!linkConnection->waitFor(remotePlayerId, needsReset)) return; } } - while (link->canRead(remotePlayerId) && + while (linkConnection->canRead(remotePlayerId) && (!withSync || expectedCounter + 1 == localCounter)) { expectedCounter++; - u16 message = link->read(remotePlayerId); + u16 message = linkConnection->read(remotePlayerId); if (message != expectedCounter) { error = true; @@ -196,10 +196,11 @@ void test(bool withSync) { if (error) { output += "ERROR!\nExpected " + std::to_string(expectedCounter) + " but got " + std::to_string(receivedRemoteCounter) + "\n\n"; - if (link->canRead(remotePlayerId)) { + if (linkConnection->canRead(remotePlayerId)) { output += "Remaining packets: |"; - while (link->canRead(remotePlayerId)) - output += std::to_string(link->read(remotePlayerId)) + "| "; + while (linkConnection->canRead(remotePlayerId)) + output += + std::to_string(linkConnection->read(remotePlayerId)) + "| "; output += "\n\n"; } } @@ -241,15 +242,15 @@ void measureLatency(bool withPong) { if (needsReset()) return; - link->sync(); - auto playerCount = link->playerCount(); + linkConnection->sync(); + auto playerCount = linkConnection->playerCount(); - if (link->isConnected() && playerCount == 2) { - auto currentPlayerId = link->currentPlayerId(); + if (linkConnection->isConnected() && playerCount == 2) { + auto currentPlayerId = linkConnection->currentPlayerId(); auto remotePlayerId = !currentPlayerId; if (!didInitialize) { - counter = 11 + link->currentPlayerId() * 10; + counter = 11 + linkConnection->currentPlayerId() * 10; didInitialize = true; } @@ -258,23 +259,23 @@ void measureLatency(bool withPong) { u32 sentPacket = ++counter; profileStart(); - link->send(sentPacket); - if (!link->waitFor(remotePlayerId, needsReset)) { + linkConnection->send(sentPacket); + if (!linkConnection->waitFor(remotePlayerId, needsReset)) { log("No response! (1) Press DOWN"); profileStop(); waitFor(KEY_DOWN); return; } - u16 receivedPacket = link->read(remotePlayerId); + u16 receivedPacket = linkConnection->read(remotePlayerId); if (withPong) { - link->send(receivedPacket); - if (!link->waitFor(remotePlayerId, needsReset)) { + linkConnection->send(receivedPacket); + if (!linkConnection->waitFor(remotePlayerId, needsReset)) { log("No response! (2) Press DOWN"); profileStop(); waitFor(KEY_DOWN); return; } - u16 validation = link->read(remotePlayerId); + u16 validation = linkConnection->read(remotePlayerId); if (validation != sentPacket) { log("Invalid response! Press DOWN\n value = " + std::to_string(validation) + @@ -304,13 +305,13 @@ void measureLatency(bool withPong) { } void forceSync() { - auto remotePlayerId = !link->currentPlayerId(); + auto remotePlayerId = !linkConnection->currentPlayerId(); - link->send(10); - while (link->isConnected() && !needsReset() && - link->peek(remotePlayerId) != 10) - link->waitFor(remotePlayerId); - link->read(remotePlayerId); + linkConnection->send(10); + while (linkConnection->isConnected() && !needsReset() && + linkConnection->peek(remotePlayerId) != 10) + linkConnection->waitFor(remotePlayerId); + linkConnection->read(remotePlayerId); } void log(std::string text) { diff --git a/examples/LinkCable_stress/src/main.h b/examples/LinkCable_stress/src/main.h index 0f685d0..b7001b1 100644 --- a/examples/LinkCable_stress/src/main.h +++ b/examples/LinkCable_stress/src/main.h @@ -8,10 +8,10 @@ // #define USE_LINK_UNIVERSAL #ifndef USE_LINK_UNIVERSAL -extern LinkCable* link; +extern LinkCable* linkConnection; #endif #ifdef USE_LINK_UNIVERSAL -extern LinkUniversal* link; +extern LinkUniversal* linkConnection; #endif #endif // MAIN_H From cb0bec9d79c96fec1d702940423bc4bf954c9fa5 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 5 Feb 2024 00:45:09 -0300 Subject: [PATCH 71/74] Fixing build script --- examples/compile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/compile.sh b/examples/compile.sh index 465fd69..4f76579 100644 --- a/examples/compile.sh +++ b/examples/compile.sh @@ -93,5 +93,5 @@ cd LinkWirelessMultiboot_demo/ sed -i -e "s/\/\/ #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/#define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/g" ../../lib/LinkWirelessMultiboot.hpp make rebuild cp LinkWirelessMultiboot_demo.out.gba ../LinkWirelessMultiboot_demo.gba -sed -i -e "s/\/\/ #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/#define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/g" ../../lib/LinkWirelessMultiboot.hpp +sed -i -e "s/#define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/\/\/ #define LINK_WIRELESS_MULTIBOOT_ENABLE_LOGGING/g" ../../lib/LinkWirelessMultiboot.hpp cd .. From 400f60f45700d181b7359c206cc1cb8aa4a276b2 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 5 Feb 2024 01:47:49 -0300 Subject: [PATCH 72/74] FIX: Read broadcasts poll in LinkRawWireless returning no rooms --- examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp | 5 ++++- lib/LinkRawWireless.hpp | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp index 15b85c0..19ef1b8 100644 --- a/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp +++ b/examples/LinkRawWireless_demo/src/scenes/DebugScene.cpp @@ -618,7 +618,10 @@ void DebugScene::processCommand(u32 selectedCommandIndex) { linkRawWireless->toHex(servers[i].nextClientNumber, 2)); } - log("NOW CALL 0x1e!"); + if (response.serversSize > 0) + log("NOW CALL 0x1e!"); + else + log("No rooms? NOW CALL 0x1e!"); } return success; diff --git a/lib/LinkRawWireless.hpp b/lib/LinkRawWireless.hpp index faccc56..e170911 100644 --- a/lib/LinkRawWireless.hpp +++ b/lib/LinkRawWireless.hpp @@ -318,7 +318,7 @@ class LinkRawWireless { return true; } - bool broadcastReadPoll(BroadcastReadPollResponse response) { + bool broadcastReadPoll(BroadcastReadPollResponse& response) { auto result = sendCommand(LINK_RAW_WIRELESS_COMMAND_BROADCAST_READ_POLL); bool success = result.success && @@ -354,9 +354,6 @@ class LinkRawWireless { response.servers[response.serversSize++] = server; } - LRWLOG("state = AUTHENTICATED"); - state = AUTHENTICATED; - return true; } @@ -369,6 +366,9 @@ class LinkRawWireless { return false; } + LRWLOG("state = AUTHENTICATED"); + state = AUTHENTICATED; + return true; } From 7ce1e9bfd23893b4a4c2895b24f39ef49115c965 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 5 Feb 2024 02:05:04 -0300 Subject: [PATCH 73/74] Making 0x28 docs more clear --- docs/wireless_adapter.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/wireless_adapter.md b/docs/wireless_adapter.md index e1aa7b6..8e0605f 100644 --- a/docs/wireless_adapter.md +++ b/docs/wireless_adapter.md @@ -525,12 +525,14 @@ Waiting βœ… When there's new data available, the adapter sends to the GBA a `0x99660028`. -⚠️ If some children didn't receive the data, the adapter sends to the GBA a `0x99660128`. +πŸ’¨ Clients receive the `0x28` when new data from the host is available, but the host receives it immediately (well, after the transfer completes), as it can be used to know which clients received data or are disconnected. + +⚠️ If some children didn't receive the data, the adapter sends to the host GBA a `0x99660128`. - The extra parameter has two bitarrays: * Bits `0-4`: The clients that _received_ data. * Bits `8-11`: The clients marked as _inactive_. This depends on the # of maximum transmissions configured with the [Setup](#setup---0x17) command. -πŸ”— When the adapter is disconnected from the parent, it sends a `0x99660029`. +πŸ”— When the adapter is disconnected from the host, it sends a `0x99660029`. - Bit 8 of the response indicates the reason: * `0` = manual disconnect (aka the host used [DisconnectClient](#disconnectclient---0x30)) * `1` = the connection was lost From 1262af953dc63be7d248c8e4c9b7a811c1ca7d06 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 5 Feb 2024 02:42:24 -0300 Subject: [PATCH 74/74] Updating screenshots --- README.md | 4 ++-- docs/wireless_adapter.md | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e940211..2313a88 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Name | Return type | Description This tool allows sending Multiboot ROMs (small 256KiB programs that fit in EWRAM) from one GBA to up to 3 slaves, using a single cartridge. -![photo](https://user-images.githubusercontent.com/1631752/213667130-fafcbdb1-767f-4f74-98cb-d7e36c4d7e4e.jpg) +![screenshot](https://github.com/afska/gba-link-connection/assets/1631752/6ff55944-5437-436f-bcc7-a89b05dc5486) ## Methods @@ -272,7 +272,7 @@ Name | Return type | Description This is the default Link Port mode, and it allows users to manipulate pins `SI`, `SO`, `SD` and `SC` directly. -![photo](https://user-images.githubusercontent.com/1631752/212867547-e0a795aa-da00-4b2c-8640-8db7ea857e19.jpg) +![photo](https://github.com/afska/gba-link-connection/assets/1631752/b53ddc3d-46c5-441b-9036-489150d9de9f) ## Methods diff --git a/docs/wireless_adapter.md b/docs/wireless_adapter.md index 8e0605f..bff0ea8 100644 --- a/docs/wireless_adapter.md +++ b/docs/wireless_adapter.md @@ -637,47 +637,47 @@ enum CommState : unsigned int { ### (1) Client handshake -- Server: repeatedly performs _ghost sends_ (see [SendData](#senddata---0x24)) until the client talks. -- Client: sends `0x06010486`, `0x00001A00`. +- Server: repeatedly performs _ghost sends_ (see [SendData](#senddata---0x24)) until the client talks +- Client: sends `0x06010486`, `0x00001A00` - Header: `0x0486` (`size=6, n=1, ph=0, ack=0, commState=1`) (`1 = STARTING`) - Payload: `0x01`, `0x06`, `0x00`, `0x1A`, `0x00`, `0x00` - Server: ACKs the packet (`size=0, n=1, ph=0, ack=1, commState=1`) -- Client: sends `0x00000501`. +- Client: sends `0x00000501` - Header: `0x0501` (`size=1, n=2, ph=0, ack=0, commState=1`) - Payload: `0x00` - Server: ACKs the packet -- Client: sends `0x00000886`, `0x2D554652`. +- Client: sends `0x00000886`, `0x2D554652` - Header: `0x0886` (`size=6, n=1, ph=0, ack=0, commState=2`) (`2 = COMMUNICATING`) - Payload: `0x00`, `0x00`, `0x52`, `0x46`, `0x55`, `0x2D` - => `RFU-` - Server: ACKs the packet -- Client: sends `0x424D08A6`, `0x004C442D`. +- Client: sends `0x424D08A6`, `0x004C442D` - Header: `0x08A6` (`size=6, n=1, ph=1, ack=0, commState=2`) - Payload: `MB-DL` - Server: ACKs the packet -- Client: sends `0x000008C6`, `0x50000000`. +- Client: sends `0x000008C6`, `0x50000000` - Header: `0x08C6` (`size=6, n=1, ph=2, ack=0, commState=2`) - Payload: `P` - Server: ACKs the packet -- Client: sends `0x414C08E6`, `0x20524559`. +- Client: sends `0x414C08E6`, `0x20524559` - Header: `0x08E6` (`size=6, n=1, ph=3, ack=0, commState=2`) - Payload: `LAYER` - Server: ACKs the packet -- Client: sends `0x00410902`. +- Client: sends `0x00410902` - Header: `0x0902` (`size=2, n=2, ph=0, ack=0, commState=2`) - Payload: `A` - Server: ACKs the packet -- Client: sends `0x00000C00`. +- Client: sends `0x00000C00` - Header: `0x0C00` (`size=0, n=0, ph=0, ack=0, commState=3`) (`3 = ENDING`) - No payload - Server: ACKs the packet -- Client: sends `0x00000080`. +- Client: sends `0x00000080` - Header: `0x0080` (`size=0, n=1, ph=0, ack=0, commState=0`) (`0 = OFF`) - No payload ## (2) ROM start command -- Server: sends `0x00044807`, `0x00000054`, `0x00000002`. +- Server: sends `0x00044807`, `0x00000054`, `0x00000002` - Header: `0x044807` (`size=7, n=1, ph=0, ack=0, commState=1`) (`1 = STARTING`) - Payload: `0x00`, `0x54`, `0x00`, `0x00`, `0x00`, `0x02`, `0x00` - Client: ACKs the packet (`size=0, n=1, ph=0, ack=1, commState=1`)