diff --git a/README.md b/README.md index 684e33c..0a29db2 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ A set of Game Boy Advance (GBA) C++ libraries to interact with the Serial Port. - [🔌](#-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! - [⏱️](#%EF%B8%8F-LinkUART) [LinkUART.hpp](lib/LinkUART.hpp): Easily connect to **any PC** using a USB to UART cable! +- [📱](#-LinkMobile) [LinkMobile.hpp](lib/LinkMobile.hpp): Connect to **the internet** using the *Mobile Adapter GB*, brought back to life thanks to the [REON](https://github.com/REONTeam) project! - [🖱️](#%EF%B8%8F-LinkPS2Mouse) [LinkPS2Mouse.hpp](lib/LinkPS2Mouse.hpp): Connect a **PS/2 mouse** to the GBA for extended controls! - [⌨️](#%EF%B8%8F-LinkPS2Keyboard) [LinkPS2Keyboard.hpp](lib/LinkPS2Keyboard.hpp): Connect a **PS/2 keyboard** to the GBA for extended controls! @@ -402,12 +403,62 @@ The GBA operates using `1` stop bit, but everything else can be configured. By d - Green wire (`TX`) -> GBA `SI`. - White wire (`RX`) -> GBA `SO`. +# 📱 LinkMobile + +*(aka Mobile Adapter GB)* + +This is a driver for an accessory that enables online P2P connections between 2 players, or up to two TCP/UDP connections. The protocol was reverse-engineered by the *REON Team*. + +The original accessory was sold in Japan only and doesn't work anymore since it relies on old tech, but REON has created an open-source implementation called [libmobile](https://github.com/REONTeam/libmobile), as well as support for emulators and microcontrollers. + +![screenshot](https://github.com/user-attachments/assets/ff0ea09f-807c-4ba9-9dac-9cae8f831408) + +## Constructor + +`new LinkMobile(...)` accepts these **optional** parameters: + +Name | Type | Default | Description +--- | --- | --- | --- +`timeout` | **u32** | `180` | Number of *frames* without completing a request to reset a connection. +`timerId` | **u8** *(0~3)* | `3` | GBA Timer to use for sending. + +You can update these values at any time without creating a new instance: +- Call `deactivate()`. +- Mutate the `config` property. +- Call `activate()`. + +You can also change these compile-time constants: +- `LINK_MOBILE_QUEUE_SIZE`: to set a custom request queue size (how many commands can be queued at the same time). The default value is `10`, which seems fine for most games. + - This affects how much memory is allocated. With the default value, it's around `3` KB. + +## Methods + +- All actions are asynchronous/nonblocking. That means, they will return `true` if nothing is awfully wrong, but the actual consequence will occur some frames later. You can call `getState()` at any time to know what it's doing. +- On fatal errors, the library will transition to a `NEEDS_RESET` state. In that case, you can call `getError()` to know more details on what happened, and then `activate()` to restart. +- When calling `deactivate()`, the adapter automatically turns itself off after `3` seconds of inactivity. However, to gracefully turn it off, it's recommended to call `shutdown()` first, wait until the state is `SHUTDOWN`, and then `deactivate()`. + +Name | Return type | Description +--- | --- | --- +`isActive()` | **bool** | Returns whether the library is active or not. +`activate()` | - | Activates the library. After some time, if an adapter is connected, the state will be changed to `SESSION_ACTIVE`. If not, the state will be `NEEDS_RESET`, and you can retrieve the error with `getError()`. +`deactivate()` | - | Deactivates the library, resetting the serial mode to GPIO. Calling `shutdown()` first is recommended, but the adapter will put itself in sleep mode after 3 seconds anyway. +`shutdown()` | **bool** | Gracefully shuts down the adapter, closing all connections. After some time, the state will be changed to `SHUTDOWN`, and only then it's safe to call `deactivate()`. +`call(phoneNumber)` | **bool** | Initiates a P2P connection with a `phoneNumber`. After some time, the state will be `CALL_ESTABLISHED`, or `ACTIVE_SESSION` if the connection fails or ends. In REON/libmobile the phone number can be a number assigned by the relay server, or a 12-digit IPv4 address (for example, `"127000000001"` would be `127.0.0.1`). +`transfer(dataToSend, receivedData)` | **bool** | Requests a data transfer (up to `254` bytes) within a P2P connection and responds the received data. The `receivedData` is a pointer to a `LinkWireless::DataTransfer` struct that will be filled with data. It can point to `dataToSend`. When the transfer is completed, the `completed` field will be `true`. +`hangUp()` | **bool** | Hangs up the current P2P call. +`readConfiguration(configurationData)` | **bool** | Retrieves the adapter configuration, and puts it in the `configurationData` struct. If the adapter has an active session, the data is already loaded, so it's instantaneous. +`getState()` | **LinkMobile::State** | Returns the current state (one of `LinkMobile::State::NEEDS_RESET`, `LinkMobile::State::PINGING`, `LinkMobile::State::WAITING_TO_START`, `LinkMobile::State::STARTING_SESSION`, `LinkMobile::State::ACTIVATING_SIO32`, `LinkMobile::State::WAITING_32BIT_SWITCH`, `LinkMobile::State::READING_CONFIGURATION`, `LinkMobile::State::SESSION_ACTIVE`, `LinkMobile::State::CALL_REQUESTED`, `LinkMobile::State::CALLING`, `LinkMobile::State::CALL_ESTABLISHED`, `LinkMobile::State::SESSION_ACTIVE_ISP`, `LinkMobile::State::SHUTDOWN_REQUESTED`, `LinkMobile::State::ENDING_SESSION`, `LinkMobile::State::WAITING_8BIT_SWITCH`, or `LinkMobile::State::SHUTDOWN`). +`getRole()` | **LinkMobile::Role** | Returns the current role in the P2P connection (one of `LinkMobile::Role::NO_P2P_CONNECTION`, `LinkMobile::Role::CALLER`, or `LinkMobile::Role::RECEIVER`). +`isConnected()` | **bool** | Returns `true` if a P2P call is established (the state is `CALL_ESTABLISHED`). +`isSessionActive()` | **bool** | Returns `true` if the session is active. +`canShutdown()` | **bool** | Returns `true` if there's an active session and there's no previous shutdown requests. +`getDataSize()` | **LinkSPI::DataSize** | Returns the current operation mode (`LinkSPI::DataSize`). +`getError()` | **LinkMobile::Error** | Returns details about the last error that caused the connection to be aborted. + # 🖱️ LinkPS2Mouse A PS/2 mouse driver for the GBA. Use it to add mouse support to your homebrew games. It's a straight port from [this library](https://github.com/kristopher/PS2-Mouse-Arduino). -⚠️ calling `activate()` or `report(...)` could freeze the system if nothing is connected: detecting timeouts using timer interrupts is the user's responsibility. - ![photo](https://github.com/afska/gba-link-connection/assets/1631752/6856ff0d-0f06-4a9d-8ded-280052e02b8d) ## Constructor @@ -423,6 +474,8 @@ Name | Return type | Description `deactivate()` | - | Deactivates the library. `report(data[3])` | - | Fills the `data` int array with a report. The first int contains *clicks* that you can check against the bitmasks `LINK_PS2_MOUSE_LEFT_CLICK`, `LINK_PS2_MOUSE_MIDDLE_CLICK`, and `LINK_PS2_MOUSE_RIGHT_CLICK`. The second int is the *X movement*, and the third int is the *Y movement*. +⚠️ calling `activate()` or `report(...)` could freeze the system if nothing is connected: detecting timeouts using interrupts is the user's responsibility. + ## Pinout ``` diff --git a/lib/LinkMobile.hpp b/lib/LinkMobile.hpp index 25f8b22..6c5982b 100644 --- a/lib/LinkMobile.hpp +++ b/lib/LinkMobile.hpp @@ -207,7 +207,7 @@ class LinkMobile { /** * @brief Constructs a new LinkMobile object. * @param timeout Number of *frames* without completing a request to reset a - * connection. + * connection. Defaults to 180 (3 seconds). * @param timerId GBA Timer to use for waiting. */ explicit LinkMobile(u32 timeout = LINK_MOBILE_DEFAULT_TIMEOUT, @@ -245,8 +245,8 @@ class LinkMobile { /** * @brief Deactivates the library, resetting the serial mode to GPIO. - * Calling `shutdown()` first is recommended, but the adapter will put itself - * in sleep mode after 3 seconds anyway. + * \warning Calling `shutdown()` first is recommended, but the adapter will + * put itself in sleep mode after 3 seconds anyway. */ void deactivate() { error = {}; @@ -256,7 +256,7 @@ class LinkMobile { } /** - * @brief Gracefully shutdowns the adapter, closing all connections. + * @brief Gracefully shuts down the adapter, closing all connections. * After some time, the state will be changed to `SHUTDOWN`, and only then * it's safe to call `deactivate()`. * \warning Non-blocking. Returns `true` immediately, or `false` if there's no @@ -330,7 +330,7 @@ class LinkMobile { } /** - * @brief Returns the adapter configuration. + * @brief Retrieves the adapter configuration. * @param configurationData A structure that will be filled with the * configuration data. If the adapter has an active session, the data is * already loaded, so it's instantaneous. @@ -357,6 +357,12 @@ class LinkMobile { */ [[nodiscard]] Role getRole() { return role; } + /** + * @brief Returns `true` if a P2P call is established (the state is + * `CALL_ESTABLISHED`). + */ + [[nodiscard]] bool isConnected() { return state == CALL_ESTABLISHED; } + /** * @brief Returns `true` if the session is active. */ @@ -364,12 +370,6 @@ class LinkMobile { return state >= SESSION_ACTIVE && state <= SHUTDOWN_REQUESTED; } - /** - * @brief Returns `true` if a P2P call is established (the state is - * `CALL_ESTABLISHED`). - */ - [[nodiscard]] bool isConnected() { return state == CALL_ESTABLISHED; } - /** * @brief Returns `true` if there's an active session and there's no previous * shutdown requests. diff --git a/lib/LinkPS2Mouse.hpp b/lib/LinkPS2Mouse.hpp index f395712..c8d922b 100644 --- a/lib/LinkPS2Mouse.hpp +++ b/lib/LinkPS2Mouse.hpp @@ -23,7 +23,7 @@ // -------------------------------------------------------------------------- // considerations: // - `activate()` or `report(...)` could freeze the system if not connected! -// - detecting timeouts using timer interrupts is the user's responsibility! +// - detecting timeouts using interrupts is the user's responsibility! // -------------------------------------------------------------------------- // ____________ // | Pinout |