| 1 |
# SM-X WiFi Emulation Information |
| 2 |
|
| 3 |
This document describes the SM-X WiFi device emulated by openMSX |
| 4 |
(`smxwifi`), its I/O interface and how to use it. The SM-X WiFi is built-in |
| 5 |
on SM-X computers and on some OCM FPGA computers, not an external cartridge. |
| 6 |
|
| 7 |
## Overview |
| 8 |
|
| 9 |
The `smxwifi` device emulates the DucaSP SM-X WiFi (2020): an I/O-mapped |
| 10 |
UART interface between the MSX and an ESP WiFi module, where the ESP side |
| 11 |
runs the ESP32-UNAPI firmware. The UART link is either: |
| 12 |
|
| 13 |
* **an emulated ESP32** (default), or |
| 14 |
* **a real ESP32** on a host serial port, attached through the device's RS232 connector with `rs232-raw` |
| 15 |
|
| 16 |
This should not be a concern for most users, where the emulated ESP32 will |
| 17 |
be used. In some cases, user might want to not use the host network / bypass |
| 18 |
host network limitations without changing firewall / binding / settings, |
| 19 |
those users can refer to the Using a real ESP32 section. |
| 20 |
|
| 21 |
MSX software uses the adapter through the SM-X WiFi UNAPI BIOS / driver, which |
| 22 |
implements the TCP-IP UNAPI specification over the UART protocol spoken by |
| 23 |
the ESP32 firmware. |
| 24 |
|
| 25 |
## UNAPI BIOS / driver |
| 26 |
|
| 27 |
To add UNAPI capabilities to the MSX, download `ESPUNAPI_IO.rom` |
| 28 |
from the [UNAPI_BIOS_CUSTOM_ESP_V2](https://github.com/ducasp/MSX-Development/tree/master/UNAPI_BIOS_CUSTOM_ESP_V2) |
| 29 |
|
| 30 |
It is free for use and required for this device. When using with a real ESP32 module, |
| 31 |
keep F1 pressed during reset to connect to an AP, configure some features, etc. |
| 32 |
While you can access this setup with an emulated ESP, no configuration is needed |
| 33 |
and most of the configurations will not take any effect (as the host system will |
| 34 |
govern those). |
| 35 |
|
| 36 |
## Emulated ESP32 |
| 37 |
|
| 38 |
The emulated ESP32 implements the complete TCP-IP UNAPI firmware inside |
| 39 |
openMSX: the TCP-IP UNAPI specification 1.1 (TCP and UDP connections, DNS |
| 40 |
resolution, TLS/SSL over TCP active connections) using the network stack of |
| 41 |
the host operating system. It DOES NOT emulate SSH UNAPI, that is available |
| 42 |
only attaching a real ESP32. |
| 43 |
|
| 44 |
### TLS/SSL support |
| 45 |
|
| 46 |
TLS (spec 1.1, "Use TLS" flag in `TCPIP_TCP_OPEN`) is supported for TCP |
| 47 |
**active** connections. The emulation uses the OpenSSL runtime library |
| 48 |
**installed on the host operating system**, which is loaded dynamically at |
| 49 |
startup. |
| 50 |
|
| 51 |
* If OpenSSL is found, the "_Use TLS in TCP active connections_" capability |
| 52 |
bit is advertised and TLS connections can be opened. The OpenSSL version |
| 53 |
is printed to the openMSX console when the extension is inserted. |
| 54 |
* If OpenSSL is not found, the capability bit is **not** advertised and a |
| 55 |
`TCPIP_TCP_OPEN` with the TLS flag returns `ERR_NOT_IMP`. A message is |
| 56 |
printed to the openMSX console when the extension is inserted. |
| 57 |
|
| 58 |
TLS in **passive** connections is not implemented (the "_Use TLS in TCP |
| 59 |
passive connections_" capability bit is never advertised, and requesting it |
| 60 |
returns `ERR_NOT_IMP`, as the specification requires). |
| 61 |
|
| 62 |
### Certificate verification |
| 63 |
|
| 64 |
The "Verify the server certificate" flag of `TCPIP_TCP_OPEN` is honored: the |
| 65 |
server certificate is validated against the root certificates of the host |
| 66 |
operating system: |
| 67 |
|
| 68 |
* **Windows:** the Windows system certificate store (`ROOT`) is used. |
| 69 |
* **Linux:** the default OpenSSL system trust store is used |
| 70 |
(`/etc/ssl/cert.pem` and `/etc/ssl/certs` and friends). |
| 71 |
* **macOS:** the `/etc/ssl/cert.pem` bundle is used (this file is provided by |
| 72 |
the system). |
| 73 |
|
| 74 |
The server host name sent in the `TCPIP_TCP_OPEN` command is used for SNI |
| 75 |
and, when verification is enabled, to validate the host name in the |
| 76 |
certificate (matching the way the ESP32-UNAPI firmware reads the host name |
| 77 |
from the command data). When no host name is supplied, the certificate chain |
| 78 |
is still validated, but the host name is not checked. |
| 79 |
|
| 80 |
A failed TLS handshake or certificate validation closes the connection; the |
| 81 |
close reason (spec 4.5.4, reasons 9-19) is reported by `TCPIP_TCP_STATE` as |
| 82 |
`ERR_NO_CONN` with the reason code. |
| 83 |
|
| 84 |
### Installing OpenSSL |
| 85 |
|
| 86 |
* **Linux:** OpenSSL is almost always pre-installed (package `openssl` / |
| 87 |
`libssl`). Nothing to do. |
| 88 |
* **Windows:** install a Win64 OpenSSL build, e.g. the one distributed by |
| 89 |
[slproweb.com](https://slproweb.com/products/Win32OpenSSL.html) or the |
| 90 |
"OpenSSL" builds by [FireDaemon](https://firedaemon.com) (which are based |
| 91 |
on LibreSSL and use `libssl-4-x64.dll` / `libcrypto-4-x64.dll`). OpenSSL |
| 92 |
1.1.x, 3.x and LibreSSL 3.5.x/4.x are all supported. Make sure the OpenSSL |
| 93 |
DLLs (`libssl-*.dll` / `libcrypto-*.dll`) are in `PATH` or in the openMSX |
| 94 |
executable directory. |
| 95 |
* **macOS:** install via Homebrew: `brew install openssl@3`. |
| 96 |
|
| 97 |
### Network adapter preference |
| 98 |
|
| 99 |
The IP address the emulated ESP32 reports to the MSX (`TCPIP_GET_IPINFO`) is |
| 100 |
the one other machines use to reach the MSX, so it must be reachable on the |
| 101 |
network. When the host has several adapters, the primary physical one is |
| 102 |
chosen: |
| 103 |
|
| 104 |
1. **Wired** (Ethernet) — preferred whenever it is up. |
| 105 |
2. **Wireless** (Wi-Fi) — used when no wired adapter is up. |
| 106 |
3. **Other physical** adapters (PPP, modems, ...). |
| 107 |
4. **Virtual adapters** — only used when no physical adapter has a usable |
| 108 |
IPv4 address, so the emulation still works on machines that only have a |
| 109 |
virtual adapter (e.g. WSL-only setups). |
| 110 |
|
| 111 |
The loopback adapter and adapters that are down are never used. Tunnels |
| 112 |
(6to4, ISATAP, Teredo) and VPN software (Tailscale, ZeroTier, WireGuard, ...) |
| 113 |
count as virtual. |
| 114 |
|
| 115 |
When adapters tie in preference, the first one is used. |
| 116 |
|
| 117 |
### Host ports and operating-system services |
| 118 |
|
| 119 |
The emulation binds sockets on the **host** operating system to emulate the |
| 120 |
network interface of the ESP32. Consequently, a port that is already in use |
| 121 |
on the host cannot be used by the device: |
| 122 |
|
| 123 |
* The emulation reports the host's own network configuration to the MSX (its |
| 124 |
primary IPv4 address, netmask, gateway and DNS servers, read fresh on each |
| 125 |
`TCPIP_GET_IPINFO` call; see *Network adapter preference* above), so other |
| 126 |
machines on the same network reach the MSX at that address, without any |
| 127 |
configuration on those machines. When the host has no usable IPv4 |
| 128 |
configuration the fields report `0.0.0.0`, except the primary DNS which |
| 129 |
falls back to the well-known `8.8.8.8` (DNS lookups are performed on the |
| 130 |
host anyway, so the reported value is informational). |
| 131 |
* Opening a connection (TCP or UDP) on a local port that is occupied by a |
| 132 |
host application returns `ERR_CONN_EXISTS` (or fails). |
| 133 |
* In particular, well-known ports used by operating-system services are not |
| 134 |
available. On Windows, the Windows Time service (W32Time) is started by |
| 135 |
default and keeps UDP port 123 in use permanently, so the |
| 136 |
`TCPIP_SNTP_SET`/SNTP functionality cannot use its standard port. To use |
| 137 |
the SNTP port, the W32Time service must be stopped (see below). |
| 138 |
* To free a port for the device, stop the host service that occupies it. On |
| 139 |
Windows, stop the Windows Time service temporarily with |
| 140 |
`net stop w32time`, or permanently by setting its startup type to |
| 141 |
"Manual"/"Disabled" in the Services manager (`services.msc`) or with |
| 142 |
`sc config w32time start= disabled`. |
| 143 |
|
| 144 |
### Firewalls and inbound connections |
| 145 |
|
| 146 |
The emulation binds sockets on the host operating system, so inbound traffic |
| 147 |
to the emulated MSX has to pass the host's firewall. This applies to servers |
| 148 |
running on the MSX (for example an FTP server on port 21) and to |
| 149 |
"connect-back" connections, where a remote machine connects to the MSX's |
| 150 |
advertised port (for example the data connection of an FTP client). Outbound |
| 151 |
connections are normally not affected. |
| 152 |
|
| 153 |
* **Linux (ufw):** ufw denies inbound connections by default. Allow the |
| 154 |
ports used by the MSX applications; for example, for an FTP server and |
| 155 |
the data connections of an FTP client: |
| 156 |
|
| 157 |
sudo ufw allow from 192.168.0.0/24 to any port 21 proto tcp # FTP server (control) |
| 158 |
sudo ufw allow from 192.168.0.0/24 to any port 113 proto tcp # FTP client (IDENT listener) |
| 159 |
sudo ufw allow from 192.168.0.0/24 to any port 1024:65535 proto tcp # FTP data connections |
| 160 |
|
| 161 |
(the FTP client opens its data connection on a random port in the range |
| 162 |
16384-32767; the FTP server opens its data ports starting at 1024). |
| 163 |
|
| 164 |
UDP traffic is subject to the same inbound deny, and replies to MSX UDP |
| 165 |
clients are affected as well: TFTP and SNTP on the MSX are clients, so |
| 166 |
their requests go out, but the replies (and subsequent data packets) |
| 167 |
arrive on the host as inbound UDP traffic and are dropped unless allowed. |
| 168 |
Allow the local ports those clients bind - SNTP uses UDP port 123, the |
| 169 |
TFTP client uses port 69 or a random port in the 16384-32767 range: |
| 170 |
|
| 171 |
sudo ufw allow from 192.168.0.0/24 to any port 123 proto udp # SNTP client (local port) |
| 172 |
sudo ufw allow from 192.168.0.0/24 to any port 69 proto udp # TFTP client (local port) |
| 173 |
sudo ufw allow from 192.168.0.0/24 to any port 16384:32767 proto udp # TFTP client (random local port) |
| 174 |
|
| 175 |
When in doubt, allow the local port(s) the application uses. Port 69 is |
| 176 |
privileged like port 21 (see below). |
| 177 |
|
| 178 |
For a quick test the firewall can be disabled temporarily with |
| 179 |
`sudo ufw disable` and re-enabled with `sudo ufw enable`. iptables- or |
| 180 |
nftables-based firewalls need equivalent rules. |
| 181 |
|
| 182 |
* **Privileged ports:** on Linux and macOS, binding a port below 1024 |
| 183 |
requires privileges. If an MSX application uses such a port (for |
| 184 |
example an FTP server on port 21), grant the capability once so regular |
| 185 |
users can bind these ports: |
| 186 |
|
| 187 |
sudo setcap 'cap_net_bind_service=+ep' /path/to/openmsx |
| 188 |
|
| 189 |
(the capability must be reapplied after every openMSX update, and the |
| 190 |
filesystem must support file capabilities). Alternatively, |
| 191 |
`authbind --deep openmsx` can be used. Windows has no privileged ports. |
| 192 |
|
| 193 |
## Technical / Advanced Information |
| 194 |
|
| 195 |
### Setting: simulated WiFi link |
| 196 |
|
| 197 |
The `smxwifi-emulatedesp32-link-enabled` setting (default `true`) simulates |
| 198 |
the WiFi link state of the emulated ESP32: with the setting disabled, the |
| 199 |
emulated ESP32 reports its network link as closed (which is what |
| 200 |
`TCPIP_NET_STATE` reports), like a real ESP32 whose WiFi radio is off. It |
| 201 |
does not affect a real ESP32 attached through `rs232-raw`. This is useful |
| 202 |
for developers that want to test cable / AP disconnected. |
| 203 |
|
| 204 |
### I/O ports |
| 205 |
|
| 206 |
The extension (`smxwifi.xml`) occupies 2 I/O ports at base `0x06`: |
| 207 |
|
| 208 |
|
| 209 |
|
| 210 |
| base | read | UART RX FIFO byte (from the ESP module) | |
| 211 |
| base | write | UART command (baud-rate select, FIFO reset) | |
| 212 |
| base + 1 | read | UART status | |
| 213 |
| base + 1 | write | UART TX byte (to the ESP module) | |
| 214 |
|
| 215 |
UART status bits: |
| 216 |
|
| 217 |
|
| 218 |
|
| 219 |
| 0 | RX data available in the FIFO | |
| 220 |
| 3 | Quick receive supported | |
| 221 |
| 4 | Underrun (read of an empty FIFO), cleared by read | |
| 222 |
|
| 223 |
Quick receive is on by default, it works by safeguarding the read of an UART |
| 224 |
RX FIFO byte, if FIFO is empty it will assert WAIT on the BUS (so z80 will |
| 225 |
not continue processing) until a byte arrives OR a timeout elapses (about 30 |
| 226 |
ms); if no data arrives, `0xFF` is returned and the underrun bit is set. |
| 227 |
UNAPI Driver make use of this feature to accelerate receiving data from |
| 228 |
connections, in a way that it is guaranteed that extra bytes are coming, in |
| 229 |
all other operations it is always checking if there is a byte available |
| 230 |
before reading. The end result is faster receiving at the "expense" of |
| 231 |
asserting WAIT for some time. The `smxwifi-emulatedesp32-quickrcv-enabled` |
| 232 |
setting (default `true`) can be used to turn this feature off. |
| 233 |
|
| 234 |
UART command values (write to the data port base): |
| 235 |
|
| 236 |
|
| 237 |
|
| 238 |
| 0-9 | Select baud rate (see table below) | |
| 239 |
| 20 | Reset the RX FIFO | |
| 240 |
|
| 241 |
Baud rates (value = index): |
| 242 |
|
| 243 |
|
| 244 |
|
| 245 |
| 0 | 859372 | 5 | 38400 | |
| 246 |
| 1 | 346520 | 6 | 31250 | |
| 247 |
| 2 | 231014 | 7 | 19200 | |
| 248 |
| 3 | 115200 | 8 | 9600 | |
| 249 |
| 4 | 57600 | 9 | 4800 | |
| 250 |
|
| 251 |
The default after reset is baud rate 0 (859372). The UART parameters are |
| 252 |
always 8 data bits, 1 stop bit, no parity. With the emulated ESP32 the baud |
| 253 |
rate is not considered (there is no real serial communication). |
| 254 |
|
| 255 |
## Using a real ESP32 |
| 256 |
|
| 257 |
### ESP Firmware |
| 258 |
|
| 259 |
A real ESP32 module can be useful for developers or advanced users that do |
| 260 |
not want to run into the host network limitations or want to have a separate |
| 261 |
physical connection to the MSX. This module must run the ESP32-UNAPI firmware, |
| 262 |
which is built from the [ESP32-UNAPI-Firmware](https://github.com/ducasp/ESP32-UNAPI-Firmware) |
| 263 |
repository and flashed to the ESP32. We recommend using an **ESP32-C6** or |
| 264 |
**ESP32-S3** board with **at least 8 MB of flash; 16 MB is recommended**. |
| 265 |
|
| 266 |
### Connector and pluggables |
| 267 |
|
| 268 |
Once the extension is inserted, it exposes an RS232 connector named `smxwifi`. |
| 269 |
If you plan to use a real ESP32 module instead of the emulated one, plug |
| 270 |
`rs232-raw` into it to connect the UART to a real ESP32 on a host serial port: |
| 271 |
|
| 272 |
``` |
| 273 |
ext smxwifi |
| 274 |
plug smxwifi rs232-raw |
| 275 |
set rs232-raw-port COM7 |
| 276 |
``` |
| 277 |
|
| 278 |
As long as the connector has no `rs232-raw` device plugged in, or its host |
| 279 |
serial port is not open (no port selected, port in use, ...), the device |
| 280 |
talks to the emulated ESP32, so the adapter keeps working with no real |
| 281 |
hardware attached. Plugging a device other than `rs232-raw` also leaves the |
| 282 |
emulated ESP32 in charge. |
| 283 |
|
| 284 |
Note: changes can be done pretty easily on the GUI as well, using the connectors menu. |
| 285 |
|
| 286 |
With `rs232-raw` the networking happens on the attached real device, so the |
| 287 |
host networking considerations of the emulated ESP32 (firewall, ports, TLS) |
| 288 |
do not apply. |