Home

Awesome

gba-link-connection

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. C bindings are also included for compatibility.

(click on the emojis for documentation)

<img alt="rlabs" width="16" height="16" src="https://user-images.githubusercontent.com/1631752/116227197-400d2380-a72a-11eb-9e7b-389aae76f13e.png" /> Created by [r]labs.

💬 Check out my other GBA projects: piuGBA, beat-beast, gba-remote-play, gba-flashcartio.

Usage

The files use some compiler extensions, so using GCC is required.

The example ROMs were compiled with devkitPro, using GCC 14.1.0 with -std=c++17 as the standard and -Ofast as the optimization level.

To learn implementation details, you might also want to check out the docs/ folder, which contains important documentation.

Compiling the examples

Running ./compile.sh builds all the examples with the right configuration.

The project must be in a path without spaces; devkitARM and some *nix commands are required.

All the projects understand these Makefile actions:

make [ clean | build | start | rebuild | restart ]

C bindings

// Instantiating
LinkSomething* linkSomething = new LinkSomething(a, b); // C++
LinkSomethingHandle cLinkSomething = C_LinkSomething_create(a, b); // C

// Calling methods
linkSomething->method(a, b); // C++
C_LinkSomething_method(cLinkSomething, a, b); // C

// Destroying
delete linkSomething; // C++
C_LinkSomething_destroy(cLinkSomething); // C

👾 LinkCable

(aka Multi-Play Mode)

⬆️ This is the Link Port mode that games use for multiplayer.

The library uses message queues to send/receive data and transmits when it's possible. As it uses CPU interrupts, the connection is alive even if a console drops a frame or gets stuck in a long iteration loop. After such an event, all nodes end up receiving all the pending messages.

screenshot

Constructor

new LinkCable(...) accepts these optional parameters:

NameTypeDefaultDescription
baudRateBaudRateBAUD_RATE_1Sets a specific baud rate.
timeoutu323Maximum number of frames without receiving data from other player before marking them as disconnected or resetting the connection.
intervalu1650Number of 1024-cycle ticks (61.04μs) between transfers (50 = 3.052ms). It's the interval of Timer #sendTimerId. Lower values will transfer faster but also consume more CPU. You can use Link::perFrame(...) to convert from packets per frame to interval values.
sendTimerIdu8 (0~3)3GBA Timer to use for sending.

You can update these values at any time without creating a new instance:

You can also change these compile-time constants:

Methods

NameReturn typeDescription
isActive()boolReturns whether the library is active or not.
activate()-Activates the library.
deactivate()-Deactivates the library.
isConnected()boolReturns true if there are at least 2 connected players.
playerCount()u8 (0~4)Returns the number of connected players.
currentPlayerId()u8 (0~3)Returns the current player ID.
sync()-Call this method every time you need to fetch new data.
waitFor(playerId)boolWaits for data from player #playerId. Returns true on success, or false on disconnection.
waitFor(playerId, cancel)boolLike waitFor(playerId) but accepts a cancel() function. The library will continuously invoke it, and abort the wait if it returns true.
canRead(playerId)boolReturns true if there are pending messages from player #playerId. Keep in mind that if this returns false, it will keep doing so until you fetch new data with sync().
read(playerId)u16Dequeues and returns the next message from player #playerId. If there's no data from that player, a 0 will be returned.
peek(playerId)u16Returns the next message from player #playerId without dequeuing it. If there's no data from that player, a 0 will be returned.
send(data)-Sends data to all connected players.

⚠️ 0xFFFF and 0x0 are reserved values, so don't send them!

💻 LinkCableMultiboot

(aka Multiboot through Multi-Play Mode)

⬆️ 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.

Its demo (LinkCableMultiboot_demo) has all the other gba-link-connection ROMs bundled with it, so it can be used to quickly test the library.

screenshot

You can change these compile-time constants:

Methods

NameReturn typeDescription
sendRom(rom, romSize, cancel, [mode])LinkCableMultiboot::ResultSends 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. The mode can be either LinkCableMultiboot::TransferMode::MULTI_PLAY for GBA cable (default value) or LinkCableMultiboot::TransferMode::SPI for GBC cable. Once completed, the return value should be LinkCableMultiboot::Result::SUCCESS.

⚠️ stop DMA before sending the ROM! (you might need to stop your audio player)

🔧👾 LinkRawCable

⬆️

screenshot

Methods

