Refactoring async receive

This commit is contained in:
Rodrigo Alfonso
2024-08-12 06:37:13 -03:00
parent c5abe13afd
commit e897f78a1b

View File

@@ -52,7 +52,7 @@ class LinkMobile {
static constexpr int FRAME_LINES = 228;
static constexpr int PING_WAIT = FRAME_LINES * 7;
static constexpr int CMD_TIMEOUT = FRAME_LINES * 3;
static constexpr int CMD_TIMEOUT = FRAME_LINES * 5;
static constexpr int ADAPTER_WAITING = 0xD2;
static constexpr int GBA_WAITING = 0x4B;
static constexpr int OR_VALUE = 0x80;
@@ -96,6 +96,7 @@ class LinkMobile {
enum State {
NEEDS_RESET,
AUTHENTICATING,
SESSION_ACTIVE,
};
@@ -113,9 +114,14 @@ class LinkMobile {
[[nodiscard]] bool isActive() { return isEnabled; }
/**
* @brief ...
*
* \warning Blocks the system for ~15 frames!
*/
bool activate() {
lastError = NONE;
isEnabled = false; // TODO: EARLY ACTIVATE
isEnabled = false;
LINK_MOBILE_BARRIER;
bool success = reset();
@@ -165,10 +171,8 @@ class LinkMobile {
}
u32 newData = linkSPI->getAsyncData();
if (state != SESSION_ACTIVE) { // TODO: AUTHENTICATING state
mgbalog("SESSION NOT ACTIVE");
if (state == NEEDS_RESET)
return;
}
if (asyncCommand.isActive) {
if (asyncCommand.state == AsyncCommand::State::PENDING) {
@@ -182,9 +186,8 @@ class LinkMobile {
receiveAsyncCommandSIO8(newData);
}
if (asyncCommand.state == AsyncCommand::State::COMPLETED) {
if (asyncCommand.state == AsyncCommand::State::COMPLETED)
processAsyncCommand();
}
}
}
}
@@ -213,27 +216,22 @@ class LinkMobile {
struct PacketHeader {
u8 commandId;
u8 _unused_ = 0;
u8 _unusedDataSizeH_ = 0; // The Mobile Adapter discards any packets bigger
// than 255 bytes, effectively forcing the high
// byte of the packet data length to be 0.
u8 dataSizeL;
u8 _unusedSizeHigh_ = 0;
u8 size;
u16 sum() { return commandId + _unused_ + _unusedDataSizeH_ + dataSizeL; }
u16 sum() { return commandId + _unused_ + _unusedSizeHigh_ + size; }
} __attribute__((packed));
struct PacketChecksum {
// The Packet Checksum is simply the 16-bit sum of all previous header bytes
// and all previous packet data bytes. It does not include the magic bytes.
// The checksum is transmitted big-endian.
u8 checksumH;
u8 checksumL;
u8 high;
u8 low;
} __attribute__((packed));
struct Command {
MagicBytes magicBytes;
PacketHeader packetHeader;
PacketData packetData;
PacketChecksum packetChecksum;
PacketHeader header;
PacketData data;
PacketChecksum checksum;
};
enum CommandResult {
@@ -246,7 +244,8 @@ class LinkMobile {
WEIRD_DATA_SIZE,
WRONG_CHECKSUM,
ERROR,
UNEXPECTED_RESPONSE
UNEXPECTED_RESPONSE,
TIMEOUT
};
struct CommandResponse {
@@ -282,11 +281,10 @@ class LinkMobile {
State state = NEEDS_RESET;
PacketData nextCommandData;
u32 nextCommandDataSize = 0;
volatile bool isSendingSyncCommand = false;
Error lastError = NONE;
volatile bool isEnabled = false;
void processAsyncCommand() { // (irq only)
void processAsyncCommand() {
asyncCommand.isActive = false;
// TODO: IMPLEMENT
mgbalog("PROCESSED! %d", asyncCommand.result);
@@ -333,6 +331,11 @@ class LinkMobile {
pingAdapter();
LINK_MOBILE_BARRIER;
state = AUTHENTICATING;
isEnabled = true;
LINK_MOBILE_BARRIER;
if (!login())
return false;
@@ -352,6 +355,7 @@ class LinkMobile {
// Link::_REG_TM[config.sendTimerId].start = -config.interval;
// Link::_REG_TM[config.sendTimerId].cnt =
// Link::_TM_ENABLE | Link::_TM_IRQ | BASE_FREQUENCY;
// TODO: START TIMER AFTER transferAsync
}
void pingAdapter() {
@@ -364,69 +368,44 @@ class LinkMobile {
addData(LOGIN_PARTS[i], i == 0);
auto command = buildCommand(COMMAND_BEGIN_SESSION, true);
return withSyncCommand([&command, this]() {
auto response = sendCommandWithResponse(command);
if (response.result != CommandResult::SUCCESS) {
// TODO: RESET
if (response.result == CommandResult::ERROR) {
mgbalog("error %d", response.command.packetData.bytes[1]);
} else {
mgbalog("result %d", response.result);
}
auto response = sendCommandWithResponse(command);
if (response.result != CommandResult::SUCCESS)
return false;
if (response.command.header.size != LOGIN_PARTS_SIZE)
return false;
for (u32 i = 0; i < LOGIN_PARTS_SIZE; i++) {
if (response.command.data.bytes[i] != LOGIN_PARTS[i])
return false;
} else {
mgbalog("success size %d", response.command.packetHeader.dataSizeL);
}
}
return response.result == CommandResult::SUCCESS;
});
}
template <typename F>
bool withSyncCommand(F action) {
LINK_MOBILE_BARRIER;
isSendingSyncCommand = true;
LINK_MOBILE_BARRIER;
bool result = action();
LINK_MOBILE_BARRIER;
isSendingSyncCommand = false;
LINK_MOBILE_BARRIER;
return result;
return true;
}
CommandResponse sendCommandWithResponse(Command command) {
CommandResponse response;
isEnabled = true; // TODO: !!!
state = SESSION_ACTIVE; // TODO: !!!
sendCommandAsync(command);
while (asyncCommand.isActive)
;
waitUntilAsyncCommandFinishes();
if (asyncCommand.result != CommandResult::SUCCESS) {
mgbalog("NOT SUCCESS SEND %d", asyncCommand.result);
response.result = asyncCommand.result;
return response;
}
receiveCommandAsync();
while (asyncCommand.isActive)
;
waitUntilAsyncCommandFinishes();
if (asyncCommand.result != CommandResult::SUCCESS) {
mgbalog("NOT SUCCESS RESPONSE %d", asyncCommand.result);
response.result = asyncCommand.result;
return response;
}
response.result = asyncCommand.result;
response.command = asyncCommand.cmd;
auto remoteCommand = response.command.packetHeader.commandId;
auto remoteCommand = response.command.header.commandId;
if (remoteCommand == COMMAND_ERROR_STATUS) {
if (response.command.packetHeader.dataSizeL != 2 ||
response.command.packetData.bytes[0] !=
command.packetHeader.commandId) {
if (response.command.header.size != 2 ||
response.command.data.bytes[0] != command.header.commandId) {
response.result = CommandResult::UNEXPECTED_RESPONSE;
return response;
} else {
@@ -435,7 +414,7 @@ class LinkMobile {
}
}
if (remoteCommand != (command.packetHeader.commandId | OR_VALUE)) {
if (remoteCommand != (command.header.commandId | OR_VALUE)) {
response.result = CommandResult::UNEXPECTED_RESPONSE;
return response;
}
@@ -443,11 +422,22 @@ class LinkMobile {
return response;
}
bool sendCommandAsync(Command command) { // (irq only)
if (asyncCommand.isActive /* || isSendingSyncCommand*/)
return false;
void waitUntilAsyncCommandFinishes() {
u32 lines = 0;
u32 vCount = Link::_REG_VCOUNT;
mgbalog("Sending");
while (asyncCommand.isActive)
if (cmdTimeout(lines, vCount)) {
asyncCommand.isActive = false;
asyncCommand.state = AsyncCommand::State::COMPLETED;
asyncCommand.result = CommandResult::TIMEOUT;
break;
}
}
bool sendCommandAsync(Command command) {
if (asyncCommand.isActive)
return false;
asyncCommand.state = AsyncCommand::State::PENDING;
asyncCommand.result = CommandResult::PENDING;
@@ -461,20 +451,16 @@ class LinkMobile {
if (isSIO32Mode()) {
transferAsync(*((u32*)&command)); // TODO: CHECK ENDIANNESS
asyncCommand.transferred += 4;
} else {
transferAsync(command.magicBytes.magic1);
asyncCommand.transferred++;
}
} else
advance(command.magicBytes.magic1);
return true;
}
bool receiveCommandAsync() { // (irq only)
if (asyncCommand.isActive /* || isSendingSyncCommand*/)
bool receiveCommandAsync() {
if (asyncCommand.isActive)
return false;
mgbalog("Receiving");
asyncCommand.state = AsyncCommand::State::PENDING;
asyncCommand.result = CommandResult::PENDING;
asyncCommand.transferred = 0;
@@ -489,9 +475,9 @@ class LinkMobile {
return true;
}
void sendAsyncCommandSIO8(u32 newData) { // (irq only)
void sendAsyncCommandSIO8(u32 newData) {
const u8* commandBytes = (const u8*)&asyncCommand.cmd;
u32 mainSize = PREAMBLE_SIZE + asyncCommand.cmd.packetHeader.dataSizeL;
u32 mainSize = PREAMBLE_SIZE + asyncCommand.cmd.header.size;
bool isAcknowledgement =
asyncCommand.transferred >= mainSize + CHECKSUM_SIZE + 1;
@@ -500,35 +486,31 @@ class LinkMobile {
if (asyncCommand.transferred < mainSize) {
// Magic Bytes + Packet Header + Packet Data
transferAsync(commandBytes[asyncCommand.transferred]);
asyncCommand.transferred++;
advance(commandBytes[asyncCommand.transferred]);
} else if (asyncCommand.transferred < mainSize + CHECKSUM_SIZE) {
// Packet Checksum
commandBytes += PREAMBLE_SIZE + LINK_MOBILE_MAX_COMMAND_TRANSFER_LENGTH;
transferAsync(commandBytes[asyncCommand.transferred - mainSize]);
asyncCommand.transferred++;
advance(commandBytes[asyncCommand.transferred - mainSize]);
} else if (asyncCommand.transferred == mainSize + CHECKSUM_SIZE) {
// Acknowledgement Signal (1)
transferAsync(DEVICE_GBA | OR_VALUE);
asyncCommand.transferred++;
advance(DEVICE_GBA | OR_VALUE);
} else if (asyncCommand.transferred == mainSize + CHECKSUM_SIZE + 1) {
// Acknowledgement Signal (2)
if (!isSupportedAdapter(newData))
return asyncCommand.fail(CommandResult::INVALID_DEVICE_ID);
transferAsync(0);
asyncCommand.transferred++;
advance(0);
} else if (asyncCommand.transferred == mainSize + CHECKSUM_SIZE + 2) {
// Acknowledgement Signal (3)
if (newData != (asyncCommand.cmd.packetHeader.commandId ^ OR_VALUE))
if (newData != (asyncCommand.cmd.header.commandId ^ OR_VALUE))
return asyncCommand.fail(CommandResult::INVALID_COMMAND_ACK);
asyncCommand.result = CommandResult::SUCCESS;
asyncCommand.state = AsyncCommand::State::COMPLETED;
}
}
void receiveAsyncCommandSIO8(u32 newData) { // (irq only)
void receiveAsyncCommandSIO8(u32 newData) {
u8* commandBytes = (u8*)&asyncCommand.cmd;
u32 mainSize = PREAMBLE_SIZE + asyncCommand.cmd.packetHeader.dataSizeL;
u32 mainSize = PREAMBLE_SIZE + asyncCommand.cmd.header.size;
if (asyncCommand.transferred == 0) {
// Magic Bytes (1)
@@ -536,47 +518,40 @@ class LinkMobile {
return transferAsync(GBA_WAITING);
if (newData != COMMAND_MAGIC_VALUE1)
return asyncCommand.fail(CommandResult::INVALID_MAGIC_BYTES);
transferAsync(GBA_WAITING);
asyncCommand.transferred++;
advance(GBA_WAITING);
} else if (asyncCommand.transferred == 1) {
// Magic Bytes (1)
if (newData != COMMAND_MAGIC_VALUE2)
return asyncCommand.fail(CommandResult::INVALID_MAGIC_BYTES);
transferAsync(GBA_WAITING);
asyncCommand.transferred++;
advance(GBA_WAITING);
} else if (asyncCommand.transferred < PREAMBLE_SIZE) {
// Packet Header
commandBytes[asyncCommand.transferred] = newData;
if (asyncCommand.cmd.packetHeader._unusedDataSizeH_ != 0)
if (asyncCommand.cmd.header._unusedSizeHigh_ != 0)
return asyncCommand.fail(CommandResult::WEIRD_DATA_SIZE);
transferAsync(GBA_WAITING);
asyncCommand.transferred++;
advance(GBA_WAITING);
if (asyncCommand.transferred == PREAMBLE_SIZE)
asyncCommand.expectedChecksum = asyncCommand.cmd.packetHeader.sum();
asyncCommand.expectedChecksum = asyncCommand.cmd.header.sum();
} else if (asyncCommand.transferred < mainSize) {
// Packet Data
commandBytes[asyncCommand.transferred] = newData;
asyncCommand.expectedChecksum += newData;
transferAsync(GBA_WAITING);
asyncCommand.transferred++;
advance(GBA_WAITING);
} else if (asyncCommand.transferred == mainSize) {
// Packet Checksum (1)
if (newData != msB16(asyncCommand.expectedChecksum))
return asyncCommand.fail(CommandResult::WRONG_CHECKSUM);
transferAsync(GBA_WAITING);
asyncCommand.transferred++;
advance(GBA_WAITING);
} else if (asyncCommand.transferred == mainSize + 1) {
// Packet Checksum (2)
if (newData != lsB16(asyncCommand.expectedChecksum))
return asyncCommand.fail(CommandResult::WRONG_CHECKSUM);
transferAsync(DEVICE_GBA | OR_VALUE);
asyncCommand.transferred++;
advance(DEVICE_GBA | OR_VALUE);
} else if (asyncCommand.transferred == mainSize + CHECKSUM_SIZE) {
// Acknowledgement Signal (1)
if (!isSupportedAdapter(newData))
return asyncCommand.fail(CommandResult::INVALID_DEVICE_ID);
transferAsync(asyncCommand.cmd.packetHeader.commandId ^ OR_VALUE);
asyncCommand.transferred++;
advance(asyncCommand.cmd.header.commandId ^ OR_VALUE);
} else if (asyncCommand.transferred == mainSize + CHECKSUM_SIZE + 1) {
// Acknowledgement Signal (2)
if (newData != 0)
@@ -586,20 +561,6 @@ class LinkMobile {
}
}
// TODO: USE?
// u8 waitForMeaningfulByte() {
// u32 lines = 0;
// u32 vCount = Link::_REG_VCOUNT;
// u8 value = GBA_WAITING;
// while ((value = transfer(GBA_WAITING)) == ADAPTER_WAITING) {
// if (cmdTimeout(lines, vCount))
// break;
// }
// return value;
// }
CommandResult acknowledge(u8 localCommandAck, u8 remoteCommandAck) {
if (!isSupportedAdapter(transfer(DEVICE_GBA | OR_VALUE)))
return CommandResult::INVALID_DEVICE_ID;
@@ -620,21 +581,26 @@ class LinkMobile {
Command buildCommand(u8 type, bool withData = false) {
Command command;
command.packetHeader.commandId = type;
command.packetHeader._unused_ = 0;
command.packetHeader._unusedDataSizeH_ = 0;
command.packetHeader.dataSizeL = withData ? (u8)nextCommandDataSize : 0;
command.header.commandId = type;
command.header._unused_ = 0;
command.header._unusedSizeHigh_ = 0;
command.header.size = withData ? (u8)nextCommandDataSize : 0;
if (withData)
command.packetData = nextCommandData;
u16 checksum = command.packetHeader.sum();
for (u32 i = 0; i < command.packetHeader.dataSizeL; i++)
checksum += command.packetData.bytes[i];
command.packetChecksum.checksumH = msB16(checksum);
command.packetChecksum.checksumL = lsB16(checksum);
command.data = nextCommandData;
u16 checksum = command.header.sum();
for (u32 i = 0; i < command.header.size; i++)
checksum += command.data.bytes[i];
command.checksum.high = msB16(checksum);
command.checksum.low = lsB16(checksum);
return command;
}
void advance(u32 data) {
transferAsync(data);
asyncCommand.transferred++;
}
void transferAsync(u32 data) {
// TODO: SEND WITH TIMER INTERRUPT; ADD SMALL WAIT (4 ticks for sio8, 8
// ticks for sio32)