JBus
JoyBus communication server
Endpoint.hpp
Go to the documentation of this file.
1 #ifndef JBUS_ENDPOINT_HPP
2 #define JBUS_ENDPOINT_HPP
3 
4 #include "Common.hpp"
5 #include "Socket.hpp"
6 #include "optional.hpp"
7 #include <thread>
8 #include <mutex>
9 
10 namespace jbus
11 {
12 
15 class Endpoint
16 {
23  class KawasedoChallenge
24  {
27  struct DSPSecParms
28  {
29  /* Nonce challenge (first read from GBA, hence already little-endian) */
30  u32 x0_gbaChallenge;
31 
32  /* Palette of pulsing logo on GBA during transmission [0,6] */
33  u32 x4_logoPalette;
34 
35  /* Speed and direction of palette interpolation [-4,4] */
36  u32 x8_logoSpeed;
37 
38  /* Length of JoyBoot program to upload */
39  u32 xc_progLength;
40 
41  /* Unwrapped public key */
42  u32 x20_publicKey;
43 
44  /* Message authentication code */
45  u32 x24_authInitCode;
46 
47  void ProcessGBACrypto()
48  {
49  /* Unwrap key from challenge using 'sedo' magic number (to encrypt JoyBoot program) */
50  x20_publicKey = x0_gbaChallenge ^ 0x6f646573;
51 
52  /* Pack palette parameters */
53  u16 paletteSpeedCoded;
54  s16 logoSpeed = static_cast<s8>(x8_logoSpeed);
55  if (logoSpeed < 0)
56  paletteSpeedCoded = ((-logoSpeed + 2) * 2) | (x4_logoPalette << 4);
57  else if (logoSpeed == 0)
58  paletteSpeedCoded = (x4_logoPalette * 2) | 0x70;
59  else /* logo_speed > 0 */
60  paletteSpeedCoded = ((logoSpeed - 1) * 2) | (x4_logoPalette << 4);
61 
62  /* JoyBoot ROMs start with a padded header; this is the length beyond that header */
63  s32 lengthNoHeader = ROUND_UP_8(xc_progLength) - 0x200;
64 
65  /* The JoyBus protocol transmits in 4-byte packets while flipping a state flag;
66  * so the GBA BIOS counts the program length in 8-byte packet-pairs */
67  u16 packetPairCount = (lengthNoHeader < 0) ? 0 : lengthNoHeader / 8;
68  paletteSpeedCoded |= (packetPairCount & 0x4000) >> 14;
69 
70  /* Pack together encoded transmission parameters */
71  u32 t1 = (((packetPairCount << 16) | 0x3f80) & 0x3f80ffff) * 2;
72  t1 += (static_cast<s16>(static_cast<s8>(t1 >> 8)) & packetPairCount) << 16;
73  u32 t2 = ((paletteSpeedCoded & 0xff) << 16) + (t1 & 0xff0000) + ((t1 >> 8) & 0xffff00);
74  u32 t3 = paletteSpeedCoded << 16 | ((t2 << 8) & 0xff000000) | (t1 >> 16) | 0x80808080;
75 
76  /* Wrap with 'Kawa' or 'sedo' (Kawasedo is the author of the BIOS cipher) */
77  x24_authInitCode = t3 ^ ((t3 & 0x200) != 0 ? 0x6f646573 : 0x6177614b);
78  }
79  } xf8_dspHmac;
80 
81  s32 x0_pColor;
82  s32 x4_pSpeed;
83  const u8* x8_progPtr;
84  u32 xc_progLen;
85  u8* x10_statusPtr;
86  FGBACallback x14_callback;
87  u8 x18_readBuf[4];
88  u8 x1c_writeBuf[4];
89  s32 x20_byteInWindow;
90  u64 x28_ticksAfterXf;
91  u32 x30_justStarted;
92  u32 x34_bytesSent;
93  u32 x38_crc;
94  u32 x3c_checkStore[7];
95  s32 x58_currentKey;
96  s32 x5c_initMessage;
97  s32 x60_gameId;
98  u32 x64_totalBytes;
99  bool m_started = true;
100 
101  void _0Reset(ThreadLocalEndpoint& endpoint, EJoyReturn status);
102  void _1GetStatus(ThreadLocalEndpoint& endpoint, EJoyReturn status);
103  void _2ReadChallenge(ThreadLocalEndpoint& endpoint, EJoyReturn status);
104  void _3DSPCrypto(ThreadLocalEndpoint& endpoint, EJoyReturn status);
105  void _DSPCryptoInit();
106  void _DSPCryptoDone(ThreadLocalEndpoint& endpoint);
107  void _4TransmitProgram(ThreadLocalEndpoint& endpoint, EJoyReturn status);
108  void _5StartBootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status);
109  void _6BootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status);
110  void _7BootAcknowledge(ThreadLocalEndpoint& endpoint, EJoyReturn status);
111  void _8BootDone(ThreadLocalEndpoint& endpoint, EJoyReturn status);
112 
113  auto bindThis(void(KawasedoChallenge::*ptmf)(ThreadLocalEndpoint&, EJoyReturn))
114  {
115  return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2);
116  }
117 
118  public:
119  KawasedoChallenge(Endpoint& endpoint, s32 paletteColor, s32 paletteSpeed,
120  const u8* programp, s32 length, u8* status, FGBACallback&& callback);
121  bool started() const { return m_started; }
122  u8 percentComplete() const
123  {
124  if (!x64_totalBytes)
125  return 0;
126  return x34_bytesSent * 100 / x64_totalBytes;
127  }
128  bool isDone() const { return !x14_callback; }
129  };
130 
131  friend class ThreadLocalEndpoint;
132 
133  enum EJoybusCmds
134  {
135  CMD_RESET = 0xff,
136  CMD_STATUS = 0x00,
137  CMD_READ = 0x14,
138  CMD_WRITE = 0x15
139  };
140 
141  static const u64 BITS_PER_SECOND = 115200;
142  static const u64 BYTES_PER_SECOND = BITS_PER_SECOND / 8;
143 
144  net::Socket m_dataSocket;
145  net::Socket m_clockSocket;
146  std::thread m_transferThread;
147  std::mutex m_syncLock;
148  std::condition_variable m_syncCv;
149  std::condition_variable m_issueCv;
150  std::experimental::optional<KawasedoChallenge> m_joyBoot;
151  FGBACallback m_callback;
152  u8 m_buffer[5];
153  u8* m_readDstPtr = nullptr;
154  u8* m_statusPtr = nullptr;
155  u64 m_lastGCTick = 0;
156  u8 m_lastCmd = 0;
157  u8 m_chan;
158  bool m_booted = false;
159  bool m_cmdIssued = false;
160  bool m_running = true;
161 
162  void clockSync();
163  void send(const u8* buffer);
164  size_t receive(u8* buffer);
165  size_t runBuffer(u8* buffer, std::unique_lock<std::mutex>& lk);
166  bool idleGetStatus(std::unique_lock<std::mutex>& lk);
167  void transferProc();
168  void transferWakeup(ThreadLocalEndpoint& endpoint, u8 status);
169 
170  auto bindSync()
171  {
172  return std::bind(&Endpoint::transferWakeup, this,
173  std::placeholders::_1, std::placeholders::_2);
174  }
175 
176 public:
180  void stop();
181 
185  EJoyReturn GBAGetProcessStatus(u8& percentOut);
186 
191  EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback);
192 
196  EJoyReturn GBAGetStatus(u8* status);
197 
202  EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback);
203 
207  EJoyReturn GBAReset(u8* status);
208 
214  EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback);
215 
220  EJoyReturn GBARead(u8* dst, u8* status);
221 
227  EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback);
228 
233  EJoyReturn GBAWrite(const u8* src, u8* status);
234 
243  EJoyReturn GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed,
244  const u8* programp, s32 length, u8* status,
245  FGBACallback&& callback);
246 
249  int GetChan() const { return m_chan; }
250 
251  Endpoint(u8 chan, net::Socket&& data, net::Socket&& clock);
252  ~Endpoint();
253 };
254 
259 {
260  friend class Endpoint;
261  Endpoint& m_ep;
262  ThreadLocalEndpoint(Endpoint& ep) : m_ep(ep) {}
263 
264 public:
269  EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback);
270 
275  EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback);
276 
282  EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback);
283 
289  EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback);
290 
293  int GetChan() const { return m_ep.GetChan(); }
294 };
295 
296 }
297 
298 #endif // JBUS_ENDPOINT_HPP
EJoyReturn GBARead(u8 *dst, u8 *status)
Send READ command to GBA synchronously.
uint16_t u16
Definition: Common.hpp:14
EJoyReturn GBAGetStatus(u8 *status)
Get JOYSTAT register from GBA synchronously.
int GetChan() const
Get virtual SI channel assigned to this endpoint.
Definition: Endpoint.hpp:293
int8_t s8
Definition: Common.hpp:11
std::function< void(ThreadLocalEndpoint &endpoint, EJoyReturn status)> FGBACallback
Standard callback for asynchronous jbus::Endpoint APIs.
Definition: Common.hpp:169
uint8_t u8
Definition: Common.hpp:12
friend class ThreadLocalEndpoint
Definition: Endpoint.hpp:131
Definition: Common.hpp:8
Definition: Socket.hpp:103
EJoyReturn GBAWriteAsync(const u8 *src, u8 *status, FGBACallback &&callback)
Send WRITE command to GBA asynchronously.
uint64_t u64
Definition: Common.hpp:18
Endpoint(u8 chan, net::Socket &&data, net::Socket &&clock)
int16_t s16
Definition: Common.hpp:13
int32_t s32
Definition: Common.hpp:15
EJoyReturn GBAGetStatusAsync(u8 *status, FGBACallback &&callback)
Get JOYSTAT register from GBA asynchronously.
EJoyReturn GBAReadAsync(u8 *dst, u8 *status, FGBACallback &&callback)
Send READ command to GBA asynchronously.
EJoyReturn GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed, const u8 *programp, s32 length, u8 *status, FGBACallback &&callback)
Initiate JoyBoot sequence on this endpoint.
EJoyReturn GBAWrite(const u8 *src, u8 *status)
Send WRITE command to GBA synchronously.
EJoyReturn
Definition: Common.hpp:157
Definition: Endpoint.hpp:15
uint32_t u32
Definition: Common.hpp:16
void stop()
Request stop of I/O thread and block until joined. Further use of this Endpoint is undefined behavior...
EJoyReturn GBAReset(u8 *status)
Send RESET command to GBA synchronously.
int GetChan() const
Get virtual SI channel assigned to this endpoint.
Definition: Endpoint.hpp:249
EJoyReturn GBAResetAsync(u8 *status, FGBACallback &&callback)
Send RESET command to GBA asynchronously.
EJoyReturn GBAGetProcessStatus(u8 &percentOut)
Get status of last asynchronous operation.
Definition: Endpoint.hpp:258