NameReturn typeDescription
isActive()boolReturns 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::ResponseExchanges data with the connected consoles. Returns the received data, including the assigned player ID.
transfer(data, cancel)LinkRawCable::ResponseLike 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::AsyncStateReturns the state of the last async transfer (one of LinkRawCable::AsyncState::IDLE, LinkRawCable::AsyncState::WAITING, or LinkRawCable::AsyncState::READY).
getAsyncData()LinkRawCable::ResponseIf the async state is READY, returns the remote data and switches the state back to IDLE. If not, returns an empty response.
getBaudRate()LinkRawCable::BaudRateReturns the current baudRate.
isMaster()boolReturns whether the console is connected as master or not. Returns garbage when the cable is not properly connected.
isReady()boolReturns whether all connected consoles have entered the multiplayer mode. Returns garbage when the cable is not properly connected.

📻 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 blog post is very helpful. I've updated it to add more details about the things I learned by 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.

https://github.com/afska/gba-link-connection/assets/1631752/7eeafc49-2dfa-4902-aa78-57b391720564

Constructor

new LinkWireless(...) accepts these optional parameters:

NameTypeDefaultDescription
forwardingbooltrueIf true, the server forwards all messages to the clients. Otherwise, clients only see messages sent from the server (ignoring other peers).
retransmissionbooltrueIf true, the library handles retransmission for you, so there should be no packet loss.
maxPlayersu8 (2~5)5Maximum number of allowed players.
timeoutu3210Maximum number of frames without receiving data from other player before resetting the connection.
intervalu1650Number of 1024-cycle ticks (61.04μs) between transfers (50 = 3.052ms). It's the interval of Timer #sendTimerId. Lower values will transfer faster but also consume more CPU. You can use Link::perFrame(...) to convert from packets per frame to interval values.
sendTimerIdu8 (0~3)3GBA Timer to use for sending.

You can update these values at any time without creating a new instance:

You can also change these compile-time constants:

Methods

NameReturn typeDescription
isActive()boolReturns whether the library is active or not.
activate()boolActivates 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()boolPuts 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])boolStarts 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])boolFills 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()boolStarts looking for broadcasting servers and changes the state to SEARCHING. After this, call getServersAsyncEnd(...) 1 second later.
getServersAsyncEnd(servers)boolFills the servers array with all the currently broadcasting servers. Changes the state to AUTHENTICATED again.
connect(serverId)boolStarts a connection with serverId and changes the state to CONNECTING.
keepConnecting()boolWhen 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)boolEnqueues data to be sent to other nodes.
receive(messages)boolFills the messages array with incoming messages, forwarding if needed.
getState()LinkWireless::StateReturns 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).
isConnected()boolReturns true if the player count is higher than 1.
isSessionActive()boolReturns true if the state is SERVING or CONNECTED.
playerCount()u8 (1~5)Returns the number of connected players.
currentPlayerId()u8 (0~4)Returns the current player ID.
getLastError([clear])LinkWireless::ErrorIf one of the other methods returns false, you can inspect this to know the cause. After this call, the last error is cleared if clear is true (default behavior).

⚠️ 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.

Its demo (LinkWirelessMultiboot_demo) has all the other gba-link-connection ROMs bundled with it, so it can be used to quickly test the library.

https://github.com/afska/gba-link-connection/assets/1631752/9a648bff-b14f-4a85-92d4-ccf366adce2d

Methods

NameReturn typeDescription
sendRom(rom, romSize, gameName, userName, gameId, players, cancel)LinkWirelessMultiboot::ResultSends 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, since this also ensures compatibility with Multiboot via Link Cable. Once completed, the return value should be LinkWirelessMultiboot::Result::SUCCESS.

🔧📻 LinkRawWireless

⬆️

screenshot

Methods

🔧🏛 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.

Methods

NameReturn typeDescription
getChildrenData(response)LinkWirelessOpenSDK::ChildrenDataParses the response and returns a struct containing all the received packets from the connected clients.
getParentData(response)LinkWirelessOpenSDK::ParentDataParses the response and returns a struct containing all the received packets from the host.
createServerBuffer(fullPayload, fullPayloadSize, sequence, [targetSlots], [offset])LinkWirelessOpenSDK::SendBuffer<ServerSDKHeader>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<ServerSDKHeader>Creates a buffer for the host to acknowledge a header received from a certain clientNumber.
createClientBuffer(fullPayload, fullPayloadSize, sequence, [offset])LinkWirelessOpenSDK::SendBuffer<ClientSDKHeader>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<ServerSDKHeader>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 and 📻 LinkWireless.

