Mercurial > repos > blastem
comparison megawifi.c @ 1692:5dacaef602a7 segacd
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 05 Jan 2019 00:58:08 -0800 |
parents | 1a09422b87a5 |
children | 6d99bdbf1e3e |
comparison
equal
deleted
inserted
replaced
1504:95b3a1a8b26c | 1692:5dacaef602a7 |
---|---|
1 #include <stdlib.h> | |
2 #include <stdint.h> | |
3 #include <string.h> | |
4 #include <sys/types.h> | |
5 #ifdef _WIN32 | |
6 #define WINVER 0x501 | |
7 #include <winsock2.h> | |
8 #include <ws2tcpip.h> | |
9 #else | |
10 #include <sys/socket.h> | |
11 #include <unistd.h> | |
12 #include <netinet/in.h> | |
13 #endif | |
14 #include <errno.h> | |
15 #include <fcntl.h> | |
16 #include "genesis.h" | |
17 #include "net.h" | |
18 | |
19 enum { | |
20 TX_IDLE, | |
21 TX_LEN1, | |
22 TX_LEN2, | |
23 TX_PAYLOAD, | |
24 TX_WAIT_ETX | |
25 }; | |
26 #define STX 0x7E | |
27 #define ETX 0x7E | |
28 #define MAX_RECV_SIZE 1440 | |
29 | |
30 #define E(N) N | |
31 enum { | |
32 #include "mw_commands.c" | |
33 CMD_ERROR = 255 | |
34 }; | |
35 #undef E | |
36 #define E(N) #N | |
37 static const char *cmd_names[] = { | |
38 #include "mw_commands.c" | |
39 [255] = "CMD_ERROR" | |
40 }; | |
41 | |
42 #ifndef MSG_NOSIGNAL | |
43 #define MSG_NOSIGNAL 0 | |
44 #endif | |
45 | |
46 enum { | |
47 STATE_IDLE=1, | |
48 STATE_AP_JOIN, | |
49 STATE_SCAN, | |
50 STATE_READY, | |
51 STATE_TRANSPARENT | |
52 }; | |
53 | |
54 #define FLAG_ONLINE | |
55 | |
56 typedef struct { | |
57 uint32_t transmit_bytes; | |
58 uint32_t expected_bytes; | |
59 uint32_t receive_bytes; | |
60 uint32_t receive_read; | |
61 int sock_fds[15]; | |
62 uint16_t channel_flags; | |
63 uint8_t channel_state[15]; | |
64 uint8_t scratchpad; | |
65 uint8_t transmit_channel; | |
66 uint8_t transmit_state; | |
67 uint8_t module_state; | |
68 uint8_t flags; | |
69 uint8_t transmit_buffer[4096]; | |
70 uint8_t receive_buffer[4096]; | |
71 } megawifi; | |
72 | |
73 static megawifi *get_megawifi(void *context) | |
74 { | |
75 m68k_context *m68k = context; | |
76 genesis_context *gen = m68k->system; | |
77 if (!gen->extra) { | |
78 gen->extra = calloc(1, sizeof(megawifi)); | |
79 megawifi *mw = gen->extra; | |
80 mw->module_state = STATE_IDLE; | |
81 for (int i = 0; i < 15; i++) | |
82 { | |
83 mw->sock_fds[i] = -1; | |
84 } | |
85 } | |
86 return gen->extra; | |
87 } | |
88 | |
89 static void mw_putc(megawifi *mw, uint8_t v) | |
90 { | |
91 if (mw->receive_bytes == sizeof(mw->receive_buffer)) { | |
92 return; | |
93 } | |
94 mw->receive_buffer[mw->receive_bytes++] = v; | |
95 } | |
96 | |
97 static void mw_set(megawifi *mw, uint8_t val, uint32_t count) | |
98 { | |
99 if (count + mw->receive_bytes > sizeof(mw->receive_buffer)) { | |
100 count = sizeof(mw->receive_buffer) - mw->receive_bytes; | |
101 } | |
102 memset(mw->receive_buffer + mw->receive_bytes, val, count); | |
103 mw->receive_bytes += count; | |
104 } | |
105 | |
106 static void mw_copy(megawifi *mw, const uint8_t *src, uint32_t count) | |
107 { | |
108 if (count + mw->receive_bytes > sizeof(mw->receive_buffer)) { | |
109 count = sizeof(mw->receive_buffer) - mw->receive_bytes; | |
110 } | |
111 memcpy(mw->receive_buffer + mw->receive_bytes, src, count); | |
112 mw->receive_bytes += count; | |
113 } | |
114 | |
115 static void mw_puts(megawifi *mw, char *s) | |
116 { | |
117 uint32_t len = strlen(s); | |
118 if ((mw->receive_bytes + len) > sizeof(mw->receive_buffer)) { | |
119 return; | |
120 } | |
121 memcpy(mw->receive_buffer + mw->receive_bytes, s, len); | |
122 mw->receive_bytes += len; | |
123 } | |
124 | |
125 static void poll_socket(megawifi *mw, uint8_t channel) | |
126 { | |
127 if (mw->sock_fds[channel] < 0) { | |
128 return; | |
129 } | |
130 if (mw->channel_state[channel] == 1) { | |
131 int res = accept(mw->sock_fds[channel], NULL, NULL); | |
132 if (res >= 0) { | |
133 close(mw->sock_fds[channel]); | |
134 #ifndef _WIN32 | |
135 //FIXME: Set nonblocking on Windows too | |
136 fcntl(res, F_SETFL, O_NONBLOCK); | |
137 #endif | |
138 mw->sock_fds[channel] = res; | |
139 mw->channel_state[channel] = 2; | |
140 mw->channel_flags |= 1 << (channel + 1); | |
141 } else if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
142 close(mw->sock_fds[channel]); | |
143 mw->channel_state[channel] = 0; | |
144 mw->channel_flags |= 1 << (channel + 1); | |
145 } | |
146 } else if (mw->channel_state[channel] == 2 && mw->receive_bytes < sizeof(mw->receive_buffer) - 4) { | |
147 size_t max = sizeof(mw->receive_buffer) - 4 - mw->receive_bytes; | |
148 if (max > MAX_RECV_SIZE) { | |
149 max = MAX_RECV_SIZE; | |
150 } | |
151 int bytes = recv(mw->sock_fds[channel], mw->receive_buffer + mw->receive_bytes + 3, max, 0); | |
152 if (bytes > 0) { | |
153 mw_putc(mw, STX); | |
154 mw_putc(mw, bytes >> 8 | (channel+1) << 4); | |
155 mw_putc(mw, bytes); | |
156 mw->receive_bytes += bytes; | |
157 mw_putc(mw, ETX); | |
158 //should this set the channel flag? | |
159 } else if (bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { | |
160 close(mw->sock_fds[channel]); | |
161 mw->channel_state[channel] = 0; | |
162 mw->channel_flags |= 1 << (channel + 1); | |
163 } | |
164 } | |
165 } | |
166 | |
167 static void poll_all_sockets(megawifi *mw) | |
168 { | |
169 for (int i = 0; i < 15; i++) | |
170 { | |
171 poll_socket(mw, i); | |
172 } | |
173 } | |
174 | |
175 static void start_reply(megawifi *mw, uint8_t cmd) | |
176 { | |
177 mw_putc(mw, STX); | |
178 //reserve space for length | |
179 mw->receive_bytes += 2; | |
180 //cmd | |
181 mw_putc(mw, 0); | |
182 mw_putc(mw, cmd); | |
183 //reserve space for length | |
184 mw->receive_bytes += 2; | |
185 } | |
186 | |
187 static void end_reply(megawifi *mw) | |
188 { | |
189 uint32_t len = mw->receive_bytes - 3; | |
190 //LSD packet length | |
191 mw->receive_buffer[1] = len >> 8; | |
192 mw->receive_buffer[2] = len; | |
193 //command length | |
194 len -= 4; | |
195 mw->receive_buffer[5] = len >> 8; | |
196 mw->receive_buffer[6] = len; | |
197 mw_putc(mw, ETX); | |
198 } | |
199 | |
200 static void process_packet(megawifi *mw) | |
201 { | |
202 if (mw->transmit_channel == 0) { | |
203 uint32_t command = mw->transmit_buffer[0] << 8 | mw->transmit_buffer[1]; | |
204 uint32_t size = mw->transmit_buffer[2] << 8 | mw->transmit_buffer[3]; | |
205 if (size > mw->transmit_bytes - 4) { | |
206 size = mw->transmit_bytes - 4; | |
207 } | |
208 int orig_receive_bytes = mw->receive_bytes; | |
209 switch (command) | |
210 { | |
211 case CMD_VERSION: | |
212 start_reply(mw, CMD_OK); | |
213 mw_putc(mw, 1); | |
214 mw_putc(mw, 0); | |
215 mw_puts(mw, "blastem"); | |
216 end_reply(mw); | |
217 break; | |
218 case CMD_ECHO: | |
219 mw->receive_bytes = mw->transmit_bytes; | |
220 memcpy(mw->receive_buffer, mw->transmit_buffer, mw->transmit_bytes); | |
221 break; | |
222 case CMD_IP_CURRENT: { | |
223 iface_info i; | |
224 if (get_host_address(&i)) { | |
225 start_reply(mw, CMD_OK); | |
226 //config number and reserved bytes | |
227 mw_set(mw, 0, 4); | |
228 //ip | |
229 mw_copy(mw, i.ip, sizeof(i.ip)); | |
230 //net mask | |
231 mw_copy(mw, i.net_mask, sizeof(i.net_mask)); | |
232 //gateway guess | |
233 mw_putc(mw, i.ip[0] & i.net_mask[0]); | |
234 mw_putc(mw, i.ip[1] & i.net_mask[1]); | |
235 mw_putc(mw, i.ip[2] & i.net_mask[2]); | |
236 mw_putc(mw, (i.ip[3] & i.net_mask[3]) + 1); | |
237 //dns | |
238 static const uint8_t localhost[] = {127,0,0,1}; | |
239 mw_copy(mw, localhost, sizeof(localhost)); | |
240 mw_copy(mw, localhost, sizeof(localhost)); | |
241 | |
242 } else { | |
243 start_reply(mw, CMD_ERROR); | |
244 } | |
245 end_reply(mw); | |
246 break; | |
247 } | |
248 case CMD_AP_JOIN: | |
249 mw->module_state = STATE_READY; | |
250 start_reply(mw, CMD_OK); | |
251 end_reply(mw); | |
252 break; | |
253 case CMD_TCP_BIND:{ | |
254 if (size < 7){ | |
255 start_reply(mw, CMD_ERROR); | |
256 end_reply(mw); | |
257 break; | |
258 } | |
259 uint8_t channel = mw->transmit_buffer[10]; | |
260 if (!channel || channel > 15) { | |
261 start_reply(mw, CMD_ERROR); | |
262 end_reply(mw); | |
263 break; | |
264 } | |
265 channel--; | |
266 if (mw->sock_fds[channel] >= 0) { | |
267 close(mw->sock_fds[channel]); | |
268 } | |
269 mw->sock_fds[channel] = socket(AF_INET, SOCK_STREAM, 0); | |
270 if (mw->sock_fds[channel] < 0) { | |
271 start_reply(mw, CMD_ERROR); | |
272 end_reply(mw); | |
273 break; | |
274 } | |
275 int value = 1; | |
276 setsockopt(mw->sock_fds[channel], SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); | |
277 struct sockaddr_in bind_addr; | |
278 memset(&bind_addr, 0, sizeof(bind_addr)); | |
279 bind_addr.sin_family = AF_INET; | |
280 bind_addr.sin_port = htons(mw->transmit_buffer[8] << 8 | mw->transmit_buffer[9]); | |
281 if (bind(mw->sock_fds[channel], (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0) { | |
282 close(mw->sock_fds[channel]); | |
283 mw->sock_fds[channel] = -1; | |
284 start_reply(mw, CMD_ERROR); | |
285 end_reply(mw); | |
286 break; | |
287 } | |
288 int res = listen(mw->sock_fds[channel], 2); | |
289 start_reply(mw, res ? CMD_ERROR : CMD_OK); | |
290 if (res) { | |
291 close(mw->sock_fds[channel]); | |
292 mw->sock_fds[channel] = -1; | |
293 } else { | |
294 mw->channel_flags |= 1 << (channel + 1); | |
295 mw->channel_state[channel] = 1; | |
296 #ifndef _WIN32 | |
297 //FIXME: Set nonblocking on Windows too | |
298 fcntl(mw->sock_fds[channel], F_SETFL, O_NONBLOCK); | |
299 #endif | |
300 } | |
301 end_reply(mw); | |
302 break; | |
303 } | |
304 case CMD_SOCK_STAT: { | |
305 uint8_t channel = mw->transmit_buffer[4]; | |
306 if (!channel || channel > 15) { | |
307 start_reply(mw, CMD_ERROR); | |
308 end_reply(mw); | |
309 break; | |
310 } | |
311 mw->channel_flags &= ~(1 << channel); | |
312 channel--; | |
313 poll_socket(mw, channel); | |
314 start_reply(mw, CMD_OK); | |
315 mw_putc(mw, mw->channel_state[channel]); | |
316 end_reply(mw); | |
317 break; | |
318 } | |
319 case CMD_SYS_STAT: | |
320 poll_all_sockets(mw); | |
321 start_reply(mw, CMD_OK); | |
322 mw_putc(mw, mw->module_state); | |
323 mw_putc(mw, mw->flags); | |
324 mw_putc(mw, mw->channel_flags >> 8); | |
325 mw_putc(mw, mw->channel_flags); | |
326 end_reply(mw); | |
327 break; | |
328 default: | |
329 printf("Unhandled MegaWiFi command %s(%d) with length %X\n", cmd_names[command], command, size); | |
330 break; | |
331 } | |
332 } else if (mw->sock_fds[mw->transmit_channel - 1] >= 0 && mw->channel_state[mw->transmit_channel - 1] == 2) { | |
333 uint8_t channel = mw->transmit_channel - 1; | |
334 int sent = send(mw->sock_fds[channel], mw->transmit_buffer, mw->transmit_bytes, MSG_NOSIGNAL); | |
335 if (sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { | |
336 close(mw->sock_fds[channel]); | |
337 mw->sock_fds[channel] = -1; | |
338 mw->channel_state[channel] = 0; | |
339 mw->channel_flags |= 1 << mw->transmit_channel; | |
340 } else if (sent < mw->transmit_bytes) { | |
341 //TODO: save this data somewhere so it can be sent in poll_socket | |
342 printf("Sent %d bytes on channel %d, but %d were requested\n", sent, mw->transmit_channel, mw->transmit_bytes); | |
343 } | |
344 } else { | |
345 printf("Unhandled receive of MegaWiFi data on channel %d\n", mw->transmit_channel); | |
346 } | |
347 mw->transmit_bytes = mw->expected_bytes = 0; | |
348 } | |
349 | |
350 void *megawifi_write_b(uint32_t address, void *context, uint8_t value) | |
351 { | |
352 if (!(address & 1)) { | |
353 return context; | |
354 } | |
355 megawifi *mw = get_megawifi(context); | |
356 address = address >> 1 & 7; | |
357 switch (address) | |
358 { | |
359 case 0: | |
360 switch (mw->transmit_state) | |
361 { | |
362 case TX_IDLE: | |
363 if (value == STX) { | |
364 mw->transmit_state = TX_LEN1; | |
365 } | |
366 break; | |
367 case TX_LEN1: | |
368 mw->transmit_channel = value >> 4; | |
369 mw->expected_bytes = value << 8 & 0xF00; | |
370 mw->transmit_state = TX_LEN2; | |
371 break; | |
372 case TX_LEN2: | |
373 mw->expected_bytes |= value; | |
374 mw->transmit_state = TX_PAYLOAD; | |
375 break; | |
376 case TX_PAYLOAD: | |
377 mw->transmit_buffer[mw->transmit_bytes++] = value; | |
378 if (mw->transmit_bytes == mw->expected_bytes) { | |
379 mw->transmit_state = TX_WAIT_ETX; | |
380 } | |
381 break; | |
382 case TX_WAIT_ETX: | |
383 if (value == ETX) { | |
384 mw->transmit_state = TX_IDLE; | |
385 process_packet(mw); | |
386 } | |
387 break; | |
388 } | |
389 break; | |
390 case 7: | |
391 mw->scratchpad = value; | |
392 break; | |
393 default: | |
394 printf("Unhandled write to MegaWiFi UART register %X: %X\n", address, value); | |
395 } | |
396 return context; | |
397 } | |
398 | |
399 void *megawifi_write_w(uint32_t address, void *context, uint16_t value) | |
400 { | |
401 return megawifi_write_b(address | 1, context, value); | |
402 } | |
403 | |
404 uint8_t megawifi_read_b(uint32_t address, void *context) | |
405 { | |
406 | |
407 if (!(address & 1)) { | |
408 return 0xFF; | |
409 } | |
410 megawifi *mw = get_megawifi(context); | |
411 address = address >> 1 & 7; | |
412 switch (address) | |
413 { | |
414 case 0: | |
415 poll_all_sockets(mw); | |
416 if (mw->receive_read < mw->receive_bytes) { | |
417 uint8_t ret = mw->receive_buffer[mw->receive_read++]; | |
418 if (mw->receive_read == mw->receive_bytes) { | |
419 mw->receive_read = mw->receive_bytes = 0; | |
420 } | |
421 return ret; | |
422 } | |
423 return 0xFF; | |
424 case 5: | |
425 poll_all_sockets(mw); | |
426 //line status | |
427 return 0x60 | (mw->receive_read < mw->receive_bytes); | |
428 case 7: | |
429 return mw->scratchpad; | |
430 default: | |
431 printf("Unhandled read from MegaWiFi UART register %X\n", address); | |
432 return 0xFF; | |
433 } | |
434 } | |
435 | |
436 uint16_t megawifi_read_w(uint32_t address, void *context) | |
437 { | |
438 return 0xFF00 | megawifi_read_b(address | 1, context); | |
439 } |