Finishing TCP/UDP commands

This commit is contained in:
Rodrigo Alfonso
2024-08-18 11:07:22 -03:00
parent 855accae1c
commit 0aa2c1b5b8
3 changed files with 142 additions and 41 deletions

View File

@@ -419,7 +419,7 @@ The original accessory was sold in Japan only and doesn't work anymore since it
Name | Type | Default | Description
--- | --- | --- | ---
`timeout` | **u32** | `180` | Number of *frames* without completing a request to reset a connection.
`timeout` | **u32** | `600` | 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:
@@ -448,7 +448,7 @@ Name | Return type | Description
`dnsQuery(domainName, result)` | **bool** | Looks up the IPv4 address for a `domainName` (a null-terminated string, max `253` characters). 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)` | **bool** | Opens 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)` | **bool** | Closes 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])` | **bool** | Requests 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 an ISP 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`.
`transfer(dataToSend, result, [connectionId])` | **bool** | Requests 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 an ISP 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.
`hangUp()` | **bool** | Hangs up the current P2P or ISP call. Closes all connections.
`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::ISP_CALL_REQUESTED`, `LinkMobile::State::ISP_CALLING`, `LinkMobile::State::ISP_LOGIN`, `LinkMobile::State::ISP_ACTIVE`, `LinkMobile::State::SHUTDOWN_REQUESTED`, `LinkMobile::State::ENDING_SESSION`, `LinkMobile::State::WAITING_8BIT_SWITCH`, or `LinkMobile::State::SHUTDOWN`).

View File

