From ff22a2dc9fec5b3d519d0ce28521d0d284c46bbf Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 26 Aug 2024 23:29:54 -0300 Subject: [PATCH] Adding LinkWireless::activateAsync(), turnOff parameter to deactivate(), stopping everything on errors --- README.md | 7 +-- lib/LinkWireless.hpp | 84 ++++++++++++++++++++++++++++--- lib/c_bindings/C_LinkWireless.cpp | 8 +++ lib/c_bindings/C_LinkWireless.h | 5 ++ 4 files changed, 94 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a315365..571066c 100644 --- a/README.md +++ b/README.md @@ -229,8 +229,9 @@ You can also change these compile-time constants: Name | Return type | Description --- | --- | --- `isActive()` | **bool** | Returns whether the library is active or not. -`activate()` | **bool** | Activates the library. When an adapter is connected, it changes the state to `AUTHENTICATED`. It can also be used to disconnect or reset the adapter. -`deactivate()` | **bool** | Puts the adapter into a low consumption mode and then deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful. +`activate()` | **bool** | Activates the library. When an adapter is connected, it changes the state to `AUTHENTICATED`. It can also be used to disconnect or reset the adapter. This can block the system for ~1 frame. +`activateAsync()` | **bool** | Activates the library in 3 steps to avoid burning extra cycles for waiting. Call this once per frame! If it returns `false` at any point, the adapter is not connected and you should stop. On success, after the third call, `getState()` should be `AUTHENTICATED`. +`deactivate([turnOff])` | **bool** | Puts the adapter into a low consumption mode and then deactivates the library. It returns a boolean indicating whether the transition to low consumption mode was successful. You can disable the transition and deactivate directly by setting `turnOff` to `true`. `serve([gameName], [userName], [gameId])` | **bool** | Starts broadcasting a server and changes the state to `SERVING`. You can, optionally, provide a `gameName` (max `14` characters), a `userName` (max `8` characters), and a `gameId` *(0 ~ 0x7FFF)* that games will be able to read. The strings must be null-terminated character arrays. If the adapter is already serving, this method only updates the broadcast data. Updating broadcast data while serving can fail if the adapter is busy. In that case, this will return `false` and `getLastError()` will be `BUSY_TRY_AGAIN`. `getServers(servers, [onWait])` | **bool** | Fills the `servers` array with all the currently broadcasting servers. This action takes 1 second to complete, but you can optionally provide an `onWait()` function which will be invoked each time VBlank starts. `getServersAsyncStart()` | **bool** | Starts looking for broadcasting servers and changes the state to `SEARCHING`. After this, call `getServersAsyncEnd(...)` 1 second later. @@ -239,7 +240,7 @@ Name | Return type | Description `keepConnecting()` | **bool** | When connecting, this needs to be called until the state is `CONNECTED`. It assigns a player ID. Keep in mind that `isConnected()` and `playerCount()` won't be updated until the first message from the server arrives. `send(data)` | **bool** | Enqueues `data` to be sent to other nodes. `receive(messages)` | **bool** | Fills the `messages` array with incoming messages, forwarding if needed. -`getState()` | **LinkWireless::State** | Returns the current state (one of `LinkWireless::State::NEEDS_RESET`, `LinkWireless::State::AUTHENTICATED`, `LinkWireless::State::SEARCHING`, `LinkWireless::State::SERVING`, `LinkWireless::State::CONNECTING`, or `LinkWireless::State::CONNECTED`). +`getState()` | **LinkWireless::State** | Returns the current state (one of `LinkWireless::State::NEEDS_RESET`, `LinkWireless::State::WAITING_TO_START`, `LinkWireless::State::STARTING`, `LinkWireless::State::AUTHENTICATED`, `LinkWireless::State::SEARCHING`, `LinkWireless::State::SERVING`, `LinkWireless::State::CONNECTING`, or `LinkWireless::State::CONNECTED`). `isConnected()` | **bool** | Returns `true` if the player count is higher than `1`. `isSessionActive()` | **bool** | Returns `true` if the state is `SERVING` or `CONNECTED`. `playerCount()` | **u8** *(1~5)* | Returns the number of connected players. diff --git a/lib/LinkWireless.hpp b/lib/LinkWireless.hpp index 5e55411..e5b77ce 100644 --- a/lib/LinkWireless.hpp +++ b/lib/LinkWireless.hpp @@ -246,6 +246,8 @@ class LinkWireless { enum State { NEEDS_RESET, + WAITING_TO_START, + STARTING, AUTHENTICATED, SEARCHING, SERVING, @@ -339,6 +341,7 @@ class LinkWireless { * @brief Activates the library. When an adapter is connected, it changes the * state to `AUTHENTICATED`. It can also be used to disconnect or reset the * adapter. + * \warning This can block the system for ~1 frame. */ bool activate() { lastError = NONE; @@ -352,14 +355,72 @@ class LinkWireless { return success; } + /** + * @brief Activates the library in 3 steps to avoid burning extra cycles for + * waiting. Call this once per frame! If it returns `false` at any point, the + * adapter is not connected and you should stop. On success, after the third + * call, `getState()` should be `AUTHENTICATED`. + */ + bool activateAsync() { + switch (state) { + case WAITING_TO_START: { + pingAdapterEnd(); + linkSPI->activate(LinkSPI::Mode::MASTER_256KBPS); + if (!login()) { + stop(); + state = NEEDS_RESET; + return false; + } + state = STARTING; + return true; + } + case STARTING: { + if (!sendCommand(COMMAND_HELLO).success) { + stop(); + state = NEEDS_RESET; + return false; + } + if (!setup()) { + stop(); + state = NEEDS_RESET; + return false; + } + linkSPI->activate(LinkSPI::Mode::MASTER_2MBPS); + state = AUTHENTICATED; + return true; + } + default: { + lastError = NONE; + + LINK_WIRELESS_BARRIER; + isEnabled = false; + LINK_WIRELESS_BARRIER; + + resetState(); + stop(); + + startTimer(); + pingAdapterStart(); + state = WAITING_TO_START; + return true; + } + } + } + /** * @brief Puts the adapter into a low consumption mode and then deactivates * the library. It returns a boolean indicating whether the transition to low * consumption mode was successful. + * @param turnOff Whether the library should put the adapter in the low + * consumption mode or not before deactivation. Defaults to `true`. */ - bool deactivate() { - activate(); - bool success = sendCommand(COMMAND_BYE).success; + bool deactivate(bool turnOff = true) { + bool success = true; + + if (turnOff) { + activate(); + success = sendCommand(COMMAND_BYE).success; + } lastError = NONE; isEnabled = false; @@ -1604,13 +1665,16 @@ class LinkWireless { resetState(); stop(); - bool result = start(); + bool success = start(); + + if (!success) + stop(); LINK_WIRELESS_BARRIER; isEnabled = wasEnabled; LINK_WIRELESS_BARRIER; - return result; + return success; } void resetState() { @@ -1695,13 +1759,19 @@ class LinkWireless { } void pingAdapter() { + pingAdapterStart(); + wait(PING_WAIT); + pingAdapterEnd(); + } + + void pingAdapterStart() { linkGPIO->setMode(LinkGPIO::Pin::SO, LinkGPIO::Direction::OUTPUT); linkGPIO->setMode(LinkGPIO::Pin::SD, LinkGPIO::Direction::OUTPUT); linkGPIO->writePin(LinkGPIO::SD, true); - wait(PING_WAIT); - linkGPIO->writePin(LinkGPIO::SD, false); } + void pingAdapterEnd() { linkGPIO->writePin(LinkGPIO::SD, false); } + bool login() { LoginMemory memory; diff --git a/lib/c_bindings/C_LinkWireless.cpp b/lib/c_bindings/C_LinkWireless.cpp index f786def..3259e39 100644 --- a/lib/c_bindings/C_LinkWireless.cpp +++ b/lib/c_bindings/C_LinkWireless.cpp @@ -26,10 +26,18 @@ bool C_LinkWireless_activate(C_LinkWirelessHandle handle) { return static_cast(handle)->activate(); } +bool C_LinkWireless_activateAsync(C_LinkWirelessHandle handle) { + return static_cast(handle)->activateAsync(); +} + bool C_LinkWireless_deactivate(C_LinkWirelessHandle handle) { return static_cast(handle)->deactivate(); } +bool C_LinkWireless_deactivateButKeepOn(C_LinkWirelessHandle handle) { + return static_cast(handle)->deactivate(false); +} + bool C_LinkWireless_serve(C_LinkWirelessHandle handle, const char* gameName, const char* userName, diff --git a/lib/c_bindings/C_LinkWireless.h b/lib/c_bindings/C_LinkWireless.h index 2f464ed..b92d5f4 100644 --- a/lib/c_bindings/C_LinkWireless.h +++ b/lib/c_bindings/C_LinkWireless.h @@ -30,6 +30,8 @@ typedef void* C_LinkWirelessHandle; typedef enum { C_LINK_WIRELESS_STATE_NEEDS_RESET, + C_LINK_WIRELESS_STATE_WAITING_TO_START, + C_LINK_WIRELESS_STATE_STARTING, C_LINK_WIRELESS_STATE_AUTHENTICATED, C_LINK_WIRELESS_STATE_SEARCHING, C_LINK_WIRELESS_STATE_SERVING, @@ -78,7 +80,10 @@ C_LinkWirelessHandle C_LinkWireless_create(bool forwarding, void C_LinkWireless_destroy(C_LinkWirelessHandle handle); bool C_LinkWireless_activate(C_LinkWirelessHandle handle); +bool C_LinkWireless_activateAsync(C_LinkWirelessHandle handle); bool C_LinkWireless_deactivate(C_LinkWirelessHandle handle); +bool C_LinkWireless_deactivateButKeepOn(C_LinkWirelessHandle handle); + bool C_LinkWireless_serve(C_LinkWirelessHandle handle, const char* gameName, const char* userName,