summaryrefslogtreecommitdiff
path: root/megawifi.c
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2018-03-24 22:18:23 -0700
committerMichael Pavone <pavone@retrodev.com>2018-03-24 22:18:23 -0700
commit371d5418e6b1bfba88b55382b0a1b91d97023ae5 (patch)
tree26e75e6766ec3f43284b90f3ba5acbf72d78d646 /megawifi.c
parent484e97a4e318d18b867acea7773853dfc7616a30 (diff)
parent15af9462392967b6adf7ba6ff4f7ff778cf10eb3 (diff)
Merge
--HG-- branch : nuklear_ui
Diffstat (limited to 'megawifi.c')
-rw-r--r--megawifi.c423
1 files changed, 423 insertions, 0 deletions
diff --git a/megawifi.c b/megawifi.c
new file mode 100644
index 0000000..a0adfba
--- /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 MAX_RECV_SIZE 1440
+
+#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) {
+ 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], 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 && 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, MSG_NOSIGNAL);
+ 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);
+}