OXYGENHUB
◆ main openmsx / doc / smxwifi.md
13.7 KiB · Markdown · last changed chore(): initial commit 1 month ago renderedsource blame history raw

SM-X WiFi Emulation Information

This document describes the SM-X WiFi device emulated by openMSX (smxwifi), its I/O interface and how to use it. The SM-X WiFi is built-in on SM-X computers and on some OCM FPGA computers, not an external cartridge.

Overview

The smxwifi device emulates the DucaSP SM-X WiFi (2020): an I/O-mapped UART interface between the MSX and an ESP WiFi module, where the ESP side runs the ESP32-UNAPI firmware. The UART link is either:

  • an emulated ESP32 (default), or
  • a real ESP32 on a host serial port, attached through the device's RS232 connector with rs232-raw

This should not be a concern for most users, where the emulated ESP32 will be used. In some cases, user might want to not use the host network / bypass host network limitations without changing firewall / binding / settings, those users can refer to the Using a real ESP32 section.

MSX software uses the adapter through the SM-X WiFi UNAPI BIOS / driver, which implements the TCP-IP UNAPI specification over the UART protocol spoken by the ESP32 firmware.

UNAPI BIOS / driver

To add UNAPI capabilities to the MSX, download ESPUNAPI_IO.rom from the UNAPI_BIOS_CUSTOM_ESP_V2

It is free for use and required for this device. When using with a real ESP32 module, keep F1 pressed during reset to connect to an AP, configure some features, etc. While you can access this setup with an emulated ESP, no configuration is needed and most of the configurations will not take any effect (as the host system will govern those).

Emulated ESP32

The emulated ESP32 implements the complete TCP-IP UNAPI firmware inside openMSX: the TCP-IP UNAPI specification 1.1 (TCP and UDP connections, DNS resolution, TLS/SSL over TCP active connections) using the network stack of the host operating system. It DOES NOT emulate SSH UNAPI, that is available only attaching a real ESP32.

TLS/SSL support

TLS (spec 1.1, "Use TLS" flag in TCPIP_TCP_OPEN) is supported for TCP active connections. The emulation uses the OpenSSL runtime library installed on the host operating system, which is loaded dynamically at startup.

  • If OpenSSL is found, the "Use TLS in TCP active connections" capability bit is advertised and TLS connections can be opened. The OpenSSL version is printed to the openMSX console when the extension is inserted.
  • If OpenSSL is not found, the capability bit is not advertised and a TCPIP_TCP_OPEN with the TLS flag returns ERR_NOT_IMP. A message is printed to the openMSX console when the extension is inserted.

TLS in passive connections is not implemented (the "Use TLS in TCP passive connections" capability bit is never advertised, and requesting it returns ERR_NOT_IMP, as the specification requires).

Certificate verification

The "Verify the server certificate" flag of TCPIP_TCP_OPEN is honored: the server certificate is validated against the root certificates of the host operating system:

  • Windows: the Windows system certificate store (ROOT) is used.
  • Linux: the default OpenSSL system trust store is used (/etc/ssl/cert.pem and /etc/ssl/certs and friends).
  • macOS: the /etc/ssl/cert.pem bundle is used (this file is provided by the system).

The server host name sent in the TCPIP_TCP_OPEN command is used for SNI and, when verification is enabled, to validate the host name in the certificate (matching the way the ESP32-UNAPI firmware reads the host name from the command data). When no host name is supplied, the certificate chain is still validated, but the host name is not checked.

A failed TLS handshake or certificate validation closes the connection; the close reason (spec 4.5.4, reasons 9-19) is reported by TCPIP_TCP_STATE as ERR_NO_CONN with the reason code.

Installing OpenSSL

  • Linux: OpenSSL is almost always pre-installed (package openssl / libssl). Nothing to do.
  • Windows: install a Win64 OpenSSL build, e.g. the one distributed by slproweb.com or the "OpenSSL" builds by FireDaemon (which are based on LibreSSL and use libssl-4-x64.dll / libcrypto-4-x64.dll). OpenSSL 1.1.x, 3.x and LibreSSL 3.5.x/4.x are all supported. Make sure the OpenSSL DLLs (libssl-*.dll / libcrypto-*.dll) are in PATH or in the openMSX executable directory.
  • macOS: install via Homebrew: brew install openssl@3.

