From 2a998b1edfbc4cfabab393e12ff385870df7450b Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Fri, 26 Jan 2024 07:17:10 -0300 Subject: [PATCH] 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;