Skip to content

Commit

Permalink
Make network connections explicit
Browse files Browse the repository at this point in the history
Also fixed a lack of timeout for Wifi, sets an explicit timeout for
wired, fixes error masking during menu setup, and rejects unknown
commands.
  • Loading branch information
joeyparrish committed Sep 15, 2024
1 parent 668c0a4 commit a09c3aa
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 24 deletions.
14 changes: 12 additions & 2 deletions emulator-patches/kinetoscope.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#define CMD_STOP_VIDEO 0x03
#define CMD_FLIP_REGION 0x04
#define CMD_GET_ERROR 0x05
#define CMD_CONNECT_NET 0x06

// NOTE: The addresses sent to us are all relative to the base of 0xA13000.
// So we only check the offset from there. All addresses are even because the
Expand Down Expand Up @@ -125,7 +126,13 @@ static void write_sram(uint32_t offset, const uint8_t* data, uint32_t size) {
}
}

static void report_error(const char *message) {
static void report_error(const char *format, ...) {
char message[256];
va_list args;
va_start(args, format);
vsnprintf(message, sizeof(message), format, args);
va_end(args);

if (global_error == 0) {
printf("Kinetoscope: Simulating error: %s\n", message);
global_error = 1;
Expand Down Expand Up @@ -379,8 +386,11 @@ static void execute_command() {
} else if (global_command == CMD_GET_ERROR) {
printf("Kinetoscope: CMD_GET_ERROR\n");
write_error_to_sram();
} else if (global_command == CMD_CONNECT_NET) {
printf("Kinetoscope: CMD_CONNECT_NET\n");
// Nothing to do: already connected.
} else {
printf("Kinetoscope: unknown command 0x%02x\n", global_command);
report_error("Unrecognized command 0x%02X!", global_command);
}

// Completed.
Expand Down
32 changes: 26 additions & 6 deletions firmware/firmware.ino
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#define MAX_PATH 256
#define MAX_FETCH_SIZE (1024 * 1024)

#define NETWORK_TIMEOUT_SECONDS 30

// Allocate a second 8kB stack for the second core.
// https://github.com/earlephilhower/arduino-pico/blob/master/docs/multicore.rst
bool core1_separate_stack = true;
Expand All @@ -61,6 +63,7 @@ static http_data_callback fetch_callback = NULL;
static uint8_t* fetch_buffer = NULL;
static int fetch_buffer_size = 0;

static bool network_connected = false;
static int chunk_size = 0;
static int total_chunks = 0;
static int next_chunk_num = 0;
Expand All @@ -74,8 +77,13 @@ static void init_all_hardware() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);

Serial.println("All hardware initialized.");
}

static void connect_network() {
// Prefer wired, fall back to WiFi if configured.
Client* client = internet_init_wired(MAC_ADDR);
Serial.println("Connecting to the network...");
Client* client = internet_init_wired(MAC_ADDR, NETWORK_TIMEOUT_SECONDS);

if (!client) {
Serial.println("Wired connection failed!");
Expand All @@ -92,7 +100,8 @@ static void init_all_hardware() {
Serial.println("WiFi not configured!");
report_error("Wired connection failed and WiFi not configured!");
} else {
client = internet_init_wifi(SECRET_WIFI_SSID, SECRET_WIFI_PASS);
client = internet_init_wifi(SECRET_WIFI_SSID, SECRET_WIFI_PASS,
NETWORK_TIMEOUT_SECONDS);
if (!client) {
Serial.println("WiFi connection failed!");
report_error("WiFi connection failed!");
Expand All @@ -102,11 +111,9 @@ static void init_all_hardware() {

if (!client) {
Serial.println("Failed to connect to the network!");
} else {
http_init(client);
}

Serial.println("All hardware initialized.");
http_init(client);
network_connected = client != NULL;
}

static bool http_sram_callback(const uint8_t* buffer, int bytes) {
Expand Down Expand Up @@ -265,6 +272,17 @@ static void process_command(uint8_t command, uint8_t arg) {
// Write a buffered error message to SRAM so the ROM software can read it.
write_error_to_sram();
break;

case KINETOSCOPE_CMD_CONNECT_NET:
if (!network_connected) {
connect_network();
}
break;

default: {
report_error("Unrecognized command 0x%02X!", command);
break;
}
}

#ifdef DEBUG
Expand Down Expand Up @@ -292,6 +310,8 @@ void setup() {
init_all_hardware();

#ifdef RUN_TESTS
// Automatically connect to the network to run speed tests.
connect_network();
run_tests();
#endif

Expand Down
11 changes: 9 additions & 2 deletions firmware/internet-wifi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ static int status = WL_IDLE_STATUS;

static WiFiClient wifi_client;

Client* internet_init_wifi(const char* ssid, const char* password) {
Client* internet_init_wifi(const char* ssid, const char* password,
unsigned int timeout_seconds) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);

Expand All @@ -33,8 +34,14 @@ Client* internet_init_wifi(const char* ssid, const char* password) {
WiFi.begin(ssid);
}

// FIXME: Handle timeouts here
long timeout_ms = timeout_seconds * 1000L;
long start_ms = millis();
while (WiFi.status() != WL_CONNECTED) {
long end_ms = millis();
if (end_ms - start_ms >= timeout_ms) {
Serial.println("WiFi timeout!");
return NULL;
}
delay(500);
}
Serial.println("Connected to WiFi!");
Expand Down
25 changes: 17 additions & 8 deletions firmware/internet-wired.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
#include <Ethernet.h>
#include <HardwareSerial.h>
#include <SPI.h>
#include <utility/w5100.h> // Inside Ethernet library

#include "internet.h"

EthernetClient client;

Client* internet_init_wired(const uint8_t* mac) {
Client* internet_init_wired(const uint8_t* mac, unsigned int timeout_seconds) {
SPI.begin();

// Default SPI pins for RP2040:
Expand All @@ -28,19 +29,27 @@ Client* internet_init_wired(const uint8_t* mac) {
// MISO == GP16
// SCK == 18

// It's really stupid that this library doesn't take a const input.
int ok = Ethernet.begin(const_cast<uint8_t*>(mac));
// hardwareStatus() isn't valid until the W5100 library is initialized, which
// normally happens during DHCP negotiation. Since that has a long timeout,
// go around the Ethernet library and initialize the chipset directly first.
W5100.init();
int hardware_status = Ethernet.hardwareStatus();

if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found.");
} else if (Ethernet.hardwareStatus() == EthernetW5100) {
if (hardware_status == EthernetW5100) {
Serial.println("W5100 Ethernet controller detected.");
} else if (Ethernet.hardwareStatus() == EthernetW5200) {
} else if (hardware_status == EthernetW5200) {
Serial.println("W5200 Ethernet controller detected.");
} else if (Ethernet.hardwareStatus() == EthernetW5500) {
} else if (hardware_status == EthernetW5500) {
Serial.println("W5500 Ethernet controller detected.");
} else {
Serial.println("No Ethernet controller found.");
return NULL;
}

unsigned long timeout_ms = timeout_seconds * 1000L;
// It's really stupid that this library doesn't take a const input.
int ok = Ethernet.begin(const_cast<uint8_t*>(mac), timeout_ms);

Serial.print("DHCP: ");
Serial.println(ok ? "success" : "failure");
if (!ok) {
Expand Down
5 changes: 3 additions & 2 deletions firmware/internet.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
#ifndef _KINETOSCOPE_INTERNET_H

// Password can be blank or null if there is no authentication required.
Client* internet_init_wifi(const char* ssid, const char* password);
Client* internet_init_wifi(const char* ssid, const char* password,
unsigned int timeout_seconds);

Client* internet_init_wired(const uint8_t* mac);
Client* internet_init_wired(const uint8_t* mac, unsigned int timeout_seconds);

#endif // _KINETOSCOPE_INTERNET_H
1 change: 1 addition & 0 deletions firmware/registers.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#define KINETOSCOPE_CMD_STOP_VIDEO 0x03 // Stops streaming
#define KINETOSCOPE_CMD_FLIP_REGION 0x04 // Switch SRAM banks for streaming
#define KINETOSCOPE_CMD_GET_ERROR 0x05 // Load error information into SRAM
#define KINETOSCOPE_CMD_CONNECT_NET 0x06 // Connect/reconnect to the network

void registers_init();

Expand Down
15 changes: 11 additions & 4 deletions software/player/src/segavideo_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ static int max_status_y = 0;
#define CMD_STOP_VIDEO 0x03 // Stops streaming
#define CMD_FLIP_REGION 0x04 // Switch SRAM banks for streaming
#define CMD_GET_ERROR 0x05 // Load error information into SRAM
#define CMD_CONNECT_NET 0x06 // Connect to the network

// Token values for async communication.
#define TOKEN_CONTROL_TO_SEGA 0
Expand Down Expand Up @@ -364,15 +365,21 @@ bool segavideo_menu_load() {
clearScreen();
drawLogo();

statusMessage("Fetching video list...");

statusMessage("Connecting to the network...");
#if defined(SIMULATE_HARDWARE)
waitMs(3000);
#endif
uint16_t command_timeout = 40; // seconds
if (!sendCommandAndWait(CMD_CONNECT_NET, 0, command_timeout)) {
return false;
}

uint16_t command_timeout = 30; // seconds
statusMessage("Fetching video list...");
#if defined(SIMULATE_HARDWARE)
waitMs(3000);
#endif
command_timeout = 30; // seconds
if (!sendCommandAndWait(CMD_LIST_VIDEOS, 0, command_timeout)) {
errorMessage("Failed to fetch video list!");
return false;
}

Expand Down

0 comments on commit a09c3aa

Please sign in to comment.