Network adapter preference

The IP address the emulated ESP32 reports to the MSX (TCPIP_GET_IPINFO) is the one other machines use to reach the MSX, so it must be reachable on the network. When the host has several adapters, the primary physical one is chosen:

  1. Wired (Ethernet) — preferred whenever it is up.
  2. Wireless (Wi-Fi) — used when no wired adapter is up.
  3. Other physical adapters (PPP, modems, ...).
  4. Virtual adapters — only used when no physical adapter has a usable IPv4 address, so the emulation still works on machines that only have a virtual adapter (e.g. WSL-only setups).

The loopback adapter and adapters that are down are never used. Tunnels (6to4, ISATAP, Teredo) and VPN software (Tailscale, ZeroTier, WireGuard, ...) count as virtual.

When adapters tie in preference, the first one is used.

Host ports and operating-system services

The emulation binds sockets on the host operating system to emulate the network interface of the ESP32. Consequently, a port that is already in use on the host cannot be used by the device:

  • The emulation reports the host's own network configuration to the MSX (its primary IPv4 address, netmask, gateway and DNS servers, read fresh on each TCPIP_GET_IPINFO call; see Network adapter preference above), so other machines on the same network reach the MSX at that address, without any configuration on those machines. When the host has no usable IPv4 configuration the fields report 0.0.0.0, except the primary DNS which falls back to the well-known 8.8.8.8 (DNS lookups are performed on the host anyway, so the reported value is informational).
  • Opening a connection (TCP or UDP) on a local port that is occupied by a host application returns ERR_CONN_EXISTS (or fails).
  • In particular, well-known ports used by operating-system services are not available. On Windows, the Windows Time service (W32Time) is started by default and keeps UDP port 123 in use permanently, so the TCPIP_SNTP_SET/SNTP functionality cannot use its standard port. To use the SNTP port, the W32Time service must be stopped (see below).
  • To free a port for the device, stop the host service that occupies it. On Windows, stop the Windows Time service temporarily with net stop w32time, or permanently by setting its startup type to "Manual"/"Disabled" in the Services manager (services.msc) or with sc config w32time start= disabled.

Firewalls and inbound connections

The emulation binds sockets on the host operating system, so inbound traffic to the emulated MSX has to pass the host's firewall. This applies to servers running on the MSX (for example an FTP server on port 21) and to "connect-back" connections, where a remote machine connects to the MSX's advertised port (for example the data connection of an FTP client). Outbound connections are normally not affected.

  • Linux (ufw): ufw denies inbound connections by default. Allow the ports used by the MSX applications; for example, for an FTP server and the data connections of an FTP client:

    sudo ufw allow from 192.168.0.0/24 to any port 21 proto tcp         # FTP server (control)
    sudo ufw allow from 192.168.0.0/24 to any port 113 proto tcp        # FTP client (IDENT listener)
    sudo ufw allow from 192.168.0.0/24 to any port 1024:65535 proto tcp # FTP data connections
    

    (the FTP client opens its data connection on a random port in the range 16384-32767; the FTP server opens its data ports starting at 1024).

    UDP traffic is subject to the same inbound deny, and replies to MSX UDP clients are affected as well: TFTP and SNTP on the MSX are clients, so their requests go out, but the replies (and subsequent data packets) arrive on the host as inbound UDP traffic and are dropped unless allowed. Allow the local ports those clients bind - SNTP uses UDP port 123, the TFTP client uses port 69 or a random port in the 16384-32767 range:

    sudo ufw allow from 192.168.0.0/24 to any port 123 proto udp        # SNTP client (local port)
    sudo ufw allow from 192.168.0.0/24 to any port 69 proto udp         # TFTP client (local port)
    sudo ufw allow from 192.168.0.0/24 to any port 16384:32767 proto udp # TFTP client (random local port)
    

    When in doubt, allow the local port(s) the application uses. Port 69 is privileged like port 21 (see below).

    For a quick test the firewall can be disabled temporarily with sudo ufw disable and re-enabled with sudo ufw enable. iptables- or nftables-based firewalls need equivalent rules.

  • Privileged ports: on Linux and macOS, binding a port below 1024 requires privileges. If an MSX application uses such a port (for example an FTP server on port 21), grant the capability once so regular users can bind these ports:

    sudo setcap 'cap_net_bind_service=+ep' /path/to/openmsx
    

    (the capability must be reapplied after every openMSX update, and the filesystem must support file capabilities). Alternatively, authbind --deep openmsx can be used. Windows has no privileged ports.

