Mercurial > repos > blastem
view megawifi.c @ 1971:80920c21bb52
Add an event log soft flush and call it twice per frame in between hard flushes to netplay latency when there are insufficient hardware updates to flush packets in the middle of a frame
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 08 May 2020 11:40:30 -0700 |
parents | 41b9509ede38 |
children | a8e3e816a50d |
line wrap: on
line source
#include <stdlib.h> #include <stdint.h> #include <string.h> #include <sys/types.h> #ifdef _WIN32 #define WINVER 0x501 #include <winsock2.h> #include <ws2tcpip.h> #include <sys/param.h> #else #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <netinet/in.h> #include <netdb.h> #endif #include <errno.h> #include <fcntl.h> #include <time.h> #include "genesis.h" #include "net.h" #include "util.h" #ifdef _WIN32 # if BYTE_ORDER == LITTLE_ENDIAN #define htobe64(val) ((((uint64_t)htonl((val)&0xFFFFFFFF))<<32) | htonl((val)>>32)) # else #define htobe64(val) (val) # endif #endif enum { TX_IDLE, TX_LEN1, TX_LEN2, TX_PAYLOAD, TX_WAIT_ETX }; #define STX 0x7E #define ETX 0x7E #define MAX_RECV_SIZE 1460 #define E(N) N enum { #include "mw_commands.c" CMD_ERROR = 255 }; #undef E #define E(N) #N static const char *cmd_names[] = { #include "mw_commands.c" [255] = "CMD_ERROR" }; #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif enum mw_state { STATE_IDLE=1, STATE_AP_JOIN, STATE_SCAN, STATE_READY, STATE_TRANSPARENT }; enum { SOCKST_NONE = 0, SOCKST_TCP_LISTEN, SOCKST_TCP_EST, SOCKST_UDP_READY }; // TCP/UDP address message struct mw_addr_msg { char dst_port[6]; char src_port[6]; uint8_t channel; char host[]; }; #define FLAG_ONLINE typedef struct { uint32_t transmit_bytes; uint32_t expected_bytes; uint32_t receive_bytes; uint32_t receive_read; int sock_fds[15]; uint16_t channel_flags; uint8_t channel_state[15]; uint8_t scratchpad; uint8_t transmit_channel; uint8_t transmit_state; uint8_t module_state; uint8_t flags; uint8_t transmit_buffer[4096]; uint8_t receive_buffer[4096]; struct sockaddr_in remote_addr[15]; // Needed for UDP sockets } megawifi; static megawifi *get_megawifi(void *context) { m68k_context *m68k = context; genesis_context *gen = m68k->system; if (!gen->extra) { socket_init(); gen->extra = calloc(1, sizeof(megawifi)); megawifi *mw = gen->extra; mw->module_state = STATE_IDLE; mw->flags = 0xE0; // cfg_ok, dt_ok, online for (int i = 0; i < 15; i++) { mw->sock_fds[i] = -1; } } return gen->extra; } static void mw_putc(megawifi *mw, uint8_t v) { if (mw->receive_bytes == sizeof(mw->receive_buffer)) { return; } mw->receive_buffer[mw->receive_bytes++] = v; } static void mw_set(megawifi *mw, uint8_t val, uint32_t count) { if (count + mw->receive_bytes > sizeof(mw->receive_buffer)) { count = sizeof(mw->receive_buffer) - mw->receive_bytes; } memset(mw->receive_buffer + mw->receive_bytes, val, count); mw->receive_bytes += count; } static void mw_copy(megawifi *mw, const uint8_t *src, uint32_t count) { if (count + mw->receive_bytes > sizeof(mw->receive_buffer)) { count = sizeof(mw->receive_buffer) - mw->receive_bytes; } memcpy(mw->receive_buffer + mw->receive_bytes, src, count); mw->receive_bytes += count; } static void mw_puts(megawifi *mw, const char *s) { size_t len = strlen(s); mw_copy(mw, (uint8_t*)s, len); } static void udp_recv(megawifi *mw, uint8_t idx) { ssize_t recvd; int s = mw->sock_fds[idx]; struct sockaddr_in remote; socklen_t addr_len = sizeof(struct sockaddr_in); if (mw->remote_addr[idx].sin_addr.s_addr != htonl(INADDR_ANY)) { // Receive only from specified address recvd = recvfrom(s, (char*)mw->receive_buffer + 3, MAX_RECV_SIZE, 0, (struct sockaddr*)&remote, &addr_len); if (recvd > 0) { if (remote.sin_addr.s_addr != mw->remote_addr[idx].sin_addr.s_addr) { printf("Discarding UDP packet from unknown addr %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port)); recvd = 0; } } } else { // Reuse mode, data is preceded by remote IPv4 and port recvd = recvfrom(s, (char*)mw->receive_buffer + 9, MAX_RECV_SIZE - 6, 0, (struct sockaddr*)&remote, &addr_len); if (recvd > 0) { mw->receive_buffer[3] = remote.sin_addr.s_addr; mw->receive_buffer[4] = remote.sin_addr.s_addr>>8; mw->receive_buffer[5] = remote.sin_addr.s_addr>>16; mw->receive_buffer[6] = remote.sin_addr.s_addr>>24; mw->receive_buffer[7] = remote.sin_port; mw->receive_buffer[8] = remote.sin_port>>8; recvd += 6; } } if (recvd > 0) { mw_putc(mw, STX); mw_putc(mw, (recvd >> 8) | ((idx+1) << 4)); mw_putc(mw, recvd); mw->receive_bytes += recvd; mw_putc(mw, ETX); //should this set the channel flag? } else if (recvd < 0 && !socket_error_is_wouldblock()) { socket_close(mw->sock_fds[idx]); mw->channel_state[idx] = SOCKST_NONE; mw->channel_flags |= 1 << (idx + 1); } } static void udp_send(megawifi *mw, uint8_t idx) { struct sockaddr_in remote; int s = mw->sock_fds[idx]; int sent; char *data = (char*)mw->transmit_buffer; if (mw->remote_addr[idx].sin_addr.s_addr != htonl(INADDR_ANY)) { sent = sendto(s, data, mw->transmit_bytes, 0, (struct sockaddr*)&mw->remote_addr[idx], sizeof(struct sockaddr_in)); } else { // Reuse mode, extract address from leading bytes // NOTE: mw->remote_addr[idx].sin_addr.s_addr == INADDR_ANY remote.sin_addr.s_addr = *((int32_t*)data); remote.sin_port = *((int16_t*)(data + 4)); remote.sin_family = AF_INET; memset(remote.sin_zero, 0, sizeof(remote.sin_zero)); sent = sendto(s, data + 6, mw->transmit_bytes - 6, 0, (struct sockaddr*)&remote, sizeof(struct sockaddr_in)) + 6; } if (sent < 0 && !socket_error_is_wouldblock()) { socket_close(s); mw->sock_fds[idx] = -1; mw->channel_state[idx] = SOCKST_NONE; mw->channel_flags |= 1 << (idx + 1); } else if (sent < mw->transmit_bytes) { //TODO: save this data somewhere so it can be sent in poll_socket printf("Sent %d bytes on channel %d, but %d were requested\n", sent, idx + 1, mw->transmit_bytes); } } static void poll_socket(megawifi *mw, uint8_t channel) { if (mw->sock_fds[channel] < 0) { return; } if (mw->channel_state[channel] == SOCKST_TCP_LISTEN) { int res = accept(mw->sock_fds[channel], NULL, NULL); if (res >= 0) { socket_close(mw->sock_fds[channel]); socket_blocking(res, 0); mw->sock_fds[channel] = res; mw->channel_state[channel] = SOCKST_TCP_EST; mw->channel_flags |= 1 << (channel + 1); } else if (errno != EAGAIN && errno != EWOULDBLOCK) { socket_close(mw->sock_fds[channel]); mw->channel_state[channel] = SOCKST_NONE; mw->channel_flags |= 1 << (channel + 1); } } else if (mw->channel_state[channel] == SOCKST_TCP_EST && mw->receive_bytes < (sizeof(mw->receive_buffer) - 4)) { size_t max = sizeof(mw->receive_buffer) - 4 - mw->receive_bytes; if (max > MAX_RECV_SIZE) { max = MAX_RECV_SIZE; } int bytes = recv(mw->sock_fds[channel], (char*)(mw->receive_buffer + mw->receive_bytes + 3), max, 0); if (bytes > 0) { mw_putc(mw, STX); mw_putc(mw, bytes >> 8 | (channel+1) << 4); mw_putc(mw, bytes); mw->receive_bytes += bytes; mw_putc(mw, ETX); //should this set the channel flag? } else if (bytes < 0 && !socket_error_is_wouldblock()) { socket_close(mw->sock_fds[channel]); mw->channel_state[channel] = SOCKST_NONE; mw->channel_flags |= 1 << (channel + 1); } } else if (mw->channel_state[channel] == SOCKST_UDP_READY && !mw->receive_bytes) { udp_recv(mw, channel); } } static void poll_all_sockets(megawifi *mw) { for (int i = 0; i < 15; i++) { poll_socket(mw, i); } } static void start_reply(megawifi *mw, uint8_t cmd) { mw_putc(mw, STX); //reserve space for length mw->receive_bytes += 2; //cmd mw_putc(mw, 0); mw_putc(mw, cmd); //reserve space for length mw->receive_bytes += 2; } static void end_reply(megawifi *mw) { uint32_t len = mw->receive_bytes - 3; //LSD packet length mw->receive_buffer[1] = len >> 8; mw->receive_buffer[2] = len; //command length len -= 4; mw->receive_buffer[5] = len >> 8; mw->receive_buffer[6] = len; mw_putc(mw, ETX); } static void cmd_ap_cfg_get(megawifi *mw) { char ssid[32] = {0}; char pass[64] = {0}; uint8_t slot = mw->transmit_buffer[4]; sprintf(ssid, "BLASTEM! SSID %d", slot + 1); sprintf(pass, "BLASTEM! PASS %d", slot + 1); start_reply(mw, CMD_OK); mw_putc(mw, slot); mw_putc(mw, 7); /// 11bgn mw_copy(mw, (uint8_t*)ssid, 32); mw_copy(mw, (uint8_t*)pass, 64); end_reply(mw); } static void cmd_ip_cfg_get(megawifi *mw) { uint32_t ipv4s[5] = {0}; start_reply(mw, CMD_OK); mw_putc(mw, mw->transmit_buffer[4]); mw_putc(mw, 0); mw_putc(mw, 0); mw_putc(mw, 0); mw_copy(mw, (uint8_t*)ipv4s, sizeof(ipv4s)); end_reply(mw); } static void cmd_tcp_con(megawifi *mw, uint32_t size) { struct mw_addr_msg *addr = (struct mw_addr_msg*)(mw->transmit_buffer + 4); struct addrinfo hints; struct addrinfo *res = NULL; int s; int err; uint8_t channel = addr->channel; if (!channel || channel > 15 || mw->sock_fds[channel - 1] >= 0) { start_reply(mw, CMD_ERROR); end_reply(mw); return; } channel--; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; #ifndef _WIN32 hints.ai_flags = AI_NUMERICSERV; #endif hints.ai_socktype = SOCK_STREAM; if ((err = getaddrinfo(addr->host, addr->dst_port, &hints, &res)) != 0) { printf("getaddrinfo failed: %s\n", gai_strerror(err)); start_reply(mw, CMD_ERROR); end_reply(mw); return; } s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { goto err; } // Should this be handled in a separate thread to avoid blocking emulation? if (connect(s, res->ai_addr, res->ai_addrlen) != 0) { goto err; } socket_blocking(s, 0); mw->sock_fds[channel] = s; mw->channel_state[channel] = SOCKST_TCP_EST; mw->channel_flags |= 1 << (channel + 1); printf("Connection established on ch %d with %s:%s\n", channel + 1, addr->host, addr->dst_port); if (res) { freeaddrinfo(res); } start_reply(mw, CMD_OK); end_reply(mw); return; err: freeaddrinfo(res); printf("Connection to %s:%s failed, %s\n", addr->host, addr->dst_port, strerror(errno)); start_reply(mw, CMD_ERROR); end_reply(mw); } static void cmd_close(megawifi *mw) { int channel = mw->transmit_buffer[4] - 1; if (channel >= 15 || mw->sock_fds[channel] < 0) { start_reply(mw, CMD_ERROR); end_reply(mw); return; } socket_close(mw->sock_fds[channel]); mw->sock_fds[channel] = -1; mw->channel_state[channel] = SOCKST_NONE; mw->channel_flags |= 1 << (channel + 1); start_reply(mw, CMD_OK); end_reply(mw); } static void cmd_udp_set(megawifi *mw) { struct mw_addr_msg *addr = (struct mw_addr_msg*)(mw->transmit_buffer + 4); unsigned int local_port, remote_port; int s; struct addrinfo *raddr; struct addrinfo hints; struct sockaddr_in local; int err; uint8_t channel = addr->channel; if (!channel || channel > 15 || mw->sock_fds[channel - 1] >= 0) { goto err; } channel--; local_port = atoi(addr->src_port); remote_port = atoi(addr->dst_port); if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { printf("Datagram socket creation failed\n"); goto err; } memset(local.sin_zero, 0, sizeof(local.sin_zero)); local.sin_family = AF_INET; local.sin_addr.s_addr = htonl(INADDR_ANY); local.sin_port = htons(local_port); if (remote_port && addr->host[0]) { // Communication with remote peer printf("Set UDP ch %d, port %d to addr %s:%d\n", addr->channel, local_port, addr->host, remote_port); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; #ifndef _WIN32 hints.ai_flags = AI_NUMERICSERV; #endif hints.ai_socktype = SOCK_DGRAM; if ((err = getaddrinfo(addr->host, addr->dst_port, &hints, &raddr)) != 0) { printf("getaddrinfo failed: %s\n", gai_strerror(err)); goto err; } mw->remote_addr[channel] = *((struct sockaddr_in*)raddr->ai_addr); freeaddrinfo(raddr); } else if (local_port) { // Server in reuse mode printf("Set UDP ch %d, src port %d\n", addr->channel, local_port); mw->remote_addr[channel] = local; } else { printf("Invalid UDP socket data\n"); goto err; } if (bind(s, (struct sockaddr*)&local, sizeof(struct sockaddr_in)) < 0) { printf("bind to port %d failed\n", local_port); goto err; } socket_blocking(s, 0); mw->sock_fds[channel] = s; mw->channel_state[channel] = SOCKST_UDP_READY; mw->channel_flags |= 1 << (channel + 1); start_reply(mw, CMD_OK); end_reply(mw); return; err: start_reply(mw, CMD_ERROR); end_reply(mw); } #define AVATAR_BYTES (32 * 48 / 2) static void cmd_gamertag_get(megawifi *mw) { uint32_t id = htonl(1); char buf[AVATAR_BYTES]; start_reply(mw, CMD_OK); // TODO Get items from config file mw_copy(mw, (uint8_t*)&id, 4); strncpy(buf, "doragasu on Blastem!", 32); mw_copy(mw, (uint8_t*)buf, 32); strncpy(buf, "My cool password", 32); mw_copy(mw, (uint8_t*)buf, 32); strncpy(buf, "All your WiFi are belong to me!", 32); mw_copy(mw, (uint8_t*)buf, 32); memset(buf, 0, 64); // Telegram token mw_copy(mw, (uint8_t*)buf, 64); mw_copy(mw, (uint8_t*)buf, AVATAR_BYTES); // Avatar tiles mw_copy(mw, (uint8_t*)buf, 32); // Avatar palette end_reply(mw); } static void cmd_hrng_get(megawifi *mw) { uint16_t len = (mw->transmit_buffer[4]<<8) + mw->transmit_buffer[5]; if (len > (MAX_RECV_SIZE - 4)) { start_reply(mw, CMD_ERROR); end_reply(mw); return; } // Pseudo-random, but who cares start_reply(mw, CMD_OK); srand(time(NULL)); for (uint16_t i = 0; i < len; i++) { mw_putc(mw, rand()); } end_reply(mw); } static void cmd_datetime(megawifi *mw) { start_reply(mw, CMD_OK); #ifdef _WIN32 __time64_t t = _time64(NULL); int64_t t_be = htobe64(t); mw_copy(mw, (uint8_t*)&t_be, sizeof(int64_t)); mw_puts(mw, _ctime64(&t)); #else time_t t = time(NULL); int64_t t_be = htobe64(t); mw_copy(mw, (uint8_t*)&t_be, sizeof(int64_t)); mw_puts(mw, ctime(&t)); #endif mw_putc(mw, '\0'); end_reply(mw); } static void process_command(megawifi *mw) { uint32_t command = mw->transmit_buffer[0] << 8 | mw->transmit_buffer[1]; uint32_t size = mw->transmit_buffer[2] << 8 | mw->transmit_buffer[3]; if (size > mw->transmit_bytes - 4) { size = mw->transmit_bytes - 4; } int orig_receive_bytes = mw->receive_bytes; switch (command) { case CMD_VERSION: start_reply(mw, CMD_OK); mw_putc(mw, 1); mw_putc(mw, 3); mw_putc(mw, 0); mw_puts(mw, "blastem"); mw_putc(mw, '\0'); end_reply(mw); break; case CMD_ECHO: mw->receive_bytes = mw->transmit_bytes; memcpy(mw->receive_buffer, mw->transmit_buffer, mw->transmit_bytes); break; case CMD_AP_CFG_GET: cmd_ap_cfg_get(mw); break; case CMD_IP_CURRENT: { iface_info i; if (get_host_address(&i)) { start_reply(mw, CMD_OK); //config number and reserved bytes mw_set(mw, 0, 4); //ip mw_copy(mw, i.ip, sizeof(i.ip)); //net mask mw_copy(mw, i.net_mask, sizeof(i.net_mask)); //gateway guess mw_putc(mw, i.ip[0] & i.net_mask[0]); mw_putc(mw, i.ip[1] & i.net_mask[1]); mw_putc(mw, i.ip[2] & i.net_mask[2]); mw_putc(mw, (i.ip[3] & i.net_mask[3]) + 1); //dns static const uint8_t localhost[] = {127,0,0,1}; mw_copy(mw, localhost, sizeof(localhost)); mw_copy(mw, localhost, sizeof(localhost)); } else { start_reply(mw, CMD_ERROR); } end_reply(mw); break; } case CMD_IP_CFG_GET: cmd_ip_cfg_get(mw); break; case CMD_DEF_AP_CFG_GET: start_reply(mw, CMD_OK); mw_putc(mw, 0); end_reply(mw); break; case CMD_AP_JOIN: mw->module_state = STATE_READY; start_reply(mw, CMD_OK); end_reply(mw); break; case CMD_TCP_CON: cmd_tcp_con(mw, size); break; case CMD_TCP_BIND:{ if (size < 7){ start_reply(mw, CMD_ERROR); end_reply(mw); break; } uint8_t channel = mw->transmit_buffer[10]; if (!channel || channel > 15) { start_reply(mw, CMD_ERROR); end_reply(mw); break; } channel--; if (mw->sock_fds[channel] >= 0) { socket_close(mw->sock_fds[channel]); } mw->sock_fds[channel] = socket(AF_INET, SOCK_STREAM, 0); if (mw->sock_fds[channel] < 0) { start_reply(mw, CMD_ERROR); end_reply(mw); break; } int value = 1; setsockopt(mw->sock_fds[channel], SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)); struct sockaddr_in bind_addr; memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin_family = AF_INET; bind_addr.sin_port = htons(mw->transmit_buffer[8] << 8 | mw->transmit_buffer[9]); if (bind(mw->sock_fds[channel], (struct sockaddr *)&bind_addr, sizeof(bind_addr)) != 0) { socket_close(mw->sock_fds[channel]); mw->sock_fds[channel] = -1; start_reply(mw, CMD_ERROR); end_reply(mw); break; } int res = listen(mw->sock_fds[channel], 2); start_reply(mw, res ? CMD_ERROR : CMD_OK); if (res) { socket_close(mw->sock_fds[channel]); mw->sock_fds[channel] = -1; } else { mw->channel_flags |= 1 << (channel + 1); mw->channel_state[channel] = SOCKST_TCP_LISTEN; socket_blocking(mw->sock_fds[channel], 0); } end_reply(mw); break; } case CMD_CLOSE: cmd_close(mw); break; case CMD_UDP_SET: cmd_udp_set(mw); break; case CMD_SOCK_STAT: { uint8_t channel = mw->transmit_buffer[4]; if (!channel || channel > 15) { start_reply(mw, CMD_ERROR); end_reply(mw); break; } mw->channel_flags &= ~(1 << channel); channel--; poll_socket(mw, channel); start_reply(mw, CMD_OK); mw_putc(mw, mw->channel_state[channel]); end_reply(mw); break; } case CMD_DATETIME: cmd_datetime(mw); break; case CMD_SYS_STAT: poll_all_sockets(mw); start_reply(mw, CMD_OK); mw_putc(mw, mw->module_state); mw_putc(mw, mw->flags); mw_putc(mw, mw->channel_flags >> 8); mw_putc(mw, mw->channel_flags); end_reply(mw); break; case CMD_GAMERTAG_GET: cmd_gamertag_get(mw); break; case CMD_LOG: start_reply(mw, CMD_OK); puts((char*)&mw->transmit_buffer[4]); end_reply(mw); break; case CMD_HRNG_GET: cmd_hrng_get(mw); break; case CMD_SERVER_URL_GET: start_reply(mw, CMD_OK); // FIXME: This should be get from config file mw_puts(mw, "doragasu.com"); mw_putc(mw,'\0'); end_reply(mw); break; default: printf("Unhandled MegaWiFi command %s(%d) with length %X\n", cmd_names[command], command, size); break; } } static void process_packet(megawifi *mw) { if (mw->transmit_channel == 0) { process_command(mw); } else { uint8_t channel = mw->transmit_channel - 1; int channel_state = mw->channel_state[channel]; int sock_fd = mw->sock_fds[channel]; if (sock_fd >= 0 && channel_state == SOCKST_TCP_EST) { int sent = send(sock_fd, (char*)mw->transmit_buffer, mw->transmit_bytes, 0); if (sent < 0 && !socket_error_is_wouldblock()) { socket_close(sock_fd); mw->sock_fds[channel] = -1; mw->channel_state[channel] = SOCKST_NONE; mw->channel_flags |= 1 << mw->transmit_channel; } else if (sent < mw->transmit_bytes) { //TODO: save this data somewhere so it can be sent in poll_socket printf("Sent %d bytes on channel %d, but %d were requested\n", sent, mw->transmit_channel, mw->transmit_bytes); } } else if (sock_fd >= 0 && channel_state == SOCKST_UDP_READY) { udp_send(mw, channel); } else { printf("Unhandled receive of MegaWiFi data on channel %d\n", mw->transmit_channel); } } mw->transmit_bytes = mw->expected_bytes = 0; } void *megawifi_write_b(uint32_t address, void *context, uint8_t value) { if (!(address & 1)) { return context; } megawifi *mw = get_megawifi(context); address = address >> 1 & 7; switch (address) { case 0: switch (mw->transmit_state) { case TX_IDLE: if (value == STX) { mw->transmit_state = TX_LEN1; } break; case TX_LEN1: mw->transmit_channel = value >> 4; mw->expected_bytes = value << 8 & 0xF00; mw->transmit_state = TX_LEN2; break; case TX_LEN2: mw->expected_bytes |= value; mw->transmit_state = TX_PAYLOAD; break; case TX_PAYLOAD: mw->transmit_buffer[mw->transmit_bytes++] = value; if (mw->transmit_bytes == mw->expected_bytes) { mw->transmit_state = TX_WAIT_ETX; } break; case TX_WAIT_ETX: if (value == ETX) { mw->transmit_state = TX_IDLE; process_packet(mw); } break; } break; case 7: mw->scratchpad = value; break; default: printf("Unhandled write to MegaWiFi UART register %X: %X\n", address, value); } return context; } void *megawifi_write_w(uint32_t address, void *context, uint16_t value) { return megawifi_write_b(address | 1, context, value); } uint8_t megawifi_read_b(uint32_t address, void *context) { if (!(address & 1)) { return 0xFF; } megawifi *mw = get_megawifi(context); address = address >> 1 & 7; switch (address) { case 0: poll_all_sockets(mw); if (mw->receive_read < mw->receive_bytes) { uint8_t ret = mw->receive_buffer[mw->receive_read++]; if (mw->receive_read == mw->receive_bytes) { mw->receive_read = mw->receive_bytes = 0; } return ret; } return 0xFF; case 5: poll_all_sockets(mw); //line status return 0x60 | (mw->receive_read < mw->receive_bytes); case 7: return mw->scratchpad; default: printf("Unhandled read from MegaWiFi UART register %X\n", address); return 0xFF; } } uint16_t megawifi_read_w(uint32_t address, void *context) { return 0xFF00 | megawifi_read_b(address | 1, context); }