Implementing async command sending

This commit is contained in:
Rodrigo Alfonso 2024-08-10 07:34:42 -03:00
parent 7f571d8b65
commit 0549f3c501
4 changed files with 208 additions and 87 deletions

View File

@ -64,8 +64,13 @@ start:
firstTime = false;
}
again:
// (3) Initialize the library
linkMobile->activate();
if (!linkMobile->activate()) {
log("Adapter not connected!\n\nPress A to try again");
waitFor(KEY_A);
goto again;
}
bool activating = false;
@ -153,4 +158,4 @@ void wait(u32 verticalLines) {
void hang() {
waitFor(KEY_DOWN);
}
}

View File

@ -200,7 +200,7 @@ class LinkCableMultiboot {
PartialResult sendHeader(const u8* rom, F cancel) {
u16* headerOut = (u16*)rom;
for (int i = 0; i < HEADER_SIZE; i += 2) {
for (u32 i = 0; i < HEADER_SIZE; i += 2) {
linkRawCable->transfer(*(headerOut++), cancel);
if (cancel())
return ABORTED;

View File

@ -96,11 +96,7 @@ class LinkMobile {
enum State {
NEEDS_RESET,
AUTHENTICATED,
SEARCHING,
SERVING,
CONNECTING,
CONNECTED
SESSION_ACTIVE,
};
enum Error {
@ -119,20 +115,22 @@ class LinkMobile {
bool activate() {
lastError = NONE;
isEnabled = false;
isEnabled = false; // TODO: EARLY ACTIVATE
LINK_MOBILE_BARRIER;
bool success = reset();
LINK_MOBILE_BARRIER;
if (!success) {
deactivate();
return false;
}
isEnabled = true;
return success;
return true;
}
bool deactivate() {
activate();
// bool success = sendCommand(LINK_MOBILE_COMMAND_BYE).success;
lastError = NONE;
isEnabled = false;
resetState();
@ -159,6 +157,33 @@ class LinkMobile {
void _onSerial() {
if (!isEnabled)
return;
linkSPI->_onSerial();
if (linkSPI->getAsyncState() != LinkSPI::AsyncState::READY) {
mgbalog("NOT READY");
return;
}
u32 newData = linkSPI->getAsyncData();
if (state != SESSION_ACTIVE) { // TODO: AUTHENTICATING state
mgbalog("SESSION NOT ACTIVE");
return;
}
if (asyncCommand.isActive) {
if (asyncCommand.state == AsyncCommand::State::PENDING) {
if (isSIO32Mode()) {
// TODO: IMPLEMENT
mgbalog("sio32");
} else {
updateAsyncCommandSIO8(newData);
}
if (asyncCommand.state == AsyncCommand::State::COMPLETED) {
processAsyncCommand();
}
}
}
}
void _onTimer() {
@ -176,10 +201,6 @@ class LinkMobile {
struct MagicBytes {
u8 magic1 = COMMAND_MAGIC_VALUE1;
u8 magic2 = COMMAND_MAGIC_VALUE2;
bool isValid() {
return magic1 == COMMAND_MAGIC_VALUE1 && magic2 == COMMAND_MAGIC_VALUE2;
}
} __attribute__((packed));
struct PacketData {
@ -212,7 +233,8 @@ class LinkMobile {
PacketChecksum packetChecksum;
};
enum CommandStatus {
enum CommandResult {
PENDING,
SUCCESS,
NOT_WAITING,
INVALID_DEVICE_ID,
@ -225,14 +247,28 @@ class LinkMobile {
};
struct CommandResponse {
CommandStatus status;
CommandResult result = CommandResult::PENDING;
Command command;
};
struct AsyncCommand {
enum State { PENDING, COMPLETED };
enum Direction { SENDING, RECEIVING };
volatile State state;
volatile CommandResult result;
volatile u32 transferred; // (in SIO32: words; in SIO8: bytes)
Command cmd;
volatile Direction direction;
volatile bool isWaiting;
volatile bool isActive = false;
};
static constexpr u32 PREAMBLE_SIZE =
sizeof(MagicBytes) + sizeof(PacketHeader);
static constexpr u32 CHECKSUM_SIZE = sizeof(PacketChecksum);
AsyncCommand asyncCommand;
LinkSPI* linkSPI = new LinkSPI();
State state = NEEDS_RESET;
PacketData nextCommandData;
@ -241,6 +277,12 @@ class LinkMobile {
Error lastError = NONE;
volatile bool isEnabled = false;
void processAsyncCommand() { // (irq only)
asyncCommand.isActive = false;
// TODO: IMPLEMENT
mgbalog("PROCESSED! %d", asyncCommand.result);
}
void addData(u8 value, bool start = false) {
if (start)
nextCommandDataSize = 0;
@ -264,7 +306,10 @@ class LinkMobile {
return start();
}
void resetState() { this->state = NEEDS_RESET; }
void resetState() {
this->asyncCommand.isActive = false;
this->state = NEEDS_RESET;
}
void stop() {
stopTimer();
@ -284,7 +329,7 @@ class LinkMobile {
// TODO: SWITCH TO 32BITS?
state = AUTHENTICATED;
state = SESSION_ACTIVE;
return true;
}
@ -312,19 +357,19 @@ class LinkMobile {
return withSyncCommand([&command, this]() {
auto response = sendCommandWithResponse(command);
if (response.status != CommandStatus::SUCCESS) {
if (response.result != CommandResult::SUCCESS) {
// TODO: RESET
if (response.status == CommandStatus::ERROR) {
if (response.result == CommandResult::ERROR) {
mgbalog("error %d", response.command.packetData.bytes[1]);
} else {
mgbalog("status %d", response.status);
mgbalog("result %d", response.result);
}
return false;
} else {
mgbalog("success size %d", response.command.packetHeader.dataSizeL);
}
mgbalog("SUCCESS!");
return true;
return response.result == CommandResult::SUCCESS;
});
}
@ -345,9 +390,15 @@ class LinkMobile {
CommandResponse sendCommandWithResponse(Command command) {
CommandResponse response;
CommandStatus status = sendCommand(command);
if (status != CommandStatus::SUCCESS) {
response.status = status;
isEnabled = true; // TODO: !!!
state = SESSION_ACTIVE; // TODO: !!!
sendCommandAsync(command);
while (asyncCommand.isActive)
;
if (asyncCommand.result != CommandResult::SUCCESS) {
mgbalog("NOT SUCCESS 1 %d", asyncCommand.result);
response.result = asyncCommand.result;
return response;
}
@ -357,57 +408,33 @@ class LinkMobile {
if (response.command.packetHeader.dataSizeL != 2 ||
response.command.packetData.bytes[0] !=
command.packetHeader.commandId) {
response.status = CommandStatus::UNEXPECTED_RESPONSE;
response.result = CommandResult::UNEXPECTED_RESPONSE;
return response;
} else {
response.status = CommandStatus::ERROR;
response.result = CommandResult::ERROR;
return response;
}
}
if (remoteCommand != (command.packetHeader.commandId | OR_VALUE)) {
response.status = CommandStatus::UNEXPECTED_RESPONSE;
response.result = CommandResult::UNEXPECTED_RESPONSE;
return response;
}
return response;
}
CommandStatus sendCommand(Command command) {
const u8* commandBytes = (const u8*)&command;
// Magic Bytes + Packet Header + Data
for (u32 i = 0; i < PREAMBLE_SIZE + command.packetHeader.dataSizeL; i++) {
if (transfer(commandBytes[i]) != ADAPTER_WAITING)
return CommandStatus::NOT_WAITING;
}
commandBytes += PREAMBLE_SIZE + LINK_MOBILE_MAX_COMMAND_TRANSFER_LENGTH;
// Packet Checksum
for (u32 i = 0; i < CHECKSUM_SIZE; i++) {
if (transfer(commandBytes[i]) != ADAPTER_WAITING)
return CommandStatus::NOT_WAITING;
}
// Acknowledgement Signal
auto ackResult = acknowledge(0, command.packetHeader.commandId ^ OR_VALUE);
if (ackResult != SUCCESS)
return ackResult;
return CommandStatus::SUCCESS;
}
CommandResponse receiveCommand() {
CommandResponse response;
u8* responseBytes = (u8*)&response.command;
// Magic Bytes
if (waitForMeaningfulByte() != COMMAND_MAGIC_VALUE1) {
response.status = CommandStatus::INVALID_MAGIC_BYTES;
response.result = CommandResult::INVALID_MAGIC_BYTES;
return response;
}
if (transfer(GBA_WAITING) != COMMAND_MAGIC_VALUE2) {
response.status = CommandStatus::INVALID_MAGIC_BYTES;
response.result = CommandResult::INVALID_MAGIC_BYTES;
return response;
}
@ -416,7 +443,7 @@ class LinkMobile {
responseBytes[i] = transfer(GBA_WAITING);
responseBytes += PREAMBLE_SIZE;
if (response.command.packetHeader._unusedDataSizeH_ != 0) {
response.status = CommandStatus::WEIRD_DATA_SIZE;
response.result = CommandResult::WEIRD_DATA_SIZE;
return response;
}
@ -433,7 +460,7 @@ class LinkMobile {
rChecksum += response.command.packetData.bytes[i];
if (msB16(rChecksum) != response.command.packetChecksum.checksumH ||
lsB16(rChecksum) != response.command.packetChecksum.checksumL) {
response.status = CommandStatus::WRONG_CHECKSUM;
response.result = CommandResult::WRONG_CHECKSUM;
return response;
}
@ -441,15 +468,110 @@ class LinkMobile {
auto ackResult =
acknowledge(response.command.packetHeader.commandId ^ OR_VALUE, 0);
if (ackResult != SUCCESS) {
response.status = ackResult;
response.result = ackResult;
return response;
}
response.status = CommandStatus::SUCCESS;
response.result = CommandResult::SUCCESS;
return response;
}
bool sendCommandAsync(Command command) { // (irq only)
if (asyncCommand.isActive /* || isSendingSyncCommand*/)
return false;
mgbalog("Sending");
asyncCommand.state = AsyncCommand::State::PENDING;
asyncCommand.result = CommandResult::PENDING;
asyncCommand.transferred = 0;
asyncCommand.cmd = command;
asyncCommand.direction = AsyncCommand::Direction::SENDING;
asyncCommand.isWaiting = false;
asyncCommand.isActive = true;
if (isSIO32Mode()) {
transferAsync(*((u32*)&command)); // TODO: CHECK ENDIANNESS
asyncCommand.transferred += 4;
} else {
transferAsync(command.magicBytes.magic1);
asyncCommand.transferred++;
}
return true;
}
void updateAsyncCommandSIO8(u32 newData) { // (irq only)
mgbalog("Updating");
const u8* commandBytes = (const u8*)&asyncCommand.cmd;
u32 preambleAndDataSize =
PREAMBLE_SIZE + asyncCommand.cmd.packetHeader.dataSizeL;
if (asyncCommand.transferred < preambleAndDataSize) {
// Magic Bytes + Packet Header + Data
if (newData != ADAPTER_WAITING) {
asyncCommand.result = CommandResult::NOT_WAITING;
asyncCommand.state = AsyncCommand::State::COMPLETED;
return;
}
transferAsync(commandBytes[asyncCommand.transferred]);
asyncCommand.transferred++;
} else if (asyncCommand.transferred < preambleAndDataSize + CHECKSUM_SIZE) {
commandBytes += PREAMBLE_SIZE + LINK_MOBILE_MAX_COMMAND_TRANSFER_LENGTH;
// Packet Checksum
if (newData != ADAPTER_WAITING) {
asyncCommand.result = CommandResult::NOT_WAITING;
asyncCommand.state = AsyncCommand::State::COMPLETED;
return;
}
transferAsync(
commandBytes[asyncCommand.transferred - preambleAndDataSize]);
asyncCommand.transferred++;
} else if (asyncCommand.transferred ==
preambleAndDataSize + CHECKSUM_SIZE) {
// Acknowledgement Signal (1)
if (newData != ADAPTER_WAITING) {
asyncCommand.result = CommandResult::NOT_WAITING;
asyncCommand.state = AsyncCommand::State::COMPLETED;
return;
}
transferAsync(DEVICE_GBA | OR_VALUE);
asyncCommand.transferred++;
} else if (asyncCommand.transferred ==
preambleAndDataSize + CHECKSUM_SIZE + 1) {
// Acknowledgement Signal (2)
if (!isSupportedAdapter(newData)) {
asyncCommand.result = CommandResult::INVALID_DEVICE_ID;
asyncCommand.state = AsyncCommand::State::COMPLETED;
return;
}
transferAsync(0);
asyncCommand.transferred++;
} else if (asyncCommand.transferred ==
preambleAndDataSize + CHECKSUM_SIZE + 2) {
// Acknowledgement Signal (3)
if (newData != (asyncCommand.cmd.packetHeader.commandId ^ OR_VALUE)) {
asyncCommand.result = CommandResult::INVALID_COMMAND_ACK;
asyncCommand.state = AsyncCommand::State::COMPLETED;
mgbalog("COMMAND ACK: %d, expected %d, cmd %d", newData,
asyncCommand.cmd.packetHeader.commandId ^ OR_VALUE,
asyncCommand.cmd.packetHeader.commandId);
return;
}
asyncCommand.result = CommandResult::SUCCESS;
asyncCommand.state = AsyncCommand::State::COMPLETED;
} else {
mgbalog("wtf"); // TODO: REMOVE
}
}
u8 waitForMeaningfulByte() {
u32 lines = 0;
u32 vCount = Link::_REG_VCOUNT;
@ -463,13 +585,13 @@ class LinkMobile {
return value;
}
CommandStatus acknowledge(u8 localCommandAck, u8 remoteCommandAck) {
CommandResult acknowledge(u8 localCommandAck, u8 remoteCommandAck) {
if (!isSupportedAdapter(transfer(DEVICE_GBA | OR_VALUE)))
return CommandStatus::INVALID_DEVICE_ID;
return CommandResult::INVALID_DEVICE_ID;
if (transfer(localCommandAck) != remoteCommandAck)
return CommandStatus::INVALID_COMMAND_ACK;
return CommandResult::INVALID_COMMAND_ACK;
return CommandStatus::SUCCESS;
return CommandResult::SUCCESS;
}
bool isSupportedAdapter(u8 ack) {
@ -498,6 +620,13 @@ class LinkMobile {
return command;
}
void transferAsync(u32 data) {
// TODO: SEND WITH TIMER INTERRUPT; ADD SMALL WAIT (4 ticks for sio8, 8
// ticks for sio32)
linkSPI->transferAsync(data);
// TODO: asyncCommand.isWaiting = true;
}
u32 transfer(u32 data) {
u32 lines = 0;
u32 vCount = Link::_REG_VCOUNT;
@ -507,6 +636,10 @@ class LinkMobile {
return receivedData;
}
bool isSIO32Mode() {
return linkSPI->getDataSize() == LinkSPI::DataSize::SIZE_32BIT;
}
bool cmdTimeout(u32& lines, u32& vCount) {
return timeout(CMD_TIMEOUT, lines, vCount);
}
@ -532,23 +665,6 @@ class LinkMobile {
};
}
template <typename F>
void waitVBlanks(u32 vBlanks, F onVBlank) {
u32 count = 0;
u32 vCount = Link::_REG_VCOUNT;
while (count < vBlanks) {
if (Link::_REG_VCOUNT != vCount) {
vCount = Link::_REG_VCOUNT;
if (vCount == 160) {
onVBlank();
count++;
}
}
};
}
u32 buildU32(u16 msB, u16 lsB) { return (msB << 16) | lsB; }
u16 buildU16(u8 msB, u8 lsB) { return (msB << 8) | lsB; }
u16 msB32(u32 value) { return value >> 16; }

View File

@ -136,7 +136,7 @@ class LinkPS2Mouse {
s16 x = readByte();
if ((status & (1 << 4)) != 0)
// negative
for (int i = 8; i < 16; i++)
for (u32 i = 8; i < 16; i++)
x |= 1 << i;
return x;
}
@ -145,7 +145,7 @@ class LinkPS2Mouse {
s16 y = readByte();
if ((status & (1 << 5)) != 0)
// negative
for (int i = 8; i < 16; i++)
for (u32 i = 8; i < 16; i++)
y |= 1 << i;
return y;
}
@ -203,7 +203,7 @@ class LinkPS2Mouse {
;
while (!getClock())
; // eat start bit
for (int i = 0; i < 8; i++) {
for (u32 i = 0; i < 8; i++) {
data |= readBit() << i;
}
readBit(); // parity bit