@@ -102,7 +102,7 @@ start:
output += getErrorString(error);
output += "\n (SELECT = stop)";
} else if (linkMobile->getState() == LinkMobile::State::SESSION_ACTIVE) {
output += "\nL = Read Configuration";
output += "\nL = Read configuration";
output += "\nR = Call someone";
output += "\nSTART = Call the ISP";
output += "\n\n (A = ok)\n (SELECT = stop)";
@@ -111,7 +111,7 @@ start:
output += "\n (A = send)";
output += "\n (L = hang up)";
} else if (linkMobile->isConnectedISP()) {
output += "\n (LEFT = DNS query)";
output += "\n (A = DNS query)";
output += "\n (L = hang up)";
}
output += "\n (SELECT = stop)";
@@ -240,8 +240,8 @@ start:
break;
}
case LinkMobile::State::ISP_ACTIVE: {
// LEFT = DNS query
if (didPress(KEY_LEFT, left)) {
// A = DNS query
if (didPress(KEY_A, a) && !waitingDNS) {
std::string domain = getDomainInput();
if (domain != "") {
// (8) Run DNS queries
@@ -590,3 +590,5 @@ template <typename I>
rc[i] = digits[(w >> j) & 0x0f];
return rc;
}
// TODO: Implement TCP/UDP sockets test functions

View File

@@ -78,7 +78,7 @@ static volatile char LINK_MOBILE_VERSION[] = "LinkMobile/v7.0.0";
#define LINK_MOBILE_MAX_DOMAIN_NAME_LENGTH 253
#define LINK_MOBILE_COMMAND_TRANSFER_BUFFER \
(LINK_MOBILE_MAX_COMMAND_TRANSFER_LENGTH + 4)
#define LINK_MOBILE_DEFAULT_TIMEOUT (60 * 3)
#define LINK_MOBILE_DEFAULT_TIMEOUT (60 * 10)
#define LINK_MOBILE_DEFAULT_TIMER_ID 3
#define LINK_MOBILE_BARRIER asm volatile("" ::: "memory")
@@ -102,18 +102,18 @@ class LinkMobile {
static constexpr int INIT_WAIT_FRAMES = 7;
static constexpr int INIT_TIMEOUT_FRAMES = 30;
static constexpr int PING_FREQUENCY_FRAMES = 60;
static constexpr int ADAPTER_WAITING = 0xD2;
static constexpr u32 ADAPTER_WAITING_32BIT = 0xD2D2D2D2;
static constexpr int GBA_WAITING = 0x4B;
static constexpr u32 GBA_WAITING_32BIT = 0x4B4B4B4B;
static constexpr int ADAPTER_WAITING = 0xd2;
static constexpr u32 ADAPTER_WAITING_32BIT = 0xd2d2d2d2;
static constexpr int GBA_WAITING = 0x4b;
static constexpr u32 GBA_WAITING_32BIT = 0x4b4b4b4b;
static constexpr int OR_VALUE = 0x80;
static constexpr int COMMAND_MAGIC_VALUE1 = 0x99;
static constexpr int COMMAND_MAGIC_VALUE2 = 0x66;
static constexpr int DEVICE_GBA = 0x1;
static constexpr int DEVICE_ADAPTER_BLUE = 0x8;
static constexpr int DEVICE_ADAPTER_YELLOW = 0x9;
static constexpr int DEVICE_ADAPTER_GREEN = 0xA;
static constexpr int DEVICE_ADAPTER_RED = 0xB;
static constexpr int DEVICE_ADAPTER_GREEN = 0xa;
static constexpr int DEVICE_ADAPTER_RED = 0xb;
static constexpr int ACK_SENDER = 0;
static constexpr int CONFIGURATION_DATA_SIZE = 192;
static constexpr int CONFIGURATION_DATA_CHUNK = CONFIGURATION_DATA_SIZE / 2;
@@ -135,7 +135,8 @@ class LinkMobile {
static constexpr int COMMAND_OPEN_UDP_CONNECTION = 0x25;
static constexpr int COMMAND_CLOSE_UDP_CONNECTION = 0x26;
static constexpr int COMMAND_DNS_QUERY = 0x28;
static constexpr int COMMAND_ERROR_STATUS = 0x6E | OR_VALUE;
static constexpr int COMMAND_CONNECTION_CLOSED = 0x1f;
static constexpr int COMMAND_ERROR_STATUS = 0x6e | OR_VALUE;
static constexpr u8 WAIT_TICKS[] = {4, 8};
static constexpr int LOGIN_PARTS_SIZE = 8;
static constexpr u8 LOGIN_PARTS[] = {0x4e, 0x49, 0x4e, 0x54,
@@ -255,7 +256,7 @@ class LinkMobile {
/**
* @brief Constructs a new LinkMobile object.
* @param timeout Number of *frames* without completing a request to reset a
* connection. Defaults to 180 (3 seconds).
* connection. Defaults to 600 (10 seconds).
* @param timerId GBA Timer to use for waiting.
*/
explicit LinkMobile(u32 timeout = LINK_MOBILE_DEFAULT_TIMEOUT,
@@ -414,7 +415,8 @@ class LinkMobile {
* 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.
* when calling the `transfer(...)` method. If not, you can assume that the
* connection was closed.
* \warning Only `2` connections can be opened at the same time.
* \warning Non-blocking. Returns `true` immediately, or `false` if there's no
* active ISP session, no available request slots.
@@ -440,7 +442,6 @@ class LinkMobile {
pushRequest(request);
return true;
}
// TODO: IMPLEMENT SOCKET METHODS
/**
* @brief Closes an active TCP/UDP (`type`) connection.
@@ -926,13 +927,44 @@ class LinkMobile {
}
break;
}
case UserRequest::Type::OPEN_CONNECTION: {
if (state != ISP_ACTIVE) {
userRequests.pop();
return;
}
if (!asyncCommand.isActive && !request.commandSent) {
if (request.type == ConnectionType::TCP)
cmdOpenTCPConnection(request.ip, request.port);
else
cmdOpenUDPConnection(request.ip, request.port);
request.commandSent = true;
}
break;
}
case UserRequest::Type::CLOSE_CONNECTION: {
if (state != ISP_ACTIVE) {
userRequests.pop();
return;
}
if (!asyncCommand.isActive && !request.commandSent) {
if (request.type == ConnectionType::TCP)
cmdCloseTCPConnection(request.connectionId);
else
cmdCloseUDPConnection(request.connectionId);
request.commandSent = true;
}
break;
}
case UserRequest::Type::TRANSFER: {
if (state != CALL_ESTABLISHED) {
userRequests.pop();
return;
}
if (!asyncCommand.isActive && !request.commandSent) {
cmdTransferData(0xff, request.send.data, request.send.size);
cmdTransferData(request.connectionId, request.send.data,
request.send.size);
request.commandSent = true;
}
break;
@@ -1125,21 +1157,13 @@ class LinkMobile {
}
if (!asyncCommand.respondsTo(COMMAND_TRANSFER_DATA))
return;
if (asyncCommand.cmd.header.size == 0)
return abort(Error::Type::WEIRD_RESPONSE);
if (userRequests.isEmpty())
return abort(Error::Type::WTF);
auto request = userRequests.peekRef();
if (request->type != UserRequest::TRANSFER)
return abort(Error::Type::WTF);
u32 size = asyncCommand.cmd.header.size - 1;
for (u32 i = 0; i < size; i++)
request->receive->data[i] = asyncCommand.cmd.data.bytes[1 + i];
request->receive->size = size;
request->receive->completed = true;
request->receive->success = true;
request->finished = true;
handleTransferDataResponse(request);
break;
}
case ISP_CALLING: {
if (!asyncCommand.respondsTo(COMMAND_DIAL_TELEPHONE))
@@ -1196,21 +1220,44 @@ class LinkMobile {
request->dns->completed = true;
request->finished = true;
break;
} else if (asyncCommand.respondsTo(COMMAND_OPEN_TCP_CONNECTION) ||
asyncCommand.respondsTo(COMMAND_OPEN_UDP_CONNECTION)) {
if (request->type != UserRequest::OPEN_CONNECTION)
return abort(Error::Type::WTF);
if (asyncCommand.result == CommandResult::SUCCESS) {
if (asyncCommand.cmd.header.size != 1)
return abort(Error::Type::WEIRD_RESPONSE);
request->open->connectionId = asyncCommand.cmd.data.bytes[0];
request->open->success = true;
} else {
request->open->success = false;
}
request->close->completed = true;
request->finished = true;
} else if (asyncCommand.respondsTo(COMMAND_CLOSE_TCP_CONNECTION) ||
asyncCommand.respondsTo(COMMAND_CLOSE_UDP_CONNECTION)) {
if (request->type != UserRequest::CLOSE_CONNECTION)
return abort(Error::Type::WTF);
request->close->success =
asyncCommand.result == CommandResult::SUCCESS;
request->close->completed = true;
request->finished = true;
} else if (asyncCommand.respondsTo(COMMAND_TRANSFER_DATA)) {
if (request->type != UserRequest::TRANSFER)
return abort(Error::Type::WTF);
// TODO: HANDLE TRANSFER DATA
// if (asyncCommand.cmd.header.size == 0)
// return abort(Error::Type::WEIRD_RESPONSE);
// TODO: IMPLEMENT
// u32 size = asyncCommand.cmd.header.size - 1;
// for (u32 i = 0; i < size; i++)
// request.receive->data[i] = asyncCommand.cmd.data.bytes[1 + i];
// request.receive->size = size;
// request.receive->completed = true;
// request.finished = true;
handleTransferDataResponse(request);
} else if (asyncCommand.respondsTo(COMMAND_CONNECTION_CLOSED)) {
if (request->type != UserRequest::TRANSFER)
return abort(Error::Type::WTF);
// (connection closed)
request->receive->success = false;
request->receive->completed = true;
request->finished = true;
}
break;
}
@@ -1227,6 +1274,27 @@ class LinkMobile {
}
}
void handleTransferDataResponse(UserRequest* request) {
if (request->type != UserRequest::TRANSFER)
return abort(Error::Type::WTF);
if (asyncCommand.result == CommandResult::SUCCESS) {
if (asyncCommand.cmd.header.size == 0)
return abort(Error::Type::WEIRD_RESPONSE);
u32 size = asyncCommand.cmd.header.size - 1;
for (u32 i = 0; i < size; i++)
request->receive->data[i] = asyncCommand.cmd.data.bytes[1 + i];
request->receive->size = size;
request->receive->success = true;
} else {
request->receive->success = false;
}
request->receive->completed = true;
request->finished = true;
}
void processLoosePacket(u32 newData) {
switch (state) {
case PINGING: {
@@ -1304,6 +1372,32 @@ class LinkMobile {
sendCommandAsync(buildCommand(COMMAND_ISP_LOGIN, true));
}
void cmdOpenTCPConnection(const u8* ip, u16 port) {
for (u32 i = 0; i < 4; i++)
addData(ip[i], i == 0);
addData(msB16(port));
addData(lsB16(port));
sendCommandAsync(buildCommand(COMMAND_OPEN_TCP_CONNECTION, true));
}
void cmdCloseTCPConnection(u8 connectionId) {
addData(connectionId);
sendCommandAsync(buildCommand(COMMAND_CLOSE_TCP_CONNECTION, true));
}
void cmdOpenUDPConnection(const u8* ip, u16 port) {
for (u32 i = 0; i < 4; i++)
addData(ip[i], i == 0);
addData(msB16(port));
addData(lsB16(port));
sendCommandAsync(buildCommand(COMMAND_OPEN_UDP_CONNECTION, true));
}
void cmdCloseUDPConnection(u8 connectionId) {
addData(connectionId);
sendCommandAsync(buildCommand(COMMAND_CLOSE_UDP_CONNECTION, true));
}
void cmdDNSQuery(const u8* data, u8 size) {
for (int i = 0; i < size; i++)
addData(data[i], i == 0);
@@ -1337,7 +1431,12 @@ class LinkMobile {
return asyncCommand.direction == AsyncCommand::Direction::SENDING ||
(commandId != COMMAND_WAIT_FOR_TELEPHONE_CALL &&
commandId != COMMAND_DIAL_TELEPHONE &&
commandId != COMMAND_DNS_QUERY);
commandId != COMMAND_DNS_QUERY &&
commandId != COMMAND_OPEN_TCP_CONNECTION &&
commandId != COMMAND_CLOSE_TCP_CONNECTION &&
commandId != COMMAND_OPEN_UDP_CONNECTION &&
commandId != COMMAND_CLOSE_UDP_CONNECTION &&
commandId != COMMAND_TRANSFER_DATA);
}
void addData(u8 value, bool start = false) {