Add general-purpose JoyBoot utility

This commit is contained in:
Jack Andersen 2017-01-06 18:57:28 -10:00
parent ab163e856a
commit 52eae7421b
6 changed files with 195 additions and 38 deletions

View File

@ -137,7 +137,7 @@ static inline double SBig(double val) {return val;}
#endif #endif
class Endpoint; class Endpoint;
class EndpointLocal; class ThreadLocalEndpoint;
enum EJStatFlags enum EJStatFlags
{ {
@ -159,7 +159,7 @@ enum EJoyReturn
GBA_JOYBOOT_ERR_INVALID = 4 GBA_JOYBOOT_ERR_INVALID = 4
}; };
using FGBACallback = std::function<void(EndpointLocal& endpoint, EJoyReturn status)>; using FGBACallback = std::function<void(ThreadLocalEndpoint& endpoint, EJoyReturn status)>;
u64 GetGCTicks(); u64 GetGCTicks();
void WaitGCTicks(u64 ticks); void WaitGCTicks(u64 ticks);

View File

@ -89,19 +89,19 @@ class KawasedoChallenge
u32 x64_totalBytes; u32 x64_totalBytes;
bool m_started = true; bool m_started = true;
void F23(EndpointLocal& endpoint, EJoyReturn status); void F23(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void F25(EndpointLocal& endpoint, EJoyReturn status); void F25(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void F27(EndpointLocal& endpoint, EJoyReturn status); void F27(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void F29(EndpointLocal& endpoint, EJoyReturn status); void F29(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void GBAX02(); void GBAX02();
void GBAX01(EndpointLocal& endpoint); void GBAX01(ThreadLocalEndpoint& endpoint);
void F31(EndpointLocal& endpoint, EJoyReturn status); void F31(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void F33(EndpointLocal& endpoint, EJoyReturn status); void F33(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void F35(EndpointLocal& endpoint, EJoyReturn status); void F35(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void F37(EndpointLocal& endpoint, EJoyReturn status); void F37(ThreadLocalEndpoint& endpoint, EJoyReturn status);
void F39(EndpointLocal& endpoint, EJoyReturn status); void F39(ThreadLocalEndpoint& endpoint, EJoyReturn status);
auto bindThis(void(KawasedoChallenge::*ptmf)(EndpointLocal&, EJoyReturn)) auto bindThis(void(KawasedoChallenge::*ptmf)(ThreadLocalEndpoint&, EJoyReturn))
{ {
return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2); return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2);
} }
@ -121,7 +121,7 @@ public:
class Endpoint class Endpoint
{ {
friend class EndpointLocal; friend class ThreadLocalEndpoint;
enum EJoybusCmds enum EJoybusCmds
{ {
@ -169,7 +169,7 @@ class Endpoint
size_t runBuffer(u8* buffer, u64& remTicks, EWaitResp resp); size_t runBuffer(u8* buffer, u64& remTicks, EWaitResp resp);
bool idleGetStatus(u64& remTicks); bool idleGetStatus(u64& remTicks);
void transferProc(); void transferProc();
void transferWakeup(EndpointLocal& endpoint, u8 status); void transferWakeup(ThreadLocalEndpoint& endpoint, u8 status);
auto bindSync() auto bindSync()
{ {
@ -196,11 +196,11 @@ public:
~Endpoint(); ~Endpoint();
}; };
class EndpointLocal class ThreadLocalEndpoint
{ {
friend class Endpoint; friend class Endpoint;
Endpoint& m_ep; Endpoint& m_ep;
EndpointLocal(Endpoint& ep) : m_ep(ep) {} ThreadLocalEndpoint(Endpoint& ep) : m_ep(ep) {}
public: public:
EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback); EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback);
EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback); EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback);

View File

@ -122,7 +122,7 @@ class Socket
m_socket = socket(PF_INET, SOCK_STREAM, 0); m_socket = socket(PF_INET, SOCK_STREAM, 0);
if (m_socket == -1) if (m_socket == -1)
{ {
fprintf(stderr, "Can't allocate socket"); fprintf(stderr, "Can't allocate socket\n");
return false; return false;
} }
@ -192,14 +192,14 @@ public:
if (bind(m_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) if (bind(m_socket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
{ {
/* Not likely to happen, but... */ /* Not likely to happen, but... */
fprintf(stderr, "Failed to bind listener socket to port %d", port); fprintf(stderr, "Failed to bind listener socket to port %d\n", port);
return false; return false;
} }
if (::listen(m_socket, 0) == -1) if (::listen(m_socket, 0) == -1)
{ {
/* Oops, socket is deaf */ /* Oops, socket is deaf */
fprintf(stderr, "Failed to listen to port %d", port); fprintf(stderr, "Failed to listen to port %d\n", port);
return false; return false;
} }
@ -220,7 +220,7 @@ public:
{ {
EResult res = (errno == EAGAIN) ? EResult::Busy : EResult::Error; EResult res = (errno == EAGAIN) ? EResult::Busy : EResult::Error;
if (res == EResult::Error) if (res == EResult::Error)
fprintf(stderr, "Failed to accept incoming connection: %s", strerror(errno)); fprintf(stderr, "Failed to accept incoming connection: %s\n", strerror(errno));
return res; return res;
} }

View File

@ -1,9 +1,11 @@
#include "jbus/Endpoint.hpp" #include "jbus/Endpoint.hpp"
#define LOG_TRANSFER 0
namespace jbus namespace jbus
{ {
void KawasedoChallenge::F23(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F23(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status != GBA_READY || if (status != GBA_READY ||
(status = endpoint.GBAResetAsync(x10_statusPtr, (status = endpoint.GBAResetAsync(x10_statusPtr,
@ -18,7 +20,7 @@ void KawasedoChallenge::F23(EndpointLocal& endpoint, EJoyReturn status)
} }
} }
void KawasedoChallenge::F25(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F25(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status == GBA_READY) if (status == GBA_READY)
if (*x10_statusPtr != GBA_JSTAT_SEND) if (*x10_statusPtr != GBA_JSTAT_SEND)
@ -37,7 +39,7 @@ void KawasedoChallenge::F25(EndpointLocal& endpoint, EJoyReturn status)
} }
} }
void KawasedoChallenge::F27(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F27(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status == GBA_READY) if (status == GBA_READY)
if (*x10_statusPtr != (GBA_JSTAT_PSF0 | GBA_JSTAT_SEND)) if (*x10_statusPtr != (GBA_JSTAT_PSF0 | GBA_JSTAT_SEND))
@ -56,7 +58,7 @@ void KawasedoChallenge::F27(EndpointLocal& endpoint, EJoyReturn status)
} }
} }
void KawasedoChallenge::F29(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F29(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status != GBA_READY) if (status != GBA_READY)
{ {
@ -83,7 +85,7 @@ void KawasedoChallenge::GBAX02()
xf8_dspHmac.ProcessGBACrypto(); xf8_dspHmac.ProcessGBACrypto();
} }
void KawasedoChallenge::GBAX01(EndpointLocal& endpoint) void KawasedoChallenge::GBAX01(ThreadLocalEndpoint& endpoint)
{ {
x58_currentKey = xf8_dspHmac.x20_publicKey; x58_currentKey = xf8_dspHmac.x20_publicKey;
x5c_initMessage = xf8_dspHmac.x24_authInitCode; x5c_initMessage = xf8_dspHmac.x24_authInitCode;
@ -116,7 +118,7 @@ void KawasedoChallenge::GBAX01(EndpointLocal& endpoint)
} }
} }
void KawasedoChallenge::F31(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F31(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status != GBA_READY) if (status != GBA_READY)
{ {
@ -129,7 +131,9 @@ void KawasedoChallenge::F31(EndpointLocal& endpoint, EJoyReturn status)
return; return;
} }
#if LOG_TRANSFER
printf("PROG [%d/%d]\n", x34_bytesSent, x64_totalBytes); printf("PROG [%d/%d]\n", x34_bytesSent, x64_totalBytes);
#endif
if (x30_justStarted) if (x30_justStarted)
{ {
x30_justStarted = 0; x30_justStarted = 0;
@ -261,7 +265,7 @@ void KawasedoChallenge::F31(EndpointLocal& endpoint, EJoyReturn status)
} }
} }
void KawasedoChallenge::F33(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F33(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status != GBA_READY || if (status != GBA_READY ||
(status = endpoint.GBAGetStatusAsync(x10_statusPtr, (status = endpoint.GBAGetStatusAsync(x10_statusPtr,
@ -276,7 +280,7 @@ void KawasedoChallenge::F33(EndpointLocal& endpoint, EJoyReturn status)
} }
} }
void KawasedoChallenge::F35(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F35(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status == GBA_READY) if (status == GBA_READY)
if (*x10_statusPtr & (GBA_JSTAT_FLAGS_MASK | GBA_JSTAT_RECV)) if (*x10_statusPtr & (GBA_JSTAT_FLAGS_MASK | GBA_JSTAT_RECV))
@ -320,7 +324,7 @@ void KawasedoChallenge::F35(EndpointLocal& endpoint, EJoyReturn status)
} }
} }
void KawasedoChallenge::F37(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F37(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status != GBA_READY || if (status != GBA_READY ||
(status = endpoint.GBAWriteAsync(x18_readBuf, x10_statusPtr, (status = endpoint.GBAWriteAsync(x18_readBuf, x10_statusPtr,
@ -335,10 +339,10 @@ void KawasedoChallenge::F37(EndpointLocal& endpoint, EJoyReturn status)
} }
} }
void KawasedoChallenge::F39(EndpointLocal& endpoint, EJoyReturn status) void KawasedoChallenge::F39(ThreadLocalEndpoint& endpoint, EJoyReturn status)
{ {
if (status == GBA_READY) if (status == GBA_READY)
*x10_statusPtr = GBA_READY; *x10_statusPtr = 0;
x28_ticksAfterXf = 0; x28_ticksAfterXf = 0;
@ -436,11 +440,13 @@ void Endpoint::send(const u8* buffer)
{ {
m_running = false; m_running = false;
} }
#if LOG_TRANSFER
else else
{ {
printf("Send %02x [> %02x%02x%02x%02x] (%lu)\n", buffer[0], printf("Send %02x [> %02x%02x%02x%02x] (%lu)\n", buffer[0],
buffer[1], buffer[2], buffer[3], buffer[4], sentBytes); buffer[1], buffer[2], buffer[3], buffer[4], sentBytes);
} }
#endif
m_timeCmdSent = GetGCTicks(); m_timeCmdSent = GetGCTicks();
} }
@ -483,6 +489,7 @@ size_t Endpoint::seceive(u8* buffer)
if (recvBytes > 5) if (recvBytes > 5)
recvBytes = 5; recvBytes = 5;
#if LOG_TRANSFER
if (recvBytes > 0) if (recvBytes > 0)
{ {
if (m_lastCmd == CMD_STATUS || m_lastCmd == CMD_RESET) if (m_lastCmd == CMD_STATUS || m_lastCmd == CMD_RESET)
@ -498,6 +505,7 @@ size_t Endpoint::seceive(u8* buffer)
(u8)buffer[3], (u8)buffer[4], recvBytes); (u8)buffer[3], (u8)buffer[4], recvBytes);
} }
} }
#endif
return recvBytes; return recvBytes;
} }
@ -542,7 +550,9 @@ bool Endpoint::idleGetStatus(u64& remTicks)
void Endpoint::transferProc() void Endpoint::transferProc()
{ {
#if LOG_TRANSFER
printf("Starting JoyBus transfer thread for channel %d\n", m_chan); printf("Starting JoyBus transfer thread for channel %d\n", m_chan);
#endif
std::unique_lock<std::mutex> lk(m_syncLock); std::unique_lock<std::mutex> lk(m_syncLock);
while (m_running) while (m_running)
@ -596,7 +606,7 @@ void Endpoint::transferProc()
{ {
FGBACallback cb = std::move(m_callback); FGBACallback cb = std::move(m_callback);
m_callback = {}; m_callback = {};
EndpointLocal ep(*this); ThreadLocalEndpoint ep(*this);
cb(ep, xferStatus); cb(ep, xferStatus);
} }
@ -628,10 +638,13 @@ void Endpoint::transferProc()
m_syncCv.notify_all(); m_syncCv.notify_all();
m_dataSocket.close(); m_dataSocket.close();
m_clockSocket.close(); m_clockSocket.close();
#if LOG_TRANSFER
printf("Stopping JoyBus transfer thread for channel %d\n", m_chan); printf("Stopping JoyBus transfer thread for channel %d\n", m_chan);
#endif
} }
void Endpoint::transferWakeup(EndpointLocal& endpoint, u8 status) void Endpoint::transferWakeup(ThreadLocalEndpoint& endpoint, u8 status)
{ {
m_syncCv.notify_all(); m_syncCv.notify_all();
} }
@ -819,7 +832,7 @@ Endpoint::Endpoint(u8 chan, net::Socket&& data, net::Socket&& clock)
Endpoint::~Endpoint() { stop(); } Endpoint::~Endpoint() { stop(); }
EJoyReturn EndpointLocal::GBAGetStatusAsync(u8* status, FGBACallback&& callback) EJoyReturn ThreadLocalEndpoint::GBAGetStatusAsync(u8* status, FGBACallback&& callback)
{ {
if (m_ep.m_cmdIssued) if (m_ep.m_cmdIssued)
return GBA_NOT_READY; return GBA_NOT_READY;
@ -832,7 +845,7 @@ EJoyReturn EndpointLocal::GBAGetStatusAsync(u8* status, FGBACallback&& callback)
return GBA_READY; return GBA_READY;
} }
EJoyReturn EndpointLocal::GBAResetAsync(u8* status, FGBACallback&& callback) EJoyReturn ThreadLocalEndpoint::GBAResetAsync(u8* status, FGBACallback&& callback)
{ {
if (m_ep.m_cmdIssued) if (m_ep.m_cmdIssued)
return GBA_NOT_READY; return GBA_NOT_READY;
@ -845,7 +858,7 @@ EJoyReturn EndpointLocal::GBAResetAsync(u8* status, FGBACallback&& callback)
return GBA_READY; return GBA_READY;
} }
EJoyReturn EndpointLocal::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback) EJoyReturn ThreadLocalEndpoint::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback)
{ {
if (m_ep.m_cmdIssued) if (m_ep.m_cmdIssued)
return GBA_NOT_READY; return GBA_NOT_READY;
@ -859,7 +872,7 @@ EJoyReturn EndpointLocal::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callb
return GBA_READY; return GBA_READY;
} }
EJoyReturn EndpointLocal::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback) EJoyReturn ThreadLocalEndpoint::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback)
{ {
if (m_ep.m_cmdIssued) if (m_ep.m_cmdIssued)
return GBA_NOT_READY; return GBA_NOT_READY;

View File

@ -1,12 +1,16 @@
#include "jbus/Listener.hpp" #include "jbus/Listener.hpp"
#include "jbus/Endpoint.hpp" #include "jbus/Endpoint.hpp"
#define LOG_LISTENER 0
namespace jbus namespace jbus
{ {
void Listener::listenerProc() void Listener::listenerProc()
{ {
#if LOG_LISTENER
printf("JoyBus listener started\n"); printf("JoyBus listener started\n");
#endif
net::IPAddress localhost("0.0.0.0"); net::IPAddress localhost("0.0.0.0");
bool dataBound = false; bool dataBound = false;
@ -18,22 +22,34 @@ void Listener::listenerProc()
if (!(dataBound = m_dataServer.openAndListen(localhost, DataPort))) if (!(dataBound = m_dataServer.openAndListen(localhost, DataPort)))
{ {
m_dataServer = net::Socket(false); m_dataServer = net::Socket(false);
#if LOG_LISTENER
printf("data open failed %s; will retry\n", strerror(errno)); printf("data open failed %s; will retry\n", strerror(errno));
#endif
sleep(1); sleep(1);
} }
else else
{
#if LOG_LISTENER
printf("data listening on port %d\n", DataPort); printf("data listening on port %d\n", DataPort);
#endif
}
} }
if (!clockBound) if (!clockBound)
{ {
if (!(clockBound = m_clockServer.openAndListen(localhost, ClockPort))) if (!(clockBound = m_clockServer.openAndListen(localhost, ClockPort)))
{ {
m_clockServer = net::Socket(false); m_clockServer = net::Socket(false);
#if LOG_LISTENER
printf("clock open failed %s; will retry\n", strerror(errno)); printf("clock open failed %s; will retry\n", strerror(errno));
#endif
sleep(1); sleep(1);
} }
else else
{
#if LOG_LISTENER
printf("clock listening on port %d\n", ClockPort); printf("clock listening on port %d\n", ClockPort);
#endif
}
} }
} }
@ -44,9 +60,17 @@ void Listener::listenerProc()
while (m_running && chan < 4) while (m_running && chan < 4)
{ {
if (m_dataServer.accept(acceptData, hostname) == net::Socket::EResult::OK) if (m_dataServer.accept(acceptData, hostname) == net::Socket::EResult::OK)
{
#if LOG_LISTENER
printf("accepted data connection from %s\n", hostname.c_str()); printf("accepted data connection from %s\n", hostname.c_str());
#endif
}
if (m_clockServer.accept(acceptClock, hostname) == net::Socket::EResult::OK) if (m_clockServer.accept(acceptClock, hostname) == net::Socket::EResult::OK)
{
#if LOG_LISTENER
printf("accepted clock connection from %s\n", hostname.c_str()); printf("accepted clock connection from %s\n", hostname.c_str());
#endif
}
if (acceptData && acceptClock) if (acceptData && acceptClock)
{ {
std::unique_lock<std::mutex> lk(m_queueLock); std::unique_lock<std::mutex> lk(m_queueLock);
@ -58,7 +82,9 @@ void Listener::listenerProc()
m_dataServer.close(); m_dataServer.close();
m_clockServer.close(); m_clockServer.close();
#if LOG_LISTENER
printf("JoyBus listener stopped\n"); printf("JoyBus listener stopped\n");
#endif
} }
void Listener::start() void Listener::start()

View File

@ -1,4 +1,122 @@
#include <stdio.h>
#include "jbus/Listener.hpp"
#include "jbus/Endpoint.hpp"
static void clientPadComplimentCheck(jbus::u8* buffer)
{
jbus::u8 check = 0x19;
for (int i=0xa0 ; i<0xbd ; ++i)
check += buffer[i];
buffer[0xbd] = -check;
}
static jbus::EJoyReturn BootStatus = jbus::GBA_JOYBOOT_ERR_INVALID;
static void JoyBootDone(jbus::ThreadLocalEndpoint& endpoint, jbus::EJoyReturn status)
{
BootStatus = status;
}
static bool DonePoll(jbus::Endpoint& endpoint)
{
jbus::u8 status;
if (endpoint.GBAReset(&status) == jbus::GBA_NOT_READY)
if (endpoint.GBAReset(&status) == jbus::GBA_NOT_READY)
return false;
if (endpoint.GBAGetStatus(&status) == jbus::GBA_NOT_READY)
return false;
if (status != (jbus::GBA_JSTAT_PSF1 | jbus::GBA_JSTAT_SEND))
return false;
return true;
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
if (argc < 2)
{
printf("Usage: joyboot <client_pad.bin>\n");
return 1;
}
FILE* fp = fopen(argv[1], "rb");
if (!fp)
{
fprintf(stderr, "Unable to open %s\n", argv[1]);
return 1;
}
fseek(fp, 0, SEEK_END);
int fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
std::unique_ptr<jbus::u8[]> data(new jbus::u8[fsize]);
fread(data.get(), 1, fsize, fp);
fclose(fp);
if (fsize < 512)
{
fprintf(stderr, "%s must be at least 512 bytes\n", argv[1]);
return 1;
}
clientPadComplimentCheck(data.get());
jbus::Initialize();
printf("Listening for client\n");
jbus::Listener listener;
listener.start();
std::unique_ptr<jbus::Endpoint> endpoint;
while (true)
{
jbus::s64 frameStart = jbus::GetGCTicks();
endpoint = listener.accept();
if (endpoint)
break;
jbus::s64 frameEnd = jbus::GetGCTicks();
jbus::s64 passedTicks = frameEnd - frameStart;
jbus::s64 waitTicks = jbus::GetGCTicksPerSec() / 60 - passedTicks;
if (waitTicks > 0)
jbus::WaitGCTicks(waitTicks);
}
printf("Waiting 4 sec\n");
jbus::WaitGCTicks(jbus::GetGCTicksPerSec() * 4);
jbus::u8 status;
if (endpoint->GBAJoyBootAsync(2, 2, data.get(), fsize,
&status, JoyBootDone) != jbus::GBA_READY)
{
fprintf(stderr, "Unable to start JoyBoot\n");
return 1;
}
jbus::s64 start = jbus::GetGCTicks();
jbus::u8 percent = 0;
while (endpoint->GBAGetProcessStatus(&percent) == jbus::GBA_BUSY)
{
printf("\rUpload %d%%", percent);
fflush(stdout);
jbus::s64 curTime = jbus::GetGCTicks();
jbus::s64 passedTicks = curTime - start;
if (passedTicks > jbus::GetGCTicksPerSec() * 5)
{
fprintf(stderr, "JoyBoot timeout\n");
return 1;
}
jbus::WaitGCTicks(jbus::GetGCTicksPerSec() / 60);
}
printf("\nJoy Boot finished with %d status\n", status);
while (!DonePoll(*endpoint))
{
jbus::s64 curTime = jbus::GetGCTicks();
jbus::s64 passedTicks = curTime - start;
if (passedTicks > jbus::GetGCTicksPerSec() * 10)
{
fprintf(stderr, "JoyBoot timeout\n");
return 1;
}
jbus::WaitGCTicks(jbus::GetGCTicksPerSec() / 60);
}
return 0;
} }