Technical / Advanced Information

Setting: simulated WiFi link

The smxwifi-emulatedesp32-link-enabled setting (default true) simulates the WiFi link state of the emulated ESP32: with the setting disabled, the emulated ESP32 reports its network link as closed (which is what TCPIP_NET_STATE reports), like a real ESP32 whose WiFi radio is off. It does not affect a real ESP32 attached through rs232-raw. This is useful for developers that want to test cable / AP disconnected.

I/O ports

The extension (smxwifi.xml) occupies 2 I/O ports at base 0x06:

PortDirectionFunction
basereadUART RX FIFO byte (from the ESP module)
basewriteUART command (baud-rate select, FIFO reset)
base + 1readUART status
base + 1writeUART TX byte (to the ESP module)

UART status bits:

BitMeaning
0RX data available in the FIFO
3Quick receive supported
4Underrun (read of an empty FIFO), cleared by read

Quick receive is on by default, it works by safeguarding the read of an UART RX FIFO byte, if FIFO is empty it will assert WAIT on the BUS (so z80 will not continue processing) until a byte arrives OR a timeout elapses (about 30 ms); if no data arrives, 0xFF is returned and the underrun bit is set. UNAPI Driver make use of this feature to accelerate receiving data from connections, in a way that it is guaranteed that extra bytes are coming, in all other operations it is always checking if there is a byte available before reading. The end result is faster receiving at the "expense" of asserting WAIT for some time. The smxwifi-emulatedesp32-quickrcv-enabled setting (default true) can be used to turn this feature off.

UART command values (write to the data port base):

ValueFunction
0-9Select baud rate (see table below)
20Reset the RX FIFO

Baud rates (value = index):

ValueBaud rateValueBaud rate
0859372538400
1346520631250
2231014719200
311520089600
45760094800

The default after reset is baud rate 0 (859372). The UART parameters are always 8 data bits, 1 stop bit, no parity. With the emulated ESP32 the baud rate is not considered (there is no real serial communication).

Using a real ESP32

ESP Firmware

A real ESP32 module can be useful for developers or advanced users that do not want to run into the host network limitations or want to have a separate physical connection to the MSX. This module must run the ESP32-UNAPI firmware, which is built from the ESP32-UNAPI-Firmware repository and flashed to the ESP32. We recommend using an ESP32-C6 or ESP32-S3 board with at least 8 MB of flash; 16 MB is recommended.

Connector and pluggables

Once the extension is inserted, it exposes an RS232 connector named smxwifi. If you plan to use a real ESP32 module instead of the emulated one, plug rs232-raw into it to connect the UART to a real ESP32 on a host serial port:

ext smxwifi
plug smxwifi rs232-raw
set rs232-raw-port COM7

As long as the connector has no rs232-raw device plugged in, or its host serial port is not open (no port selected, port in use, ...), the device talks to the emulated ESP32, so the adapter keeps working with no real hardware attached. Plugging a device other than rs232-raw also leaves the emulated ESP32 in charge.

Note: changes can be done pretty easily on the GUI as well, using the connectors menu.

With rs232-raw the networking happens on the attached real device, so the host networking considerations of the emulated ESP32 (firewall, ports, TLS) do not apply.