-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathserial.c
306 lines (262 loc) · 9.69 KB
/
serial.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "serial.h"
#include "mobile_data.h"
void mobile_serial_init(struct mobile_adapter *adapter)
{
adapter->serial.state = MOBILE_SERIAL_INIT;
adapter->serial.mode_32bit = false;
adapter->serial.active = false;
}
uint8_t mobile_serial_transfer(struct mobile_adapter *adapter, uint8_t c)
{
struct mobile_adapter_serial *s = &adapter->serial;
struct mobile_buffer_serial *b = &adapter->buffer.serial;
// Workaround for atomic load in clang...
enum mobile_serial_state state = s->state;
switch (state) {
case MOBILE_SERIAL_INIT:
b->current = 0;
s->state = MOBILE_SERIAL_WAITING;
// fallthrough
case MOBILE_SERIAL_WAITING:
// Wait for the bytes that indicate a packet will be sent.
if (c == 0x99) {
b->current = 1;
} else if (c == 0x66 && b->current == 1) {
// Initialize transfer state
b->data_size = 0;
b->checksum = 0;
b->error = 0;
b->current = 0;
s->state = MOBILE_SERIAL_HEADER;
} else {
b->current = 0;
}
break;
case MOBILE_SERIAL_HEADER:
// Receive the header.
b->header[b->current++] = c;
b->checksum += c;
if (b->current < sizeof(b->header)) break;
// Done receiving the header, read content size.
b->data_size = b->header[3];
// Data size is a u16be, but it may not be bigger than 0xff...
if (b->header[2] != 0) {
b->current = 0;
s->state = MOBILE_SERIAL_WAITING;
}
if (!adapter->commands.session_started) {
// If we haven't begun a session, this is as good as any place
// to stop parsing, as we shouldn't react to this.
// TODO: Re-verify this behavior on hardware.
if (b->header[0] != MOBILE_COMMAND_START) {
b->current = 0;
s->state = MOBILE_SERIAL_WAITING;
}
// Update device type
unsigned char d = adapter->config.device;
s->device = d & ~MOBILE_CONFIG_DEVICE_UNMETERED;
s->device_unmetered = d & MOBILE_CONFIG_DEVICE_UNMETERED;
}
// If the command doesn't exist, set the error...
if (!mobile_commands_exists(b->header[0])) {
b->error = MOBILE_SERIAL_ERROR_UNKNOWN_COMMAND;
}
b->current = 0;
if (b->data_size) {
s->state = MOBILE_SERIAL_DATA;
} else {
s->state = MOBILE_SERIAL_CHECKSUM;
}
break;
case MOBILE_SERIAL_DATA:
// Receive the data
s->buffer[b->current++] = c;
b->checksum += c;
if (b->current >= b->data_size) {
if (s->mode_32bit && b->current % 4) {
b->current = 4 - (b->current % 4);
s->state = MOBILE_SERIAL_DATA_PAD;
} else {
b->current = 0;
s->state = MOBILE_SERIAL_CHECKSUM;
}
}
break;
case MOBILE_SERIAL_DATA_PAD:
// In 32bit mode, we must add some extra padding
if (!--b->current) s->state = MOBILE_SERIAL_CHECKSUM;
break;
case MOBILE_SERIAL_CHECKSUM:
// Receive the checksum, verify it when done.
b->footer[b->current++] = c;
if (b->current >= sizeof(b->footer)) {
uint16_t in_checksum = b->footer[0] << 8 | b->footer[1];
if (b->checksum != in_checksum) {
b->error = MOBILE_SERIAL_ERROR_CHECKSUM;
}
b->current = 0;
s->state = MOBILE_SERIAL_ACKNOWLEDGE;
return s->device | 0x80;
}
break;
case MOBILE_SERIAL_ACKNOWLEDGE:
// Receive the acknowledgement byte, send error if applicable.
// The acknowledgement header is only properly handled by 32bit mode
// in mobile_serial_transfer_32bit(). This exists for software
// emulators that only implement 8-bit mode.
if (s->mode_32bit) {
b->current = 2;
s->state = MOBILE_SERIAL_ACKNOWLEDGE_PAD;
return b->error ? b->error : b->header[0] ^ 0x80;
}
// The blue adapter doesn't check the device ID apparently,
// the other adapters don't check it in 32bit mode.
if (s->device != MOBILE_ADAPTER_BLUE &&
c != (MOBILE_ADAPTER_GAMEBOY | 0x80) &&
c != (MOBILE_ADAPTER_GAMEBOY_ADVANCE | 0x80)) {
s->state = MOBILE_SERIAL_WAITING;
// Yellow/Red adapters are probably bugged to return the
// device ID again, instead of the idle byte.
break;
}
b->current = 1;
s->state = MOBILE_SERIAL_IDLE_CHECK;
return b->error ? b->error : b->header[0] ^ 0x80;
case MOBILE_SERIAL_ACKNOWLEDGE_PAD:
// In 32bit mode, we must add some extra padding
if (!--b->current) {
b->current = 1;
s->state = MOBILE_SERIAL_IDLE_CHECK;
}
return 0;
case MOBILE_SERIAL_IDLE_CHECK:
// Skip at least one byte
if (b->current--) break;
// If an error was raised or the empty command was sent, reset here.
if (b->header[0] == MOBILE_COMMAND_NULL || b->error) {
s->state = MOBILE_SERIAL_WAITING;
if (c == 0x99) b->current = 1;
break;
}
// If an idle byte isn't received, reset here.
if (c != 0x4B) {
s->state = MOBILE_SERIAL_WAITING;
if (c == 0x99) b->current = 1;
break;
}
// Otherwise, start processing
s->state = MOBILE_SERIAL_RESPONSE_WAITING;
break;
case MOBILE_SERIAL_RESPONSE_WAITING:
// Wait while processing the received packet and crafting a response.
break;
case MOBILE_SERIAL_RESPONSE_INIT:
b->current = 0;
s->state = MOBILE_SERIAL_RESPONSE_START;
// fallthrough
case MOBILE_SERIAL_RESPONSE_START:
// Start sending the response.
if (b->current++ == 0) {
return 0x99;
} else {
b->data_size = b->header[3];
b->error = 0;
b->current = 0;
s->state = MOBILE_SERIAL_RESPONSE_HEADER;
return 0x66;
}
case MOBILE_SERIAL_RESPONSE_HEADER:
c = b->header[b->current++];
if (b->current >= sizeof(b->header)) {
b->current = 0;
if (b->data_size) {
s->state = MOBILE_SERIAL_RESPONSE_DATA;
} else {
s->state = MOBILE_SERIAL_RESPONSE_CHECKSUM;
}
}
return c;
case MOBILE_SERIAL_RESPONSE_DATA:
// Send all that's in the response buffer.
// This includes the header, content and the checksum.
c = s->buffer[b->current++];
if (b->current >= b->data_size) {
if (s->mode_32bit && b->current % 4) {
b->current = 4 - (b->current % 4);
s->state = MOBILE_SERIAL_RESPONSE_DATA_PAD;
} else {
b->current = 0;
s->state = MOBILE_SERIAL_RESPONSE_CHECKSUM;
}
}
return c;
case MOBILE_SERIAL_RESPONSE_DATA_PAD:
// In 32bit mode, we must add some extra padding
if (!--b->current) s->state = MOBILE_SERIAL_RESPONSE_CHECKSUM;
return 0;
case MOBILE_SERIAL_RESPONSE_CHECKSUM:
c = b->footer[b->current++];
if (b->current >= sizeof(b->footer)) {
b->current = 0;
s->state = MOBILE_SERIAL_RESPONSE_ACKNOWLEDGE;
}
return c;
case MOBILE_SERIAL_RESPONSE_ACKNOWLEDGE:
if (b->current == 0) {
b->current++;
return s->device | 0x80;
} else if (b->current == 1) {
// There's nothing we can do with the received device ID.
// In fact, the real adapter doesn't care for this value, either.
b->current++;
return 0;
} else if (b->current == 2) {
// Catch the error
b->error = c;
}
if (s->mode_32bit && b->current < 4) {
b->current++;
return 0;
}
b->current = 0;
// If an error happened, retry.
if (b->error == MOBILE_SERIAL_ERROR_UNKNOWN_COMMAND ||
b->error == MOBILE_SERIAL_ERROR_CHECKSUM ||
b->error == MOBILE_SERIAL_ERROR_INTERNAL) {
s->state = MOBILE_SERIAL_RESPONSE_START;
break;
}
// Start over after this
s->state = MOBILE_SERIAL_WAITING;
break;
}
return MOBILE_SERIAL_IDLE_BYTE;
}
uint32_t mobile_serial_transfer_32bit(struct mobile_adapter *adapter, uint32_t c)
{
struct mobile_adapter_serial *s = &adapter->serial;
struct mobile_buffer_serial *b = &adapter->buffer.serial;
// Unpack the data
uint8_t d[4] = {c >> 24, c >> 16, c >> 8, c >> 0};
// Use the 8-bit logic for most things
for (unsigned i = 0; i < 4; i++) {
d[i] = mobile_serial_transfer(adapter, d[i]);
}
// Handle acknowledgement footer separately
// This is the entire reason the functions are split at all, the checksum
// isn't available in the 8bit function by the time the error byte has to
// be sent.
// For this same reason, the received device byte can't be verified either.
if (s->state == MOBILE_SERIAL_ACKNOWLEDGE) {
d[0] = s->device | 0x80;
d[1] = b->error ? b->error : b->header[0] ^ 0x80;
d[2] = 0;
d[3] = 0;
// Ignore the next packet, we can't do anything with it.
b->current = 4;
s->state = MOBILE_SERIAL_IDLE_CHECK;
}
// Repack the data
return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3] << 0;
}