summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--megawifi.c423
-rw-r--r--megawifi.h9
-rw-r--r--mw_commands.c33
-rw-r--r--net.c49
-rw-r--r--net.h12
-rw-r--r--rom.db13
-rw-r--r--romdb.c7
8 files changed, 547 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 4350168..67f1a01 100644
--- a/Makefile
+++ b/Makefile
@@ -127,7 +127,7 @@ Z80OBJS=z80inst.o z80_to_x86.o
AUDIOOBJS=ym2612.o psg.o wave.o
CONFIGOBJS=config.o tern.o util.o
-MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o ppm.o io.o romdb.o hash.o menu.o xband.o realtec.o i2c.o nor.o sega_mapper.o multi_game.o serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
+MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o render_sdl.o ppm.o io.o romdb.o hash.o menu.o xband.o realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o net.o serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
ifeq ($(CPU),x86_64)
CFLAGS+=-DX86_64 -m64
diff --git a/megawifi.c b/megawifi.c
new file mode 100644
index 0000000..23b2530
--- /dev/null
+++ b/megawifi.c
@@ -0,0 +1,423 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "genesis.h"
+#include "net.h"
+
+enum {
+ TX_IDLE,
+ TX_LEN1,
+ TX_LEN2,
+ TX_PAYLOAD,
+ TX_WAIT_ETX
+};
+#define STX 0x7E
+#define ETX 0x7E
+
+#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"
+};
+
+enum {
+ STATE_IDLE=1,
+ STATE_AP_JOIN,
+ STATE_SCAN,
+ STATE_READY,
+ STATE_TRANSPARENT
+};
+
+#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];
+} megawifi;
+
+static megawifi *get_megawifi(void *context)
+{
+ m68k_context *m68k = context;
+ genesis_context *gen = m68k->system;
+ if (!gen->extra) {
+ gen->extra = calloc(1, sizeof(megawifi));
+ megawifi *mw = gen->extra;
+ mw->module_state = STATE_IDLE;
+ 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, char *s)
+{
+ uint32_t len = strlen(s);
+ if ((mw->receive_bytes + len) > sizeof(mw->receive_buffer)) {
+ return;
+ }
+ memcpy(mw->receive_buffer + mw->receive_bytes, s, len);
+ mw->receive_bytes += len;
+}
+
+static void poll_socket(megawifi *mw, uint8_t channel)
+{
+ if (mw->sock_fds[channel] < 0) {
+ return;
+ }
+ if (mw->channel_state[channel] == 1) {
+ int res = accept(mw->sock_fds[channel], NULL, NULL);
+ if (res >= 0) {
+ close(mw->sock_fds[channel]);
+ fcntl(res, F_SETFL, O_NONBLOCK);
+ mw->sock_fds[channel] = res;
+ mw->channel_state[channel] = 2;
+ mw->channel_flags |= 1 << (channel + 1);
+ } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ close(mw->sock_fds[channel]);
+ mw->channel_state[channel] = 0;
+ mw->channel_flags |= 1 << (channel + 1);
+ }
+ } else if (mw->channel_state[channel] == 2 && mw->receive_bytes < sizeof(mw->receive_buffer) - 4) {
+ int bytes = recv(
+ mw->sock_fds[channel],
+ mw->receive_buffer + mw->receive_bytes + 3,
+ sizeof(mw->receive_buffer) - 4 - mw->receive_bytes,
+ 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 && errno != EAGAIN && errno != EWOULDBLOCK) {
+ close(mw->sock_fds[channel]);
+ mw->channel_state[channel] = 0;
+ mw->channel_flags |= 1 << (channel + 1);
+ }
+ }
+}
+
+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 process_packet(megawifi *mw)
+{
+ if (mw->transmit_channel == 0) {
+ 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, 0);
+ mw_puts(mw, "blastem");
+ 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_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_AP_JOIN:
+ mw->module_state = STATE_READY;
+ start_reply(mw, CMD_OK);
+ end_reply(mw);
+ 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) {
+ 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, &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) {
+ 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) {
+ close(mw->sock_fds[channel]);
+ mw->sock_fds[channel] = -1;
+ } else {
+ mw->channel_flags |= 1 << (channel + 1);
+ mw->channel_state[channel] = 1;
+ fcntl(mw->sock_fds[channel], F_SETFL, O_NONBLOCK);
+ }
+ end_reply(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_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;
+ default:
+ printf("Unhandled MegaWiFi command %s(%d) with length %X\n", cmd_names[command], command, size);
+ break;
+ }
+ } else if (mw->sock_fds[mw->transmit_channel - 1] >= 0 && mw->channel_state[mw->transmit_channel - 1] == 2) {
+ uint8_t channel = mw->transmit_channel - 1;
+ int sent = send(mw->sock_fds[channel], mw->transmit_buffer, mw->transmit_bytes, 0);
+ if (sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+ close(mw->sock_fds[channel]);
+ mw->sock_fds[channel] = -1;
+ mw->channel_state[channel] = 0;
+ 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 {
+ 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);
+}
diff --git a/megawifi.h b/megawifi.h
new file mode 100644
index 0000000..30aab1c
--- /dev/null
+++ b/megawifi.h
@@ -0,0 +1,9 @@
+#ifndef MEGAWIFI_H_
+#define MEGAWIFI_H_
+
+void *megawifi_write_w(uint32_t address, void *context, uint16_t value);
+void *megawifi_write_b(uint32_t address, void *context, uint8_t value);
+uint16_t megawifi_read_w(uint32_t address, void *context);
+uint8_t megawifi_read_b(uint32_t address, void *context);
+
+#endif //MEGAWIFI_H_
diff --git a/mw_commands.c b/mw_commands.c
new file mode 100644
index 0000000..9b87ccb
--- /dev/null
+++ b/mw_commands.c
@@ -0,0 +1,33 @@
+ E(CMD_OK),
+ E(CMD_VERSION),
+ E(CMD_ECHO),
+ E(CMD_AP_SCAN),
+ E(CMD_AP_CFG),
+ E(CMD_AP_CFG_GET),
+ E(CMD_IP_CURRENT),
+ E(CMD_RESERVED),
+ E(CMD_IP_CFG),
+ E(CMD_IP_CFG_GET),
+ E(CMD_DEF_AP_CFG),
+ E(CMD_DEF_AP_CFG_GET),
+ E(CMD_AP_JOIN),
+ E(CMD_AP_LEAVE),
+ E(CMD_TCP_CON),
+ E(CMD_TCP_BIND),
+ E(CMD_TCP_ACCEPT),
+ E(CMD_TCP_DISC),
+ E(CMD_UDP_SET),
+ E(CMD_UDP_CLR),
+ E(CMD_SOCK_STAT),
+ E(CMD_PING),
+ E(CMD_SNTP_CFG),
+ E(CMD_SNTP_CFG_GET),
+ E(CMD_DATETIME),
+ E(CMD_DT_SET),
+ E(CMD_FLASH_WRITE),
+ E(CMD_FLASH_READ),
+ E(CMD_FLASH_ERASE),
+ E(CMD_FLASH_ID),
+ E(CMD_SYS_STAT),
+ E(CMD_DEF_CFG_SET),
+ E(CMD_HRNG_GET), \ No newline at end of file
diff --git a/net.c b/net.c
new file mode 100644
index 0000000..eefc9cd
--- /dev/null
+++ b/net.c
@@ -0,0 +1,49 @@
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include "net.h"
+
+static uint8_t is_loopback(struct sockaddr_in *addr)
+{
+ return (addr->sin_addr.s_addr & 0xFF) == 127;
+}
+
+static void format_address(uint8_t *dst, struct sockaddr_in *addr)
+{
+ long ip = addr->sin_addr.s_addr;
+ dst[0] = ip;
+ dst[1] = ip >> 8;
+ dst[2] = ip >> 16;
+ dst[3] = ip >> 24;
+}
+
+uint8_t get_host_address(iface_info *out)
+{
+ struct ifaddrs *entries, *current, *localhost;
+ if (getifaddrs(&entries)) {
+ return 0;
+ }
+
+ for (current = entries; current; current = current->ifa_next)
+ {
+ if (current->ifa_addr && current->ifa_addr->sa_family == AF_INET) {
+ struct sockaddr_in *addr = (struct sockaddr_in *)current->ifa_addr;
+ if (is_loopback(addr)) {
+ localhost = current;
+ } else {
+ break;
+ }
+ }
+ }
+ if (!current && localhost) {
+ current = localhost;
+ }
+ uint8_t ret = 0;
+ if (current) {
+ ret = 1;
+ format_address(out->ip, (struct sockaddr_in *)current->ifa_addr);
+ format_address(out->net_mask, (struct sockaddr_in *)current->ifa_netmask);
+ }
+ freeifaddrs(entries);
+ return ret;
+} \ No newline at end of file
diff --git a/net.h b/net.h
new file mode 100644
index 0000000..bcd5ca6
--- /dev/null
+++ b/net.h
@@ -0,0 +1,12 @@
+#ifndef NET_H_
+#define NET_H_
+#include <stdint.h>
+
+typedef struct {
+ uint8_t ip[4];
+ uint8_t net_mask[4];
+} iface_info;
+
+uint8_t get_host_address(iface_info *out);
+
+#endif //NET_H_
diff --git a/rom.db b/rom.db
index 1be4279..e74cbc9 100644
--- a/rom.db
+++ b/rom.db
@@ -1299,3 +1299,16 @@ e1c041ba69da087c428dcda16850159f3caebd4b {
}
}
}
+cda73e4caf53cbc8f0750b69e5e7f394ad3735d1 {
+ name MegaWiFi Bootloader
+ map {
+ 0 {
+ device ROM
+ last 3FFFFF
+ }
+ A130C0 {
+ device megawifi
+ last A130CF
+ }
+ }
+}
diff --git a/romdb.c b/romdb.c
index 983829b..e580797 100644
--- a/romdb.c
+++ b/romdb.c
@@ -11,6 +11,7 @@
#include "nor.h"
#include "sega_mapper.h"
#include "multi_game.h"
+#include "megawifi.h"
#define DOM_TITLE_START 0x120
#define DOM_TITLE_END 0x150
@@ -778,6 +779,12 @@ void map_iter_fun(char *key, tern_val val, uint8_t valtype, void *data)
map->mask = 0xFF;
map->write_16 = write_multi_game_w;
map->write_8 = write_multi_game_b;
+ } else if (!strcmp(dtype, "megawifi")) {
+ map->write_16 = megawifi_write_w;
+ map->write_8 = megawifi_write_b;
+ map->read_16 = megawifi_read_w;
+ map->read_8 = megawifi_read_b;
+ map->mask = 0xFFFFFF;
} else {
fatal_error("Invalid device type %s for ROM DB map entry %d with address %s\n", dtype, state->index, key);
}