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