https://github.com/afska/gba-link-connection/assets/1631752/d1f49a48-6b17-4954-99d6-d0b7586f5730

Constructor

new LinkUniversal(...) accepts these optional parameters:

NameTypeDefaultDescription
protocolLinkUniversal::ProtocolAUTODETECTSpecifies 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).
gameNameconst 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.
cableOptionsLinkUniversal::CableOptionssame as LinkCableAll the 👾 LinkCable constructor parameters in one struct.
wirelessOptionsLinkUniversal::WirelessOptionssame as LinkWirelessAll the 📻 LinkWireless constructor parameters in one struct.
randomSeedint123Random seed used for waits to prevent livelocks. If you use libtonc, pass __qran_seed.

You can also change these compile-time constants:

Methods

The interface is the same as 👾 LinkCable. Additionally, it supports these methods:

NameReturn typeDescription
getState()LinkUniversal::StateReturns the current state (one of LinkUniversal::State::INITIALIZING, LinkUniversal::State::WAITING, or LinkUniversal::State::CONNECTED).
getMode()LinkUniversal::ModeReturns the active mode (one of LinkUniversal::Mode::LINK_CABLE, or LinkUniversal::Mode::LINK_WIRELESS).
getProtocol()LinkUniversal::ProtocolReturns the active protocol (one of LinkUniversal::Protocol::AUTODETECT, LinkUniversal::Protocol::CABLE, LinkUniversal::Protocol::WIRELESS_AUTO, LinkUniversal::Protocol::WIRELESS_SERVER, or LinkUniversal::Protocol::WIRELESS_CLIENT).
getWirelessState()LinkWireless::StateReturns the wireless state (same as 📻 LinkWireless's getState()).
setProtocol(protocol)-Sets the active protocol.

🔌 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

Methods

NameReturn typeDescription
reset()-Resets communication mode to General Purpose (same as Link::reset()). Required to initialize the library!
setMode(pin, direction)-Configures a pin to use a direction (input or output).
getMode(pin)LinkGPIO::DirectionReturns the direction set at pin.
readPin(pin)boolReturns 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!

⚠️ call reset() when you finish doing GPIO stuff! (for compatibility with the other libraries)

🔗 LinkSPI

(aka Normal Mode)

⬆️ This is the GBA's implementation of SPI. You can use this to interact with other GBAs or computers that know SPI.

screenshot

Methods

NameReturn typeDescription
isActive()boolReturns whether the library is active or not.
activate(mode, [dataSize])-Activates the library in a specific mode (one of LinkSPI::Mode::SLAVE, LinkSPI::Mode::MASTER_256KBPS, or LinkSPI::Mode::MASTER_2MBPS). By default, the dataSize is 32-bit, but can be changed to LinkSPI::DataSize::SIZE_8BIT.
deactivate()-Deactivates the library.
transfer(data)u32Exchanges data with the other end. Returns the received data.
transfer(data, cancel)u32Like 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::AsyncStateReturns the state of the last async transfer (one of LinkSPI::AsyncState::IDLE, LinkSPI::AsyncState::WAITING, or LinkSPI::AsyncState::READY).
getAsyncData()u32If the async state is READY, returns the remote data and switches the state back to IDLE. If not, returns an empty response.
getMode()LinkSPI::ModeReturns the current mode.
getDataSize()LinkSPI::DataSizeReturns the current dataSize.
setWaitModeActive(isActive)-Enables or disables waitMode (*).
isWaitModeActive()boolReturns 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 (ready: MISO=LOW), and wait if it's not (not ready: MISO=HIGH). 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.

MISO means SO on the slave side and SI on the master side.

⚠️ when using Normal Mode between two GBAs, use a GBC Link Cable!

⚠️ only use the 2Mbps mode with custom hardware (very short wires)!

⚠️ returns 0xFFFFFFFF (or 0xFF) on misuse or cancelled transfers!

SPI Configuration

The GBA operates using SPI mode 3 (CPOL=1, CPHA=1). Here's a connection diagram that illustrates how to connect a Link Cable to a Raspberry Pi 3's SPI pins:

<table> <tr> <td> <p> <img src="https://github.com/afska/gba-link-connection/assets/1631752/a5fffad6-3aef-4f81-8d6c-ae4e99c2d5b4" alt="pinout"> </p> </td> <td> <p> <img src="https://github.com/afska/gba-link-connection/assets/1631752/203dc766-e316-4d92-a4b7-bc2264bffe71" alt="rpigba"> </p> </td> </tr> </table>

⏱️ LinkUART

(aka UART Mode)

⬆️ This is the GBA's implementation of UART. You can use this to interact with a PC using a USB to UART cable. You can change the buffer size by setting the compile-time constant LINK_UART_QUEUE_SIZE.

photo

Methods

NameReturn typeDescription
isActive()boolReturns whether the library is active or not.
activate(baudRate, dataSize, parity, useCTS)-Activates the library using a specific UART mode. Defaults: 9600bps, 8-bit data, no parity bit, no CTS.
deactivate()-Deactivates the library.
sendLine(string)-Takes a null-terminated string, and sends it followed by a '\n' character. The null character is not sent.
sendLine(data, cancel)-Like sendLine(string) but accepts a cancel() function. The library will continuously invoke it, and abort the transfer if it returns true.
readLine(string, [limit])boolReads characters into string until finding a '\n' character or a character limit is reached. A null terminator is added at the end. Returns false if the limit has been reached without finding a newline character.
readLine(string, cancel, [limit])boolLike readLine(string, [limit]) but accepts a cancel() function. The library will continuously invoke it, and abort the transfer if it returns true.
send(buffer, size, offset)-Sends size bytes from buffer, starting at byte offset.
read(buffer, size, offset)u32Tries to read size bytes into (u8*)(buffer + offset). Returns the number of read bytes.
canRead()boolReturns whether there are bytes to read or not.
canSend()boolReturns whether there is room to send new messages or not.
availableForRead()u32Returns the number of bytes available for read.
availableForSend()u32Returns the number of bytes available for send (buffer size - queued bytes).
read()u8Reads a byte. Returns 0 if nothing is found.
send(data)-Sends a data byte.

UART Configuration

The GBA operates using 1 stop bit, but everything else can be configured. By default, the library uses 8N1, which means 8-bit data and no parity bit. RTS/CTS is disabled by default.

diagram

🟪 LinkCube

(aka JOYBUS Mode)

⬆️ This is the GBA's implementation of JOYBUS, in which users connect the console to a GameCube (or Wii with GC ports) using an official adapter. The library can be tested using Dolphin/mGBA and gba-joybus-tester.

screenshot

You can change these compile-time constants:

Methods

NameReturn typeDescription
isActive()boolReturns whether the library is active or not.
activate()-Activates the library.
deactivate()-Deactivates the library.
wait()boolWaits for data. Returns true on success, or false on JOYBUS reset.
wait(cancel)boolLike wait() but accepts a cancel() function. The library will invoke it after every SERIAL interrupt, and abort the wait if it returns true.
canRead()boolReturns true if there are pending received values to read.
read()u32Dequeues and returns the next received value. If there's no received data, a 0 will be returned.
peek()u32Returns the next received value without dequeuing it. If there's no received data, a 0 will be returned.
send(data)-Sends 32-bit data. If the other end asks for data at the same time you call this method, a 0x00000000 will be sent.
pendingCount()u32Returns the number of pending outgoing transfers.
didReset([clear])boolReturns whether a JOYBUS reset was requested or not. After this call, the reset flag is cleared if clear is true (default behavior).

📱 LinkMobile

(aka Mobile Adapter GB)

⬆️ This is a driver for an accessory that enables online connectivity on the GB and GBA. The protocol was reverse-engineered by the REON Team.

The original accessory was sold in Japan only and using it nowadays is hard since it relies on old tech, but REON has created an open-source implementation called libmobile, as well as support for emulators and microcontrollers.

It has two modes of operation:

screenshot

Constructor

new LinkMobile(...) accepts these optional parameters:

NameTypeDefaultDescription
timeoutu32600Number of frames without completing a request to reset a connection.
timerIdu8 (0~3)3GBA Timer to use for sending.

You can update these values at any time without creating a new instance:

You can also change these compile-time constants:

Methods

NameReturn typeDescription
isActive()boolReturns 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()boolGracefully 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)boolInitiates 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).
callISP(password, loginId)boolCalls the ISP number registered in the adapter configuration, or a default number if the adapter hasn't been configured. Then, performs a login operation using the provided password and loginId. After some time, the state will be PPP_ACTIVE. If loginId is empty and the adapter has been configured, it will use the one stored in the configuration. Both parameters are null-terminated strings (max 32 characters).
dnsQuery(domainName, result)boolLooks up the IPv4 address for a domainName (a null-terminated string, max 253 characters). It also accepts an ASCII IPv4 address, converting it into a 4-byte address instead of querying the DNS server. The result is a pointer to a LinkMobile::DNSQuery struct that will be filled with the result. When the request is completed, the completed field will be true. If an IP address was found, the success field will be true and the ipv4 field can be read as a 4-byte address.
openConnection(ip, port, type, result)boolOpens a TCP/UDP (type) connection at the given ip (4-byte address) on the given port. The result is a pointer to a LinkMobile::OpenConn struct that will be filled with the result. When the request is completed, the completed field will be true. If the connection was successful, the success field will be true and the connectionId field can be used when calling the transfer(...) method. Only 2 connections can be opened at the same time.
closeConnection(connectionId, type, result)boolCloses an active TCP/UDP (type) connection. The result is a pointer to a LinkMobile::CloseConn struct that will be filled with the result. When the request is completed, the completed field will be true. If the connection was closed correctly, the success field will be true.
transfer(dataToSend, result, [connectionId])boolRequests a data transfer (up to 254 bytes) and responds the received data. The transfer can be done with the other node in a P2P connection, or with any open TCP/UDP connection if a PPP session is active. In the case of a TCP/UDP connection, the connectionId must be provided. The result is a pointer to a LinkMobile::DataTransfer struct that will be filled with the received data. It can also point to dataToSend to reuse the struct. When the request is completed, the completed field will be true. If the transfer was successful, the success field will be true. If not, you can assume that the connection was closed.
waitFor(asyncRequest)boolWaits for asyncRequest to be completed. Returns true if the request was completed && successful, and the adapter session is still alive. Otherwise, it returns false. The asyncRequest is a pointer to a LinkMobile::DNSQuery, LinkMobile::OpenConn, LinkMobile::CloseConn, or LinkMobile::DataTransfer.
hangUp()boolHangs up the current P2P or PPP call. Closes all connections.
readConfiguration(configurationData)boolRetrieves 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::StateReturns 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::ISP_CALL_REQUESTED, LinkMobile::State::ISP_CALLING, LinkMobile::State::PPP_LOGIN, LinkMobile::State::PPP_ACTIVE, LinkMobile::State::SHUTDOWN_REQUESTED, LinkMobile::State::ENDING_SESSION, LinkMobile::State::WAITING_8BIT_SWITCH, or LinkMobile::State::SHUTDOWN).
getRole()LinkMobile::RoleReturns the current role in the P2P connection (one of LinkMobile::Role::NO_P2P_CONNECTION, LinkMobile::Role::CALLER, or LinkMobile::Role::RECEIVER).
isConfigurationValid()intReturns whether the adapter has been configured or not. Returns 1 = yes, 0 = no, -1 = unknown (no session active).
isConnectedP2P()boolReturns true if a P2P call is established (the state is CALL_ESTABLISHED).
isConnectedPPP()boolReturns true if a PPP session is active (the state is PPP_ACTIVE).
isSessionActive()boolReturns true if the session is active.
canShutdown()boolReturns true if there's an active session and there's no previous shutdown requests.
getDataSize()LinkSPI::DataSizeReturns the current operation mode (LinkSPI::DataSize).
getError()LinkMobile::ErrorReturns 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.

photo

Constructor

new LinkPS2Mouse(timerId), where timerId is the GBA Timer used for delays.

Methods

NameReturn typeDescription
isActive()boolReturns whether the library is active or not.
activate()-Activates the library.
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

 ____________
|PS/2 --- GBA|
|------------|
|CLOCK -> SI |
|DATA --> SO |
|VCC ---> VCC|
|GND ---> GND|

⌨️ LinkPS2Keyboard

⬆️ A PS/2 keyboard driver for the GBA. Use it to add keyboard support to your homebrew games.

photo

Constructor

new LinkPS2Keyboard(onEvent), where onEvent is a function pointer that will receive the scan codes (u8). You should check a PS/2 scan code list online, but most common keys/events are included in enums like LINK_PS2_KEYBOARD_KEY::ENTER and LINK_PS2_KEYBOARD_EVENT::RELEASE.

Methods

NameReturn typeDescription
isActive()boolReturns whether the library is active or not.
activate()-Activates the library.
deactivate()-Deactivates the library.

Pinout

 ____________
|PS/2 --- GBA|
|------------|
|CLOCK -> SI |
|DATA --> SO |
|VCC ---> VCC|
|GND ---> GND|