From 9eb21eb3e3a3068130a4c4cfdab00828256c086b Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 7 Aug 2026 10:36:53 +0200 Subject: [PATCH] feat: wi-fi - expand support and fix issues, add GUI components --- docs/wifi.md | 161 ++++ kernel/src/Api/BuildNo.hpp | 2 +- kernel/src/Api/Net.hpp | 32 + kernel/src/Api/Syscall.cpp | 14 + kernel/src/Api/Syscall.hpp | 24 + kernel/src/Api/WifiSyscall.hpp | 15 + kernel/src/Drivers/Net/Wifi/Iwx.hpp | 23 + kernel/src/Drivers/Net/Wifi/IwxConnect.cpp | 82 ++ kernel/src/Drivers/Net/Wifi/IwxTrans.cpp | 116 ++- kernel/src/Drivers/Net/Wifi/Wifi.cpp | 302 ++++++- kernel/src/Drivers/Net/Wifi/Wifi.hpp | 14 + programs/include/Api/Syscall.hpp | 24 + programs/include/gui/desktop.hpp | 35 + programs/include/libc/montauk.h | 4 + programs/include/montauk/syscall.h | 31 +- programs/include/montauk/wifi.h | 261 ++++++ programs/man/wifi.1 | 39 +- programs/src/desktop/Makefile | 2 +- programs/src/desktop/compose.cpp | 5 + programs/src/desktop/desktop_builtin.cpp | 1 + programs/src/desktop/desktop_internal.hpp | 10 + programs/src/desktop/input.cpp | 23 + programs/src/desktop/launcher.cpp | 1 + programs/src/desktop/main.cpp | 39 + programs/src/desktop/panel.cpp | 122 ++- programs/src/desktop/wifi.cpp | 867 +++++++++++++++++++ programs/src/network/main.cpp | 631 +++++++++++++- programs/src/wifi/main.cpp | 66 +- scripts/copy_icons.sh | 2 + template/sysroot/include/Api/Syscall.hpp | 152 +++- template/sysroot/include/gui/canvas.hpp | 44 +- template/sysroot/include/gui/desktop.hpp | 53 +- template/sysroot/include/gui/font.hpp | 6 +- template/sysroot/include/gui/mtk/widgets.hpp | 239 ++++- template/sysroot/include/gui/terminal.hpp | 14 +- template/sysroot/include/gui/truetype.hpp | 19 +- template/sysroot/include/libc/ctype.h | 3 + template/sysroot/include/libc/dirent.h | 5 + template/sysroot/include/libc/errno.h | 94 +- template/sysroot/include/libc/fcntl.h | 24 +- template/sysroot/include/libc/inttypes.h | 33 + template/sysroot/include/libc/math.h | 39 + template/sysroot/include/libc/memory.h | 9 + template/sysroot/include/libc/montauk.h | 275 +++--- template/sysroot/include/libc/signal.h | 24 +- template/sysroot/include/libc/spawn.h | 60 ++ template/sysroot/include/libc/stdio.h | 21 + template/sysroot/include/libc/stdlib.h | 16 +- template/sysroot/include/libc/string.h | 3 + template/sysroot/include/libc/sys/mman.h | 41 + template/sysroot/include/libc/sys/param.h | 23 + template/sysroot/include/libc/sys/stat.h | 52 +- template/sysroot/include/libc/sys/types.h | 13 + template/sysroot/include/libc/sys/wait.h | 31 + template/sysroot/include/libc/time.h | 3 + template/sysroot/include/libc/unistd.h | 35 + template/sysroot/include/libc/utime.h | 25 + template/sysroot/include/libc/wchar.h | 29 + template/sysroot/include/montauk/heap.h | 25 +- template/sysroot/include/montauk/syscall.h | 138 ++- template/sysroot/include/montauk/wifi.h | 255 ++++++ template/sysroot/lib/crt1.o | Bin 1480 -> 2832 bytes template/sysroot/lib/libjpeg.a | Bin 330158 -> 330174 bytes template/sysroot/lib/liblibc.a | Bin 388078 -> 582934 bytes template/sysroot/lib/libtls.a | Bin 113598 -> 114598 bytes tests/wifi/ap_mlme.py | 58 ++ tests/wifi/mlme_harness.cpp | 9 + 67 files changed, 4573 insertions(+), 245 deletions(-) create mode 100644 programs/include/montauk/wifi.h create mode 100644 programs/src/desktop/wifi.cpp create mode 100644 template/sysroot/include/libc/memory.h create mode 100644 template/sysroot/include/libc/spawn.h create mode 100644 template/sysroot/include/libc/sys/mman.h create mode 100644 template/sysroot/include/libc/sys/param.h create mode 100644 template/sysroot/include/libc/sys/wait.h create mode 100644 template/sysroot/include/libc/utime.h create mode 100644 template/sysroot/include/libc/wchar.h create mode 100644 template/sysroot/include/montauk/wifi.h diff --git a/docs/wifi.md b/docs/wifi.md index 601cffc..f897e33 100644 --- a/docs/wifi.md +++ b/docs/wifi.md @@ -12,8 +12,12 @@ wifi scan list nearby networks wifi connect join one dhcp pick up an address wifi status what you are connected to +wifi saved / wifi forget networks remembered for next time ``` +There is a graphical path too: a Wi-Fi entry in the desktop panel and a +Wi-Fi tab in the Network app. See "The desktop side" below. + ## Layout ``` @@ -147,6 +151,163 @@ removes the MAC and drops the PHY context - the order `iwl_mvm_mld_vif_cfg_changed_station` and the paths below it use on the way down. `tests/wifi/ap_mlme.py` pins that order. +### A bad access point must not become a bad computer + +Three separate defects turned "the Wi-Fi connection went wrong" into "the whole +machine went wrong". They are worth keeping straight because they have nothing +to do with each other beyond sharing a trigger. + +**The command wait is a busy spin on a core the scheduler has been told not to +touch.** `ServiceEvents()` runs from `ApicTimer::ServiceDeferredWork()`, which +sets `cpu->reservedForKernelWork` so a bottom half holding a process-context +mutex cannot be preempted into the process that would wait on it. The +scheduler honours that by refusing to place any process on that core and by +skipping it for the reschedule IPI, and on the BSP `RunBspMaintenance()` - the +thing that wakes sleeping processes - is not reached until the pass returns. +`IwxSendCmd` then waits for the firmware by spinning on `IwxDelayUs`, up to a +second. One pass of `TearDown()` is eight commands. Against an adapter that has +stopped answering, that pinned a core for the better part of ten seconds. The +symptom is not a Wi-Fi symptom at all: the cursor crawls, windows stop +repainting, everything stutters. + +So a service pass now carries a budget - `IwxBeginServicePass()` / +`IwxEndServicePass()` - of roughly one command's worth of waiting in total. +Commands that do not fit are not sent at all (an abandoned command still holds +its ring slot, and a late answer would be misread as the *next* command's +completion) and are retried on the next trip round the idle loop. The bring-up +path does not bracket itself: it reserves its CPU deliberately and has nothing +to starve, so it keeps the full per-command timeout. + +**Counting consecutive failures never fires on the failure that matters.** The +give-up rule was three unanswered commands in a row, with any success resetting +the count. An adapter that answers some commands and drops others - which is +exactly what a marginal link leaves the firmware doing - therefore never +reached the cutoff, while every drop still cost a full timeout. The stall was +not a one-off; it repeated indefinitely, and the driver never concluded +anything was wrong. It is a leaky bucket now: a failure adds one, a success +drains one, so a firmware failing even a fraction of its commands trips the +cutoff in bounded time. + +**`IwxFwState::Error` was a one-way door.** Nothing anywhere cleared it. A +single firmware assert left `StartJoin()` returning `WIFI_ERR_NO_ADAPTER` - +which the user sees as *no adapter is ready* - for the rest of the uptime, and +the only way back was a reboot. Disconnecting and reconnecting could not help: +there was nothing to reconnect with. Since `IwxReadFirmware()` is idempotent +and the parsed image stays resident, the cure is to stop the device and run the +same bring-up again, which `ServiceRecovery()` now does. It is rate-limited to +one attempt per five seconds and capped at three, because the reset has its own +multi-second handshakes and an adapter that will not come back after three +tries is genuinely broken - repeating it forever would be its own kind of +stall. + +### The link is up only while the access point says so + +`IwxLinkUp()` was nothing but `g_state == Connected`, and nothing moved it off +that state unless the access point was polite enough to send a +deauthentication frame. An access point that simply stops being there - a phone +hotspot that sleeps, wanders off channel, or drops the station without saying +so - left the link reported as up indefinitely. `NetIf::Active()` went on +choosing `wlan0`, every packet went into the void, and the desktop showed a +healthy connection while nothing resolved and nothing connected. + +Beacon loss cannot be used to notice this: once associated, `MacConfigCmd` +stops asking for beacons and the firmware tracks them itself. What is +observable is that our own frames stop being acknowledged, since `IwxTxComplete` +gets a status per frame. A long enough run of failures - with any +acknowledgement, or any frame received from the BSS, restarting the count - +means the access point is gone, and the link comes down so the stack can fall +back to a cable and the panel can report the truth. + +The threshold is counted in frames rather than seconds on purpose: a link that +is merely idle has nothing to send and must not be torn down for it. And +`IwxTxComplete` runs inside the RX pump, so it only sets a flag; the teardown +happens in `IwxConnectService()`, where sending the commands it needs is +allowed. `tests/wifi/ap_mlme.py` pins both halves of that. + +## The desktop side + +### Nothing in the GUI may block + +`SYS_WIFI_SCAN` sweeps for up to twenty seconds and `SYS_WIFI_CONNECT` waits +out a whole handshake. Either one called from `desktop.elf` would freeze the +compositor - and with it the mouse, the panel and every window - for seconds at +a time. So the same work has a second, non-blocking entry point: + +| | | +|---|---| +| `SYS_WIFI_SCAN_START` | starts a sweep and returns immediately | +| `SYS_WIFI_RESULTS` | copies the scan table out without touching the radio | +| `SYS_WIFI_CONNECT_ASYNC` | starts a join and returns immediately | +| `SYS_NETIFS` | lists the registered interfaces (see below) | + +`WifiInfo` grew the fields that make polling enough to follow along: +`scanning` and `scanGeneration` for the sweep, `joining` and `connState` for +the handshake, and `lastError` for how the last join ended. The GUI reads +`SYS_WIFI_INFO` every 400 ms while something is in flight and every three +seconds otherwise. + +The deadlines belong to the kernel, not the caller: `ServiceAsync()` runs from +`ServiceEvents()` - after the RX pump has returned, so it is allowed to send +commands - and aborts a scan that overruns, tears down a join that stalls, and +records `lastError` when one fails. A failed join is unwound the moment it +fails, so `connState` is back to idle by the time anyone looks; `lastError` is +what survives to be reported. + +### Wired and wireless are separate on the panel + +The IP configuration is global to the stack, so "which interface does this +address belong to?" is not a question `SYS_GETNETCFG` can answer. `SYS_NETIFS` +reports each registered interface with its name, MAC, link state and an +`active` flag - the one `NetIf::Active()` currently sends through. The Ethernet +popup shows the address only while the wired interface is the active one, and +says "Not in use" when the cable is up but Wi-Fi is carrying the traffic; the +Wi-Fi popup does the mirror image. Each icon appears only when its hardware +does: no wired interface, no Ethernet icon; no adapter, no Wi-Fi icon. + +The Wi-Fi icon stays white whatever the radio is doing. State belongs in the +popup, and an icon that changes colour next to the clock is just noise. + +Picking a network that needs a passphrase opens a real window - created with +`desktop_create_window()` and the four callbacks, exactly like the reboot and +shutdown dialogs in `dialogs.cpp` - rather than something painted into the +panel overlay. It therefore has a title bar, can be dragged and closed, appears +in the window list, and gets its text field, checkboxes and buttons from the +same `mtk` widgets the settings apps use. + +### What runs at startup + +`desktop.elf` keeps looking for an adapter until one appears (firmware loads +well after login), starts one scan as soon as the firmware reports ready, and +when the results land joins the strongest saved network that is in range. Once +the link is up, and only if no address is configured, it spawns `dhcp.elf`. + +### Saved networks + +`0:/config/wifi.toml` holds them: + +```toml +[wifi] +autoconnect = true + +[network.0] +ssid = "Home" +psk = "passphrase" +``` + +**No default copy of this file ships in the image.** It is created on the first +save, the way `bluetooth.toml`, `display.toml` and `session.toml` are. A shipped +default looks harmless - it only documents the schema - but it is laid down +again by anything that refreshes the system files, and it takes the user's saved +networks with it when it lands. That is exactly what happened the first time +this was written, and it is why the schema is documented here instead. + +`programs/include/montauk/wifi.h` is the one implementation of reading, +writing and searching that file, shared by the panel, the Network app and the +`wifi` command, so all three agree on the schema. The passphrase is stored as +typed because that is what the join needs - the kernel derives the PMK from it, +or takes a 64-character hex string as a raw PSK. There is no key store to hide +it in: anyone who can read `0:/config` can read the passphrases. + ## The interface registry `Net::NetIf` replaced the Ethernet layer's direct calls into the E1000 diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index 47eb71e..99f8f8a 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 69 +#define MONTAUK_BUILD_NUMBER 84 diff --git a/kernel/src/Api/Net.hpp b/kernel/src/Api/Net.hpp index b13a4dc..9924175 100644 --- a/kernel/src/Api/Net.hpp +++ b/kernel/src/Api/Net.hpp @@ -160,6 +160,38 @@ namespace montauk::abi { } } + // List the registered link-layer interfaces. The panel needs this to keep + // wired and wireless status apart: the IP configuration is global, so the + // only way to say which interface owns it is `active`. + static int Sys_NetIfs(NetIfInfo* out, int maxCount) { + if (out == nullptr || maxCount <= 0) return -1; + + const auto* active = ::Net::NetIf::Active(); + int count = ::Net::NetIf::Count(); + int n = 0; + + for (int i = 0; i < count && n < maxCount; i++) { + const auto* iface = ::Net::NetIf::At(i); + if (iface == nullptr) continue; + + NetIfInfo& info = out[n]; + for (uint64_t k = 0; k < sizeof(info.name); k++) info.name[k] = '\0'; + for (uint64_t k = 0; k + 1 < sizeof(info.name) && iface->Name && iface->Name[k]; k++) + info.name[k] = iface->Name[k]; + + const uint8_t* mac = iface->GetMac ? iface->GetMac() : nullptr; + for (int k = 0; k < 6; k++) info.mac[k] = mac ? mac[k] : 0; + + info.kind = iface->Type == ::Net::NetIf::Kind::Wireless + ? NETIF_KIND_WIRELESS : NETIF_KIND_ETHERNET; + info.linkUp = (iface->IsLinkUp && iface->IsLinkUp()) ? 1 : 0; + info.active = iface == active ? 1 : 0; + info._pad[0] = info._pad[1] = info._pad[2] = 0; + n++; + } + return n; + } + static int Sys_SetNetCfg(const NetCfg* in) { if (in == nullptr) return -1; Net::SetIpAddress(in->ipAddress); diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index 05798b4..fc00a28 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -457,6 +457,20 @@ namespace montauk::abi { return Sys_WifiConnect((const char*)frame->arg1, (const char*)frame->arg2); case SYS_WIFI_DISCONNECT: return Sys_WifiDisconnect(); + case SYS_WIFI_SCAN_START: + return Sys_WifiScanStart((uint32_t)frame->arg1); + case SYS_WIFI_RESULTS: + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WifiNetwork), true)) return -1; + return Sys_WifiResults((WifiNetwork*)frame->arg1, (int)frame->arg2); + case SYS_WIFI_CONNECT_ASYNC: + if (!UserMemory::String(frame->arg1, 64)) return -1; + if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, 128)) return -1; + return Sys_WifiConnectAsync((const char*)frame->arg1, (const char*)frame->arg2); + case SYS_NETIFS: + if ((int64_t)frame->arg2 < 0) return -1; + if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(NetIfInfo), true)) return -1; + return Sys_NetIfs((NetIfInfo*)frame->arg1, (int)frame->arg2); case SYS_SUSPEND: return Sys_Suspend(); case SYS_SETTZ: diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index f9055ad..885eddc 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -302,6 +302,10 @@ namespace montauk::abi { static constexpr uint64_t SYS_WIFI_INFO = 159; // (WifiInfo*) -> 0, -1 if absent static constexpr uint64_t SYS_WIFI_CONNECT = 160; // (ssid, password) -> 0, <0 on error static constexpr uint64_t SYS_WIFI_DISCONNECT = 161; // () -> 0 + static constexpr uint64_t SYS_WIFI_SCAN_START = 162; // (timeoutMs) -> 0 started, 1 busy, -1 no adapter + static constexpr uint64_t SYS_WIFI_RESULTS = 163; // (WifiNetwork*, maxCount) -> count, no radio work + static constexpr uint64_t SYS_WIFI_CONNECT_ASYNC = 164; // (ssid, password) -> 0 accepted, <0 on error + static constexpr uint64_t SYS_NETIFS = 165; // (NetIfInfo*, maxCount) -> count // Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM). static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz @@ -708,6 +712,26 @@ namespace montauk::abi { uint8_t bssid[6]; uint8_t connected; // 1 once the link can carry IP traffic uint8_t channel; + int32_t lastError; // WIFI_ERR_* from the last async join, 0 = none + uint32_t scanGeneration; // bumped every time a scan finishes + uint8_t joining; // 1 while an async join is in flight + uint8_t _pad[3]; + }; + + // Link-layer interface kinds reported in NetIfInfo.kind. + static constexpr uint8_t NETIF_KIND_ETHERNET = 0; + static constexpr uint8_t NETIF_KIND_WIRELESS = 1; + + // One registered link-layer interface (returned by SYS_NETIFS). The IP + // configuration is global to the stack, so it belongs to whichever + // interface reports active = 1. + struct NetIfInfo { + char name[16]; // "eth0", "wlan0" + uint8_t mac[6]; + uint8_t kind; // NETIF_KIND_* + uint8_t linkUp; + uint8_t active; // 1 if this is the interface carrying traffic + uint8_t _pad[3]; }; struct ThermalInfo { diff --git a/kernel/src/Api/WifiSyscall.hpp b/kernel/src/Api/WifiSyscall.hpp index c69389d..89d42e8 100644 --- a/kernel/src/Api/WifiSyscall.hpp +++ b/kernel/src/Api/WifiSyscall.hpp @@ -23,11 +23,26 @@ namespace montauk::abi { return (int64_t)Drivers::Net::Wifi::GetInfo(buf); } + static int64_t Sys_WifiScanStart(uint32_t timeoutMs) { + return (int64_t)Drivers::Net::Wifi::StartScan(timeoutMs); + } + + static int64_t Sys_WifiResults(WifiNetwork* buf, int maxCount) { + if (!buf || maxCount <= 0) return -1; + if (maxCount > 64) maxCount = 64; + return (int64_t)Drivers::Net::Wifi::GetResults(buf, maxCount); + } + static int64_t Sys_WifiConnect(const char* ssid, const char* password) { if (!ssid) return -1; return (int64_t)Drivers::Net::Wifi::Connect(ssid, password); } + static int64_t Sys_WifiConnectAsync(const char* ssid, const char* password) { + if (!ssid) return -1; + return (int64_t)Drivers::Net::Wifi::ConnectAsync(ssid, password); + } + static int64_t Sys_WifiDisconnect() { return (int64_t)Drivers::Net::Wifi::Disconnect(); } diff --git a/kernel/src/Drivers/Net/Wifi/Iwx.hpp b/kernel/src/Drivers/Net/Wifi/Iwx.hpp index 6f679b4..4a327a9 100644 --- a/kernel/src/Drivers/Net/Wifi/Iwx.hpp +++ b/kernel/src/Drivers/Net/Wifi/Iwx.hpp @@ -302,6 +302,21 @@ namespace Drivers::Net::Wifi { bool IwxSendCmdStatus(uint32_t id, const void* data, uint32_t len, uint32_t* statusOut); + // Bracket a pass of the idle-loop service work. The wait inside + // IwxSendCmd is a busy spin on a core the scheduler has been told not to + // touch, so a pass is given one command's worth of waiting in total and + // stops sending once that is gone; the rest is picked up next pass. The + // bring-up path does not bracket itself and keeps the full per-command + // budget. See the comment above IwxCmdWaitBudgetMs(). + // Returns false if another core already owns this pass; only the owner + // may end it. + bool IwxBeginServicePass(); + void IwxEndServicePass(); + + // Forget the accumulated command-failure score and the one-shot error + // dump latch. Called after the adapter has been brought back up. + void IwxResetCmdHealth(); + // Poll interrupt causes + drain the RX/notification ring. Safe to call // from any process/idle context; self-guarded against reentry. void IwxProcessEvents(); @@ -387,6 +402,14 @@ namespace Drivers::Net::Wifi { const uint8_t* rsnIe, uint32_t rsnIeLen, uint16_t beaconInterval, uint8_t dtimPeriod); void IwxConnectAbort(); + // Drop all connection state without talking to the firmware. For the + // recovery path, where the adapter is being reset out from under the state + // machine and IwxConnectAbort()'s teardown commands would only burn + // timeouts against a device that is about to be reinitialised anyway. + void IwxConnectReset(); + // Per-frame transmit outcome, reported by the TX completion path. Feeds + // the link supervision described in IwxConnect.cpp. + void IwxConnectNoteTx(bool acked); // Why the last IwxConnectStart() refused: false means the radio or the // firmware failed, true means the network's security is unsupported. The // two need very different advice, so they must not be conflated. diff --git a/kernel/src/Drivers/Net/Wifi/IwxConnect.cpp b/kernel/src/Drivers/Net/Wifi/IwxConnect.cpp index 14dacaf..fa7779e 100644 --- a/kernel/src/Drivers/Net/Wifi/IwxConnect.cpp +++ b/kernel/src/Drivers/Net/Wifi/IwxConnect.cpp @@ -75,6 +75,28 @@ namespace Drivers::Net::Wifi { static volatile bool g_postAssocPending = false; static volatile bool g_teardownPending = false; static volatile bool g_sendAssocPending = false; + static volatile bool g_linkLostPending = false; + + // Link supervision. + // + // IwxLinkUp() is nothing but "the state machine reached Connected", and + // until now nothing moved it back off that state unless the access point + // was polite enough to send a deauthentication frame. An access point that + // simply stops being there -- a phone hotspot that sleeps, wanders off + // channel, or drops the station without saying so -- left the link reported + // as up indefinitely. NetIf::Active() went on selecting wlan0, every + // packet went into the void, and the desktop showed a healthy connection + // while nothing resolved and nothing connected. + // + // Beacon loss is not observable from here: once associated, MacConfigCmd + // stops asking for beacons and the firmware tracks them itself. What is + // observable is that our own frames stop being acknowledged -- IwxTxComplete + // gets a per-frame status. A long enough run of failures with nothing + // heard from the BSS in between means the access point is gone. The + // threshold is in frames rather than time because it must not fire on an + // idle link that simply has nothing to send. + static volatile uint32_t g_txFailStreak = 0; + static constexpr uint32_t TX_FAIL_STREAK_LIMIT = 16; // Timers for retransmission and give-up. static uint64_t g_stateEnteredMs = 0; @@ -585,6 +607,10 @@ namespace Drivers::Net::Wifi { g_iwx.RxDataPackets++; + // Anything at all arriving from the BSS proves the access point is + // still there, so the transmit-failure streak starts over. + g_txFailStreak = 0; + if (etherType == ETHERTYPE_EAPOL) { KernelLogStream(INFO, "WiFi") << "EAPOL frame received (" << (uint64_t)payloadLen << " bytes)"; @@ -810,6 +836,46 @@ namespace Drivers::Net::Wifi { return true; } + // Called from the TX completion path (inside the RX pump), so it may only + // set a flag; ServiceLocked() does the actual teardown, where sending the + // firmware commands it needs is allowed. + void IwxConnectNoteTx(bool acked) { + if (acked) { g_txFailStreak = 0; return; } + if (g_state != ConnState::Connected) return; + if (++g_txFailStreak >= TX_FAIL_STREAK_LIMIT) { + g_txFailStreak = 0; + g_linkLostPending = true; + } + } + + // Drop everything without touching the firmware. Used by the recovery + // path, which is about to reinitialise the adapter: the teardown commands + // IwxConnectAbort() would send have nothing to talk to and would only burn + // a timeout each. + void IwxConnectReset() { + WpaReset(); + g_keysInstalled = false; + g_ptkKey = {}; + g_gtkKey = {}; + g_phyActive = false; + g_macActive = false; + g_macAssoc = false; + g_bindingActive = false; + g_linkActive = false; + g_staActive = false; + g_aid = 0; + g_eapolHead = 0; + g_eapolTail = 0; + g_txFailStreak = 0; + g_postAssocPending = false; + g_teardownPending = false; + g_sendAssocPending = false; + g_linkLostPending = false; + memset(&g_wpaCfg, 0, sizeof(g_wpaCfg)); + g_secured = false; + g_state = ConnState::Idle; + } + void IwxConnectAbort() { if (g_state == ConnState::Idle) return; // Tell the AP we are leaving, but only while the station context (and @@ -884,6 +950,22 @@ namespace Drivers::Net::Wifi { return; } + // The access point stopped acknowledging anything. Bring the link down + // rather than leaving the stack transmitting into a hole: NetIf can + // then fall back to a wired interface, and the desktop reports the + // truth instead of a connection that only exists on paper. + if (g_linkLostPending) { + g_linkLostPending = false; + if (g_state != ConnState::Idle) { + KernelLogStream(WARNING, "WiFi") + << "\"" << g_ssid << "\" stopped acknowledging frames; " + << "dropping the link"; + TearDown(); + g_state = ConnState::Idle; + } + return; + } + if (g_state == ConnState::Idle || g_state == ConnState::Failed) return; if (g_sendAssocPending) { diff --git a/kernel/src/Drivers/Net/Wifi/IwxTrans.cpp b/kernel/src/Drivers/Net/Wifi/IwxTrans.cpp index 0d17965..c9b18b5 100644 --- a/kernel/src/Drivers/Net/Wifi/IwxTrans.cpp +++ b/kernel/src/Drivers/Net/Wifi/IwxTrans.cpp @@ -869,11 +869,84 @@ namespace Drivers::Net::Wifi { } void IwxDumpFwError(); - static uint32_t g_cmdTimeouts = 0; // consecutive unanswered commands + + // Counting *consecutive* unanswered commands is not enough. An adapter + // that answers some commands and drops others resets a consecutive counter + // on every success and so never reaches the cutoff, while each drop still + // costs a full timeout of busy-waiting -- and that is exactly the state a + // marginal access point leaves the firmware in. The result was a machine + // that stalled a second at a time, indefinitely, with the driver never + // concluding anything was wrong. Count in a leaky bucket instead: a + // failure adds one, a success drains one, so a firmware failing even a + // fraction of its commands still trips the cutoff in bounded time. + static uint32_t g_cmdFailScore = 0; + static bool g_fwErrorDumped = false; + static constexpr uint32_t CMD_FAIL_SCORE_MAX = 6; + + void IwxResetCmdHealth() { + g_cmdFailScore = 0; + g_fwErrorDumped = false; + } + + // How long one command may wait, and whether it may be sent at all. + // + // The bring-up path reserves its CPU deliberately and has nothing to + // starve, so it keeps the full second iwlwifi allows. The idle service + // pass is a different animal: ServiceDeferredWork() sets + // reservedForKernelWork, which makes that core ineligible to run any + // process and immune to the reschedule IPI (Scheduler.cpp), and on the BSP + // it also defers RunBspMaintenance() -- the thing that wakes sleeping + // processes. The wait below is a busy spin, not a sleep. So a pass that + // issues eight commands (TearDown does exactly that) against an + // unresponsive firmware pins a core for eight seconds, which is what makes + // the mouse crawl when an access point goes bad. + // + // A pass therefore gets one command's worth of waiting in total. + static constexpr uint32_t IWX_CMD_TIMEOUT_MS = 1000; + static constexpr uint32_t IWX_PASS_BUDGET_MS = 1000; + // Below this there is no point starting a command: it would be abandoned + // almost immediately, and an abandoned command still holds its ring slot + // and may be answered later, which the *next* command would misread as its + // own completion. + static constexpr uint32_t IWX_CMD_MIN_WAIT_MS = 50; + + static uint64_t g_passDeadline = 0; // 0 = not inside a pass + static volatile bool g_passOwned = false; + + // Every idling core runs ServiceEvents(), so the bracket needs an owner: + // otherwise the second core to arrive would clear the first core's deadline + // on its way out and hand it back the unbounded wait this exists to + // prevent. A core that does not win still runs under the winner's + // deadline, which is strictly tighter than none. + bool IwxBeginServicePass() { + if (__atomic_test_and_set(&g_passOwned, __ATOMIC_ACQUIRE)) return false; + g_passDeadline = Timekeeping::GetMilliseconds() + IWX_PASS_BUDGET_MS; + return true; + } + + void IwxEndServicePass() { + g_passDeadline = 0; + __atomic_clear(&g_passOwned, __ATOMIC_RELEASE); + } + + // Remaining wait allowance, clamped to the per-command timeout. + static uint32_t IwxCmdWaitBudgetMs() { + if (!g_passDeadline) return IWX_CMD_TIMEOUT_MS; + uint64_t now = Timekeeping::GetMilliseconds(); + if (now >= g_passDeadline) return 0; + uint64_t left = g_passDeadline - now; + return left > IWX_CMD_TIMEOUT_MS ? IWX_CMD_TIMEOUT_MS : (uint32_t)left; + } bool IwxSendCmd(IwxHostCmd& hcmd) { if (g_iwx.State == IwxFwState::Error) return false; + // Out of budget for this pass. Fail quietly -- the firmware has done + // nothing wrong, so this must not count against it -- and let the + // caller's state machine retry on the next pass. + uint32_t waitMs = IwxCmdWaitBudgetMs(); + if (waitMs < IWX_CMD_MIN_WAIT_MS) return false; + IwxTxRing& ring = g_iwx.CmdQ; g_iwx.CmdLock.Acquire(); @@ -961,12 +1034,16 @@ namespace Drivers::Net::Wifi { constexpr uint32_t MAX_SPINS = 20000; // ~2 s at 100 us bool ok = false; bool died = false; + // Re-read the allowance: acquiring CmdLock above can itself have waited + // out another core's command, and the budget is for the pass, not for + // each caller's view of it when it arrived. + waitMs = IwxCmdWaitBudgetMs(); uint64_t start = Timekeeping::GetMilliseconds(); for (uint32_t spins = 0; spins < MAX_SPINS; spins++) { IwxProcessEvents(); if (g_iwx.CmdDone) { ok = true; break; } if (g_iwx.State == IwxFwState::Error) { died = true; break; } - if (Timekeeping::GetMilliseconds() - start >= 1000) break; + if (Timekeeping::GetMilliseconds() - start >= waitMs) break; IwxDelayUs(100); } @@ -979,20 +1056,26 @@ namespace Drivers::Net::Wifi { if (!died) { // Dump on the first silence: the firmware's error table names // the command that asserted, and it is overwritten as later - // commands go unanswered. - if (++g_cmdTimeouts == 1) IwxDumpFwError(); - // Repeated silence means it is wedged and every later command - // would burn the same timeout, so stop trying. A single late - // response is not worth disabling the adapter over. - if (g_cmdTimeouts >= 3) { + // commands go unanswered. Latched separately from the score, + // which now goes up and down and would otherwise re-dump every + // time it passed through one. + g_cmdFailScore++; + if (!g_fwErrorDumped) { + g_fwErrorDumped = true; + IwxDumpFwError(); + } + // Enough net silence means it is wedged and every later command + // would burn the same timeout, so stop trying and let + // ServiceRecovery() put the adapter back together. + if (g_cmdFailScore >= CMD_FAIL_SCORE_MAX) { KernelLogStream(ERROR, "WiFi") << "Firmware stopped responding to host commands"; g_iwx.FwErrors++; g_iwx.State = IwxFwState::Error; } } - } else { - g_cmdTimeouts = 0; + } else if (g_cmdFailScore) { + g_cmdFailScore--; } g_iwx.CmdWantResp = false; @@ -1272,10 +1355,15 @@ namespace Drivers::Net::Wifi { if (ring->Queued > 0) ring->Queued--; g_iwx.TxLock.Release(); - if (status == IWX_TX_STATUS_SUCCESS || status == IWX_TX_STATUS_DIRECT_DONE) - g_iwx.TxPackets++; - else - g_iwx.TxFailures++; + bool acked = status == IWX_TX_STATUS_SUCCESS + || status == IWX_TX_STATUS_DIRECT_DONE; + if (acked) g_iwx.TxPackets++; + else g_iwx.TxFailures++; + + // Feeds the link supervision in IwxConnect.cpp: an access point that + // vanishes without deauthenticating is only visible as our frames + // going unacknowledged. + IwxConnectNoteTx(acked); } // ========================================================================= diff --git a/kernel/src/Drivers/Net/Wifi/Wifi.cpp b/kernel/src/Drivers/Net/Wifi/Wifi.cpp index e5f2e24..1e2861f 100644 --- a/kernel/src/Drivers/Net/Wifi/Wifi.cpp +++ b/kernel/src/Drivers/Net/Wifi/Wifi.cpp @@ -61,6 +61,22 @@ namespace Drivers::Net::Wifi { static int g_resultCount = 0; static kcp::Spinlock g_resultLock; + // ========================================================================= + // Non-blocking scan / join state + // + // The GUI cannot afford the blocking Scan() and Connect() below: they take + // seconds, and the compositor calling them would freeze the whole desktop. + // The async entry points start the same work and return immediately; the + // deadlines and the final connect result are looked after by ServiceAsync() + // from the idle loop, and the caller polls GetInfo(). + // ========================================================================= + + static uint64_t g_scanDeadline = 0; // 0 = no async scan outstanding + static uint32_t g_scanGeneration = 0; // bumped when a scan finishes + static bool g_asyncConnect = false; + static uint64_t g_asyncConnectDeadline = 0; + static int g_lastError = 0; // result of the last async join + static void ClearResults() { g_resultLock.Acquire(); for (int i = 0; i < MAX_SCAN_RESULTS; i++) g_results[i].Used = false; @@ -305,6 +321,102 @@ namespace Drivers::Net::Wifi { << g_iwx.Fw.Version << ")"; } + // ========================================================================= + // Recovery from a wedged adapter + // + // IwxFwState::Error used to be a one-way door. Nothing anywhere cleared + // it, so a single firmware assert -- which a flaky access point provokes + // readily enough -- left StartJoin() returning WIFI_ERR_NO_ADAPTER ("no + // adapter is ready") for the rest of the uptime, and the only way back was + // a reboot. + // + // The firmware image is still parsed and resident (IwxReadFirmware() is + // idempotent and keeps g_iwx.Fw.Raw), so the cure is simply to stop the + // device and run the same bring-up again. It is not free -- the handshakes + // inside take a couple of seconds with this core reserved -- so it is + // rate-limited and capped. If the adapter will not come back after a few + // tries it is genuinely broken and repeating the reset would be its own + // kind of stall. + // ========================================================================= + + static uint32_t g_recoveryAttempts = 0; + static uint64_t g_lastRecoveryMs = 0; + static bool g_recoveryGaveUp = false; + static constexpr uint32_t MAX_RECOVERY_ATTEMPTS = 3; + static constexpr uint64_t RECOVERY_BACKOFF_MS = 5000; + + static void ServiceRecovery() { + if (g_iwx.State != IwxFwState::Error || g_recoveryGaveUp) return; + // An adapter that never finished its first bring-up is ServiceDeferredInit's + // problem, not this one. + if (!g_initialized) return; + + // Every idling core reaches here, and two of them resetting the device + // at once would be considerably worse than the fault being recovered + // from. Same test-and-set gate ServiceAsync() and IwxConnectService() + // use; the loser has nothing to do. + static volatile bool inRecovery = false; + if (__atomic_test_and_set(&inRecovery, __ATOMIC_ACQUIRE)) return; + struct Guard { + volatile bool* flag; + ~Guard() { __atomic_clear(flag, __ATOMIC_RELEASE); } + } guard{&inRecovery}; + + // Re-check under the gate: the winner of the race may have just + // finished a reset that fixed things. + if (g_iwx.State != IwxFwState::Error) return; + + uint64_t now = Timekeeping::GetMilliseconds(); + if (g_lastRecoveryMs && now - g_lastRecoveryMs < RECOVERY_BACKOFF_MS) return; + g_lastRecoveryMs = now; + + if (++g_recoveryAttempts > MAX_RECOVERY_ATTEMPTS) { + g_recoveryGaveUp = true; + KernelLogStream(ERROR, "WiFi") + << "Adapter did not come back after " << (uint64_t)MAX_RECOVERY_ATTEMPTS + << " resets; leaving it down"; + return; + } + + KernelLogStream(WARNING, "WiFi") << "Resetting the adapter after a firmware error" + << " (attempt " << (uint64_t)g_recoveryAttempts << " of " + << (uint64_t)MAX_RECOVERY_ATTEMPTS << ")"; + + // Nothing may be mid-command while the device is torn down. Taking + // CmdLock is enough: it is what serializes every sender, and the RX + // pump has its own reentrancy guard. + g_iwx.CmdLock.Acquire(); + + g_initialized = false; + g_asyncConnect = false; + g_scanDeadline = 0; + g_lastError = WIFI_ERR_NO_ADAPTER; + g_iwx.ScanActive = false; + + // Not IwxConnectAbort(): its teardown commands would be sent to a + // device that has already stopped answering, costing a timeout each for + // contexts that the reset below discards anyway. + IwxConnectReset(); + ClearResults(); + + IwxStopDevice(); + IwxResetCmdHealth(); + // Detected, not Absent: the PCI device is still claimed and mapped, and + // IsPresent() keys off Absent -- the Wi-Fi icon must not blink out of + // the panel every time the adapter is reset. + g_iwx.State = IwxFwState::Detected; + + g_iwx.CmdLock.Release(); + + CompleteInit(); + + if (g_initialized) { + KernelLogStream(OK, "WiFi") << "Adapter recovered"; + g_recoveryAttempts = 0; + g_lastError = 0; + } + } + void ServiceDeferredInit() { if (!g_initPending.load(std::memory_order_relaxed) || g_initialized) return; if (!Fs::Vfs::IsDriveRegistered(0)) return; // ramdisk not mounted yet @@ -325,12 +437,36 @@ namespace Drivers::Net::Wifi { if (cpu) cpu->reservedForKernelWork = wasReserved; } + static void ServiceAsync(); + static void ServiceRecovery(); + void ServiceEvents() { if (!g_iwx.Mmio) return; if (g_iwx.WorkPending) IwxProcessEvents(); + + // Everything below can send firmware commands, and each one waits by + // busy-spinning. This runs from ServiceDeferredWork(), which has set + // reservedForKernelWork on this core -- so the scheduler will not run a + // process here and, on the BSP, RunBspMaintenance() is not reached + // until we return. The pass budget caps the whole group at roughly one + // command's wait; whatever does not fit is retried next time round the + // idle loop. Without it a teardown against a wedged adapter held a + // core for the better part of ten seconds, which is what the stalled + // cursor and the stuttering desktop actually were. + bool ownsPass = IwxBeginServicePass(); + // Firmware commands the RX path deferred (it runs under the event // pump's reentrancy guard and cannot wait for a completion itself). IwxConnectService(); + // Deadlines for the non-blocking scan/join the GUI drives. Runs after + // the pump returns, never inside it, because both paths send commands. + ServiceAsync(); + + if (ownsPass) IwxEndServicePass(); + + // Deliberately outside the budget: a reset is a bring-up, not a pass of + // routine servicing, and it has its own wall-clock handshakes to run. + ServiceRecovery(); } bool IsInitialized() { return g_initialized; } @@ -340,6 +476,35 @@ namespace Drivers::Net::Wifi { // Public operations // ========================================================================= + // Copy the current scan table out. No radio work: whatever the last scan + // (blocking or not) left behind is what the caller sees. + static int CopyResults(WifiNetwork* out, int maxCount) { + g_resultLock.Acquire(); + int n = 0; + for (int i = 0; i < MAX_SCAN_RESULTS && n < maxCount; i++) { + if (!g_results[i].Used) continue; + const ScanEntry& e = g_results[i]; + WifiNetwork& w = out[n]; + memset(&w, 0, sizeof(w)); + for (int k = 0; k < 32 && e.Ssid[k]; k++) w.ssid[k] = e.Ssid[k]; + memcpy(w.bssid, e.Bssid, 6); + w.channel = e.Channel; + w.rssi = e.Rssi; + w.band = e.Band; + w.security = e.Security; + w.beaconInterval = e.BeaconInterval; + n++; + } + g_resultLock.Release(); + return n; + } + + int GetResults(WifiNetwork* out, int maxCount) { + if (!out || maxCount <= 0) return -1; + if (!g_initialized) return -1; + return CopyResults(out, maxCount); + } + int Scan(WifiNetwork* out, int maxCount, uint32_t timeoutMs) { if (!out || maxCount <= 0) return -1; if (!g_initialized) return -1; @@ -365,24 +530,23 @@ namespace Drivers::Net::Wifi { while (Timekeeping::GetMilliseconds() - t0 < 200) IwxProcessEvents(); } - g_resultLock.Acquire(); - int n = 0; - for (int i = 0; i < MAX_SCAN_RESULTS && n < maxCount; i++) { - if (!g_results[i].Used) continue; - const ScanEntry& e = g_results[i]; - WifiNetwork& w = out[n]; - memset(&w, 0, sizeof(w)); - for (int k = 0; k < 32 && e.Ssid[k]; k++) w.ssid[k] = e.Ssid[k]; - memcpy(w.bssid, e.Bssid, 6); - w.channel = e.Channel; - w.rssi = e.Rssi; - w.band = e.Band; - w.security = e.Security; - w.beaconInterval = e.BeaconInterval; - n++; - } - g_resultLock.Release(); - return n; + g_scanGeneration++; + return CopyResults(out, maxCount); + } + + int StartScan(uint32_t timeoutMs) { + if (!g_initialized) return -1; + if (g_iwx.State != IwxFwState::Running) return -1; + if (g_iwx.ScanActive) return 1; // already sweeping + + if (timeoutMs < 1000) timeoutMs = 1000; + if (timeoutMs > 20000) timeoutMs = 20000; + + ClearResults(); + if (!IwxStartScan(nullptr)) return -1; + + g_scanDeadline = Timekeeping::GetMilliseconds() + timeoutMs; + return 0; } int GetInfo(WifiInfo* out) { @@ -403,6 +567,9 @@ namespace Drivers::Net::Wifi { out->fwErrors = (uint32_t)g_iwx.FwErrors; out->connState = (uint32_t)IwxConnectState(); out->connected = IwxLinkUp() ? 1 : 0; + out->lastError = (int32_t)g_lastError; + out->scanGeneration = g_scanGeneration; + out->joining = g_asyncConnect ? 1 : 0; if (IwxConnectState() != (int)IwxConnStateId::Idle) { const char* ssid = IwxConnectSsid(); @@ -455,7 +622,12 @@ namespace Drivers::Net::Wifi { return WIFI_ERR_TIMEOUT; } - int Connect(const char* ssid, const char* password) { + // Everything a join needs before the exchange with the AP starts: find the + // BSS, check the ciphers are ones the supplicant implements, and hand the + // firmware its contexts. Returns 0 once the state machine is running, or a + // WIFI_ERR_* value. `rescan` controls whether an SSID missing from the + // scan table is worth a (blocking) sweep to look for it. + static int StartJoin(const char* ssid, const char* password, bool rescan) { if (!g_initialized || !ssid) return WIFI_ERR_NO_ADAPTER; if (g_iwx.State != IwxFwState::Running) return WIFI_ERR_NO_ADAPTER; @@ -476,7 +648,8 @@ namespace Drivers::Net::Wifi { // joined through the best AP rather than whichever answered first. int8_t bestRssi = -128; - for (int attempt = 0; attempt < 2 && !found; attempt++) { + int attempts = rescan ? 2 : 1; + for (int attempt = 0; attempt < attempts && !found; attempt++) { if (attempt == 1) { // Nothing matched: the caller may never have scanned, or the // results may predate this network appearing. @@ -547,15 +720,104 @@ namespace Drivers::Net::Wifi { return security_ ? WIFI_ERR_UNSUPPORTED : WIFI_ERR_FAILED; } + return 0; + } + + int Connect(const char* ssid, const char* password) { + int rc = StartJoin(ssid, password, true); + if (rc != 0) return rc; return WaitForConnection(15000); } + int ConnectAsync(const char* ssid, const char* password) { + // A join already in flight owns the firmware contexts; tear it down + // rather than stacking a second one on top. + if (g_asyncConnect || (IwxConnectState() != (int)IwxConnStateId::Idle)) + IwxConnectAbort(); + + g_asyncConnect = false; + g_lastError = 0; + + // No blocking rescan here: the caller has a scan table on screen, and + // the point of this entry point is that it returns immediately. + int rc = StartJoin(ssid, password, false); + if (rc != 0) { + g_lastError = rc; + return rc; + } + + g_asyncConnect = true; + g_asyncConnectDeadline = Timekeeping::GetMilliseconds() + 20000; + return 0; + } + int Disconnect() { if (!g_initialized) return -1; + g_asyncConnect = false; + g_lastError = 0; IwxConnectAbort(); return 0; } + // Deadlines and completion for the non-blocking entry points. Called from + // ServiceEvents() after the RX pump has returned, so sending commands (the + // scan abort, the connect teardown) is safe here. + // + // Every idling core calls this, so it takes the same test-and-set gate + // IwxConnectService() uses: two cores both deciding a join has failed would + // tear the contexts down twice. A core that loses the race has nothing to + // do - the winner is already doing it. + static void ServiceAsync() { + static volatile bool inService = false; + if (__atomic_test_and_set(&inService, __ATOMIC_ACQUIRE)) return; + struct Guard { + volatile bool* flag; + ~Guard() { __atomic_clear(flag, __ATOMIC_RELEASE); } + } guard{&inService}; + + uint64_t now = Timekeeping::GetMilliseconds(); + + if (g_scanDeadline) { + if (!g_iwx.ScanActive) { + g_scanDeadline = 0; + g_scanGeneration++; + } else if (now >= g_scanDeadline) { + IwxAbortScan(); + g_scanDeadline = 0; + g_scanGeneration++; + } + } + + if (!g_asyncConnect) return; + + auto state = (IwxConnStateId)IwxConnectState(); + if (state == IwxConnStateId::Connected) { + g_asyncConnect = false; + g_lastError = 0; + return; + } + if (state == IwxConnStateId::Failed) { + // Same reading as the blocking path: a handshake that exchanged + // EAPOL frames and then failed is almost always a wrong passphrase. + g_lastError = WpaGetState() == WpaState::Failed + ? WIFI_ERR_AUTH : WIFI_ERR_FAILED; + g_asyncConnect = false; + IwxConnectAbort(); + return; + } + if (state == IwxConnStateId::Idle || g_iwx.State == IwxFwState::Error) { + g_lastError = WIFI_ERR_FAILED; + g_asyncConnect = false; + if (state != IwxConnStateId::Idle) IwxConnectAbort(); + return; + } + if (now >= g_asyncConnectDeadline) { + g_lastError = WIFI_ERR_TIMEOUT; + g_asyncConnect = false; + IwxConnectAbort(); + } + } + // ========================================================================= // Network interface // ========================================================================= diff --git a/kernel/src/Drivers/Net/Wifi/Wifi.hpp b/kernel/src/Drivers/Net/Wifi/Wifi.hpp index 4d70a02..96628e0 100644 --- a/kernel/src/Drivers/Net/Wifi/Wifi.hpp +++ b/kernel/src/Drivers/Net/Wifi/Wifi.hpp @@ -29,6 +29,14 @@ namespace Drivers::Net::Wifi { // Returns the number of entries written, or -1 on error. int Scan(montauk::abi::WifiNetwork* out, int maxCount, uint32_t timeoutMs); + // Start a scan and return at once: 0 started, 1 one was already running, + // -1 no adapter. GetInfo().scanning falls back to 0 when it finishes and + // GetInfo().scanGeneration moves on; the results are read with GetResults. + int StartScan(uint32_t timeoutMs); + + // Copy out the current scan table without touching the radio. + int GetResults(montauk::abi::WifiNetwork* out, int maxCount); + // Fill in adapter/firmware status. int GetInfo(montauk::abi::WifiInfo* out); @@ -36,6 +44,12 @@ namespace Drivers::Net::Wifi { // or the attempt fails. Returns 0 on success, or a negative WIFI_ERR_* // value describing why it could not connect. int Connect(const char* ssid, const char* password); + + // Start a join and return at once: 0 accepted, or a WIFI_ERR_* the attempt + // failed on before any frame went out. Progress shows up in + // GetInfo().connState / .joining, and the outcome in .lastError. + int ConnectAsync(const char* ssid, const char* password); + int Disconnect(); // ------------------------------------------------------------------------- diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index 187d49d..9fc1f28 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -224,6 +224,10 @@ namespace montauk::abi { static constexpr uint64_t SYS_WIFI_INFO = 159; // (WifiInfo*) -> 0, -1 if absent static constexpr uint64_t SYS_WIFI_CONNECT = 160; // (ssid, password) -> 0, <0 on error static constexpr uint64_t SYS_WIFI_DISCONNECT = 161; // () -> 0 + static constexpr uint64_t SYS_WIFI_SCAN_START = 162; // (timeoutMs) -> 0 started, 1 busy, -1 no adapter + static constexpr uint64_t SYS_WIFI_RESULTS = 163; // (WifiNetwork*, maxCount) -> count, no radio work + static constexpr uint64_t SYS_WIFI_CONNECT_ASYNC = 164; // (ssid, password) -> 0 accepted, <0 on error + static constexpr uint64_t SYS_NETIFS = 165; // (NetIfInfo*, maxCount) -> count // Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM). static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz @@ -633,6 +637,26 @@ namespace montauk::abi { uint8_t bssid[6]; uint8_t connected; // 1 once the link can carry IP traffic uint8_t channel; + int32_t lastError; // WIFI_ERR_* from the last async join, 0 = none + uint32_t scanGeneration; // bumped every time a scan finishes + uint8_t joining; // 1 while an async join is in flight + uint8_t _pad[3]; + }; + + // Link-layer interface kinds reported in NetIfInfo.kind. + static constexpr uint8_t NETIF_KIND_ETHERNET = 0; + static constexpr uint8_t NETIF_KIND_WIRELESS = 1; + + // One registered link-layer interface (returned by SYS_NETIFS). The IP + // configuration is global to the stack, so it belongs to whichever + // interface reports active = 1. + struct NetIfInfo { + char name[16]; // "eth0", "wlan0" + uint8_t mac[6]; + uint8_t kind; // NETIF_KIND_* + uint8_t linkUp; + uint8_t active; // 1 if this is the interface carrying traffic + uint8_t _pad[3]; }; struct ThermalInfo { diff --git a/programs/include/gui/desktop.hpp b/programs/include/gui/desktop.hpp index f4ba59a..f530362 100644 --- a/programs/include/gui/desktop.hpp +++ b/programs/include/gui/desktop.hpp @@ -180,6 +180,41 @@ struct DesktopState { uint64_t net_cfg_last_poll; Rect net_icon_rect; + // Registered link-layer interfaces. The IP configuration is global to the + // stack, so the wired and wireless popups use `active` to decide which of + // them owns the address currently on screen. + static constexpr int MAX_NETIFS = 4; + montauk::abi::NetIfInfo netifs[MAX_NETIFS]; + int netif_count; + bool eth_present; + + // ---- Wi-Fi ------------------------------------------------------------- + // The panel entry exists only when a supported adapter is present. + static constexpr int MAX_WIFI_NETWORKS = 32; + static constexpr int WIFI_SSID_CAP = 36; + static constexpr int WIFI_PSK_CAP = 72; + + SvgIcon icon_wifi; + bool wifi_present; + bool wifi_popup_open; + Rect wifi_icon_rect; + montauk::abi::WifiInfo wifi_info; + montauk::abi::WifiNetwork wifi_networks[MAX_WIFI_NETWORKS]; + int wifi_network_count; + uint64_t wifi_last_poll; + uint32_t wifi_scan_generation; + bool wifi_scanning; + int wifi_scroll; // first visible row of the list + bool wifi_boot_scan_started; // the automatic scan at startup + bool wifi_autoconnect_done; // saved-network join already tried + bool wifi_joining; // a join we started is in flight + bool wifi_dhcp_pending; // ask for a lease once the link is up + bool wifi_dhcp_waiting; // dhcp.elf running, no address yet + char wifi_joining_ssid[WIFI_SSID_CAP]; + char wifi_status[96]; // last result line in the popup + uint64_t wifi_status_time; + + bool vol_popup_open; Rect vol_icon_rect; int vol_level; // 0-100 diff --git a/programs/include/libc/montauk.h b/programs/include/libc/montauk.h index dafaec9..a58800d 100644 --- a/programs/include/libc/montauk.h +++ b/programs/include/libc/montauk.h @@ -185,6 +185,10 @@ extern "C" { #define MTK_SYS_WIFI_INFO 159 #define MTK_SYS_WIFI_CONNECT 160 #define MTK_SYS_WIFI_DISCONNECT 161 +#define MTK_SYS_WIFI_SCAN_START 162 +#define MTK_SYS_WIFI_RESULTS 163 +#define MTK_SYS_WIFI_CONNECT_ASYNC 164 +#define MTK_SYS_NETIFS 165 /* @SYSCALLS-END */ #define MTK_SOCK_TCP 1 diff --git a/programs/include/montauk/syscall.h b/programs/include/montauk/syscall.h index 9cce5c1..93efccc 100644 --- a/programs/include/montauk/syscall.h +++ b/programs/include/montauk/syscall.h @@ -607,8 +607,8 @@ namespace montauk { inline int wifi_info(montauk::abi::WifiInfo* out) { return (int)syscall1(montauk::abi::SYS_WIFI_INFO, (uint64_t)out); } - // Association is groundwork only: open networks bring the firmware - // contexts up, encrypted ones are rejected with -2. + // Join a network. Blocks until the link is up or the attempt fails, and + // returns 0 or a WIFI_ERR_* code. inline int wifi_connect(const char* ssid, const char* password) { return (int)syscall2(montauk::abi::SYS_WIFI_CONNECT, (uint64_t)ssid, (uint64_t)password); @@ -617,6 +617,33 @@ namespace montauk { return (int)syscall0(montauk::abi::SYS_WIFI_DISCONNECT); } + // Non-blocking pair for GUI code, which cannot stall for the seconds a + // sweep or a handshake takes. scan_start() kicks off a sweep (0 started, + // 1 one was already running, -1 no adapter); wifi_info().scanning drops + // back to 0 and .scanGeneration moves on when it finishes, and + // wifi_results() copies out the table without touching the radio. + inline int wifi_scan_start(uint32_t timeoutMs) { + return (int)syscall1(montauk::abi::SYS_WIFI_SCAN_START, (uint64_t)timeoutMs); + } + inline int wifi_results(montauk::abi::WifiNetwork* buf, int maxCount) { + return (int)syscall2(montauk::abi::SYS_WIFI_RESULTS, (uint64_t)buf, + (uint64_t)maxCount); + } + // Returns 0 once the join is under way, or a WIFI_ERR_* it failed on + // before any frame went out. Watch wifi_info().joining for progress and + // .lastError for the outcome. + inline int wifi_connect_async(const char* ssid, const char* password) { + return (int)syscall2(montauk::abi::SYS_WIFI_CONNECT_ASYNC, (uint64_t)ssid, + (uint64_t)password); + } + + // List the registered link-layer interfaces. The IP configuration is + // global to the stack; it belongs to whichever entry has active = 1. + inline int net_interfaces(montauk::abi::NetIfInfo* buf, int maxCount) { + return (int)syscall2(montauk::abi::SYS_NETIFS, (uint64_t)buf, + (uint64_t)maxCount); + } + // Software-defined radio (Rx). Receivers are identified by index [0, count); // open() returns a handle used by the rest of the calls. Samples are read // as interleaved 8-bit unsigned I/Q (CU8) from the device's ring buffer. diff --git a/programs/include/montauk/wifi.h b/programs/include/montauk/wifi.h new file mode 100644 index 0000000..69adc5a --- /dev/null +++ b/programs/include/montauk/wifi.h @@ -0,0 +1,261 @@ +/* + * wifi.h + * Saved Wi-Fi networks (0:/config/wifi.toml) and small formatting helpers + * shared by the desktop panel, the Network app and the wifi command. + * + * The file looks like this: + * + * [wifi] + * autoconnect = true + * + * [network.0] + * ssid = "Home" + * psk = "passphrase" + * + * The passphrase is stored as typed, because that is what the join needs: + * the kernel derives the PMK from it (or takes a 64-character hex string as + * a raw PSK). Anyone who can read 0:/config can read the keys. + * + * No default copy of this file ships in the image, deliberately. Every other + * config an app writes (bluetooth.toml, display.toml, session.toml) is + * created on demand for the same reason: a shipped copy is laid down again + * by anything that refreshes the system files, and it would overwrite the + * networks the user had saved. + * + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include +#include + +namespace montauk { +namespace wifi { + + // The scan table the kernel keeps is 64 entries; saving that many networks + // is already far more than a laptop accumulates. + static constexpr int MAX_SAVED = 32; + static constexpr int SSID_CAP = 36; + static constexpr int PSK_CAP = 72; // 64-character hex PSK plus NUL + + struct SavedNetwork { + char ssid[SSID_CAP]; + char psk[PSK_CAP]; + }; + + struct SavedList { + SavedNetwork items[MAX_SAVED]; + int count; + bool autoconnect; + }; + + // ---- helpers ----------------------------------------------------------- + + inline void copy_str(char* dst, int cap, const char* src) { + int i = 0; + for (; src && src[i] && i < cap - 1; i++) dst[i] = src[i]; + dst[i] = '\0'; + } + + // "network.." + inline void network_key(char* out, int cap, int index, const char* field) { + char idx[8]; + int n = 0; + if (index == 0) { + idx[n++] = '0'; + } else { + char tmp[8]; + int t = 0; + for (int v = index; v > 0 && t < (int)sizeof(tmp); v /= 10) + tmp[t++] = (char)('0' + (v % 10)); + while (t > 0) idx[n++] = tmp[--t]; + } + idx[n] = '\0'; + + int p = 0; + const char* prefix = "network."; + while (*prefix && p < cap - 1) out[p++] = *prefix++; + for (int i = 0; i < n && p < cap - 1; i++) out[p++] = idx[i]; + if (p < cap - 1) out[p++] = '.'; + while (field && *field && p < cap - 1) out[p++] = *field++; + out[p] = '\0'; + } + + // ---- load / store ------------------------------------------------------ + + inline void saved_load(SavedList* out) { + if (!out) return; + out->count = 0; + out->autoconnect = true; + + auto doc = montauk::config::load("wifi"); + out->autoconnect = doc.get_bool("wifi.autoconnect", true); + + for (int i = 0; i < MAX_SAVED; i++) { + char key[64]; + network_key(key, sizeof(key), i, "ssid"); + const char* ssid = doc.get_string(key, nullptr); + if (!ssid || !ssid[0]) break; // entries are written contiguously + + network_key(key, sizeof(key), i, "psk"); + const char* psk = doc.get_string(key, ""); + + SavedNetwork& n = out->items[out->count++]; + copy_str(n.ssid, SSID_CAP, ssid); + copy_str(n.psk, PSK_CAP, psk); + } + + doc.destroy(); + } + + // Rewrite the file from the list. Returns 0 on success. + inline int saved_store(const SavedList* list) { + if (!list) return -1; + + montauk::toml::Doc doc; + doc.init(); + montauk::config::set_bool(&doc, "wifi.autoconnect", list->autoconnect); + + for (int i = 0; i < list->count && i < MAX_SAVED; i++) { + char key[64]; + network_key(key, sizeof(key), i, "ssid"); + montauk::config::set_string(&doc, key, list->items[i].ssid); + network_key(key, sizeof(key), i, "psk"); + montauk::config::set_string(&doc, key, list->items[i].psk); + } + + int rc = montauk::config::save("wifi", &doc); + doc.destroy(); + return rc; + } + + inline int saved_index_of(const SavedList* list, const char* ssid) { + if (!list || !ssid) return -1; + for (int i = 0; i < list->count; i++) { + if (montauk::streq(list->items[i].ssid, ssid)) return i; + } + return -1; + } + + inline const SavedNetwork* saved_find(const SavedList* list, const char* ssid) { + int idx = saved_index_of(list, ssid); + return idx < 0 ? nullptr : &list->items[idx]; + } + + // Add or update an entry in memory. Returns false when the list is full. + inline bool saved_set(SavedList* list, const char* ssid, const char* psk) { + if (!list || !ssid || !ssid[0]) return false; + int idx = saved_index_of(list, ssid); + if (idx < 0) { + if (list->count >= MAX_SAVED) return false; + idx = list->count++; + copy_str(list->items[idx].ssid, SSID_CAP, ssid); + } + copy_str(list->items[idx].psk, PSK_CAP, psk ? psk : ""); + return true; + } + + inline bool saved_remove(SavedList* list, const char* ssid) { + int idx = saved_index_of(list, ssid); + if (idx < 0) return false; + for (int i = idx; i < list->count - 1; i++) list->items[i] = list->items[i + 1]; + list->count--; + return true; + } + + // Convenience wrappers that touch the file directly. + + inline bool remember(const char* ssid, const char* psk) { + SavedList list; + saved_load(&list); + if (!saved_set(&list, ssid, psk)) return false; + return saved_store(&list) == 0; + } + + inline bool forget(const char* ssid) { + SavedList list; + saved_load(&list); + if (!saved_remove(&list, ssid)) return false; + return saved_store(&list) == 0; + } + + // Copy the saved passphrase for `ssid` into out. False when not saved. + inline bool lookup(const char* ssid, char* out, int cap) { + SavedList list; + saved_load(&list); + const SavedNetwork* n = saved_find(&list, ssid); + if (!n) return false; + copy_str(out, cap, n->psk); + return true; + } + + // ---- presentation ------------------------------------------------------ + + inline const char* security_name(uint8_t security) { + switch (security) { + case montauk::abi::WIFI_SEC_OPEN: return "Open"; + case montauk::abi::WIFI_SEC_WEP: return "WEP"; + case montauk::abi::WIFI_SEC_WPA: return "WPA"; + case montauk::abi::WIFI_SEC_WPA2: return "WPA2"; + case montauk::abi::WIFI_SEC_WPA3: return "WPA3"; + default: return "Unknown"; + } + } + + inline bool needs_key(uint8_t security) { + return security != montauk::abi::WIFI_SEC_OPEN; + } + + // 0-4 bars from an RSSI in dBm. + inline int signal_bars(int8_t rssi) { + if (rssi >= -55) return 4; + if (rssi >= -67) return 3; + if (rssi >= -75) return 2; + if (rssi >= -85) return 1; + return 0; + } + + inline const char* state_name(uint8_t state) { + switch (state) { + case montauk::abi::WIFI_STATE_ABSENT: return "No adapter"; + case montauk::abi::WIFI_STATE_DETECTED: return "Loading firmware"; + case montauk::abi::WIFI_STATE_BOOTING: return "Starting"; + case montauk::abi::WIFI_STATE_RUNNING: return "Ready"; + case montauk::abi::WIFI_STATE_ERROR: return "Adapter error"; + case montauk::abi::WIFI_STATE_RFKILL: return "Radio off"; + default: return "Unknown"; + } + } + + // What the join is doing right now, for a progress line. + inline const char* conn_state_name(uint32_t connState) { + switch (connState) { + case montauk::abi::WIFI_CONN_IDLE: return "Not connected"; + case montauk::abi::WIFI_CONN_CONTEXTS_UP: return "Preparing radio..."; + case montauk::abi::WIFI_CONN_AUTHENTICATING: return "Authenticating..."; + case montauk::abi::WIFI_CONN_AUTHENTICATED: return "Authenticated"; + case montauk::abi::WIFI_CONN_ASSOCIATING: return "Associating..."; + case montauk::abi::WIFI_CONN_ASSOCIATED: return "Associated"; + case montauk::abi::WIFI_CONN_HANDSHAKING: return "Exchanging keys..."; + case montauk::abi::WIFI_CONN_CONNECTED: return "Connected"; + case montauk::abi::WIFI_CONN_FAILED: return "Connection failed"; + default: return ""; + } + } + + inline const char* error_message(int err) { + switch (err) { + case 0: return ""; + case montauk::abi::WIFI_ERR_NO_ADAPTER: return "No Wi-Fi adapter is ready"; + case montauk::abi::WIFI_ERR_NOT_FOUND: return "That network is out of range"; + case montauk::abi::WIFI_ERR_NEED_KEY: return "This network needs a password"; + case montauk::abi::WIFI_ERR_UNSUPPORTED: return "This security type is not supported"; + case montauk::abi::WIFI_ERR_AUTH: return "Wrong password"; + case montauk::abi::WIFI_ERR_TIMEOUT: return "The network did not respond"; + default: return "Could not join the network"; + } + } + +} // namespace wifi +} // namespace montauk diff --git a/programs/man/wifi.1 b/programs/man/wifi.1 index ea7bcfc..b27b97f 100644 --- a/programs/man/wifi.1 +++ b/programs/man/wifi.1 @@ -7,7 +7,10 @@ wifi info wifi debug wifi connect [passphrase] + wifi status wifi disconnect + wifi saved + wifi forget .SH DESCRIPTION Drives the Intel Wi-Fi adapter. With no arguments, runs a five @@ -33,13 +36,24 @@ security tallies. Use this when a scan finds nothing. connect [passphrase] - Brings the firmware contexts (PHY, MAC, binding, station) up - for a network from the last scan. The 802.11 authentication - and association exchange is not implemented, so this does not - produce a usable link; encrypted networks are refused outright. + Joins a network from the last scan: open, or WPA2/WPA3-PSK + with CCMP or GCMP. Blocks until the link is up or the attempt + fails, and says which it was. A passphrase given here is + remembered in 0:/config/wifi.toml; given only an SSID, the + remembered one is used. Run dhcp(1) afterwards for an address. + + status + The network currently joined, with signal and cipher. disconnect - Tears those contexts back down. + Leaves the network and tears the firmware contexts down. + + saved + Lists the networks in 0:/config/wifi.toml and whether the + desktop rejoins them by itself at startup. + + forget + Removes a network from 0:/config/wifi.toml. .SH OUTPUT SSID SIGNAL RSSI CH BAND SECURITY @@ -59,6 +73,21 @@ yet)". The PNVM file carries regulatory data; without it the firmware falls back to conservative built-in limits. +.SH SAVED NETWORKS + 0:/config/wifi.toml holds the networks the system may rejoin + without being asked, as [network.] tables of ssid and psk, and + a wifi.autoconnect flag. The desktop writes it when "Remember this + network" is ticked, and reads it after the automatic scan it runs + at startup. Passphrases are stored as typed; anyone who can read + 0:/config can read them. + +.SH GRAPHICAL USE + The desktop panel carries a Wi-Fi icon whenever a supported + adapter is present, separate from the Ethernet one. Its menu lists + the networks in range, asks for a passphrase when one is needed, + and shows the address once the link is up. The Network app has the + same list under its Wi-Fi tab, along with Forget. + .SH DIAGNOSTICS no supported Wi-Fi adapter found No matching device, or its RF type is not GF. diff --git a/programs/src/desktop/Makefile b/programs/src/desktop/Makefile index 3973388..ad5cf88 100644 --- a/programs/src/desktop/Makefile +++ b/programs/src/desktop/Makefile @@ -56,7 +56,7 @@ LDFLAGS := \ # ---- C++ source files ---- -CORE_SRCS := main.cpp window.cpp panel.cpp compose.cpp input.cpp dialogs.cpp launcher.cpp desktop_builtin.cpp desktop_catalog.cpp font_data.cpp stb_truetype_impl.cpp +CORE_SRCS := main.cpp window.cpp panel.cpp wifi.cpp compose.cpp input.cpp dialogs.cpp launcher.cpp desktop_builtin.cpp desktop_catalog.cpp font_data.cpp stb_truetype_impl.cpp APP_SRCS := $(sort $(shell find apps -name '*.cpp' -print)) SRCS := $(CORE_SRCS) $(APP_SRCS) OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o)) diff --git a/programs/src/desktop/compose.cpp b/programs/src/desktop/compose.cpp index e718079..1af49be 100644 --- a/programs/src/desktop/compose.cpp +++ b/programs/src/desktop/compose.cpp @@ -263,6 +263,11 @@ void gui::desktop_compose(DesktopState* ds) { desktop_draw_net_popup(ds); } + // Draw Wi-Fi popup if open + if (ds->wifi_popup_open) { + desktop_draw_wifi_popup(ds); + } + // Draw volume popup if open if (ds->vol_popup_open) { desktop_draw_vol_popup(ds); diff --git a/programs/src/desktop/desktop_builtin.cpp b/programs/src/desktop/desktop_builtin.cpp index 8e1547d..e34ec60 100644 --- a/programs/src/desktop/desktop_builtin.cpp +++ b/programs/src/desktop/desktop_builtin.cpp @@ -66,6 +66,7 @@ void desktop_lock_screen(DesktopState* ds) { desktop_close_launcher(ds); ds->ctx_menu_open = false; ds->net_popup_open = false; + ds->wifi_popup_open = false; ds->vol_popup_open = false; // Cache display name for lock screen rendering diff --git a/programs/src/desktop/desktop_internal.hpp b/programs/src/desktop/desktop_internal.hpp index f231915..c65c2cd 100644 --- a/programs/src/desktop/desktop_internal.hpp +++ b/programs/src/desktop/desktop_internal.hpp @@ -158,12 +158,22 @@ gui::CursorStyle cursor_for_edge(gui::ResizeEdge edge); void desktop_draw_app_menu(gui::DesktopState* ds); void desktop_draw_net_popup(gui::DesktopState* ds); void desktop_draw_vol_popup(gui::DesktopState* ds); +const montauk::abi::NetIfInfo* desktop_eth_iface(const gui::DesktopState* ds); +bool desktop_eth_online(const gui::DesktopState* ds); + +// wifi.cpp +void desktop_wifi_init(gui::DesktopState* ds); +bool desktop_wifi_poll(gui::DesktopState* ds, uint64_t now); +void desktop_draw_wifi_popup(gui::DesktopState* ds); +bool desktop_wifi_handle_mouse(gui::DesktopState* ds, int mx, int my, + bool left_pressed, int scroll); // compose.cpp void desktop_draw_lock_screen(gui::DesktopState* ds); void desktop_mark_background_dirty(gui::DesktopState* ds); // main.cpp +void desktop_refresh_netifs(gui::DesktopState* ds); void desktop_scan_apps(gui::DesktopState* ds); void desktop_build_menu(gui::DesktopState* ds); void desktop_open_launcher(gui::DesktopState* ds); diff --git a/programs/src/desktop/input.cpp b/programs/src/desktop/input.cpp index 4eaff2e..1dbb6b0 100644 --- a/programs/src/desktop/input.cpp +++ b/programs/src/desktop/input.cpp @@ -166,6 +166,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) { ds->ctx_menu_open = false; ds->vol_popup_open = false; ds->net_popup_open = false; + ds->wifi_popup_open = false; return; } @@ -507,6 +508,13 @@ void gui::desktop_handle_mouse(DesktopState* ds) { } } + // Wi-Fi popup. The passphrase dialog is an ordinary window and needs + // nothing here. + if (ds->wifi_popup_open) { + if (desktop_wifi_handle_mouse(ds, mx, my, left_pressed, ev.scroll)) + return; + } + // Handle net popup clicks if (ds->net_popup_open && left_pressed) { int popup_w = 220; @@ -522,6 +530,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) { return; } else if (!ds->net_icon_rect.contains(mx, my)) { ds->net_popup_open = false; + ds->wifi_popup_open = false; } } @@ -531,6 +540,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) { if (mx < 36) { ds->app_menu_open = !ds->app_menu_open; ds->net_popup_open = false; + ds->wifi_popup_open = false; ds->vol_popup_open = false; ds->ctx_menu_open = false; return; @@ -542,6 +552,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) { ds->vol_dragging = false; ds->app_menu_open = false; ds->net_popup_open = false; + ds->wifi_popup_open = false; ds->ctx_menu_open = false; return; } @@ -551,6 +562,17 @@ void gui::desktop_handle_mouse(DesktopState* ds) { ds->net_popup_open = !ds->net_popup_open; ds->app_menu_open = false; ds->vol_popup_open = false; + ds->wifi_popup_open = false; + ds->ctx_menu_open = false; + return; + } + + // Wi-Fi icon + if (ds->wifi_icon_rect.w > 0 && ds->wifi_icon_rect.contains(mx, my)) { + ds->wifi_popup_open = !ds->wifi_popup_open; + ds->app_menu_open = false; + ds->vol_popup_open = false; + ds->net_popup_open = false; ds->ctx_menu_open = false; return; } @@ -775,6 +797,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) { ds->ctx_menu_y = my; ds->app_menu_open = false; ds->net_popup_open = false; + ds->wifi_popup_open = false; ds->vol_popup_open = false; } } diff --git a/programs/src/desktop/launcher.cpp b/programs/src/desktop/launcher.cpp index 81580a8..b59a4c9 100644 --- a/programs/src/desktop/launcher.cpp +++ b/programs/src/desktop/launcher.cpp @@ -406,6 +406,7 @@ void desktop_open_launcher(DesktopState* ds) { ds->app_menu_open = false; ds->ctx_menu_open = false; ds->net_popup_open = false; + ds->wifi_popup_open = false; ds->vol_popup_open = false; ds->vol_dragging = false; desktop_update_launcher_results(ds); diff --git a/programs/src/desktop/main.cpp b/programs/src/desktop/main.cpp index 00f2760..1653aaf 100644 --- a/programs/src/desktop/main.cpp +++ b/programs/src/desktop/main.cpp @@ -276,6 +276,7 @@ void gui::desktop_init(DesktopState* ds) { ds->icon_folder = svg_load("0:/icons/folder.svg", 16, 16, defColor); ds->icon_file = svg_load("0:/icons/text-x-generic.svg", 16, 16, defColor); ds->icon_network = svg_load("0:/icons/network-wired-symbolic.svg", 16, 16, colors::PANEL_TEXT); + ds->icon_wifi = svg_load("0:/icons/network-wireless-symbolic.svg", 16, 16, colors::PANEL_TEXT); ds->icon_go_up = svg_load("0:/icons/go-up-symbolic.svg", 16, 16, defColor); ds->icon_go_back = svg_load("0:/icons/go-previous-symbolic.svg", 16, 16, defColor); ds->icon_go_forward = svg_load("0:/icons/go-next-symbolic.svg", 16, 16, defColor); @@ -392,6 +393,9 @@ void gui::desktop_init(DesktopState* ds) { ds->net_cfg_last_poll = montauk::get_milliseconds(); ds->net_icon_rect = {0, 0, 0, 0}; + desktop_refresh_netifs(ds); + desktop_wifi_init(ds); + ds->vol_popup_open = false; ds->vol_icon_rect = {0, 0, 0, 0}; int vol = montauk::audio_get_master_volume(); @@ -465,6 +469,30 @@ static bool desktop_netcfg_equal(const montauk::abi::NetCfg& a, const montauk::a return true; } +// Re-read the interface registry. Cheap, and it is the only way to tell the +// wired and wireless halves of the panel apart. +void desktop_refresh_netifs(DesktopState* ds) { + int n = montauk::net_interfaces(ds->netifs, DesktopState::MAX_NETIFS); + ds->netif_count = n > 0 ? n : 0; + ds->eth_present = false; + for (int i = 0; i < ds->netif_count; i++) { + if (ds->netifs[i].kind == montauk::abi::NETIF_KIND_ETHERNET) + ds->eth_present = true; + } +} + +static bool desktop_netifs_equal(const montauk::abi::NetIfInfo* a, int an, + const montauk::abi::NetIfInfo* b, int bn) { + if (an != bn) return false; + for (int i = 0; i < an; i++) { + if (a[i].linkUp != b[i].linkUp || a[i].active != b[i].active || + a[i].kind != b[i].kind) { + return false; + } + } + return true; +} + static bool desktop_refresh_panel_state(DesktopState* ds, uint64_t now) { if (ds->screen_locked) return false; @@ -476,9 +504,20 @@ static bool desktop_refresh_panel_state(DesktopState* ds, uint64_t now) { ds->cached_net_cfg = next; changed = true; } + + montauk::abi::NetIfInfo ifaces[DesktopState::MAX_NETIFS]; + int count = montauk::net_interfaces(ifaces, DesktopState::MAX_NETIFS); + if (count < 0) count = 0; + if (!desktop_netifs_equal(ifaces, count, ds->netifs, ds->netif_count)) { + desktop_refresh_netifs(ds); + changed = true; + } + ds->net_cfg_last_poll = now; } + if (desktop_wifi_poll(ds, now)) changed = true; + // Event-driven sync: the kernel mixer bumps a serial on every state // change. Reading it is one cheap syscall; refresh only when the serial // moved since we last looked. The audio app (and any other client) will diff --git a/programs/src/desktop/panel.cpp b/programs/src/desktop/panel.cpp index 715f714..1c80221 100644 --- a/programs/src/desktop/panel.cpp +++ b/programs/src/desktop/panel.cpp @@ -141,25 +141,48 @@ void gui::desktop_draw_panel(DesktopState* ds) { } } - // Network icon (to the left of the volume icon) - int net_icon_x = vol_icon_x - 16 - 10; - int net_icon_y = (PANEL_HEIGHT - 16) / 2; - ds->net_icon_rect = {net_icon_x, net_icon_y, 16, 16}; + // Status icons fill in leftwards from the volume icon. Ethernet only + // appears when a wired interface is registered, Wi-Fi only when a + // supported adapter is present, so a machine without either shows neither. + int icon_y = (PANEL_HEIGHT - 16) / 2; + int next_icon_x = vol_icon_x - 16 - 10; - if (ds->icon_network.pixels) { - if (ds->cached_net_cfg.ipAddress == 0) { - uint32_t* src = ds->icon_network.pixels; - int npx = 16 * 16; - uint32_t tinted[256]; - for (int p = 0; p < npx; p++) { - uint32_t px = src[p]; - uint8_t a = (px >> 24) & 0xFF; - tinted[p] = ((uint32_t)a << 24) | 0x004444CC; + if (ds->eth_present) { + ds->net_icon_rect = {next_icon_x, icon_y, 16, 16}; + next_icon_x -= 16 + 10; + + if (ds->icon_network.pixels) { + // Tinted while the cable is doing nothing for us: no link, or a + // link that is not the one carrying traffic. + if (!desktop_eth_online(ds)) { + uint32_t* src = ds->icon_network.pixels; + uint32_t tinted[256]; + for (int p = 0; p < 16 * 16; p++) { + uint8_t a = (src[p] >> 24) & 0xFF; + tinted[p] = ((uint32_t)a << 24) | 0x004444CC; + } + fb.blit_alpha(ds->net_icon_rect.x, icon_y, 16, 16, tinted); + } else { + fb.blit_alpha(ds->net_icon_rect.x, icon_y, ds->icon_network.width, + ds->icon_network.height, ds->icon_network.pixels); } - fb.blit_alpha(net_icon_x, net_icon_y, 16, 16, tinted); - } else { - fb.blit_alpha(net_icon_x, net_icon_y, ds->icon_network.width, ds->icon_network.height, ds->icon_network.pixels); } + } else { + ds->net_icon_rect = {0, 0, 0, 0}; + } + + // Wi-Fi stays white whatever the radio is doing: the popup carries the + // state, and a colour-shifting icon next to the clock is just noise. + if (ds->wifi_present) { + ds->wifi_icon_rect = {next_icon_x, icon_y, 16, 16}; + next_icon_x -= 16 + 10; + + if (ds->icon_wifi.pixels) { + fb.blit_alpha(ds->wifi_icon_rect.x, icon_y, ds->icon_wifi.width, + ds->icon_wifi.height, ds->icon_wifi.pixels); + } + } else { + ds->wifi_icon_rect = {0, 0, 0, 0}; } } @@ -265,16 +288,38 @@ void desktop_draw_app_menu(DesktopState* ds) { // Network Popup // ============================================================================ +// The wired interface, if one is registered. +const montauk::abi::NetIfInfo* desktop_eth_iface(const DesktopState* ds) { + for (int i = 0; i < ds->netif_count; i++) { + if (ds->netifs[i].kind == montauk::abi::NETIF_KIND_ETHERNET) + return &ds->netifs[i]; + } + return nullptr; +} + +// "Online" for the panel icon means the cable is both up and the interface the +// stack is actually sending through, with an address to send from. +bool desktop_eth_online(const DesktopState* ds) { + const montauk::abi::NetIfInfo* eth = desktop_eth_iface(ds); + return eth && eth->linkUp && eth->active && ds->cached_net_cfg.ipAddress != 0; +} + void desktop_draw_net_popup(DesktopState* ds) { Framebuffer& fb = ds->fb; montauk::abi::NetCfg& nc = ds->cached_net_cfg; - bool connected = nc.ipAddress != 0; + + const montauk::abi::NetIfInfo* eth = desktop_eth_iface(ds); + bool link_up = eth && eth->linkUp; + // The IP configuration is global to the stack, so it is only this + // interface's address while this interface is the active one. + bool owns_address = eth && eth->active && nc.ipAddress != 0; + bool connected = link_up && owns_address; int popup_w = 220; int fh = system_font_height(); int row_h = fh + 8; int header_h = row_h + 8; // title + status row + padding - int body_rows = 5; // IP, Subnet, Gateway, DNS, MAC + int body_rows = 6; // Interface, IP, Subnet, Gateway, DNS, MAC int popup_h = header_h + row_h * body_rows + 12; int popup_x = ds->net_icon_rect.x + ds->net_icon_rect.w - popup_w; int popup_y = PANEL_HEIGHT + 2; @@ -290,11 +335,14 @@ void desktop_draw_net_popup(DesktopState* ds) { // Header: "Ethernet" + status dot draw_text(fb, lx, ty, "Ethernet", colors::TEXT_COLOR); - // Status dot + label (right-aligned in header) + // Status dot + label (right-aligned in header). A cable that is plugged + // in but idle (Wi-Fi is carrying the traffic) is not the same as unplugged. Color dot_color = connected ? Color::from_rgb(0x4C, 0xAF, 0x50) // green - : Color::from_rgb(0xCC, 0x33, 0x33); // red - const char* status_str = connected ? "Connected" : "Disconnected"; + : (link_up ? Color::from_rgb(0xD0, 0x9A, 0x2E) // amber + : Color::from_rgb(0xCC, 0x33, 0x33)); // red + const char* status_str = connected ? "Connected" + : (link_up ? "Link up" : "Disconnected"); int sw = text_width(status_str); int dot_r = 4; int status_x = popup_x + popup_w - 14 - sw; @@ -318,23 +366,31 @@ void desktop_draw_net_popup(DesktopState* ds) { int val_x = popup_x + 76; // fixed column for values struct NetRow { const char* label; char value[24]; }; - NetRow rows[5]; + NetRow rows[6]; - rows[0].label = "IP"; - if (connected) format_ip(rows[0].value, nc.ipAddress); - else montauk::strcpy(rows[0].value, "0.0.0.0"); + rows[0].label = "Interface"; + montauk::strncpy(rows[0].value, eth ? eth->name : "None", sizeof(rows[0].value)); - rows[1].label = "Subnet"; - format_ip(rows[1].value, nc.subnetMask); + // Addresses are shown only when this interface owns them; otherwise the + // wireless popup is where they belong. + rows[1].label = "IP"; + if (owns_address) format_ip(rows[1].value, nc.ipAddress); + else montauk::strcpy(rows[1].value, link_up ? "Not in use" : "0.0.0.0"); - rows[2].label = "Gateway"; - format_ip(rows[2].value, nc.gateway); + rows[2].label = "Subnet"; + if (owns_address) format_ip(rows[2].value, nc.subnetMask); + else montauk::strcpy(rows[2].value, "-"); - rows[3].label = "DNS"; - format_ip(rows[3].value, nc.dnsServer); + rows[3].label = "Gateway"; + if (owns_address) format_ip(rows[3].value, nc.gateway); + else montauk::strcpy(rows[3].value, "-"); - rows[4].label = "MAC"; - format_mac(rows[4].value, nc.macAddress); + rows[4].label = "DNS"; + if (owns_address) format_ip(rows[4].value, nc.dnsServer); + else montauk::strcpy(rows[4].value, "-"); + + rows[5].label = "MAC"; + format_mac(rows[5].value, eth ? eth->mac : nc.macAddress); for (int i = 0; i < body_rows; i++) { draw_text(fb, lx, ty, rows[i].label, dim); diff --git a/programs/src/desktop/wifi.cpp b/programs/src/desktop/wifi.cpp new file mode 100644 index 0000000..43fa35f --- /dev/null +++ b/programs/src/desktop/wifi.cpp @@ -0,0 +1,867 @@ +/* + * wifi.cpp + * Wi-Fi panel entry: the network list popup, the passphrase dialog window, + * and the automatic scan and saved-network join that run at startup. + * + * Everything here goes through the non-blocking Wi-Fi syscalls + * (wifi_scan_start / wifi_results / wifi_connect_async). The blocking pair + * takes seconds, and the compositor cannot stand still for that long. + * + * Copyright (c) 2026 Daniel Hammer +*/ + +#include "desktop_internal.hpp" +#include +#include + +using namespace gui; + +// ============================================================================ +// Geometry +// ============================================================================ + +static constexpr int WIFI_POPUP_W = 264; +static constexpr int WIFI_ROW_H = 30; +static constexpr int WIFI_VISIBLE = 6; // network rows on screen at once +static constexpr int WIFI_BTN_H = 26; + +static int wifi_popup_x(const DesktopState* ds) { + int x = ds->wifi_icon_rect.x + ds->wifi_icon_rect.w - WIFI_POPUP_W; + return x < 4 ? 4 : x; +} + +static int wifi_visible_rows(const DesktopState* ds) { + int rows = ds->wifi_network_count; + if (rows > WIFI_VISIBLE) rows = WIFI_VISIBLE; + if (rows < 1) rows = 1; // the "no networks" line + return rows; +} + +static int wifi_popup_h(const DesktopState* ds) { + int fh = system_font_height(); + int header = fh + 10; // title + status + int detail = ds->wifi_info.connected ? (fh + 6) * 3 + 8 : 0; + int list = wifi_visible_rows(ds) * WIFI_ROW_H; + int footer = WIFI_BTN_H + 14 + fh + 6; + return header + 10 + detail + list + 10 + footer; +} + +static Rect wifi_popup_rect(const DesktopState* ds) { + return {wifi_popup_x(ds), PANEL_HEIGHT + 2, WIFI_POPUP_W, wifi_popup_h(ds)}; +} + +// y of the first network row +static int wifi_list_y(const DesktopState* ds) { + int fh = system_font_height(); + int y = PANEL_HEIGHT + 2 + 10 + fh + 10; + if (ds->wifi_info.connected) y += (fh + 6) * 3 + 8; + return y; +} + +static Rect wifi_scan_button(const DesktopState* ds) { + Rect popup = wifi_popup_rect(ds); + int fh = system_font_height(); + int y = popup.y + popup.h - 10 - fh - 6 - WIFI_BTN_H; + return {popup.x + 12, y, 76, WIFI_BTN_H}; +} + +static Rect wifi_action_button(const DesktopState* ds) { + Rect scan = wifi_scan_button(ds); + Rect popup = wifi_popup_rect(ds); + int w = 96; + return {popup.x + popup.w - 12 - w, scan.y, w, WIFI_BTN_H}; +} + +// ============================================================================ +// State helpers +// ============================================================================ + +// The saved-network list lives here rather than on the stack: it is several +// kilobytes, and the compositor's stack is 32 KiB shared with the TrueType +// rasteriser. One copy also saves re-parsing the file on every click. +static montauk::wifi::SavedList g_saved; + +static void wifi_reload_saved() { + montauk::wifi::saved_load(&g_saved); +} + +static const char* wifi_saved_psk(const char* ssid) { + const montauk::wifi::SavedNetwork* entry = + montauk::wifi::saved_find(&g_saved, ssid); + return (entry && entry->psk[0]) ? entry->psk : nullptr; +} + +static void wifi_set_status(DesktopState* ds, const char* msg) { + montauk::strncpy(ds->wifi_status, msg ? msg : "", sizeof(ds->wifi_status)); + ds->wifi_status_time = montauk::get_milliseconds(); +} + +// Sorted strongest-first so the list reads the way a user expects. +static void wifi_sort_results(DesktopState* ds) { + for (int i = 1; i < ds->wifi_network_count; i++) { + montauk::abi::WifiNetwork key = ds->wifi_networks[i]; + int j = i - 1; + while (j >= 0 && ds->wifi_networks[j].rssi < key.rssi) { + ds->wifi_networks[j + 1] = ds->wifi_networks[j]; + j--; + } + ds->wifi_networks[j + 1] = key; + } +} + +static void wifi_refresh_results(DesktopState* ds) { + montauk::abi::WifiNetwork raw[DesktopState::MAX_WIFI_NETWORKS]; + int n = montauk::wifi_results(raw, DesktopState::MAX_WIFI_NETWORKS); + if (n < 0) n = 0; + + // One row per network rather than one per access point: a house with a + // repeater answers on several BSSIDs under the same name, and the strongest + // is the one worth joining. Hidden networks have no name to join by, so + // they are left out of the list entirely. + ds->wifi_network_count = 0; + for (int i = 0; i < n; i++) { + if (!raw[i].ssid[0]) continue; + + int existing = -1; + for (int k = 0; k < ds->wifi_network_count; k++) { + if (montauk::streq(ds->wifi_networks[k].ssid, raw[i].ssid)) { + existing = k; + break; + } + } + if (existing >= 0) { + if (raw[i].rssi > ds->wifi_networks[existing].rssi) + ds->wifi_networks[existing] = raw[i]; + continue; + } + ds->wifi_networks[ds->wifi_network_count++] = raw[i]; + } + + wifi_sort_results(ds); + if (ds->wifi_scroll > ds->wifi_network_count - 1) ds->wifi_scroll = 0; +} + +static void wifi_start_scan(DesktopState* ds) { + int rc = montauk::wifi_scan_start(6000); + if (rc < 0) { + wifi_set_status(ds, "The adapter is not ready yet"); + return; + } + ds->wifi_scanning = true; + // No status line here: the button reads "Scanning" and the list says so + // too, and three copies of the same word is not progress reporting. + ds->wifi_status[0] = '\0'; +} + +static void wifi_begin_join(DesktopState* ds, const char* ssid, const char* psk) { + int rc = montauk::wifi_connect_async(ssid, psk); + if (rc < 0) { + wifi_set_status(ds, montauk::wifi::error_message(rc)); + ds->wifi_joining = false; + return; + } + ds->wifi_joining = true; + ds->wifi_dhcp_pending = true; + montauk::strncpy(ds->wifi_joining_ssid, ssid, sizeof(ds->wifi_joining_ssid)); + + char msg[96]; + snprintf(msg, sizeof(msg), "Connecting to %s...", ssid); + wifi_set_status(ds, msg); +} + +// Defined below: the passphrase dialog is a real desktop window, created the +// same way the reboot and shutdown dialogs are. +static void wifi_open_password_dialog(DesktopState* ds, + const montauk::abi::WifiNetwork& net); + +// Join the network under the cursor: straight away when it is open or its +// passphrase is already saved, otherwise ask for the key. +static void wifi_select_network(DesktopState* ds, int index) { + if (index < 0 || index >= ds->wifi_network_count) return; + const montauk::abi::WifiNetwork& net = ds->wifi_networks[index]; + + if (!montauk::wifi::needs_key(net.security)) { + wifi_begin_join(ds, net.ssid, ""); + return; + } + + const char* saved = wifi_saved_psk(net.ssid); + if (saved) { + wifi_begin_join(ds, net.ssid, saved); + return; + } + + wifi_open_password_dialog(ds, net); +} + +// ============================================================================ +// Startup and polling +// ============================================================================ + +void desktop_wifi_init(DesktopState* ds) { + ds->wifi_popup_open = false; + ds->wifi_icon_rect = {0, 0, 0, 0}; + ds->wifi_network_count = 0; + ds->wifi_scroll = 0; + ds->wifi_scanning = false; + ds->wifi_boot_scan_started = false; + ds->wifi_autoconnect_done = false; + ds->wifi_joining = false; + ds->wifi_dhcp_pending = false; + ds->wifi_dhcp_waiting = false; + ds->wifi_joining_ssid[0] = '\0'; + ds->wifi_status[0] = '\0'; + ds->wifi_status_time = 0; + ds->wifi_scan_generation = 0; + ds->wifi_last_poll = 0; + wifi_reload_saved(); + + montauk::memset(&ds->wifi_info, 0, sizeof(ds->wifi_info)); + ds->wifi_present = montauk::wifi_info(&ds->wifi_info) == 0 && ds->wifi_info.present; +} + +// Try the strongest saved network that is actually in range. +static void wifi_try_autoconnect(DesktopState* ds) { + if (!g_saved.autoconnect || g_saved.count == 0) { + ds->wifi_autoconnect_done = true; + return; + } + + for (int i = 0; i < ds->wifi_network_count; i++) { // strongest first + const montauk::abi::WifiNetwork& net = ds->wifi_networks[i]; + const montauk::wifi::SavedNetwork* entry = + montauk::wifi::saved_find(&g_saved, net.ssid); + if (!entry) continue; + wifi_begin_join(ds, entry->ssid, entry->psk); + break; + } + + ds->wifi_autoconnect_done = true; +} + +// Called from the panel refresh. Returns true when something on screen moved. +bool desktop_wifi_poll(DesktopState* ds, uint64_t now) { + // The Network app writes the same file, so pick up its edits whenever the + // popup is opened rather than trusting the copy read at startup. + static bool popup_was_open = false; + if (ds->wifi_popup_open && !popup_was_open) wifi_reload_saved(); + popup_was_open = ds->wifi_popup_open; + + // The adapter finishes its firmware load well after login, so keep looking + // for it until it shows up rather than deciding once at startup. + if (!ds->wifi_present) { + if (now - ds->wifi_last_poll < 3000) return false; + ds->wifi_last_poll = now; + montauk::abi::WifiInfo info; + if (montauk::wifi_info(&info) != 0 || !info.present) return false; + ds->wifi_info = info; + ds->wifi_present = true; + return true; // the icon appears now + } + + // Poll faster while something is in flight so progress text keeps up. + bool busy = ds->wifi_scanning || ds->wifi_joining || ds->wifi_popup_open; + if (now - ds->wifi_last_poll < (busy ? 400u : 3000u)) return false; + ds->wifi_last_poll = now; + + montauk::abi::WifiInfo info; + if (montauk::wifi_info(&info) != 0) return false; + + bool changed = info.connected != ds->wifi_info.connected + || info.connState != ds->wifi_info.connState + || info.scanning != ds->wifi_info.scanning + || info.scanGeneration != ds->wifi_info.scanGeneration + || info.state != ds->wifi_info.state; + ds->wifi_info = info; + + // The first scan runs by itself once the firmware is up, so the list is + // already populated the first time the user opens the popup. + if (!ds->wifi_boot_scan_started && info.state == montauk::abi::WIFI_STATE_RUNNING) { + ds->wifi_boot_scan_started = true; + wifi_start_scan(ds); + return true; + } + + if (info.scanGeneration != ds->wifi_scan_generation) { + ds->wifi_scan_generation = info.scanGeneration; + ds->wifi_scanning = false; + wifi_refresh_results(ds); + changed = true; + + char msg[64]; + if (ds->wifi_network_count > 0) { + snprintf(msg, sizeof(msg), "Found %d network%s", + ds->wifi_network_count, ds->wifi_network_count == 1 ? "" : "s"); + } else { + snprintf(msg, sizeof(msg), "No networks found"); + } + wifi_set_status(ds, msg); + + // Rejoin whatever was saved as soon as the first sweep lands. + if (!ds->wifi_autoconnect_done && !info.connected && !ds->wifi_joining) { + wifi_reload_saved(); + wifi_try_autoconnect(ds); + } + } else if (ds->wifi_scanning && !info.scanning) { + ds->wifi_scanning = false; + changed = true; + } + + if (ds->wifi_joining) { + if (info.connected) { + ds->wifi_joining = false; + char msg[96]; + snprintf(msg, sizeof(msg), "Connected to %s", info.ssid); + wifi_set_status(ds, msg); + changed = true; + } else if (info.lastError != 0 && !info.joining) { + ds->wifi_joining = false; + ds->wifi_dhcp_pending = false; + ds->wifi_dhcp_waiting = false; + wifi_set_status(ds, montauk::wifi::error_message(info.lastError)); + changed = true; + } + } + + // A wireless link with no address of its own needs a lease; the wired + // interface, if there is one, keeps whatever it already had. + if (ds->wifi_dhcp_pending && info.connected && ds->cached_net_cfg.ipAddress == 0) { + ds->wifi_dhcp_pending = false; + ds->wifi_dhcp_waiting = true; + montauk::spawn("0:/os/dhcp.elf"); + wifi_set_status(ds, "Requesting an address..."); + changed = true; + } else if (ds->wifi_dhcp_pending && info.connected) { + ds->wifi_dhcp_pending = false; + } + + // "Requesting an address..." is only true until the lease lands. Leaving it + // up afterwards contradicts the address row further up the popup, so it is + // replaced the moment there is an address (or the link goes away). + if (ds->wifi_dhcp_waiting && (ds->cached_net_cfg.ipAddress != 0 || !info.connected)) { + bool leased = ds->cached_net_cfg.ipAddress != 0; + ds->wifi_dhcp_waiting = false; + if (leased) { + char msg[96]; + snprintf(msg, sizeof(msg), "Connected to %s", info.ssid); + wifi_set_status(ds, msg); + } else { + ds->wifi_status[0] = '\0'; + } + changed = true; + } + + return changed; +} + +// ============================================================================ +// Drawing +// ============================================================================ + +static void draw_signal_bars(Framebuffer& fb, int x, int y, int8_t rssi, Color on, Color off) { + int bars = montauk::wifi::signal_bars(rssi); + for (int i = 0; i < 4; i++) { + int h = 3 + i * 3; + fb.fill_rect(x + i * 4, y + 12 - h, 3, h, i < bars ? on : off); + } +} + +// A small padlock, so encrypted networks read at a glance. +static void draw_lock(Framebuffer& fb, int x, int y, Color c) { + fb.fill_rect(x + 1, y + 5, 8, 6, c); + fb.fill_rect(x + 2, y + 2, 1, 3, c); + fb.fill_rect(x + 7, y + 2, 1, 3, c); + fb.fill_rect(x + 3, y + 1, 4, 1, c); +} + +static void draw_kv_row(Framebuffer& fb, int lx, int vx, int y, + const char* label, const char* value, Color dim, Color text) { + draw_text(fb, lx, y, label, dim); + draw_text(fb, vx, y, value, text); +} + +void desktop_draw_wifi_popup(DesktopState* ds) { + Framebuffer& fb = ds->fb; + Rect popup = wifi_popup_rect(ds); + int fh = system_font_height(); + Color dim = Color::from_rgb(0x66, 0x66, 0x66); + Color faint = Color::from_rgb(0xCC, 0xCC, 0xCC); + + draw_shadow(fb, popup.x, popup.y, popup.w, popup.h, 4, colors::SHADOW); + fill_rounded_rect(fb, popup.x, popup.y, popup.w, popup.h, 8, colors::MENU_BG); + draw_rect(fb, popup.x, popup.y, popup.w, popup.h, colors::BORDER); + + int lx = popup.x + 14; + int ty = popup.y + 10; + + // Header: "Wi-Fi" + connection state + draw_text(fb, lx, ty, "Wi-Fi", colors::TEXT_COLOR); + + const char* status_str; + if (ds->wifi_info.connected) status_str = "Connected"; + else if (ds->wifi_joining) status_str = "Connecting"; + else if (ds->wifi_info.state == montauk::abi::WIFI_STATE_RFKILL) status_str = "Radio off"; + else status_str = "Not connected"; + + Color dot_color = ds->wifi_info.connected + ? Color::from_rgb(0x4C, 0xAF, 0x50) + : (ds->wifi_joining ? Color::from_rgb(0xD0, 0x9A, 0x2E) + : Color::from_rgb(0xCC, 0x33, 0x33)); + int sw = text_width(status_str); + int status_x = popup.x + popup.w - 14 - sw; + fill_circle(fb, status_x - 9, ty + fh / 2, 4, dot_color); + draw_text(fb, status_x, ty, status_str, dim); + + ty += fh + 10; + + // What this interface has, when it has it. The address belongs to the + // wireless interface only while that is the one carrying traffic. + if (ds->wifi_info.connected) { + int vx = popup.x + 76; + char value[40]; + + montauk::strncpy(value, ds->wifi_info.ssid[0] ? ds->wifi_info.ssid : "-", sizeof(value)); + draw_kv_row(fb, lx, vx, ty, "Network", value, dim, colors::TEXT_COLOR); + ty += fh + 6; + + bool wireless_active = false; + for (int i = 0; i < ds->netif_count; i++) { + if (ds->netifs[i].kind == montauk::abi::NETIF_KIND_WIRELESS && ds->netifs[i].active) + wireless_active = true; + } + + if (wireless_active && ds->cached_net_cfg.ipAddress != 0) + format_ip(value, ds->cached_net_cfg.ipAddress); + else + montauk::strcpy(value, "No address"); + draw_kv_row(fb, lx, vx, ty, "IP", value, dim, colors::TEXT_COLOR); + ty += fh + 6; + + snprintf(value, sizeof(value), "Channel %d", (int)ds->wifi_info.channel); + draw_kv_row(fb, lx, vx, ty, "Radio", value, dim, colors::TEXT_COLOR); + ty += fh + 6 + 8; + } + + // Network list + int list_y = wifi_list_y(ds); + int mx = ds->mouse.x, my = ds->mouse.y; + + if (ds->wifi_network_count == 0) { + const char* empty = ds->wifi_scanning ? "Looking for networks..." + : "No networks found"; + draw_text(fb, lx, list_y + (WIFI_ROW_H - fh) / 2, empty, dim); + } + + int rows = wifi_visible_rows(ds); + for (int r = 0; r < rows && (ds->wifi_scroll + r) < ds->wifi_network_count; r++) { + int idx = ds->wifi_scroll + r; + const montauk::abi::WifiNetwork& net = ds->wifi_networks[idx]; + Rect row = {popup.x + 6, list_y + r * WIFI_ROW_H, popup.w - 12, WIFI_ROW_H}; + + bool current = ds->wifi_info.connected + && montauk::streq(net.ssid, ds->wifi_info.ssid); + if (row.contains(mx, my)) + fill_rounded_rect(fb, row.x, row.y, row.w, row.h, 4, + gui::mtk::accent_hover_tint(ds->settings.accent_color)); + else if (current) + fill_rounded_rect(fb, row.x, row.y, row.w, row.h, 4, + Color::from_rgb(0xEC, 0xF2, 0xFB)); + + draw_signal_bars(fb, row.x + 8, row.y + (WIFI_ROW_H - 12) / 2, net.rssi, + current ? ds->settings.accent_color : Color::from_rgb(0x55, 0x55, 0x55), + faint); + + int text_x = row.x + 32; + int text_y = row.y + (WIFI_ROW_H - fh) / 2; + int text_max = row.w - 32 - 26; + + char label[40]; + montauk::strncpy(label, net.ssid[0] ? net.ssid : "(hidden)", sizeof(label)); + while (label[0] && text_width(label) > text_max) { + int n = montauk::slen(label); + label[n - 1] = '\0'; + } + draw_text(fb, text_x, text_y, label, colors::TEXT_COLOR); + + if (montauk::wifi::needs_key(net.security)) + draw_lock(fb, row.x + row.w - 20, row.y + (WIFI_ROW_H - 12) / 2, dim); + } + + // A slim scrollbar rather than a line of text: the list scrolls with the + // wheel, and there is no room under it for a hint. + if (ds->wifi_network_count > WIFI_VISIBLE) { + int track_x = popup.x + popup.w - 8; + int track_y = list_y + 2; + int track_h = rows * WIFI_ROW_H - 4; + fb.fill_rect(track_x, track_y, 3, track_h, faint); + + int thumb_h = track_h * WIFI_VISIBLE / ds->wifi_network_count; + if (thumb_h < 12) thumb_h = 12; + int span = ds->wifi_network_count - WIFI_VISIBLE; + int thumb_y = track_y + (span > 0 ? (track_h - thumb_h) * ds->wifi_scroll / span : 0); + fb.fill_rect(track_x, thumb_y, 3, thumb_h, Color::from_rgb(0x99, 0x99, 0x99)); + } + + // Footer: scan / disconnect and the last status line + Rect scan = wifi_scan_button(ds); + Rect action = wifi_action_button(ds); + + auto draw_btn = [&](const Rect& r, const char* label, bool primary) { + Color bg = primary ? ds->settings.accent_color : Color::from_rgb(0xE0, 0xE0, 0xE0); + if (r.contains(mx, my)) + bg = primary ? gui::mtk::darken(ds->settings.accent_color, 32) + : Color::from_rgb(0xD0, 0xD0, 0xD0); + fill_rounded_rect(fb, r.x, r.y, r.w, r.h, 6, bg); + int tw = text_width(label); + draw_text(fb, r.x + (r.w - tw) / 2, r.y + (r.h - fh) / 2, label, + primary ? colors::WHITE : colors::TEXT_COLOR); + }; + + draw_btn(scan, ds->wifi_scanning ? "Scanning" : "Scan", false); + draw_btn(action, ds->wifi_info.connected ? "Disconnect" : "Rescan", + ds->wifi_info.connected); + + // A result worth reading now, not one left over from ten minutes ago. + bool status_fresh = ds->wifi_status[0] + && montauk::get_milliseconds() - ds->wifi_status_time < 20000; + const char* line = status_fresh ? ds->wifi_status : ""; + if (ds->wifi_joining && ds->wifi_info.connState != montauk::abi::WIFI_CONN_IDLE) + line = montauk::wifi::conn_state_name(ds->wifi_info.connState); + if (line && line[0]) { + char fitted[64]; + montauk::strncpy(fitted, line, sizeof(fitted)); + while (fitted[0] && text_width(fitted) > popup.w - 28) { + int n = montauk::slen(fitted); + fitted[n - 1] = '\0'; + } + draw_text(fb, lx, scan.y + scan.h + 8, fitted, dim); + } +} + +// ============================================================================ +// Input +// ============================================================================ + +// Returns true when the click was consumed. +bool desktop_wifi_handle_mouse(DesktopState* ds, int mx, int my, + bool left_pressed, int scroll) { + if (!ds->wifi_popup_open) return false; + + Rect popup = wifi_popup_rect(ds); + + if (scroll != 0 && popup.contains(mx, my)) { + int max_scroll = ds->wifi_network_count - WIFI_VISIBLE; + if (max_scroll < 0) max_scroll = 0; + ds->wifi_scroll -= scroll; + if (ds->wifi_scroll < 0) ds->wifi_scroll = 0; + if (ds->wifi_scroll > max_scroll) ds->wifi_scroll = max_scroll; + return true; + } + + if (!left_pressed) return false; + + if (!popup.contains(mx, my)) { + if (!ds->wifi_icon_rect.contains(mx, my)) ds->wifi_popup_open = false; + return false; + } + + if (wifi_scan_button(ds).contains(mx, my)) { + if (!ds->wifi_scanning) wifi_start_scan(ds); + return true; + } + + if (wifi_action_button(ds).contains(mx, my)) { + if (ds->wifi_info.connected) { + montauk::wifi_disconnect(); + ds->wifi_joining = false; + ds->wifi_dhcp_pending = false; + ds->wifi_dhcp_waiting = false; + wifi_set_status(ds, "Disconnected"); + } else if (!ds->wifi_scanning) { + wifi_start_scan(ds); + } + return true; + } + + int list_y = wifi_list_y(ds); + if (my >= list_y && my < list_y + wifi_visible_rows(ds) * WIFI_ROW_H) { + int row = (my - list_y) / WIFI_ROW_H; + wifi_select_network(ds, ds->wifi_scroll + row); + return true; + } + + return true; // a click inside the popup never falls through +} + +// ============================================================================ +// Passphrase dialog +// +// A real desktop window, created and driven exactly like the reboot and +// shutdown dialogs in dialogs.cpp: desktop_create_window() plus the four +// callbacks. It gets a title bar, dragging, a close button and a place in the +// window list for free, and the compositor draws it in the window layer rather +// than in the panel overlay. +// ============================================================================ + +static constexpr int PWD_W = 400; +static constexpr int PWD_PAD = 20; +static constexpr int PWD_FIELD_H = 32; +static constexpr int PWD_BTN_W = 96; +static constexpr int PWD_BTN_H = 32; + +// The vertical rhythm, in one place, so the window height computed at open +// time and the rects drawn into it cannot drift apart. +static constexpr int PWD_TOP = 18; // content top to the heading +static constexpr int PWD_HEAD_GAP = 6; // heading to the network line +static constexpr int PWD_FIELD_GAP = 22; // network line to the field label +static constexpr int PWD_CHECK_GAP = 20; // field to the first checkbox +static constexpr int PWD_ROW_GAP = 6; // between the checkboxes +static constexpr int PWD_BTN_GAP = 30; // last checkbox to the buttons + +struct WifiPasswordDialog { + DesktopState* ds; + char ssid[DesktopState::WIFI_SSID_CAP]; + char password[DesktopState::WIFI_PSK_CAP]; + uint8_t security; + bool reveal; + bool remember; + mtk::TextInputState input; +}; + +static mtk::Theme wifi_dialog_theme(const DesktopState* ds) { + return mtk::make_theme(ds->settings.accent_color); +} + +// Content-relative layout. Everything above the buttons is measured down from +// the top and everything below them up from the bottom, so the two meet in the +// middle wherever the window is sized. +static int pwd_row_h() { + return system_font_height() + 10; // a checkbox row +} + +static int pwd_label_y() { + int fh = system_font_height(); + return PWD_TOP + fh + PWD_HEAD_GAP + fh + PWD_FIELD_GAP; +} + +static Rect pwd_field_rect(int cw, const mtk::Theme& theme) { + return mtk::labeled_text_input_rect(PWD_PAD, pwd_label_y(), cw - PWD_PAD * 2, + theme, PWD_FIELD_H); +} + +static Rect pwd_show_rect(int cw, const mtk::Theme& theme) { + Rect field = pwd_field_rect(cw, theme); + return {PWD_PAD, field.y + field.h + PWD_CHECK_GAP, 190, pwd_row_h()}; +} + +static Rect pwd_remember_rect(int cw, const mtk::Theme& theme) { + Rect show = pwd_show_rect(cw, theme); + return {show.x, show.y + show.h + PWD_ROW_GAP, 240, show.h}; +} + +// The height the content needs for all of that plus the button row. +static int pwd_content_h(const mtk::Theme& theme) { + Rect remember = pwd_remember_rect(PWD_W, theme); + return remember.y + remember.h + PWD_BTN_GAP + PWD_BTN_H + PWD_PAD; +} + +static Rect pwd_join_rect(const Canvas& c) { + return {c.w - PWD_PAD - PWD_BTN_W, c.h - PWD_PAD - PWD_BTN_H, + PWD_BTN_W, PWD_BTN_H}; +} + +static Rect pwd_cancel_rect(const Canvas& c) { + Rect join = pwd_join_rect(c); + return {join.x - 12 - PWD_BTN_W, join.y, PWD_BTN_W, PWD_BTN_H}; +} + +static void wifi_dialog_close(WifiPasswordDialog* pd) { + if (!pd) return; + for (int i = 0; i < pd->ds->window_count; i++) { + if (pd->ds->windows[i].app_data == pd) { + desktop_close_window(pd->ds, i); + return; + } + } +} + +static void wifi_dialog_submit(WifiPasswordDialog* pd) { + if (!pd->password[0]) return; + + // Join either way; a passphrase that cannot be written down still works + // for this session. + bool save_failed = false; + if (pd->remember) { + montauk::wifi::saved_set(&g_saved, pd->ssid, pd->password); + save_failed = montauk::wifi::saved_store(&g_saved) != 0; + } + + wifi_begin_join(pd->ds, pd->ssid, pd->password); + if (save_failed) + wifi_set_status(pd->ds, "Connected, but the password could not be saved"); + wifi_dialog_close(pd); +} + +static void wifi_dialog_on_draw(Window* win, Framebuffer& fb) { + (void)fb; + auto* pd = (WifiPasswordDialog*)win->app_data; + if (!pd) return; + + Canvas c(win); + mtk::Theme theme = wifi_dialog_theme(pd->ds); + c.fill(theme.window_bg); + + int fh = system_font_height(); + int mx = pd->ds->mouse.x - win->content_rect().x; + int my = pd->ds->mouse.y - win->content_rect().y; + + char line[96]; + snprintf(line, sizeof(line), "Enter the password for %s", pd->ssid); + while (line[0] && text_width(line) > c.w - PWD_PAD * 2) { + int n = montauk::slen(line); + line[n - 1] = '\0'; + } + c.text(PWD_PAD, PWD_TOP, line, theme.text); + + char sub[64]; + snprintf(sub, sizeof(sub), "%s network", + montauk::wifi::security_name(pd->security)); + c.text(PWD_PAD, PWD_TOP + fh + PWD_HEAD_GAP, sub, theme.text_subtle); + + Rect field = pwd_field_rect(c.w, theme); + mtk::draw_labeled_text_field(c, PWD_PAD, field.y - fh - theme.gap_xs, + c.w - PWD_PAD * 2, "Password", pd->password, + pd->input.cursor, true, !pd->reveal, theme, + PWD_FIELD_H, pd->input.selection_anchor); + + Rect show = pwd_show_rect(c.w, theme); + mtk::draw_checkbox(c, show, "Show password", + mtk::check_state(pd->reveal), theme, true, + show.contains(mx, my)); + + Rect remember = pwd_remember_rect(c.w, theme); + mtk::draw_checkbox(c, remember, "Remember this network", + mtk::check_state(pd->remember), theme, true, + remember.contains(mx, my)); + + Rect cancel = pwd_cancel_rect(c); + Rect join = pwd_join_rect(c); + mtk::draw_button(c, cancel, "Cancel", mtk::BUTTON_SECONDARY, + mtk::widget_state(false, cancel.contains(mx, my), true), theme); + mtk::draw_button(c, join, "Join", mtk::BUTTON_PRIMARY, + mtk::widget_state(false, join.contains(mx, my), + pd->password[0] != '\0'), theme); +} + +static void wifi_dialog_on_mouse(Window* win, MouseEvent& ev) { + auto* pd = (WifiPasswordDialog*)win->app_data; + if (!pd) return; + + Canvas c(win); + mtk::Theme theme = wifi_dialog_theme(pd->ds); + Rect cr = win->content_rect(); + int mx = ev.x - cr.x; + int my = ev.y - cr.y; + + // The field owns the pointer while a drag or its context menu is running. + Rect field = pwd_field_rect(c.w, theme); + if (pd->input.context.open || pd->input.dragging || field.contains(mx, my)) { + mtk::text_input_handle_mouse(pd->input, field, pd->password, + (int)sizeof(pd->password), mx, my, + ev.buttons, ev.prev_buttons, c.w, c.h, + true, !pd->reveal, nullptr); + return; + } + + if (!ev.left_pressed()) return; + + if (pwd_show_rect(c.w, theme).contains(mx, my)) { + pd->reveal = !pd->reveal; + return; + } + if (pwd_remember_rect(c.w, theme).contains(mx, my)) { + pd->remember = !pd->remember; + return; + } + if (pwd_cancel_rect(c).contains(mx, my)) { + wifi_dialog_close(pd); + return; + } + if (pwd_join_rect(c).contains(mx, my)) { + wifi_dialog_submit(pd); + return; + } +} + +static void wifi_dialog_on_key(Window* win, const montauk::abi::KeyEvent& key) { + auto* pd = (WifiPasswordDialog*)win->app_data; + if (!pd || !key.pressed) return; + + if (key.scancode == 0x01) { // Escape + wifi_dialog_close(pd); + return; + } + if (key.ascii == '\n' || key.ascii == '\r') { + wifi_dialog_submit(pd); + return; + } + mtk::text_input_key(pd->input, pd->password, (int)sizeof(pd->password), + key, nullptr); +} + +static void wifi_dialog_on_close(Window* win) { + if (!win->app_data) return; + // Do not leave the passphrase behind in freed memory. + auto* pd = (WifiPasswordDialog*)win->app_data; + montauk::memset(pd, 0, sizeof(*pd)); + montauk::mfree(win->app_data); + win->app_data = nullptr; +} + +static void wifi_open_password_dialog(DesktopState* ds, + const montauk::abi::WifiNetwork& net) { + // One dialog at a time: asking twice for the same key helps nobody. + for (int i = 0; i < ds->window_count; i++) { + if (ds->windows[i].on_close == wifi_dialog_on_close + && ds->windows[i].state != WIN_CLOSED) { + desktop_raise_window(ds, i); + return; + } + } + + char title[MAX_TITLE_LEN]; + snprintf(title, sizeof(title), "Join %s", net.ssid); + + // Height comes from the layout above rather than a constant: at the shipped + // UI size a fixed 250px window put the checkboxes under the buttons. + int pwd_h = pwd_content_h(wifi_dialog_theme(ds)) + + TITLEBAR_HEIGHT + BORDER_WIDTH; + + int wx = (ds->screen_w - PWD_W) / 2; + int wy = (ds->screen_h - pwd_h) / 2; + if (wy < PANEL_HEIGHT + 8) wy = PANEL_HEIGHT + 8; + int idx = desktop_create_window(ds, title, wx, wy, PWD_W, pwd_h); + if (idx < 0) return; + + auto* pd = (WifiPasswordDialog*)montauk::malloc(sizeof(WifiPasswordDialog)); + if (!pd) { + desktop_close_window(ds, idx); + return; + } + montauk::memset(pd, 0, sizeof(*pd)); + pd->ds = ds; + montauk::strncpy(pd->ssid, net.ssid, sizeof(pd->ssid)); + pd->security = net.security; + pd->remember = true; + mtk::text_input_reset(pd->input, 0); + + Window* win = &ds->windows[idx]; + win->app_data = pd; + win->on_draw = wifi_dialog_on_draw; + win->on_mouse = wifi_dialog_on_mouse; + win->on_key = wifi_dialog_on_key; + win->on_close = wifi_dialog_on_close; + + // The popup has done its job; the dialog is where the interaction is now. + ds->wifi_popup_open = false; +} diff --git a/programs/src/network/main.cpp b/programs/src/network/main.cpp index 591b9d7..868ab27 100644 --- a/programs/src/network/main.cpp +++ b/programs/src/network/main.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -17,8 +18,8 @@ extern "C" { using namespace gui; -static constexpr int WIN_W = 560; -static constexpr int WIN_H = 420; +static constexpr int WIN_W = 620; +static constexpr int WIN_H = 480; static constexpr int TAB_H = 36; static constexpr int FOOTER_H = 44; static constexpr int PAD = 16; @@ -31,8 +32,9 @@ static constexpr int FIELD_H = 32; enum Tab { TAB_STATUS = 0, - TAB_CONFIG = 1, - TAB_COUNT = 2, + TAB_WIFI = 1, + TAB_CONFIG = 2, + TAB_COUNT = 3, }; struct Field { @@ -43,6 +45,7 @@ struct Field { static const char* const kTabLabels[TAB_COUNT] = { "Status", + "Wi-Fi", "Configure", }; @@ -59,6 +62,26 @@ static Field g_fields[FIELD_COUNT] = { {"Gateway", {}, {}}, {"DNS Server", {}, {}}, }; + +// ---- Wi-Fi tab state ------------------------------------------------------- + +static constexpr int WIFI_MAX_NETWORKS = 32; +static constexpr int WIFI_ROW_H = 34; + +static montauk::abi::WifiInfo g_wifi = {}; +static montauk::abi::WifiNetwork g_wifi_nets[WIFI_MAX_NETWORKS]; +static int g_wifi_count = 0; +static int g_wifi_selected = -1; +static int g_wifi_scroll = 0; // first visible row +static uint32_t g_wifi_generation = 0; +static bool g_wifi_scanning = false; +static bool g_wifi_joining = false; +static char g_wifi_psk[montauk::wifi::PSK_CAP] = {}; +static mtk::TextInputState g_wifi_psk_input = {}; +static bool g_wifi_psk_focus = false; +static bool g_wifi_remember = true; +static montauk::wifi::SavedList g_wifi_saved = {}; + static bool g_dirty = false; static char g_status[128] = {}; static uint64_t g_status_time = 0; @@ -246,12 +269,15 @@ static void copy_cfg_to_fields() { g_dirty = false; } +static void wifi_refresh(); + static void refresh_state(bool update_fields) { montauk::get_netcfg(&g_cfg); if (montauk::net_status(&g_net) < 0) { montauk::memset(&g_net, 0, sizeof(g_net)); snprintf(g_net.driver, sizeof(g_net.driver), "Unavailable"); } + wifi_refresh(); if (update_fields) copy_cfg_to_fields(); g_last_refresh = montauk::get_milliseconds(); } @@ -387,6 +413,434 @@ static void draw_status_tab(Canvas& c, const mtk::Theme& theme) { draw_kv(c, &y, "TX Packets", tx, theme); } +// ============================================================================ +// Wi-Fi tab +// +// Laid out the way the Display app lays out its mode list: borderless rows on +// the window background, a radio for the selection, and state as right-aligned +// text rather than a pill. The block under the list keeps a fixed height so the +// row count does not change as the selection moves between open and encrypted +// networks. +// ============================================================================ + +static int wifi_status_y() { + return TAB_H + 20; +} + +static int wifi_separator_y() { + return wifi_status_y() + system_font_height() * 2 + 26; +} + +static int wifi_list_header_y() { + return wifi_separator_y() + 16; +} + +static int wifi_list_y() { + return wifi_list_header_y() + system_font_height() + 10; +} + +// The passphrase field, the checkbox under it, and the padding around them. +static int wifi_action_h() { + return mtk::labeled_text_height(app_theme(), FIELD_H) + 10 + + system_font_height() + 6; +} + +static Rect wifi_action_rect() { + int h = wifi_action_h(); + return {PAD, footer_rect().y - 14 - h, g_win.width - PAD * 2, h}; +} + +static Rect wifi_psk_input_rect() { + Rect action = wifi_action_rect(); + return mtk::labeled_text_input_rect(action.x, action.y, action.w, + app_theme(), FIELD_H); +} + +static Rect wifi_remember_rect() { + Rect action = wifi_action_rect(); + int block = mtk::labeled_text_height(app_theme(), FIELD_H); + return {action.x, action.y + block + 10, 240, system_font_height() + 6}; +} + +static int wifi_visible_rows() { + int available = wifi_action_rect().y - 14 - wifi_list_y(); + int rows = available / WIFI_ROW_H; + if (rows < 1) rows = 1; + if (rows > WIFI_MAX_NETWORKS) rows = WIFI_MAX_NETWORKS; + return rows; +} + +static Rect wifi_row_rect(int visibleIndex) { + return {PAD, wifi_list_y() + visibleIndex * WIFI_ROW_H, + g_win.width - PAD * 2, WIFI_ROW_H}; +} + +static void wifi_meta_text(char* out, size_t cap, + const montauk::abi::WifiNetwork& net) { + snprintf(out, cap, "%s - ch %d - %d dBm", + montauk::wifi::security_name(net.security), + (int)net.channel, (int)net.rssi); +} + +static void wifi_clamp_scroll() { + int max_scroll = g_wifi_count - wifi_visible_rows(); + if (max_scroll < 0) max_scroll = 0; + if (g_wifi_scroll > max_scroll) g_wifi_scroll = max_scroll; + if (g_wifi_scroll < 0) g_wifi_scroll = 0; +} + +static Rect wifi_forget_button() { + Rect refresh = status_refresh_button(); + return {refresh.x - (GAP + BUTTON_W) * 2, refresh.y, BUTTON_W, BUTTON_H}; +} + +static Rect wifi_scan_button() { + Rect refresh = status_refresh_button(); + return {refresh.x - GAP - BUTTON_W, refresh.y, BUTTON_W, BUTTON_H}; +} + +static Rect wifi_connect_button() { + return status_refresh_button(); +} + +static bool wifi_selection_valid() { + return g_wifi_selected >= 0 && g_wifi_selected < g_wifi_count; +} + +static bool wifi_selection_is_current() { + return wifi_selection_valid() && g_wifi.connected + && montauk::streq(g_wifi_nets[g_wifi_selected].ssid, g_wifi.ssid); +} + +// True when joining the selection would need a passphrase typed in: encrypted, +// and nothing remembered for it. +static bool wifi_selection_needs_key() { + if (!wifi_selection_valid()) return false; + const montauk::abi::WifiNetwork& net = g_wifi_nets[g_wifi_selected]; + return montauk::wifi::needs_key(net.security); +} + +static void wifi_sort() { + for (int i = 1; i < g_wifi_count; i++) { + montauk::abi::WifiNetwork key = g_wifi_nets[i]; + int j = i - 1; + while (j >= 0 && g_wifi_nets[j].rssi < key.rssi) { + g_wifi_nets[j + 1] = g_wifi_nets[j]; + j--; + } + g_wifi_nets[j + 1] = key; + } +} + +static void wifi_load_results() { + char previous[montauk::wifi::SSID_CAP] = {}; + if (wifi_selection_valid()) + montauk::strncpy(previous, g_wifi_nets[g_wifi_selected].ssid, sizeof(previous)); + + montauk::abi::WifiNetwork raw[WIFI_MAX_NETWORKS]; + int n = montauk::wifi_results(raw, WIFI_MAX_NETWORKS); + if (n < 0) n = 0; + + // One row per network, not one per access point: a house with a repeater + // answers on several BSSIDs and the strongest is the one worth joining. + // Hidden networks have no SSID to join by, so they are left out entirely. + g_wifi_count = 0; + for (int i = 0; i < n; i++) { + if (!raw[i].ssid[0]) continue; + + int existing = -1; + for (int k = 0; k < g_wifi_count; k++) { + if (montauk::streq(g_wifi_nets[k].ssid, raw[i].ssid)) { existing = k; break; } + } + if (existing >= 0) { + if (raw[i].rssi > g_wifi_nets[existing].rssi) + g_wifi_nets[existing] = raw[i]; + continue; + } + g_wifi_nets[g_wifi_count++] = raw[i]; + } + wifi_sort(); + + // Keep the selection on the network it was on, wherever it moved to. + g_wifi_selected = -1; + if (previous[0]) { + for (int i = 0; i < g_wifi_count; i++) { + if (montauk::streq(g_wifi_nets[i].ssid, previous)) { g_wifi_selected = i; break; } + } + } + wifi_clamp_scroll(); +} + +// Keeps the adapter snapshot, the scan table and the saved list in step. +static void wifi_refresh() { + if (montauk::wifi_info(&g_wifi) != 0) { + montauk::memset(&g_wifi, 0, sizeof(g_wifi)); + return; + } + + if (g_wifi.scanGeneration != g_wifi_generation) { + g_wifi_generation = g_wifi.scanGeneration; + g_wifi_scanning = false; + wifi_load_results(); + char msg[64]; + snprintf(msg, sizeof(msg), "Found %d network%s", g_wifi_count, + g_wifi_count == 1 ? "" : "s"); + set_status(msg); + } else if (g_wifi_scanning && !g_wifi.scanning) { + g_wifi_scanning = false; + } + + if (g_wifi_joining) { + if (g_wifi.connected) { + g_wifi_joining = false; + char msg[96]; + snprintf(msg, sizeof(msg), "Connected to %s", g_wifi.ssid); + set_status(msg); + } else if (g_wifi.lastError != 0 && !g_wifi.joining) { + g_wifi_joining = false; + set_status(montauk::wifi::error_message(g_wifi.lastError)); + } + } +} + +static void wifi_start_scan() { + int rc = montauk::wifi_scan_start(6000); + if (rc < 0) { + set_status("The Wi-Fi adapter is not ready"); + return; + } + g_wifi_scanning = true; + set_status("Scanning for networks..."); +} + +static void wifi_join_selected() { + if (!wifi_selection_valid()) { + set_status("Select a network first"); + return; + } + + const montauk::abi::WifiNetwork& net = g_wifi_nets[g_wifi_selected]; + const char* psk = ""; + if (montauk::wifi::needs_key(net.security)) { + if (g_wifi_psk[0]) { + psk = g_wifi_psk; + } else { + const montauk::wifi::SavedNetwork* saved = + montauk::wifi::saved_find(&g_wifi_saved, net.ssid); + if (saved && saved->psk[0]) { + psk = saved->psk; + } else { + g_wifi_psk_focus = true; + set_status("This network needs a password"); + return; + } + } + } + + bool save_failed = false; + if (montauk::wifi::needs_key(net.security) && g_wifi_remember && psk[0]) { + montauk::wifi::saved_set(&g_wifi_saved, net.ssid, psk); + save_failed = montauk::wifi::saved_store(&g_wifi_saved) != 0; + } + + int rc = montauk::wifi_connect_async(net.ssid, psk); + if (rc < 0) { + set_status(montauk::wifi::error_message(rc)); + return; + } + + g_wifi_joining = true; + char msg[96]; + if (save_failed) { + snprintf(msg, sizeof(msg), + "Connecting to %s, but the password could not be saved", net.ssid); + } else { + snprintf(msg, sizeof(msg), "Connecting to %s...", net.ssid); + } + set_status(msg); +} + +static void wifi_forget_selected() { + if (!wifi_selection_valid()) return; + const char* ssid = g_wifi_nets[g_wifi_selected].ssid; + if (!montauk::wifi::saved_remove(&g_wifi_saved, ssid)) { + set_status("That network was not saved"); + return; + } + bool save_failed = montauk::wifi::saved_store(&g_wifi_saved) != 0; + g_wifi_psk[0] = '\0'; + mtk::text_input_reset(g_wifi_psk_input, 0); + + char msg[96]; + if (save_failed) { + snprintf(msg, sizeof(msg), "Could not update the saved network list"); + } else { + snprintf(msg, sizeof(msg), "Forgot %s", ssid); + } + set_status(msg); +} + +static void draw_signal(Canvas& c, int x, int y, int8_t rssi, Color on, Color off) { + int bars = montauk::wifi::signal_bars(rssi); + for (int i = 0; i < 4; i++) { + int h = 3 + i * 3; + c.fill_rect(x + i * 4, y + 12 - h, 3, h, i < bars ? on : off); + } +} + +static void draw_wifi_tab(Canvas& c, const mtk::Theme& theme) { + int fh = system_font_height(); + int y = wifi_status_y(); + + bool present = g_wifi.present != 0; + bool ready = present && g_wifi.state == montauk::abi::WIFI_STATE_RUNNING; + + Color dot = !present ? theme.danger + : (g_wifi.connected ? Color::from_rgb(0x27, 0xA0, 0x58) + : Color::from_rgb(0xD0, 0x9A, 0x2E)); + fill_circle(c, PAD + 6, y + fh / 2, 6, dot); + + char headline[128]; + if (!present) { + snprintf(headline, sizeof(headline), "No Wi-Fi adapter"); + } else if (g_wifi.connected) { + snprintf(headline, sizeof(headline), "Connected to %s", g_wifi.ssid); + } else if (g_wifi_joining) { + snprintf(headline, sizeof(headline), "%s", + montauk::wifi::conn_state_name(g_wifi.connState)); + } else { + snprintf(headline, sizeof(headline), "%s", + montauk::wifi::state_name(g_wifi.state)); + } + draw_text_fit(c, PAD + 22, y, headline, g_win.width - PAD * 2 - 22, + present ? theme.text : theme.danger); + + char sub[160]; + if (!present) { + snprintf(sub, sizeof(sub), + "Nothing to configure until a supported adapter is fitted."); + } else if (g_wifi.connected) { + snprintf(sub, sizeof(sub), "Channel %d - firmware %s", + (int)g_wifi.channel, + g_wifi.fwVersion[0] ? g_wifi.fwVersion : "unknown"); + } else { + snprintf(sub, sizeof(sub), "Firmware %s", + g_wifi.fwVersion[0] ? g_wifi.fwVersion : "not loaded"); + } + draw_text_fit(c, PAD + 22, y + fh + 3, sub, g_win.width - PAD * 2 - 22, + theme.text_subtle); + + mtk::draw_separator(c, PAD, wifi_separator_y(), g_win.width - PAD * 2, theme); + + // List header, with the page indicator where the Display app puts its hint. + int header_y = wifi_list_header_y(); + c.text(PAD, header_y, "NETWORKS IN RANGE", theme.text_muted); + + int rows = wifi_visible_rows(); + char hint[48]; + hint[0] = '\0'; + if (g_wifi_scanning) { + snprintf(hint, sizeof(hint), "Scanning..."); + } else if (g_wifi_count > rows) { + int last = g_wifi_scroll + rows; + if (last > g_wifi_count) last = g_wifi_count; + snprintf(hint, sizeof(hint), "%d-%d of %d (scroll)", + g_wifi_scroll + 1, last, g_wifi_count); + } + if (hint[0]) + c.text(g_win.width - PAD - text_width(hint), header_y, hint, theme.text_muted); + + if (g_wifi_count == 0) { + const char* empty = !ready ? "The adapter is not ready yet." + : (g_wifi_scanning + ? "Looking for networks..." + : "No networks found. Press Scan to look again."); + c.text(PAD + 4, wifi_list_y() + 9, empty, theme.text_subtle); + } + + // Fixed right-hand column for the row state, so the signal/channel detail + // beside it can never be drawn over the word "Connected". + static constexpr int STATE_W = 84; + + // The detail column starts at one x for every row, sized from the widest + // line in the whole list: right-aligning each row instead would make short + // strings ("ch 1") look indented, and sizing it from the visible rows only + // would make the column jump about while scrolling. + int meta_w = 0; + for (int index = 0; index < g_wifi_count; index++) { + char meta[48]; + wifi_meta_text(meta, sizeof(meta), g_wifi_nets[index]); + int w = text_width(meta); + if (w > meta_w) meta_w = w; + } + Rect list_row = wifi_row_rect(0); + int meta_x = list_row.x + list_row.w - STATE_W - 12 - meta_w; + + for (int row = 0; row < rows; row++) { + int index = g_wifi_scroll + row; + if (index >= g_wifi_count) break; + + const montauk::abi::WifiNetwork& net = g_wifi_nets[index]; + Rect option = wifi_row_rect(row); + bool selected = index == g_wifi_selected; + bool current = g_wifi.connected && montauk::streq(net.ssid, g_wifi.ssid); + + if (selected) + c.fill_rounded_rect(option.x, option.y + 2, option.w, option.h - 4, + theme.radius_md, theme.accent_soft); + else if (option.contains(g_mouse_x, g_mouse_y)) + c.fill_rounded_rect(option.x, option.y + 2, option.w, option.h - 4, + theme.radius_md, theme.surface_hover); + + Rect radio = {option.x + 4, option.y, 18, option.h}; + mtk::draw_radio(c, radio, "", selected, theme); + + int text_y = option.y + (option.h - fh) / 2; + draw_signal(c, option.x + 34, option.y + (option.h - 12) / 2, net.rssi, + current ? theme.accent : theme.text_muted, theme.border); + + const char* state = current ? "Connected" + : (montauk::wifi::saved_find(&g_wifi_saved, net.ssid) + ? "Saved" : nullptr); + if (state) + c.text(option.x + option.w - STATE_W, text_y, state, + current ? theme.accent : theme.text_muted); + + char meta[48]; + wifi_meta_text(meta, sizeof(meta), net); + c.text(meta_x, text_y, meta, theme.text_subtle); + + int ssid_x = option.x + 60; + draw_text_fit(c, ssid_x, text_y, net.ssid, meta_x - 12 - ssid_x, theme.text); + } + + // The block below the list keeps its height whatever is in it: a passphrase + // field when one is wanted, and a line explaining why not when it is not. + Rect action = wifi_action_rect(); + if (wifi_selection_needs_key()) { + const montauk::wifi::SavedNetwork* saved = + montauk::wifi::saved_find(&g_wifi_saved, + g_wifi_nets[g_wifi_selected].ssid); + mtk::draw_labeled_text_field(c, action.x, action.y, action.w, + saved ? "Password (saved)" : "Password", + g_wifi_psk, g_wifi_psk_input.cursor, + g_wifi_psk_focus, true, theme, FIELD_H, + g_wifi_psk_input.selection_anchor); + + Rect remember = wifi_remember_rect(); + mtk::draw_checkbox(c, remember, "Remember this network", + mtk::check_state(g_wifi_remember), theme, true, + remember.contains(g_mouse_x, g_mouse_y)); + } else { + const char* line; + if (!wifi_selection_valid()) + line = ""; + else + line = "Open network - no password needed."; + if (line[0]) c.text(action.x, action.y, line, theme.text_subtle); + } +} + static void draw_config_tab(Canvas& c, const mtk::Theme& theme) { int y = config_header_y(); c.text(PAD, y, "IPv4 Configuration", theme.text); @@ -418,10 +872,33 @@ static void draw_footer(Canvas& c, const mtk::Theme& theme) { const char* msg = status_visible() ? g_status : (g_dirty ? "Unsaved static IPv4 changes" : "Network settings ready"); - int text_right = (g_tab == TAB_STATUS ? status_clear_button().x : config_dhcp_button().x) - GAP; + int text_right = PAD; + if (g_tab == TAB_STATUS) text_right = status_clear_button().x - GAP; + else if (g_tab == TAB_WIFI) text_right = wifi_forget_button().x - GAP; + else text_right = config_dhcp_button().x - GAP; draw_text_fit(c, PAD, foot.y + (FOOTER_H - system_font_height()) / 2, msg, text_right - PAD, g_dirty ? theme.text : theme.text_subtle); + if (g_tab == TAB_WIFI) { + Rect forget = wifi_forget_button(); + Rect scan = wifi_scan_button(); + Rect connect = wifi_connect_button(); + bool saved = wifi_selection_valid() + && montauk::wifi::saved_find(&g_wifi_saved, + g_wifi_nets[g_wifi_selected].ssid) != nullptr; + bool connected = wifi_selection_is_current() || (g_wifi.connected && !wifi_selection_valid()); + + mtk::draw_button(c, forget, "Forget", mtk::BUTTON_SECONDARY, + button_state(forget, saved), theme); + mtk::draw_button(c, scan, g_wifi_scanning ? "Scanning" : "Scan", + mtk::BUTTON_SECONDARY, + button_state(scan, !g_wifi_scanning && g_wifi.present), theme); + mtk::draw_button(c, connect, connected ? "Disconnect" : "Connect", + mtk::BUTTON_PRIMARY, + button_state(connect, g_wifi.present != 0), theme); + return; + } + if (g_tab == TAB_STATUS) { Rect clear = status_clear_button(); Rect dhcp = status_dhcp_button(); @@ -455,12 +932,20 @@ static void render() { if (g_tab == TAB_STATUS) { draw_status_tab(c, theme); + } else if (g_tab == TAB_WIFI) { + draw_wifi_tab(c, theme); } else { draw_config_tab(c, theme); } draw_footer(c, theme); if (g_tab == TAB_CONFIG) draw_config_context_menus(c, theme); + if (g_tab == TAB_WIFI) { + mtk::draw_text_input_context_menu( + c, g_wifi_psk_input, theme, + mtk::text_input_has_selection(g_wifi_psk_input, g_wifi_psk, + (int)sizeof(g_wifi_psk))); + } host.present(); } @@ -523,6 +1008,19 @@ static void set_tab(Tab tab) { return; } g_tab = tab; + if (tab == TAB_WIFI) { + g_focus_field = -1; + g_wifi_psk_focus = false; + montauk::wifi::saved_load(&g_wifi_saved); + wifi_refresh(); + wifi_load_results(); + // Nothing on screen yet and an adapter that can look: go and find out. + if (g_wifi_count == 0 && !g_wifi_scanning + && g_wifi.state == montauk::abi::WIFI_STATE_RUNNING) { + wifi_start_scan(); + } + return; + } if (tab == TAB_CONFIG) { focus_field(0); } else { @@ -546,6 +1044,80 @@ static bool handle_mouse(int mx, int my, uint8_t buttons, uint8_t prev_buttons) } } + if (g_tab == TAB_WIFI) { + // The passphrase field owns the click while a drag or its context menu + // is in progress, the same rule the IPv4 fields follow. + if (g_wifi_psk_input.context.open || g_wifi_psk_input.dragging) { + int result = mtk::text_input_handle_mouse( + g_wifi_psk_input, wifi_psk_input_rect(), g_wifi_psk, + (int)sizeof(g_wifi_psk), mx, my, buttons, prev_buttons, + g_win.width, g_win.height, g_wifi_psk_focus, true, nullptr); + return result != mtk::TEXT_INPUT_NONE; + } + + if (!left_pressed && !right_pressed) return false; + + if (wifi_selection_needs_key() && wifi_psk_input_rect().contains(mx, my)) { + g_wifi_psk_focus = true; + mtk::text_input_handle_mouse(g_wifi_psk_input, wifi_psk_input_rect(), + g_wifi_psk, (int)sizeof(g_wifi_psk), + mx, my, buttons, prev_buttons, + g_win.width, g_win.height, true, true, nullptr); + return true; + } + + if (!left_pressed) return false; + + int rows = wifi_visible_rows(); + for (int row = 0; row < rows; row++) { + int idx = g_wifi_scroll + row; + if (idx >= g_wifi_count) break; + if (!wifi_row_rect(row).contains(mx, my)) continue; + + if (idx != g_wifi_selected) { + g_wifi_selected = idx; + // A different network means a different key; do not carry the + // previous one over, but do offer the remembered one. + g_wifi_psk[0] = '\0'; + mtk::text_input_reset(g_wifi_psk_input, 0); + const montauk::wifi::SavedNetwork* saved = + montauk::wifi::saved_find(&g_wifi_saved, g_wifi_nets[idx].ssid); + if (saved) { + montauk::strncpy(g_wifi_psk, saved->psk, sizeof(g_wifi_psk)); + mtk::text_input_reset(g_wifi_psk_input, str_len(g_wifi_psk)); + } + } + g_wifi_psk_focus = false; + return true; + } + + if (wifi_selection_needs_key() && wifi_remember_rect().contains(mx, my)) { + g_wifi_remember = !g_wifi_remember; + return true; + } + if (wifi_scan_button().contains(mx, my)) { + if (!g_wifi_scanning) wifi_start_scan(); + return true; + } + if (wifi_forget_button().contains(mx, my)) { + wifi_forget_selected(); + return true; + } + if (wifi_connect_button().contains(mx, my)) { + if (wifi_selection_is_current() || (g_wifi.connected && !wifi_selection_valid())) { + montauk::wifi_disconnect(); + g_wifi_joining = false; + set_status("Disconnected"); + } else { + wifi_join_selected(); + } + return true; + } + + g_wifi_psk_focus = false; + return true; + } + if (g_tab == TAB_STATUS) { if (!left_pressed) return false; if (status_refresh_button().contains(mx, my)) { @@ -631,6 +1203,42 @@ static bool handle_key(const montauk::abi::KeyEvent& key) { return true; } + if (g_tab == TAB_WIFI) { + if (key.ascii == '\t') { + set_tab(TAB_CONFIG); + return true; + } + if (key.ascii == '\n' || key.ascii == '\r') { + wifi_join_selected(); + return true; + } + if (key.scancode == 0x48 || key.scancode == 0x50) { // up / down + int step = key.scancode == 0x48 ? -1 : 1; + int next = g_wifi_selected < 0 ? 0 : g_wifi_selected + step; + if (next < 0) next = 0; + if (next >= g_wifi_count) next = g_wifi_count - 1; + g_wifi_selected = next; + if (g_wifi_selected >= 0) { + if (g_wifi_selected < g_wifi_scroll) + g_wifi_scroll = g_wifi_selected; + if (g_wifi_selected >= g_wifi_scroll + wifi_visible_rows()) + g_wifi_scroll = g_wifi_selected - wifi_visible_rows() + 1; + } + return true; + } + + if (g_wifi_psk_focus && wifi_selection_needs_key()) { + int result = mtk::text_input_key(g_wifi_psk_input, g_wifi_psk, + (int)sizeof(g_wifi_psk), key, nullptr); + return (result & mtk::TEXT_INPUT_CONSUMED) != 0; + } + if (key.ascii == 's' || key.ascii == 'S') { + if (!g_wifi_scanning) wifi_start_scan(); + return true; + } + return false; + } + if (key.ascii == '\t') { if (g_tab != TAB_CONFIG) { set_tab(TAB_CONFIG); @@ -690,7 +1298,9 @@ extern "C" void _start() { montauk::exit(1); } + montauk::wifi::saved_load(&g_wifi_saved); refresh_state(true); + wifi_load_results(); render(); while (g_win.id >= 0 && !g_win.closed) { @@ -701,7 +1311,10 @@ extern "C" void _start() { if (r < 0) break; if (r == 0) { uint64_t now = montauk::get_milliseconds(); - if (now - g_last_refresh >= 3000) { + // The Wi-Fi tab has work in flight worth watching closely; the rest + // of the app is happy with the slower cadence. + bool wifi_busy = g_tab == TAB_WIFI && (g_wifi_scanning || g_wifi_joining); + if (now - g_last_refresh >= (wifi_busy ? 400u : 3000u)) { refresh_state(!g_dirty); redraw = true; } @@ -718,6 +1331,12 @@ extern "C" void _start() { g_mouse_x = ev.mouse.x; g_mouse_y = ev.mouse.y; redraw = true; + if (ev.mouse.scroll != 0 && g_tab == TAB_WIFI + && ev.mouse.y >= wifi_list_y() + && ev.mouse.y < wifi_action_rect().y) { + g_wifi_scroll += ev.mouse.scroll > 0 ? -1 : 1; + wifi_clamp_scroll(); + } if (handle_mouse(ev.mouse.x, ev.mouse.y, ev.mouse.buttons, ev.mouse.prev_buttons)) { redraw = true; diff --git a/programs/src/wifi/main.cpp b/programs/src/wifi/main.cpp index aad1b49..0579977 100644 --- a/programs/src/wifi/main.cpp +++ b/programs/src/wifi/main.cpp @@ -17,6 +17,7 @@ #include #include +#include using namespace montauk; @@ -363,14 +364,30 @@ static int cmd_debug() { return 0; } -static int cmd_connect(const char* ssid, const char* password) { +static int cmd_connect(const char* ssid, const char* password, bool remember) { abi::WifiInfo info; if (!require_adapter(info)) return 1; + // No passphrase on the command line: fall back to the one the desktop or a + // previous run saved, so "wifi connect Home" works on its own. + char saved[montauk::wifi::PSK_CAP]; + if ((!password || !password[0]) + && montauk::wifi::lookup(ssid, saved, sizeof(saved)) && saved[0]) { + print("Using the saved passphrase for \""); print(ssid); print("\".\n"); + password = saved; + remember = false; // already stored + } + print("Connecting to \""); print(ssid); print("\"...\n"); int rc = wifi_connect(ssid, password); if (rc == 0) { + if (remember && password && password[0]) { + if (montauk::wifi::remember(ssid, password)) + print("Saved to 0:/config/wifi.toml for next time.\n"); + else + print("wifi: could not write 0:/config/wifi.toml.\n"); + } print("Connected.\n"); print("Run \"dhcp\" to get an address, then the network is usable\n"); print("just like a wired connection.\n"); @@ -410,6 +427,36 @@ static int cmd_connect(const char* ssid, const char* password) { return 1; } +static int cmd_saved() { + montauk::wifi::SavedList list; + montauk::wifi::saved_load(&list); + + if (list.count == 0) { + print("No saved networks.\n"); + return 0; + } + + print("Saved networks (0:/config/wifi.toml):\n"); + for (int i = 0; i < list.count; i++) { + print(" "); + print(list.items[i].ssid); + print("\n"); + } + print("\nAutomatic reconnect is "); + print(list.autoconnect ? "on" : "off"); + print(".\n"); + return 0; +} + +static int cmd_forget(const char* ssid) { + if (montauk::wifi::forget(ssid)) { + print("Forgot \""); print(ssid); print("\".\n"); + return 0; + } + print("wifi: \""); print(ssid); print("\" was not saved.\n"); + return 1; +} + static void usage() { print("usage: wifi [command]\n\n"); print(" scan [seconds] scan and list nearby networks (default 5)\n"); @@ -417,7 +464,11 @@ static void usage() { print(" debug diagnostics plus per-BSS scan detail\n"); print(" connect [key] join a network (open or WPA2/WPA3-PSK)\n"); print(" status show the network currently joined\n"); - print(" disconnect leave the current network\n\n"); + print(" disconnect leave the current network\n"); + print(" saved list remembered networks\n"); + print(" forget remove a remembered network\n\n"); + print("A passphrase given to connect is remembered, and one that was\n"); + print("remembered is used when connect is given only an SSID.\n\n"); print("With no command, runs a 5 second scan.\n"); } @@ -454,7 +505,16 @@ extern "C" void _start() { while (p[n] && n < (int)sizeof(pass) - 1) { pass[n] = p[n]; n++; } while (n > 0 && pass[n - 1] == ' ') n--; pass[n] = '\0'; - exit(cmd_connect(ssid, pass)); + exit(cmd_connect(ssid, pass, true)); + } else if (streq(cmd, "saved")) { + exit(cmd_saved()); + } else if (streq(cmd, "forget")) { + char ssid[40]; + if (!next_token(&rest, ssid, sizeof(ssid))) { + print("wifi: forget needs an SSID\n"); + exit(1); + } + exit(cmd_forget(ssid)); } else if (streq(cmd, "disconnect")) { wifi_disconnect(); print("Disconnected.\n"); diff --git a/scripts/copy_icons.sh b/scripts/copy_icons.sh index 2876a8a..29e061a 100755 --- a/scripts/copy_icons.sh +++ b/scripts/copy_icons.sh @@ -47,6 +47,7 @@ ICONS=( "mimetypes/symbolic/application-x-executable-symbolic.svg" "devices/symbolic/computer-symbolic.svg" "devices/symbolic/network-wired-symbolic.svg" + "devices/symbolic/network-wireless-symbolic.svg" "apps/symbolic/web-browser-symbolic.svg" # Scalable (colorful) icons for app menu "apps/scalable/utilities-terminal.svg" @@ -60,6 +61,7 @@ ICONS=( "places/scalable/user-home.svg" "devices/scalable/computer.svg" "devices/scalable/network-wired.svg" + "devices/scalable/network-wireless.svg" "devices/scalable/printer.svg" "devices/symbolic/printer-symbolic.svg" "mimetypes/scalable/text-x-generic.svg" diff --git a/template/sysroot/include/Api/Syscall.hpp b/template/sysroot/include/Api/Syscall.hpp index aa7edb2..9fc1f28 100644 --- a/template/sysroot/include/Api/Syscall.hpp +++ b/template/sysroot/include/Api/Syscall.hpp @@ -182,6 +182,13 @@ namespace montauk::abi { // Paginated directory read (path, names, max, startIndex) static constexpr uint64_t SYS_READDIR_AT = 136; + // Set adapter BD_ADDR (6-byte buffer, addr[0] = LSB) + static constexpr uint64_t SYS_BTSETADDR = 137; + + // List bonded (paired) devices / forget a bond + static constexpr uint64_t SYS_BTBONDS = 138; + static constexpr uint64_t SYS_BTFORGET = 139; + /* Sdr.hpp -- software-defined radio receive API */ static constexpr uint64_t SYS_SDR_COUNT = 140; // number of receivers static constexpr uint64_t SYS_SDR_INFO = 141; // (index, SdrDeviceInfo*) @@ -192,13 +199,36 @@ namespace montauk::abi { static constexpr uint64_t SYS_SDR_READ = 146; // (handle, buf, len) -> bytes static constexpr uint64_t SYS_SDR_SETPARAM = 147; // (handle, param, value) static constexpr uint64_t SYS_SDR_GETPARAM = 148; // (handle, param) -> value + + // CPU power/thermal status + static constexpr uint64_t SYS_POWERINFO = 149; // (PowerInfo*) -> 0, -1 unsupported + + // Framebuffer page flip (double-buffered scanout) static constexpr uint64_t SYS_FBFLIP = 150; + + // Absolute path of the running executable (for argv[0]). + static constexpr uint64_t SYS_GETEXECPATH = 151; // (index, flags) -> new front index; index=-1 queries support (1/0); flags bit0 = wait vsync + + // Path metadata (size, timestamps, mode). (const char* path, FileStat* out) -> 0, -1 on error/unsupported. + static constexpr uint64_t SYS_STAT = 152; static constexpr uint64_t SYS_SETUNIXTIME = 153; + + // Display/modesetting control static constexpr uint64_t SYS_DISPLAYINFO = 154; static constexpr uint64_t SYS_DISPLAYMODES = 155; static constexpr uint64_t SYS_DISPLAYSETMODE = 156; static constexpr uint64_t SYS_DISPLAYBRIGHTNESS = 157; + // Wi-Fi adapter control + static constexpr uint64_t SYS_WIFI_SCAN = 158; // (WifiNetwork*, maxCount, timeoutMs) -> count + static constexpr uint64_t SYS_WIFI_INFO = 159; // (WifiInfo*) -> 0, -1 if absent + static constexpr uint64_t SYS_WIFI_CONNECT = 160; // (ssid, password) -> 0, <0 on error + static constexpr uint64_t SYS_WIFI_DISCONNECT = 161; // () -> 0 + static constexpr uint64_t SYS_WIFI_SCAN_START = 162; // (timeoutMs) -> 0 started, 1 busy, -1 no adapter + static constexpr uint64_t SYS_WIFI_RESULTS = 163; // (WifiNetwork*, maxCount) -> count, no radio work + static constexpr uint64_t SYS_WIFI_CONNECT_ASYNC = 164; // (ssid, password) -> 0 accepted, <0 on error + static constexpr uint64_t SYS_NETIFS = 165; // (NetIfInfo*, maxCount) -> count + // Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM). static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz static constexpr int SDR_PARAM_SAMPLE_RATE = 1; // sample rate, Hz @@ -238,7 +268,7 @@ namespace montauk::abi { static constexpr int AUDIO_CTL_PAUSE = 3; static constexpr int AUDIO_CTL_GET_OUTPUT = 4; // 0=HDA, 1=Bluetooth static constexpr int AUDIO_CTL_SET_OUTPUT = 5; // Switch audio output - static constexpr int AUDIO_CTL_BT_STATUS = 6; // Get Bluetooth status + static constexpr int AUDIO_CTL_BT_STATUS = 6; // 0=unavailable, 1=setup, 2=ready static constexpr int AUDIO_CTL_SET_MASTER_VOLUME = 7; // 0-100 static constexpr int AUDIO_CTL_GET_MASTER_VOLUME = 8; static constexpr int AUDIO_CTL_SET_MUTE = 9; // 0/1, per-stream @@ -279,6 +309,17 @@ namespace montauk::abi { uint8_t Second; }; + // Path metadata returned by SYS_STAT. Timestamps are UTC unix seconds; + // a filesystem that does not record a given time reports it as 0. + struct FileStat { + uint64_t size; // file size in bytes + int64_t mtime; // last data modification time + int64_t ctime; // last inode (metadata) change time + int64_t atime; // last access time + uint32_t mode; // ext2/POSIX mode bits (type + permissions) + uint32_t isDir; // 1 if the entry is a directory, else 0 + }; + struct FbInfo { uint64_t width; uint64_t height; @@ -321,7 +362,7 @@ namespace montauk::abi { uint32_t capabilities; uint32_t modeCount; int32_t currentMode; - int32_t brightness; + int32_t brightness; // 0..100, or -1 when unavailable uint16_t deviceId; uint8_t generation; uint8_t connectorType; @@ -509,6 +550,12 @@ namespace montauk::abi { char name[64]; }; + // Bluetooth bonded (paired) device (returned by SYS_BTBONDS) + struct BtBondInfo { + uint8_t bdAddr[6]; + uint8_t _pad[2]; + }; + // Software-defined radio receiver description (returned by SYS_SDR_INFO). struct SdrDeviceInfo { char name[64]; // e.g. "Realtek RTL2832U" @@ -527,12 +574,113 @@ namespace montauk::abi { uint32_t _pad2; }; + // Wi-Fi security suites reported in WifiNetwork.security. + static constexpr uint8_t WIFI_SEC_OPEN = 0; + static constexpr uint8_t WIFI_SEC_WEP = 1; + static constexpr uint8_t WIFI_SEC_WPA = 2; + static constexpr uint8_t WIFI_SEC_WPA2 = 3; + static constexpr uint8_t WIFI_SEC_WPA3 = 4; + + // Adapter states reported in WifiInfo.state. + static constexpr uint8_t WIFI_STATE_ABSENT = 0; // no device + static constexpr uint8_t WIFI_STATE_DETECTED = 1; // waiting for firmware load + static constexpr uint8_t WIFI_STATE_BOOTING = 2; + static constexpr uint8_t WIFI_STATE_RUNNING = 3; + static constexpr uint8_t WIFI_STATE_ERROR = 4; + static constexpr uint8_t WIFI_STATE_RFKILL = 5; // radio disabled in hardware + + // One scanned network (returned by SYS_WIFI_SCAN). + struct WifiNetwork { + char ssid[36]; // NUL-terminated; empty for hidden networks + uint8_t bssid[6]; + uint8_t channel; + int8_t rssi; // dBm + uint8_t band; // 0 = 2.4 GHz, 1 = 5 GHz + uint8_t security; // WIFI_SEC_* + uint16_t beaconInterval; // TU + }; + + // Association progress reported in WifiInfo.connState. + static constexpr uint32_t WIFI_CONN_IDLE = 0; + static constexpr uint32_t WIFI_CONN_CONTEXTS_UP = 1; + static constexpr uint32_t WIFI_CONN_AUTHENTICATING = 2; + static constexpr uint32_t WIFI_CONN_AUTHENTICATED = 3; + static constexpr uint32_t WIFI_CONN_ASSOCIATING = 4; + static constexpr uint32_t WIFI_CONN_ASSOCIATED = 5; + static constexpr uint32_t WIFI_CONN_HANDSHAKING = 6; + static constexpr uint32_t WIFI_CONN_CONNECTED = 7; + static constexpr uint32_t WIFI_CONN_FAILED = 8; + + // Negative results from SYS_WIFI_CONNECT. + static constexpr int WIFI_ERR_NO_ADAPTER = -1; // no adapter, or not ready + static constexpr int WIFI_ERR_NOT_FOUND = -2; // SSID absent from the scan + static constexpr int WIFI_ERR_NEED_KEY = -3; // encrypted, no passphrase + static constexpr int WIFI_ERR_UNSUPPORTED = -4; // WPA3-SAE, WEP, enterprise + static constexpr int WIFI_ERR_AUTH = -5; // key exchange rejected + static constexpr int WIFI_ERR_TIMEOUT = -6; // AP never answered + static constexpr int WIFI_ERR_FAILED = -7; // anything else + + // Adapter status (returned by SYS_WIFI_INFO). + struct WifiInfo { + uint8_t mac[6]; + uint8_t present; // 1 if a supported device was found + uint8_t state; // WIFI_STATE_* + uint8_t scanning; + uint8_t bands; // bit0 = 2.4 GHz, bit1 = 5 GHz + uint16_t channels; // usable channels after regulatory filtering + char fwVersion[32]; + uint64_t rxPackets; + uint32_t fwErrors; + uint32_t connState; // WIFI_CONN_* + uint64_t txPackets; + char ssid[36]; // network joined, empty when disconnected + uint8_t bssid[6]; + uint8_t connected; // 1 once the link can carry IP traffic + uint8_t channel; + int32_t lastError; // WIFI_ERR_* from the last async join, 0 = none + uint32_t scanGeneration; // bumped every time a scan finishes + uint8_t joining; // 1 while an async join is in flight + uint8_t _pad[3]; + }; + + // Link-layer interface kinds reported in NetIfInfo.kind. + static constexpr uint8_t NETIF_KIND_ETHERNET = 0; + static constexpr uint8_t NETIF_KIND_WIRELESS = 1; + + // One registered link-layer interface (returned by SYS_NETIFS). The IP + // configuration is global to the stack, so it belongs to whichever + // interface reports active = 1. + struct NetIfInfo { + char name[16]; // "eth0", "wlan0" + uint8_t mac[6]; + uint8_t kind; // NETIF_KIND_* + uint8_t linkUp; + uint8_t active; // 1 if this is the interface carrying traffic + uint8_t _pad[3]; + }; + struct ThermalInfo { char name[32]; // short zone name (e.g. "THRM", "TZ00") int32_t temperature; // tenths of degrees Celsius, or -1 if unavailable uint32_t _pad; }; + // CPU power/thermal snapshot (returned by SYS_POWERINFO) + struct PowerInfo { + uint8_t hwpActive; // hardware P-state scaling enabled + uint8_t throttling; // thermal governor currently limiting frequency + uint8_t tempC; // package temperature, degrees C (0 = unknown) + uint8_t tjMaxC; // hardware throttle temperature + uint8_t highestPerf; // HWP performance range (ratio units) + uint8_t lowestPerf; + uint8_t curMaxPerf; // thermal governor's current ceiling + uint8_t epp; // energy/perf preference (0=perf, 255=power) + uint32_t baseMHz; // nominal base frequency (0 = unknown) + uint32_t maxMHz; // max turbo frequency + uint32_t effMHz; // measured average active frequency + uint32_t apIdleHint; // MWAIT hint used for AP deep idle + }; + struct ProcInfo { int32_t pid; int32_t parentPid; diff --git a/template/sysroot/include/gui/canvas.hpp b/template/sysroot/include/gui/canvas.hpp index 873eb75..c38c260 100644 --- a/template/sysroot/include/gui/canvas.hpp +++ b/template/sysroot/include/gui/canvas.hpp @@ -161,24 +161,58 @@ struct Canvas { // ---- Text ---- + void text_bitmap(int x, int y, const char* str, Color c, int scale = 1) { + if (!str || !font_data || scale <= 0) return; + uint32_t pixel = c.to_pixel(); + int cx = x; + for (int i = 0; str[i]; i++, cx += FONT_WIDTH * scale) { + const uint8_t* glyph = + &font_data[(unsigned char)str[i] * FONT_HEIGHT]; + for (int row = 0; row < FONT_HEIGHT; row++) { + uint8_t bits = glyph[row]; + if (!bits) continue; + for (int col = 0; col < FONT_WIDTH; col++) { + if (!(bits & (0x80 >> col))) continue; + int px = cx + col * scale; + int py = y + row * scale; + for (int sy = 0; sy < scale; sy++) { + int dy = py + sy; + if (dy < 0 || dy >= h) continue; + for (int sx = 0; sx < scale; sx++) { + int dx = px + sx; + if (dx >= 0 && dx < w) pixels[dy * w + dx] = pixel; + } + } + } + } + } + } + void text(int x, int y, const char* str, Color c) { if (fonts::system_font && fonts::system_font->valid) { - fonts::system_font->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::UI_SIZE); + if (fonts::system_font->draw_to_buffer( + pixels, w, h, x, y, str, c, fonts::UI_SIZE)) + return; } + text_bitmap(x, y, str, c); } void text_2x(int x, int y, const char* str, Color c) { if (fonts::system_font && fonts::system_font->valid) { - fonts::system_font->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::LARGE_SIZE); + if (fonts::system_font->draw_to_buffer( + pixels, w, h, x, y, str, c, fonts::LARGE_SIZE)) + return; } + text_bitmap(x, y, str, c, 2); } void text_mono(int x, int y, const char* str, Color c) { if (fonts::mono && fonts::mono->valid) { - fonts::mono->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::TERM_SIZE); - return; + if (fonts::mono->draw_to_buffer( + pixels, w, h, x, y, str, c, fonts::TERM_SIZE)) + return; } - text(x, y, str, c); + text_bitmap(x, y, str, c); } // ---- Icons ---- diff --git a/template/sysroot/include/gui/desktop.hpp b/template/sysroot/include/gui/desktop.hpp index c112a29..4cc7907 100644 --- a/template/sysroot/include/gui/desktop.hpp +++ b/template/sysroot/include/gui/desktop.hpp @@ -15,9 +15,8 @@ namespace gui { -static constexpr int MAX_WINDOWS = 8; +static constexpr int MAX_WINDOWS = 32; static constexpr int PANEL_HEIGHT = 32; -static constexpr int MAX_LAUNCHER_ITEMS = 64; enum DesktopItemSection : uint8_t { DESKTOP_ITEM_SECTION_HIDDEN = 0, @@ -111,9 +110,10 @@ struct DesktopState { bool launcher_open; char launcher_query[64]; int launcher_query_len; - LauncherItem launcher_items[MAX_LAUNCHER_ITEMS]; + LauncherItem* launcher_items; // dynamically grown int launcher_item_count; - int launcher_results[MAX_LAUNCHER_ITEMS]; + int launcher_item_capacity; + int* launcher_results; // indices into launcher_items, same capacity int launcher_result_count; int launcher_selected; int launcher_scroll; @@ -122,7 +122,6 @@ struct DesktopState { SvgIcon icon_terminal; SvgIcon icon_filemanager; - SvgIcon icon_sysinfo; SvgIcon icon_appmenu; SvgIcon icon_folder; SvgIcon icon_file; @@ -181,6 +180,48 @@ struct DesktopState { uint64_t net_cfg_last_poll; Rect net_icon_rect; + // Registered link-layer interfaces. The IP configuration is global to the + // stack, so the wired and wireless popups use `active` to decide which of + // them owns the address currently on screen. + static constexpr int MAX_NETIFS = 4; + montauk::abi::NetIfInfo netifs[MAX_NETIFS]; + int netif_count; + bool eth_present; + + // ---- Wi-Fi ------------------------------------------------------------- + // The panel entry exists only when a supported adapter is present. + static constexpr int MAX_WIFI_NETWORKS = 32; + static constexpr int WIFI_SSID_CAP = 36; + static constexpr int WIFI_PSK_CAP = 72; + + SvgIcon icon_wifi; + bool wifi_present; + bool wifi_popup_open; + Rect wifi_icon_rect; + montauk::abi::WifiInfo wifi_info; + montauk::abi::WifiNetwork wifi_networks[MAX_WIFI_NETWORKS]; + int wifi_network_count; + uint64_t wifi_last_poll; + uint32_t wifi_scan_generation; + bool wifi_scanning; + int wifi_scroll; // first visible row of the list + bool wifi_boot_scan_started; // the automatic scan at startup + bool wifi_autoconnect_done; // saved-network join already tried + bool wifi_joining; // a join we started is in flight + bool wifi_dhcp_pending; // ask for a lease once the link is up + char wifi_joining_ssid[WIFI_SSID_CAP]; + char wifi_status[96]; // last result line in the popup + uint64_t wifi_status_time; + + // Passphrase prompt, shown when a selected network needs a key. + bool wifi_prompt_open; + char wifi_prompt_ssid[WIFI_SSID_CAP]; + char wifi_prompt_password[WIFI_PSK_CAP]; + int wifi_prompt_len; + bool wifi_prompt_reveal; + bool wifi_prompt_remember; + uint8_t wifi_prompt_security; + bool vol_popup_open; Rect vol_icon_rect; int vol_level; // 0-100 @@ -207,7 +248,7 @@ struct DesktopState { // IDs of external windows we've sent a close event to but that haven't // been destroyed yet by their owning process. Prevents the poll loop // from re-creating them at the default position (visible flicker). - static constexpr int MAX_CLOSING = 8; + static constexpr int MAX_CLOSING = MAX_WINDOWS; int closing_ext_ids[MAX_CLOSING]; int closing_ext_count; diff --git a/template/sysroot/include/gui/font.hpp b/template/sysroot/include/gui/font.hpp index eca26b7..4001c16 100644 --- a/template/sysroot/include/gui/font.hpp +++ b/template/sysroot/include/gui/font.hpp @@ -14,8 +14,10 @@ namespace gui { static constexpr int FONT_WIDTH = 8; static constexpr int FONT_HEIGHT = 16; -// Defined in font_data.cpp -extern const uint8_t font_data[256 * 16]; +// Defined by programs that ship the built-in VGA fallback. Keep the symbol +// weak so Canvas remains usable by small apps that intentionally rely only on +// TrueType fonts, while login/desktop can recover when TrueType setup fails. +extern const uint8_t font_data[256 * 16] __attribute__((weak)); // Dynamic font height: TTF line height or 16 (bitmap fallback) inline int system_font_height() { diff --git a/template/sysroot/include/gui/mtk/widgets.hpp b/template/sysroot/include/gui/mtk/widgets.hpp index e4171aa..b3effcc 100644 --- a/template/sysroot/include/gui/mtk/widgets.hpp +++ b/template/sysroot/include/gui/mtk/widgets.hpp @@ -678,6 +678,78 @@ inline bool same_color(Color a, Color b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } +// ============================================================================ +// Anti-aliased rounded-rect fill +// ============================================================================ + +// Coverage-based pixel blend; `cov` is 0..255 and the destination is treated +// as opaque. +inline void aa_blend_px(Canvas& c, int x, int y, Color color, int cov) { + if (x < 0 || x >= c.w || y < 0 || y >= c.h || cov <= 0) return; + if (cov > 255) cov = 255; + uint32_t dst = c.pixels[y * c.w + x]; + uint32_t dr = (dst >> 16) & 0xFF, dg = (dst >> 8) & 0xFF, db = dst & 0xFF; + uint32_t a = (uint32_t)cov, ia = 255 - a; + uint32_t nr = (color.r * a + dr * ia + 127) / 255; + uint32_t ng = (color.g * a + dg * ia + 127) / 255; + uint32_t nb = (color.b * a + db * ia + 127) / 255; + c.pixels[y * c.w + x] = 0xFF000000u | (nr << 16) | (ng << 8) | nb; +} + +// Rounded-rect fill with 4x4-supersampled corners. The straight interior is +// filled solid (fast); only the four corner arcs pay for anti-aliasing, so +// this stays cheap even for large frames. +inline void fill_round_rect_aa(Canvas& c, const Rect& box, int radius, Color color) { + if (box.w <= 0 || box.h <= 0) return; + int r = radius; + if (r < 0) r = 0; + if (r > box.w / 2) r = box.w / 2; + if (r > box.h / 2) r = box.h / 2; + + if (r == 0) { + c.fill_rect(box.x, box.y, box.w, box.h, color); + return; + } + + // Solid interior: center band + top/bottom edge bands between the corners. + c.fill_rect(box.x, box.y + r, box.w, box.h - 2 * r, color); + c.fill_rect(box.x + r, box.y, box.w - 2 * r, r, color); + c.fill_rect(box.x + r, box.y + box.h - r, box.w - 2 * r, r, color); + + const int S = 4; + double r2 = (double)r * r; + // (cell origin x, cell origin y, arc center x, arc center y) + int corners[4][4] = { + {box.x, box.y, box.x + r, box.y + r}, + {box.x + box.w - r, box.y, box.x + box.w - r, box.y + r}, + {box.x, box.y + box.h - r, box.x + r, box.y + box.h - r}, + {box.x + box.w - r, box.y + box.h - r, box.x + box.w - r, box.y + box.h - r}, + }; + for (auto& cn : corners) { + // Push the arc center a half pixel away from this corner (toward the + // box interior). Pixel-center sampling otherwise sits half a pixel + // closer to the center than the legacy pixel-corner test did, which + // filled the arcs fuller and made small radii read as square. Left + // corners share x == box.x, top corners share y == box.y. + double cx = cn[2] + (cn[0] == box.x ? 0.5 : -0.5); + double cy = cn[3] + (cn[1] == box.y ? 0.5 : -0.5); + for (int yy = 0; yy < r; yy++) { + int py = cn[1] + yy; + for (int xx = 0; xx < r; xx++) { + int px = cn[0] + xx; + int hits = 0; + for (int sy = 0; sy < S; sy++) + for (int sx = 0; sx < S; sx++) { + double dx = (px + (sx + 0.5) / S) - cx; + double dy = (py + (sy + 0.5) / S) - cy; + if (dx * dx + dy * dy <= r2) hits++; + } + if (hits) aa_blend_px(c, px, py, color, hits * 255 / (S * S)); + } + } + } +} + inline void draw_rounded_frame(Canvas& c, const Rect& bounds, int radius, @@ -686,16 +758,16 @@ inline void draw_rounded_frame(Canvas& c, int border_w = 1) { if (bounds.empty()) return; if (border_w <= 0 || same_color(fill, border)) { - c.fill_rounded_rect(bounds.x, bounds.y, bounds.w, bounds.h, gui_max(radius, 0), fill); + fill_round_rect_aa(c, bounds, gui_max(radius, 0), fill); return; } - c.fill_rounded_rect(bounds.x, bounds.y, bounds.w, bounds.h, gui_max(radius, 0), border); + fill_round_rect_aa(c, bounds, gui_max(radius, 0), border); int inner_w = bounds.w - border_w * 2; int inner_h = bounds.h - border_w * 2; if (inner_w <= 0 || inner_h <= 0) return; - c.fill_rounded_rect(bounds.x + border_w, bounds.y + border_w, - inner_w, inner_h, gui_max(radius - border_w, 0), fill); + Rect inner = {bounds.x + border_w, bounds.y + border_w, inner_w, inner_h}; + fill_round_rect_aa(c, inner, gui_max(radius - border_w, 0), fill); } inline ButtonColors resolve_button_colors(ButtonVariant variant, @@ -942,6 +1014,165 @@ inline void draw_radio(Canvas& c, label, theme.text); } +// ============================================================================ +// Checkboxes and disclosure arrows +// ============================================================================ + +enum CheckState : uint8_t { + CHECK_OFF = 0, + CHECK_ON, + CHECK_MIXED, +}; + +inline CheckState check_state(bool checked) { + return checked ? CHECK_ON : CHECK_OFF; +} + +inline Rect checkbox_indicator_rect(const Rect& option, int size = 16) { + return {option.x, option.y + (option.h - size) / 2, size, size}; +} + +// Squared distance from a point to a line segment (no sqrt; used for the +// thickness test of the checkmark strokes). +inline double checkbox_seg_dist2(double px, double py, + double ax, double ay, double bx, double by) { + double dx = bx - ax, dy = by - ay; + double len2 = dx * dx + dy * dy; + double t = len2 > 0 ? ((px - ax) * dx + (py - ay) * dy) / len2 : 0.0; + if (t < 0) t = 0; else if (t > 1) t = 1; + double ex = px - (ax + t * dx), ey = py - (ay + t * dy); + return ex * ex + ey * ey; +} + +// Anti-aliased two-stroke checkmark via 4x4 supersampling of a fixed-width +// polyline. +inline void draw_check_mark(Canvas& c, const Rect& box, Color color) { + double ax = box.x + box.w * 0.24, ay = box.y + box.h * 0.50; + double bx = box.x + box.w * 0.42, by = box.y + box.h * 0.68; + double cx = box.x + box.w * 0.74, cy = box.y + box.h * 0.26; + double hw = box.w * 0.085 + 0.35; // half stroke width + double hw2 = hw * hw; + const int S = 4; + for (int py = box.y - 1; py <= box.y + box.h + 1; py++) { + if (py < 0 || py >= c.h) continue; + for (int px = box.x - 1; px <= box.x + box.w + 1; px++) { + if (px < 0 || px >= c.w) continue; + int hits = 0; + for (int sy = 0; sy < S; sy++) + for (int sx = 0; sx < S; sx++) { + double fx = px + (sx + 0.5) / S; + double fy = py + (sy + 0.5) / S; + double d1 = checkbox_seg_dist2(fx, fy, ax, ay, bx, by); + double d2 = checkbox_seg_dist2(fx, fy, bx, by, cx, cy); + if ((d1 < d2 ? d1 : d2) <= hw2) hits++; + } + if (hits) aa_blend_px(c, px, py, color, hits * 255 / (S * S)); + } + } +} + +inline void draw_checkbox(Canvas& c, + const Rect& option, + const char* label, + CheckState state, + const Theme& theme, + bool enabled = true, + bool hovered = false) { + Rect box = checkbox_indicator_rect(option); + int radius = gui_max(box.w / 5, 2); + + Color fill, border, mark; + if (!enabled) { + fill = theme.disabled_bg; + border = theme.disabled_bg; + mark = theme.disabled_fg; + } else if (state == CHECK_OFF) { + fill = colors::WHITE; + border = hovered ? theme.accent : theme.border; + mark = theme.accent_fg; + } else { + fill = hovered ? theme.accent_hover : theme.accent; + border = fill; + mark = theme.accent_fg; + } + + if (state == CHECK_OFF) { + // Colored ring with a white interior. + fill_round_rect_aa(c, box, radius, border); + Rect inner = {box.x + 1, box.y + 1, box.w - 2, box.h - 2}; + fill_round_rect_aa(c, inner, gui_max(radius - 1, 1), fill); + } else { + fill_round_rect_aa(c, box, radius, fill); + } + + if (state == CHECK_ON) { + draw_check_mark(c, box, mark); + } else if (state == CHECK_MIXED) { + Rect dash = {box.x + 4, box.y + box.h / 2 - 1, box.w - 8, 2}; + fill_round_rect_aa(c, dash, 1, mark); + } + + if (label && label[0]) { + int fh = system_font_height(); + c.text(box.x + box.w + theme.gap_sm, + option.y + (option.h - fh) / 2, + label, enabled ? theme.text : theme.text_muted); + } +} + +inline Rect disclosure_rect(const Rect& option, int size = 16) { + return {option.x, option.y + (option.h - size) / 2, size, size}; +} + +inline void draw_disclosure(Canvas& c, + const Rect& box, + bool expanded, + const Theme& theme, + bool hovered = false) { + Color color = hovered ? theme.text : theme.text_subtle; + double cx = box.x + box.w / 2.0; + double cy = box.y + box.h / 2.0; + double s = box.w * 0.26; + if (s < 3) s = 3; + + // Triangle vertices (equilateral-ish), rotated per state. + double vx[3], vy[3]; + if (expanded) { // pointing down + vx[0] = cx - s; vy[0] = cy - s * 0.6; + vx[1] = cx + s; vy[1] = cy - s * 0.6; + vx[2] = cx; vy[2] = cy + s * 0.75; + } else { // pointing right + vx[0] = cx - s * 0.6; vy[0] = cy - s; + vx[1] = cx - s * 0.6; vy[1] = cy + s; + vx[2] = cx + s * 0.75; vy[2] = cy; + } + + // Anti-aliased fill via 4x4 supersampling of the triangle's half-plane test. + auto edge = [](double ax, double ay, double bx, double by, double px, double py) { + return (px - ax) * (by - ay) - (py - ay) * (bx - ax); + }; + const int S = 4; + for (int py = box.y; py < box.y + box.h; py++) { + if (py < 0 || py >= c.h) continue; + for (int px = box.x; px < box.x + box.w; px++) { + if (px < 0 || px >= c.w) continue; + int hits = 0; + for (int sy = 0; sy < S; sy++) + for (int sx = 0; sx < S; sx++) { + double fx = px + (sx + 0.5) / S; + double fy = py + (sy + 0.5) / S; + double e0 = edge(vx[0], vy[0], vx[1], vy[1], fx, fy); + double e1 = edge(vx[1], vy[1], vx[2], vy[2], fx, fy); + double e2 = edge(vx[2], vy[2], vx[0], vy[0], fx, fy); + bool inside = (e0 >= 0 && e1 >= 0 && e2 >= 0) || + (e0 <= 0 && e1 <= 0 && e2 <= 0); + if (inside) hits++; + } + if (hits) aa_blend_px(c, px, py, color, hits * 255 / (S * S)); + } + } +} + inline Rect swatch_rect(int row_x, int row_y, int index, int size = 24, int gap = 6) { return {row_x + index * (size + gap), row_y, size, size}; } diff --git a/template/sysroot/include/gui/terminal.hpp b/template/sysroot/include/gui/terminal.hpp index bd935fe..70f41aa 100644 --- a/template/sysroot/include/gui/terminal.hpp +++ b/template/sysroot/include/gui/terminal.hpp @@ -795,11 +795,21 @@ static inline void terminal_resize(TerminalState* t, int new_cols, int new_rows) } } - int new_scrollback = keep - new_rows; + // Anchor the new visible region to the cursor's line. Treating the bottom + // new_rows of kept content as the screen (the naive keep - new_rows) only + // works when the screen is full: with a partly filled screen -- e.g. a + // shell prompt a few lines down with blank rows below it -- it would push + // the real text up into scrollback and drop the view onto the blank region, + // so a zoom appears to scroll down and strands the cursor in empty space. + // Instead pin the cursor to the bottom row when there is enough history + // above it, otherwise keep the content anchored at the top. + int abs_cursor_y = t->scrollback_lines + t->cursor_y - discard; + if (abs_cursor_y < 0) abs_cursor_y = 0; + int new_scrollback = abs_cursor_y - (new_rows - 1); if (new_scrollback < 0) new_scrollback = 0; + if (new_scrollback > t->max_scrollback) new_scrollback = t->max_scrollback; // Adjust cursor - int abs_cursor_y = t->scrollback_lines + t->cursor_y - discard; int new_cursor_y = abs_cursor_y - new_scrollback; if (new_cursor_y < 0) new_cursor_y = 0; if (new_cursor_y >= new_rows) new_cursor_y = new_rows - 1; diff --git a/template/sysroot/include/gui/truetype.hpp b/template/sysroot/include/gui/truetype.hpp index 90ac56c..edf3593 100644 --- a/template/sysroot/include/gui/truetype.hpp +++ b/template/sysroot/include/gui/truetype.hpp @@ -121,10 +121,18 @@ struct TrueTypeFont { return false; } - montauk::read(fd, data, 0, size); + int read_result = montauk::read(fd, data, 0, size); montauk::close(fd); + if (read_result < 0 || (uint64_t)read_result != size) { + montauk::free(data); + data = nullptr; + return false; + } - if (!stbtt_InitFont(&info, data, stbtt_GetFontOffsetForIndex(data, 0))) { + // stbtt_GetFontOffsetForIndex returns -1 for non-font data; passing + // that into stbtt_InitFont makes it dereference data + (uint32)-1. + int off = stbtt_GetFontOffsetForIndex(data, 0); + if (off < 0 || !stbtt_InitFont(&info, data, off)) { montauk::free(data); data = nullptr; return false; @@ -308,13 +316,14 @@ struct TrueTypeFont { draw(fb, x, y, text, fg, pixel_size); } - void draw_to_buffer(uint32_t* pixels, int buf_w, int buf_h, + bool draw_to_buffer(uint32_t* pixels, int buf_w, int buf_h, int x, int y, const char* text, Color color, int pixel_size) { - if (!valid) return; + if (!valid) return false; GlyphCache* gc = get_cache(pixel_size); int cx = x; int baseline = y + gc->ascent; + bool drew_pixel = false; for (int i = 0; text[i]; i++) { CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]); @@ -331,6 +340,7 @@ struct TrueTypeFont { if (dx < 0 || dx >= buf_w) continue; uint8_t alpha = g->bitmap[row * g->width + col]; if (alpha == 0) continue; + drew_pixel = true; if (alpha == 255) { pixels[dy * buf_w + dx] = @@ -353,6 +363,7 @@ struct TrueTypeFont { } cx += g->advance; } + return drew_pixel; } // Draw text to buffer with clip rectangle (pixels outside clip_x..clip_x+clip_w are not drawn) diff --git a/template/sysroot/include/libc/ctype.h b/template/sysroot/include/libc/ctype.h index efca6ed..46606a2 100644 --- a/template/sysroot/include/libc/ctype.h +++ b/template/sysroot/include/libc/ctype.h @@ -16,6 +16,9 @@ int islower(int c); int isprint(int c); int ispunct(int c); int isxdigit(int c); + +#define isascii(c) (((c) & ~0x7F) == 0) +#define toascii(c) ((c) & 0x7F) int iscntrl(int c); int isgraph(int c); int toupper(int c); diff --git a/template/sysroot/include/libc/dirent.h b/template/sysroot/include/libc/dirent.h index 397d0ca..5e90839 100644 --- a/template/sysroot/include/libc/dirent.h +++ b/template/sysroot/include/libc/dirent.h @@ -9,8 +9,13 @@ extern "C" { #define NAME_MAX 255 #define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 #define DT_DIR 4 +#define DT_BLK 6 #define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 struct dirent { unsigned char d_type; diff --git a/template/sysroot/include/libc/errno.h b/template/sysroot/include/libc/errno.h index cdbda4d..40fd8b9 100644 --- a/template/sysroot/include/libc/errno.h +++ b/template/sysroot/include/libc/errno.h @@ -9,18 +9,88 @@ extern "C" { extern int errno; -#define ENOENT 2 -#define EIO 5 -#define ENOMEM 12 -#define EACCES 13 -#define EINVAL 22 -#define ERANGE 34 -#define ENOSYS 38 -#define EISDIR 21 -#define ENOTDIR 20 -#define EEXIST 17 -#define EBADF 9 -#define EPERM 1 +/* Linux errno numbering. */ +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 35 +#define ENAMETOOLONG 36 +#define ENOLCK 37 +#define ENOSYS 38 +#define ENOTEMPTY 39 +#define ELOOP 40 +#define EWOULDBLOCK EAGAIN +#define ETXTBSY 26 +#define ENOMSG 42 +#define EIDRM 43 +#define ENOSTR 60 +#define ENODATA 61 +#define ETIME 62 +#define ENOSR 63 +#define ENOLINK 67 +#define EPROTO 71 +#define EMULTIHOP 72 +#define EBADMSG 74 +#define EOVERFLOW 75 +#define EILSEQ 84 +#define ENOTSOCK 88 +#define EDESTADDRREQ 89 +#define EMSGSIZE 90 +#define EPROTOTYPE 91 +#define ENOPROTOOPT 92 +#define EPROTONOSUPPORT 93 +#define EOPNOTSUPP 95 /* == ENOTSUP, Linux numbering */ +#define EAFNOSUPPORT 97 +#define EADDRINUSE 98 +#define EADDRNOTAVAIL 99 +#define ENETDOWN 100 +#define ENETUNREACH 101 +#define ENETRESET 102 +#define ECONNABORTED 103 +#define ECONNRESET 104 +#define ENOBUFS 105 +#define EISCONN 106 +#define ENOTCONN 107 +#define ETIMEDOUT 110 +#define ECONNREFUSED 111 +#define EHOSTUNREACH 113 +#define EALREADY 114 +#define EINPROGRESS 115 +#define ESTALE 116 +#define EDQUOT 122 +#define ECANCELED 125 +#define EOWNERDEAD 130 +#define ENOTRECOVERABLE 131 +#define ENOTSUP 95 #ifdef __cplusplus } diff --git a/template/sysroot/include/libc/fcntl.h b/template/sysroot/include/libc/fcntl.h index 46890eb..dc2206e 100644 --- a/template/sysroot/include/libc/fcntl.h +++ b/template/sysroot/include/libc/fcntl.h @@ -7,13 +7,27 @@ extern "C" { #endif -#define O_RDONLY 0 -#define O_WRONLY 1 -#define O_RDWR 2 -#define O_CREAT 0x40 -#define O_TRUNC 0x200 +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define O_ACCMODE 3 +#define O_CREAT 0x40 +#define O_EXCL 0x80 +#define O_TRUNC 0x200 +#define O_APPEND 0x400 +#define O_NONBLOCK 0x800 +#define O_CLOEXEC 0x80000 + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 + +#define FD_CLOEXEC 1 int open(const char *path, int flags, ...); +int fcntl(int fd, int cmd, ...); #ifdef __cplusplus } diff --git a/template/sysroot/include/libc/inttypes.h b/template/sysroot/include/libc/inttypes.h index 398a5fc..e2a01b5 100644 --- a/template/sysroot/include/libc/inttypes.h +++ b/template/sysroot/include/libc/inttypes.h @@ -23,5 +23,38 @@ #define PRIX16 "X" #define PRIX32 "X" #define PRIX64 "lX" +#define PRIo8 "o" +#define PRIo16 "o" +#define PRIo32 "o" +#define PRIo64 "lo" + +#define PRIdPTR "ld" +#define PRIiPTR "li" +#define PRIuPTR "lu" +#define PRIxPTR "lx" +#define PRIXPTR "lX" +#define PRIdMAX "ld" +#define PRIuMAX "lu" +#define PRIxMAX "lx" + +#define SCNd8 "d" +#define SCNd16 "d" +#define SCNd32 "d" +#define SCNd64 "ld" +#define SCNi8 "i" +#define SCNi16 "i" +#define SCNi32 "i" +#define SCNi64 "li" +#define SCNu8 "u" +#define SCNu16 "u" +#define SCNu32 "u" +#define SCNu64 "lu" +#define SCNx8 "x" +#define SCNx16 "x" +#define SCNx32 "x" +#define SCNx64 "lx" +#define SCNdPTR "ld" +#define SCNuPTR "lu" +#define SCNxPTR "lx" #endif /* _LIBC_INTTYPES_H */ diff --git a/template/sysroot/include/libc/math.h b/template/sysroot/include/libc/math.h index efbce2f..6f2b779 100644 --- a/template/sysroot/include/libc/math.h +++ b/template/sysroot/include/libc/math.h @@ -11,8 +11,47 @@ extern "C" { #define INFINITY __builtin_inff() #define NAN __builtin_nanf("") +/* C99 floating-point classification. */ +#define FP_NAN 0 +#define FP_INFINITE 1 +#define FP_ZERO 2 +#define FP_SUBNORMAL 3 +#define FP_NORMAL 4 + +#define fpclassify(x) \ + __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, \ + FP_ZERO, x) +#define isnan(x) __builtin_isnan(x) +#define isinf(x) __builtin_isinf(x) +#define isfinite(x) __builtin_isfinite(x) +#define isnormal(x) __builtin_isnormal(x) +#define signbit(x) __builtin_signbit(x) + double fabs(double x); double frexp(double x, int *exp); + +/* C89 modf and the C99 float variants assumed by hosted libstdc++ + (--with-newlib crossconfig). The float forms wrap the double + implementations. */ +double modf(double x, double *iptr); +float modff(float x, float *iptr); +double hypot(double x, double y); +float hypotf(float x, float y); +float acosf(float x); +float asinf(float x); +float atanf(float x); +float atan2f(float y, float x); +float coshf(float x); +float expf(float x); +float fmodf(float x, float y); +float frexpf(float x, int *exp); +float ldexpf(float x, int exp); +float logf(float x); +float log10f(float x); +float powf(float x, float y); +float sinhf(float x); +float tanf(float x); +float tanhf(float x); double ldexp(double x, int n); long double ldexpl(long double x, int n); double floor(double x); diff --git a/template/sysroot/include/libc/memory.h b/template/sysroot/include/libc/memory.h new file mode 100644 index 0000000..b99e2ec --- /dev/null +++ b/template/sysroot/include/libc/memory.h @@ -0,0 +1,9 @@ +#ifndef _LIBC_MEMORY_H +#define _LIBC_MEMORY_H + +#pragma once + +/* Traditional alias for string.h. */ +#include + +#endif /* _LIBC_MEMORY_H */ diff --git a/template/sysroot/include/libc/montauk.h b/template/sysroot/include/libc/montauk.h index 5a1e25e..a58800d 100644 --- a/template/sysroot/include/libc/montauk.h +++ b/template/sysroot/include/libc/montauk.h @@ -19,106 +19,177 @@ extern "C" { #endif -/* ==================================================================== - Syscall numbers - ==================================================================== */ - -#define MTK_SYS_EXIT 0 -#define MTK_SYS_YIELD 1 -#define MTK_SYS_SLEEP_MS 2 -#define MTK_SYS_GETPID 3 -#define MTK_SYS_PRINT 4 -#define MTK_SYS_PUTCHAR 5 -#define MTK_SYS_OPEN 6 -#define MTK_SYS_READ 7 -#define MTK_SYS_GETSIZE 8 -#define MTK_SYS_CLOSE 9 -#define MTK_SYS_READDIR 10 -#define MTK_SYS_ALLOC 11 -#define MTK_SYS_FREE 12 -#define MTK_SYS_GETTICKS 13 -#define MTK_SYS_GETMILLISECONDS 14 -#define MTK_SYS_GETINFO 15 -#define MTK_SYS_ISKEYAVAILABLE 16 -#define MTK_SYS_GETKEY 17 -#define MTK_SYS_GETCHAR 18 -#define MTK_SYS_PING 19 -#define MTK_SYS_SPAWN 20 -#define MTK_SYS_WAITPID 23 -#define MTK_SYS_TERMSIZE 24 -#define MTK_SYS_GETARGS 25 -#define MTK_SYS_RESET 26 -#define MTK_SYS_SHUTDOWN 27 -#define MTK_SYS_SETUNIXTIME 153 -#define MTK_SYS_GETTIME 28 -#define MTK_SYS_SOCKET 29 -#define MTK_SYS_CONNECT 30 -#define MTK_SYS_BIND 31 -#define MTK_SYS_LISTEN 32 -#define MTK_SYS_ACCEPT 33 -#define MTK_SYS_SEND 34 -#define MTK_SYS_RECV 35 -#define MTK_SYS_CLOSESOCK 36 -#define MTK_SYS_GETNETCFG 37 -#define MTK_SYS_SETNETCFG 38 -#define MTK_SYS_SENDTO 39 -#define MTK_SYS_RECVFROM 40 -#define MTK_SYS_FWRITE 41 -#define MTK_SYS_FCREATE 42 -#define MTK_SYS_TERMSCALE 43 -#define MTK_SYS_RESOLVE 44 -#define MTK_SYS_GETRANDOM 45 -#define MTK_SYS_KLOG 46 -#define MTK_SYS_MOUSESTATE 47 -#define MTK_SYS_SETMOUSEBOUNDS 48 -#define MTK_SYS_SPAWN_REDIR 49 -#define MTK_SYS_CHILDIO_READ 50 -#define MTK_SYS_CHILDIO_WRITE 51 -#define MTK_SYS_WINCREATE 54 -#define MTK_SYS_WINDESTROY 55 -#define MTK_SYS_WINPRESENT 56 -#define MTK_SYS_WINPOLL 57 -#define MTK_SYS_WINENUM 58 -#define MTK_SYS_WINMAP 59 -#define MTK_SYS_WINUNMAP 97 -#define MTK_SYS_WINSENDEVENT 60 -#define MTK_SYS_PROCLIST 61 -#define MTK_SYS_KILL 62 -#define MTK_SYS_WINRESIZE 64 -#define MTK_SYS_WINSETSCALE 65 -#define MTK_SYS_WINGETSCALE 66 -#define MTK_SYS_MEMSTATS 67 -#define MTK_SYS_WINSETCURSOR 68 -#define MTK_SYS_WINSETFLAGS 126 -#define MTK_SYS_FDELETE 77 -#define MTK_SYS_FMKDIR 78 -#define MTK_SYS_FRENAME 94 -#define MTK_SYS_DRIVELIST 79 -#define MTK_SYS_DRIVELABEL 124 -#define MTK_SYS_AUDIOOPEN 80 -#define MTK_SYS_AUDIOCLOSE 81 -#define MTK_SYS_AUDIOWRITE 82 -#define MTK_SYS_AUDIOCTL 83 -#define MTK_SYS_SETTZ 90 -#define MTK_SYS_GETTZ 91 -#define MTK_SYS_GETCWD 95 -#define MTK_SYS_CHDIR 96 -#define MTK_SYS_DUPHANDLE 98 -#define MTK_SYS_WAIT_HANDLE 99 -#define MTK_SYS_STREAM_CREATE 100 -#define MTK_SYS_STREAM_READ 101 -#define MTK_SYS_STREAM_WRITE 102 -#define MTK_SYS_MAILBOX_CREATE 103 -#define MTK_SYS_MAILBOX_SEND 104 -#define MTK_SYS_MAILBOX_RECV 105 -#define MTK_SYS_WAITSET_CREATE 106 -#define MTK_SYS_WAITSET_ADD 107 -#define MTK_SYS_WAITSET_REMOVE 108 -#define MTK_SYS_WAITSET_WAIT 109 -#define MTK_SYS_PROC_OPEN 110 -#define MTK_SYS_SURFACE_CREATE 111 -#define MTK_SYS_SURFACE_MAP 112 -#define MTK_SYS_SURFACE_RESIZE 113 +/* @SYSCALLS-BEGIN + Generated from Api/Syscall.hpp by scripts/montauk-syscalls.py. + Do not edit by hand; run 'make gen-syscalls' to refresh. + Wrapper functions below are hand-written. */ +#define MTK_SYS_EXIT 0 +#define MTK_SYS_YIELD 1 +#define MTK_SYS_SLEEP_MS 2 +#define MTK_SYS_GETPID 3 +#define MTK_SYS_PRINT 4 +#define MTK_SYS_PUTCHAR 5 +#define MTK_SYS_OPEN 6 +#define MTK_SYS_READ 7 +#define MTK_SYS_GETSIZE 8 +#define MTK_SYS_CLOSE 9 +#define MTK_SYS_READDIR 10 +#define MTK_SYS_ALLOC 11 +#define MTK_SYS_FREE 12 +#define MTK_SYS_GETTICKS 13 +#define MTK_SYS_GETMILLISECONDS 14 +#define MTK_SYS_GETINFO 15 +#define MTK_SYS_ISKEYAVAILABLE 16 +#define MTK_SYS_GETKEY 17 +#define MTK_SYS_GETCHAR 18 +#define MTK_SYS_PING 19 +#define MTK_SYS_SPAWN 20 +#define MTK_SYS_FBINFO 21 +#define MTK_SYS_FBMAP 22 +#define MTK_SYS_WAITPID 23 +#define MTK_SYS_TERMSIZE 24 +#define MTK_SYS_GETARGS 25 +#define MTK_SYS_RESET 26 +#define MTK_SYS_SHUTDOWN 27 +#define MTK_SYS_GETTIME 28 +#define MTK_SYS_SOCKET 29 +#define MTK_SYS_CONNECT 30 +#define MTK_SYS_BIND 31 +#define MTK_SYS_LISTEN 32 +#define MTK_SYS_ACCEPT 33 +#define MTK_SYS_SEND 34 +#define MTK_SYS_RECV 35 +#define MTK_SYS_CLOSESOCK 36 +#define MTK_SYS_GETNETCFG 37 +#define MTK_SYS_SETNETCFG 38 +#define MTK_SYS_SENDTO 39 +#define MTK_SYS_RECVFROM 40 +#define MTK_SYS_FWRITE 41 +#define MTK_SYS_FCREATE 42 +#define MTK_SYS_TERMSCALE 43 +#define MTK_SYS_RESOLVE 44 +#define MTK_SYS_GETRANDOM 45 +#define MTK_SYS_KLOG 46 +#define MTK_SYS_MOUSESTATE 47 +#define MTK_SYS_SETMOUSEBOUNDS 48 +#define MTK_SYS_SPAWN_REDIR 49 +#define MTK_SYS_CHILDIO_READ 50 +#define MTK_SYS_CHILDIO_WRITE 51 +#define MTK_SYS_CHILDIO_WRITEKEY 52 +#define MTK_SYS_CHILDIO_SETTERMSZ 53 +#define MTK_SYS_WINCREATE 54 +#define MTK_SYS_WINDESTROY 55 +#define MTK_SYS_WINPRESENT 56 +#define MTK_SYS_WINPOLL 57 +#define MTK_SYS_WINENUM 58 +#define MTK_SYS_WINMAP 59 +#define MTK_SYS_WINSENDEVENT 60 +#define MTK_SYS_PROCLIST 61 +#define MTK_SYS_KILL 62 +#define MTK_SYS_DEVLIST 63 +#define MTK_SYS_WINRESIZE 64 +#define MTK_SYS_WINSETSCALE 65 +#define MTK_SYS_WINGETSCALE 66 +#define MTK_SYS_MEMSTATS 67 +#define MTK_SYS_WINSETCURSOR 68 +#define MTK_SYS_DISKINFO 69 +#define MTK_SYS_PARTLIST 70 +#define MTK_SYS_DISKREAD 71 +#define MTK_SYS_DISKWRITE 72 +#define MTK_SYS_GPTINIT 73 +#define MTK_SYS_GPTADD 74 +#define MTK_SYS_FSMOUNT 75 +#define MTK_SYS_FSFORMAT 76 +#define MTK_SYS_FDELETE 77 +#define MTK_SYS_FMKDIR 78 +#define MTK_SYS_DRIVELIST 79 +#define MTK_SYS_AUDIOOPEN 80 +#define MTK_SYS_AUDIOCLOSE 81 +#define MTK_SYS_AUDIOWRITE 82 +#define MTK_SYS_AUDIOCTL 83 +#define MTK_SYS_BTSCAN 84 +#define MTK_SYS_BTCONNECT 85 +#define MTK_SYS_BTDISCONNECT 86 +#define MTK_SYS_BTLIST 87 +#define MTK_SYS_BTINFO 88 +#define MTK_SYS_SUSPEND 89 +#define MTK_SYS_SETTZ 90 +#define MTK_SYS_GETTZ 91 +#define MTK_SYS_SETUSER 92 +#define MTK_SYS_GETUSER 93 +#define MTK_SYS_FRENAME 94 +#define MTK_SYS_GETCWD 95 +#define MTK_SYS_CHDIR 96 +#define MTK_SYS_WINUNMAP 97 +#define MTK_SYS_DUPHANDLE 98 +#define MTK_SYS_WAIT_HANDLE 99 +#define MTK_SYS_STREAM_CREATE 100 +#define MTK_SYS_STREAM_READ 101 +#define MTK_SYS_STREAM_WRITE 102 +#define MTK_SYS_MAILBOX_CREATE 103 +#define MTK_SYS_MAILBOX_SEND 104 +#define MTK_SYS_MAILBOX_RECV 105 +#define MTK_SYS_WAITSET_CREATE 106 +#define MTK_SYS_WAITSET_ADD 107 +#define MTK_SYS_WAITSET_REMOVE 108 +#define MTK_SYS_WAITSET_WAIT 109 +#define MTK_SYS_PROC_OPEN 110 +#define MTK_SYS_SURFACE_CREATE 111 +#define MTK_SYS_SURFACE_MAP 112 +#define MTK_SYS_SURFACE_RESIZE 113 +#define MTK_SYS_LOAD_LIB 114 +#define MTK_SYS_UNLOAD_LIB 115 +#define MTK_SYS_DLSYM 116 +#define MTK_SYS_GETLIBBASE 117 +#define MTK_SYS_CRASH_REPORT 118 +#define MTK_SYS_CLIPBOARD_SET_TEXT 119 +#define MTK_SYS_CLIPBOARD_GET_INFO 120 +#define MTK_SYS_CLIPBOARD_GET_TEXT 121 +#define MTK_SYS_CLIPBOARD_CLEAR 122 +#define MTK_SYS_INPUT_WAIT 123 +#define MTK_SYS_DRIVELABEL 124 +#define MTK_SYS_NETSTATUS 125 +#define MTK_SYS_WINSETFLAGS 126 +#define MTK_SYS_DRIVEKIND 127 +#define MTK_SYS_AUDIOLIST 128 +#define MTK_SYS_AUDIOWAIT 129 +#define MTK_SYS_THREAD_SPAWN 130 +#define MTK_SYS_THREAD_EXIT 131 +#define MTK_SYS_THREAD_JOIN 132 +#define MTK_SYS_THREAD_SELF 133 +#define MTK_SYS_FS_SYNC 134 +#define MTK_SYS_POWER_REQUEST 135 +#define MTK_SYS_READDIR_AT 136 +#define MTK_SYS_BTSETADDR 137 +#define MTK_SYS_BTBONDS 138 +#define MTK_SYS_BTFORGET 139 +#define MTK_SYS_SDR_COUNT 140 +#define MTK_SYS_SDR_INFO 141 +#define MTK_SYS_SDR_OPEN 142 +#define MTK_SYS_SDR_CLOSE 143 +#define MTK_SYS_SDR_START 144 +#define MTK_SYS_SDR_STOP 145 +#define MTK_SYS_SDR_READ 146 +#define MTK_SYS_SDR_SETPARAM 147 +#define MTK_SYS_SDR_GETPARAM 148 +#define MTK_SYS_POWERINFO 149 +#define MTK_SYS_FBFLIP 150 +#define MTK_SYS_GETEXECPATH 151 +#define MTK_SYS_STAT 152 +#define MTK_SYS_SETUNIXTIME 153 +#define MTK_SYS_DISPLAYINFO 154 +#define MTK_SYS_DISPLAYMODES 155 +#define MTK_SYS_DISPLAYSETMODE 156 +#define MTK_SYS_DISPLAYBRIGHTNESS 157 +#define MTK_SYS_WIFI_SCAN 158 +#define MTK_SYS_WIFI_INFO 159 +#define MTK_SYS_WIFI_CONNECT 160 +#define MTK_SYS_WIFI_DISCONNECT 161 +#define MTK_SYS_WIFI_SCAN_START 162 +#define MTK_SYS_WIFI_RESULTS 163 +#define MTK_SYS_WIFI_CONNECT_ASYNC 164 +#define MTK_SYS_NETIFS 165 +/* @SYSCALLS-END */ #define MTK_SOCK_TCP 1 #define MTK_SOCK_UDP 2 @@ -354,6 +425,10 @@ static inline int mtk_getargs(char *buf, unsigned long max_len) { return (int)_mtk_syscall2(MTK_SYS_GETARGS, (long)buf, (long)max_len); } +static inline int mtk_getexecpath(char *buf, unsigned long maxLen) { + return (int)mtk_syscall2(MTK_SYS_GETEXECPATH, (long)buf, (long)maxLen); +} + static inline int mtk_chdir(const char *path) { return (int)_mtk_syscall1(MTK_SYS_CHDIR, (long)path); } diff --git a/template/sysroot/include/libc/signal.h b/template/sysroot/include/libc/signal.h index 945a391..cbfcecc 100644 --- a/template/sysroot/include/libc/signal.h +++ b/template/sysroot/include/libc/signal.h @@ -3,6 +3,8 @@ #pragma once +#include + #ifdef __cplusplus extern "C" { #endif @@ -10,7 +12,26 @@ extern "C" { typedef int sig_atomic_t; typedef void (*sighandler_t)(int); -#define SIGINT 2 +/* Linux signal numbering. Only SIGINT is ever delivered today; the + rest exist so hosted code can name them. */ +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 #define SIG_DFL ((sighandler_t)0) #define SIG_IGN ((sighandler_t)1) @@ -18,6 +39,7 @@ typedef void (*sighandler_t)(int); sighandler_t signal(int sig, sighandler_t handler); int raise(int sig); +int kill(pid_t pid, int sig); #ifdef __cplusplus } diff --git a/template/sysroot/include/libc/spawn.h b/template/sysroot/include/libc/spawn.h new file mode 100644 index 0000000..bfe17bc --- /dev/null +++ b/template/sysroot/include/libc/spawn.h @@ -0,0 +1,60 @@ +#ifndef _LIBC_SPAWN_H +#define _LIBC_SPAWN_H + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * posix_spawn for MontaukOS, layered on SYS_SPAWN. + * + * Limitations (kernel spawn model): + * - argv is joined into a single args string; arguments containing + * spaces are rejected with EINVAL (no quoting in the kernel). + * - envp is ignored (no environment transfer on spawn). + * - file actions must be empty: stdio redirection needs kernel + * support that does not exist yet, so any recorded action makes + * posix_spawn fail with ENOTSUP rather than misbehave silently. +*/ + +typedef struct { + short flags; +} posix_spawnattr_t; + +typedef struct { + int action_count; +} posix_spawn_file_actions_t; + +int posix_spawnattr_init(posix_spawnattr_t *attr); +int posix_spawnattr_destroy(posix_spawnattr_t *attr); +int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags); +int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags); + +int posix_spawn_file_actions_init(posix_spawn_file_actions_t *actions); +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *actions); +int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *actions, + int fd, int newfd); +int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *actions, + int fd); +int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *actions, + int fd, const char *path, int oflag, + mode_t mode); + +int posix_spawn(pid_t *pid, const char *path, + const posix_spawn_file_actions_t *actions, + const posix_spawnattr_t *attr, + char *const argv[], char *const envp[]); +int posix_spawnp(pid_t *pid, const char *file, + const posix_spawn_file_actions_t *actions, + const posix_spawnattr_t *attr, + char *const argv[], char *const envp[]); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBC_SPAWN_H */ diff --git a/template/sysroot/include/libc/stdio.h b/template/sysroot/include/libc/stdio.h index b88194d..a3c9dfd 100644 --- a/template/sysroot/include/libc/stdio.h +++ b/template/sysroot/include/libc/stdio.h @@ -1,6 +1,12 @@ #ifndef _LIBC_STDIO_H #define _LIBC_STDIO_H +/* Conventional guard marker: packages like GMP sniff the libc's stdio + include-guard name to detect that FILE is available. */ +#ifndef _STDIO_H +#define _STDIO_H 1 +#endif + #pragma once #include @@ -36,6 +42,21 @@ extern FILE *stdin; extern FILE *stdout; extern FILE *stderr; +int putc(int c, FILE *stream); +void rewind(FILE *stream); +int getchar(void); +typedef long fpos_t; + +int fgetpos(FILE *stream, fpos_t *pos); +int fsetpos(FILE *stream, const fpos_t *pos); +void setbuf(FILE *stream, char *buf); +int fscanf(FILE *stream, const char *fmt, ...); +int scanf(const char *fmt, ...); +int vfscanf(FILE *stream, const char *fmt, va_list ap); + +int fileno(FILE *stream); +FILE *fdopen(int fd, const char *mode); + int printf(const char *fmt, ...); int fprintf(FILE *stream, const char *fmt, ...); int sprintf(char *str, const char *fmt, ...); diff --git a/template/sysroot/include/libc/stdlib.h b/template/sysroot/include/libc/stdlib.h index 37d1c83..3c8f5d1 100644 --- a/template/sysroot/include/libc/stdlib.h +++ b/template/sysroot/include/libc/stdlib.h @@ -49,10 +49,24 @@ int unsetenv(const char *name); int putenv(char *string); void qsort(void *base, size_t nmemb, size_t size, - int (*compar)(const void *, const void *)); + int (*compar)(const void *, const void *)); +void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); long strtol(const char *nptr, char **endptr, int base); unsigned long strtoul(const char *nptr, char **endptr, int base); +long long strtoll(const char *nptr, char **endptr, int base); +unsigned long long strtoull(const char *nptr, char **endptr, int base); +long long atoll(const char *nptr); +int mkstemp(char *template_); +char *mktemp(char *template_); +char *realpath(const char *path, char *resolved); +size_t mbstowcs(wchar_t *dst, const char *src, size_t n); +#define MB_CUR_MAX 1 + +int mblen(const char *s, size_t n); +int mbtowc(wchar_t *pwc, const char *s, size_t n); +int wctomb(char *s, wchar_t wc); double strtod(const char *nptr, char **endptr); float strtof(const char *nptr, char **endptr); long double strtold(const char *nptr, char **endptr); diff --git a/template/sysroot/include/libc/string.h b/template/sysroot/include/libc/string.h index d9ff799..ba815d6 100644 --- a/template/sysroot/include/libc/string.h +++ b/template/sysroot/include/libc/string.h @@ -31,6 +31,9 @@ char *strpbrk(const char *s, const char *accept); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n); int strcoll(const char *s1, const char *s2); +size_t strxfrm(char *dest, const char *src, size_t n); +char *strtok(char *str, const char *delim); +char *strtok_r(char *str, const char *delim, char **saveptr); char *strstr(const char *haystack, const char *needle); const char *strerror(int errnum); diff --git a/template/sysroot/include/libc/sys/mman.h b/template/sysroot/include/libc/sys/mman.h new file mode 100644 index 0000000..b5aa231 --- /dev/null +++ b/template/sysroot/include/libc/sys/mman.h @@ -0,0 +1,41 @@ +#ifndef _LIBC_SYS_MMAN_H +#define _LIBC_SYS_MMAN_H + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Anonymous-memory mmap over SYS_ALLOC. SYS_ALLOC returns + * page-aligned process memory, which is exactly what callers like + * GCC's page allocator need. File-backed mappings are not + * supported and fail with ENODEV. +*/ + +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS + +#define MAP_FAILED ((void *)-1) + +void *mmap(void *addr, size_t length, int prot, int flags, int fd, + long offset); +int munmap(void *addr, size_t length); +int mprotect(void *addr, size_t length, int prot); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBC_SYS_MMAN_H */ diff --git a/template/sysroot/include/libc/sys/param.h b/template/sysroot/include/libc/sys/param.h new file mode 100644 index 0000000..b72791b --- /dev/null +++ b/template/sysroot/include/libc/sys/param.h @@ -0,0 +1,23 @@ +#ifndef _LIBC_SYS_PARAM_H +#define _LIBC_SYS_PARAM_H + +#pragma once + +#include +#include + +#ifndef MAXPATHLEN +#define MAXPATHLEN 4096 +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#define howmany(x, y) (((x) + ((y) - 1)) / (y)) +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + +#endif /* _LIBC_SYS_PARAM_H */ diff --git a/template/sysroot/include/libc/sys/stat.h b/template/sysroot/include/libc/sys/stat.h index 60c38f7..7dc995d 100644 --- a/template/sysroot/include/libc/sys/stat.h +++ b/template/sysroot/include/libc/sys/stat.h @@ -12,19 +12,55 @@ extern "C" { #define S_IFMT 0170000 #define S_IFDIR 0040000 #define S_IFREG 0100000 +#define S_IFCHR 0020000 +#define S_IFLNK 0120000 -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#define S_ISFIFO(mode) (0) +#define S_ISSOCK(mode) (0) +#define S_ISBLK(mode) (0) + +/* stat() reports real permission bits: ext2 stores them natively, the + ramdisk takes them from the USTAR header, and FAT32 synthesizes them from + its read-only attribute. Changing them (chmod) is still not supported. */ +#define S_IRWXU 0700 +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +#define S_IRWXG 0070 +#define S_IRGRP 0040 +#define S_IWGRP 0020 +#define S_IXGRP 0010 +#define S_IRWXO 0007 +#define S_IROTH 0004 +#define S_IWOTH 0002 +#define S_IXOTH 0001 struct stat { - mode_t st_mode; - unsigned long st_size; - time_t st_mtime; + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + time_t st_atime; + time_t st_mtime; + time_t st_ctime; }; -int mkdir(const char *path, unsigned int mode); -int stat(const char *path, struct stat *buf); -int fstat(int fd, struct stat *buf); +int mkdir(const char *path, unsigned int mode); +int stat(const char *path, struct stat *buf); +int fstat(int fd, struct stat *buf); +int lstat(const char *path, struct stat *buf); +int chmod(const char *path, mode_t mode); +int fchmod(int fd, mode_t mode); +mode_t umask(mode_t mask); #ifdef __cplusplus } diff --git a/template/sysroot/include/libc/sys/types.h b/template/sysroot/include/libc/sys/types.h index 350e0fb..5cb4582 100644 --- a/template/sysroot/include/libc/sys/types.h +++ b/template/sysroot/include/libc/sys/types.h @@ -3,6 +3,12 @@ #pragma once +/* Fixed-width and pointer-sized integer types. Freestanding builds get + this from the kernel freestanding headers; the cross toolchain from + the GCC-provided stdint.h. Hosted code (e.g. libgcov) expects + intptr_t to be visible via the stdio/stdlib include chain. */ +#include + #ifdef __cplusplus extern "C" { #endif @@ -15,6 +21,13 @@ typedef unsigned int mode_t; typedef unsigned int uid_t; typedef unsigned int gid_t; typedef long time_t; +typedef unsigned long ino_t; +typedef unsigned long dev_t; +typedef unsigned long nlink_t; +typedef long blkcnt_t; +typedef long blksize_t; +typedef long suseconds_t; +typedef int clockid_t; #ifdef __cplusplus } diff --git a/template/sysroot/include/libc/sys/wait.h b/template/sysroot/include/libc/sys/wait.h new file mode 100644 index 0000000..8d31a16 --- /dev/null +++ b/template/sysroot/include/libc/sys/wait.h @@ -0,0 +1,31 @@ +#ifndef _LIBC_SYS_WAIT_H +#define _LIBC_SYS_WAIT_H + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX status decoding. waitpid() encodes a normal exit as code<<8 + and a killed/crashed child as the signal number in the low bits. */ +#define WIFEXITED(s) (((s) & 0x7F) == 0) +#define WEXITSTATUS(s) (((s) >> 8) & 0xFF) +#define WIFSIGNALED(s) (((s) & 0x7F) != 0) +#define WTERMSIG(s) ((s) & 0x7F) +#define WIFSTOPPED(s) (0) +#define WSTOPSIG(s) (0) + +#define WNOHANG 1 +#define WUNTRACED 2 + +pid_t wait(int *status); +pid_t waitpid(pid_t pid, int *status, int options); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBC_SYS_WAIT_H */ diff --git a/template/sysroot/include/libc/time.h b/template/sysroot/include/libc/time.h index 77c5cca..d88a2cc 100644 --- a/template/sysroot/include/libc/time.h +++ b/template/sysroot/include/libc/time.h @@ -34,6 +34,9 @@ struct tm *localtime(const time_t *timer); time_t mktime(struct tm *tm); size_t strftime(char *s, size_t max, const char *format, const struct tm *tm); +char *asctime(const struct tm *tm); +char *ctime(const time_t *timep); + #ifdef __cplusplus } #endif diff --git a/template/sysroot/include/libc/unistd.h b/template/sysroot/include/libc/unistd.h index fe665a1..2217bbd 100644 --- a/template/sysroot/include/libc/unistd.h +++ b/template/sysroot/include/libc/unistd.h @@ -4,11 +4,16 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { #endif +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + #define F_OK 0 #define X_OK 1 #define W_OK 2 @@ -22,6 +27,36 @@ int chdir(const char *path); char *getcwd(char *buf, size_t size); int access(const char *path, int mode); int isatty(int fd); +int unlink(const char *path); +int rmdir(const char *path); +int dup(int fd); +int dup2(int oldfd, int newfd); +pid_t getpid(void); +void _exit(int status) __attribute__((noreturn)); +pid_t fork(void); +int execv(const char *path, char *const argv[]); +int execve(const char *path, char *const argv[], char *const envp[]); +int execvp(const char *file, char *const argv[]); +int pipe(int fds[2]); +long pathconf(const char *path, int name); +int getpagesize(void); +long sysconf(int name); + +/* sysconf() names (Linux numbering). */ +#define _SC_OPEN_MAX 4 +#define _SC_PAGESIZE 30 +#define _SC_PAGE_SIZE 30 +#define _SC_NPROCESSORS_ONLN 84 + +/* pathconf() names (Linux numbering). */ +#define _PC_LINK_MAX 0 +#define _PC_MAX_CANON 1 +#define _PC_MAX_INPUT 2 +#define _PC_NAME_MAX 3 +#define _PC_PATH_MAX 4 +#define _PC_PIPE_BUF 5 + +extern char **environ; unsigned int sleep(unsigned int seconds); diff --git a/template/sysroot/include/libc/utime.h b/template/sysroot/include/libc/utime.h new file mode 100644 index 0000000..00327b7 --- /dev/null +++ b/template/sysroot/include/libc/utime.h @@ -0,0 +1,25 @@ +#ifndef _LIBC_UTIME_H +#define _LIBC_UTIME_H + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct utimbuf { + time_t actime; + time_t modtime; +}; + +/* Accepted for POSIX compatibility; the Montauk VFS does not support + setting file times. */ +int utime(const char *path, const struct utimbuf *times); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBC_UTIME_H */ diff --git a/template/sysroot/include/libc/wchar.h b/template/sysroot/include/libc/wchar.h new file mode 100644 index 0000000..cd90739 --- /dev/null +++ b/template/sysroot/include/libc/wchar.h @@ -0,0 +1,29 @@ +#ifndef _LIBC_WCHAR_H +#define _LIBC_WCHAR_H + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int wint_t; + +/* Opaque shift state for the (byte-oriented) C locale. */ +typedef struct { + unsigned long __opaque; +} mbstate_t; + +#define WEOF ((wint_t)-1) + +/* Byte-oriented C locale only: one byte, one character. */ +size_t mbstowcs(wchar_t *dst, const char *src, size_t n); +int mblen(const char *s, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBC_WCHAR_H */ diff --git a/template/sysroot/include/montauk/heap.h b/template/sysroot/include/montauk/heap.h index 8d306d7..5ed05ce 100644 --- a/template/sysroot/include/montauk/heap.h +++ b/template/sysroot/include/montauk/heap.h @@ -123,13 +123,28 @@ namespace heap_detail { return nullptr; } - static inline bool grow(uint64_t bytes) { - uint64_t pages = (bytes + 0xFFF) / 0x1000; - if (pages < 4) pages = 4; + // Next slab size for heap growth. The kernel tracks a finite number + // of SYS_ALLOC records per process (MaxHeapAllocs), so growing once + // per large allocation exhausts them under allocation-heavy loads + // (the native ld ran out mid-link). Doubling slabs keep the syscall + // count logarithmic in total heap size. + inline uint64_t g_grow_slab = 16 * 0x1000; - void* mem = montauk::alloc(pages * 0x1000); + static inline bool grow(uint64_t bytes) { + uint64_t want = (bytes + 0xFFF) & ~0xFFFULL; + if (want < 0x4000) want = 0x4000; + + uint64_t slab = (want > g_grow_slab) ? want : g_grow_slab; + if (g_grow_slab < 4 * 1024 * 1024) g_grow_slab *= 2; + + void* mem = montauk::alloc(slab); + if (mem == nullptr && slab > want) { + // Big slab refused (low memory): retry with the exact need. + slab = want; + mem = montauk::alloc(slab); + } if (mem == nullptr) return false; - insert_overflow(mem, pages * 0x1000); + insert_overflow(mem, slab); return true; } diff --git a/template/sysroot/include/montauk/syscall.h b/template/sysroot/include/montauk/syscall.h index 2663f53..93efccc 100644 --- a/template/sysroot/include/montauk/syscall.h +++ b/template/sysroot/include/montauk/syscall.h @@ -165,6 +165,11 @@ namespace montauk { inline int frename(const char* oldPath, const char* newPath) { return (int)syscall2(montauk::abi::SYS_FRENAME, (uint64_t)oldPath, (uint64_t)newPath); } + // Fill *out with metadata (size, timestamps, mode) for the path. + // Returns 0 on success, -1 on error or if the filesystem lacks stat support. + inline int stat(const char* path, montauk::abi::FileStat* out) { + return (int)syscall2(montauk::abi::SYS_STAT, (uint64_t)path, (uint64_t)out); + } inline int drivelist(int* outDrives, int max) { return (int)syscall2(montauk::abi::SYS_DRIVELIST, (uint64_t)outDrives, (uint64_t)max); } @@ -321,16 +326,24 @@ namespace montauk { } // Process management - inline void waitpid(int pid) { syscall1(montauk::abi::SYS_WAITPID, (uint64_t)pid); } + // Blocks until pid exits. Returns 0..255 for a normal exit, + // 256+signal when the process was killed or crashed. + inline int waitpid(int pid) { return (int)syscall1(montauk::abi::SYS_WAITPID, (uint64_t)pid); } // Framebuffer inline void fb_info(montauk::abi::FbInfo* info) { syscall1(montauk::abi::SYS_FBINFO, (uint64_t)info); } inline void* fb_map() { return (void*)syscall0(montauk::abi::SYS_FBMAP); } - // Page flip between two scanout buffers. index -1 queries support; - // index -2 acquires ownership and returns the live front buffer. + + // Page flip between the two scanout buffers. fb_map() maps buffer 1 + // directly after buffer 0 (at +page_align(height*pitch)). index selects + // the buffer to show; the hardware latches it at vblank (tear-free). + // flags bit0 = block until the flip has been latched (vsync). + // fb_flip(-1, 0) returns 1 when page flipping is available, 0 when not. + // fb_flip(-2, 0) acquires ownership and returns the live buffer (0 or 1). inline int64_t fb_flip(int64_t index, uint64_t flags) { return syscall2(montauk::abi::SYS_FBFLIP, (uint64_t)index, flags); } + inline int display_info(montauk::abi::DisplayInfo* out) { return (int)syscall1(montauk::abi::SYS_DISPLAYINFO, (uint64_t)out); } @@ -562,6 +575,20 @@ namespace montauk { inline int bt_disconnect(const uint8_t* bdAddr) { return (int)syscall1(montauk::abi::SYS_BTDISCONNECT, (uint64_t)bdAddr); } + // Change the adapter's BD_ADDR. bdAddr is a 6-byte buffer with bdAddr[0] as + // the least-significant octet (same order as BtAdapterInfo::bdAddr). Returns + // 0 on success, negative on failure. Persist separately to bluetooth.toml. + inline int bt_set_addr(const uint8_t* bdAddr) { + return (int)syscall1(montauk::abi::SYS_BTSETADDR, (uint64_t)bdAddr); + } + // List bonded (paired) devices. Returns count written to buf (may be 0). + inline int bt_bonds(montauk::abi::BtBondInfo* buf, int maxCount) { + return (int)syscall2(montauk::abi::SYS_BTBONDS, (uint64_t)buf, (uint64_t)maxCount); + } + // Forget a paired device (removes the bond; it must re-pair next time). + inline int bt_forget(const uint8_t* bdAddr) { + return (int)syscall1(montauk::abi::SYS_BTFORGET, (uint64_t)bdAddr); + } inline int bt_list(montauk::abi::BtDevInfo* buf, int maxCount) { return (int)syscall2(montauk::abi::SYS_BTLIST, (uint64_t)buf, (uint64_t)maxCount); } @@ -569,6 +596,111 @@ namespace montauk { return (int)syscall1(montauk::abi::SYS_BTINFO, (uint64_t)buf); } + // Wi-Fi. scan() runs a full channel sweep and blocks until it finishes or + // timeoutMs elapses, then fills buf with the networks seen; it returns the + // number of entries written, or -1 when no adapter is ready. + inline int wifi_scan(montauk::abi::WifiNetwork* buf, int maxCount, + uint32_t timeoutMs) { + return (int)syscall3(montauk::abi::SYS_WIFI_SCAN, (uint64_t)buf, + (uint64_t)maxCount, (uint64_t)timeoutMs); + } + inline int wifi_info(montauk::abi::WifiInfo* out) { + return (int)syscall1(montauk::abi::SYS_WIFI_INFO, (uint64_t)out); + } + // Join a network. Blocks until the link is up or the attempt fails, and + // returns 0 or a WIFI_ERR_* code. + inline int wifi_connect(const char* ssid, const char* password) { + return (int)syscall2(montauk::abi::SYS_WIFI_CONNECT, (uint64_t)ssid, + (uint64_t)password); + } + inline int wifi_disconnect() { + return (int)syscall0(montauk::abi::SYS_WIFI_DISCONNECT); + } + + // Non-blocking pair for GUI code, which cannot stall for the seconds a + // sweep or a handshake takes. scan_start() kicks off a sweep (0 started, + // 1 one was already running, -1 no adapter); wifi_info().scanning drops + // back to 0 and .scanGeneration moves on when it finishes, and + // wifi_results() copies out the table without touching the radio. + inline int wifi_scan_start(uint32_t timeoutMs) { + return (int)syscall1(montauk::abi::SYS_WIFI_SCAN_START, (uint64_t)timeoutMs); + } + inline int wifi_results(montauk::abi::WifiNetwork* buf, int maxCount) { + return (int)syscall2(montauk::abi::SYS_WIFI_RESULTS, (uint64_t)buf, + (uint64_t)maxCount); + } + // Returns 0 once the join is under way, or a WIFI_ERR_* it failed on + // before any frame went out. Watch wifi_info().joining for progress and + // .lastError for the outcome. + inline int wifi_connect_async(const char* ssid, const char* password) { + return (int)syscall2(montauk::abi::SYS_WIFI_CONNECT_ASYNC, (uint64_t)ssid, + (uint64_t)password); + } + + // List the registered link-layer interfaces. The IP configuration is + // global to the stack; it belongs to whichever entry has active = 1. + inline int net_interfaces(montauk::abi::NetIfInfo* buf, int maxCount) { + return (int)syscall2(montauk::abi::SYS_NETIFS, (uint64_t)buf, + (uint64_t)maxCount); + } + + // Software-defined radio (Rx). Receivers are identified by index [0, count); + // open() returns a handle used by the rest of the calls. Samples are read + // as interleaved 8-bit unsigned I/Q (CU8) from the device's ring buffer. + inline int sdr_count() { + return (int)syscall0(montauk::abi::SYS_SDR_COUNT); + } + inline int sdr_info(int index, montauk::abi::SdrDeviceInfo* out) { + return (int)syscall2(montauk::abi::SYS_SDR_INFO, (uint64_t)index, (uint64_t)out); + } + inline int sdr_open(int index) { + return (int)syscall1(montauk::abi::SYS_SDR_OPEN, (uint64_t)index); + } + inline int sdr_close(int handle) { + return (int)syscall1(montauk::abi::SYS_SDR_CLOSE, (uint64_t)handle); + } + inline int sdr_start(int handle) { + return (int)syscall1(montauk::abi::SYS_SDR_START, (uint64_t)handle); + } + inline int sdr_stop(int handle) { + return (int)syscall1(montauk::abi::SYS_SDR_STOP, (uint64_t)handle); + } + // Non-blocking: copies up to len bytes of queued I/Q, returns bytes copied. + inline int sdr_read(int handle, void* buf, uint32_t len) { + return (int)syscall3(montauk::abi::SYS_SDR_READ, (uint64_t)handle, (uint64_t)buf, (uint64_t)len); + } + inline int sdr_set_param(int handle, int param, uint64_t value) { + return (int)syscall3(montauk::abi::SYS_SDR_SETPARAM, (uint64_t)handle, (uint64_t)param, value); + } + inline int64_t sdr_get_param(int handle, int param) { + return syscall2(montauk::abi::SYS_SDR_GETPARAM, (uint64_t)handle, (uint64_t)param); + } + // Convenience wrappers over sdr_set_param / sdr_get_param. + inline int sdr_set_freq(int handle, uint64_t hz) { + return sdr_set_param(handle, montauk::abi::SDR_PARAM_FREQ, hz); + } + inline uint64_t sdr_get_freq(int handle) { + return (uint64_t)sdr_get_param(handle, montauk::abi::SDR_PARAM_FREQ); + } + inline int sdr_set_sample_rate(int handle, uint32_t hz) { + return sdr_set_param(handle, montauk::abi::SDR_PARAM_SAMPLE_RATE, hz); + } + inline uint32_t sdr_get_sample_rate(int handle) { + return (uint32_t)sdr_get_param(handle, montauk::abi::SDR_PARAM_SAMPLE_RATE); + } + inline int sdr_set_gain_mode(int handle, int manual) { + return sdr_set_param(handle, montauk::abi::SDR_PARAM_GAIN_MODE, (uint64_t)manual); + } + inline int sdr_set_gain(int handle, int tenthsDb) { + return sdr_set_param(handle, montauk::abi::SDR_PARAM_GAIN, (uint64_t)(int64_t)tenthsDb); + } + inline int sdr_set_freq_correction(int handle, int ppm) { + return sdr_set_param(handle, montauk::abi::SDR_PARAM_FREQ_CORR, (uint64_t)(int64_t)ppm); + } + inline int sdr_set_agc(int handle, int on) { + return sdr_set_param(handle, montauk::abi::SDR_PARAM_AGC, (uint64_t)on); + } + // Kernel introspection inline void memstats(montauk::abi::MemStats* out) { syscall1(montauk::abi::SYS_MEMSTATS, (uint64_t)out); } diff --git a/template/sysroot/include/montauk/wifi.h b/template/sysroot/include/montauk/wifi.h new file mode 100644 index 0000000..f909857 --- /dev/null +++ b/template/sysroot/include/montauk/wifi.h @@ -0,0 +1,255 @@ +/* + * wifi.h + * Saved Wi-Fi networks (0:/config/wifi.toml) and small formatting helpers + * shared by the desktop panel, the Network app and the wifi command. + * + * The file looks like this: + * + * [wifi] + * autoconnect = true + * + * [network.0] + * ssid = "Home" + * psk = "passphrase" + * + * The passphrase is stored as typed, because that is what the join needs: + * the kernel derives the PMK from it (or takes a 64-character hex string as + * a raw PSK). Anyone who can read 0:/config can read the keys. + * + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include +#include + +namespace montauk { +namespace wifi { + + // The scan table the kernel keeps is 64 entries; saving that many networks + // is already far more than a laptop accumulates. + static constexpr int MAX_SAVED = 32; + static constexpr int SSID_CAP = 36; + static constexpr int PSK_CAP = 72; // 64-character hex PSK plus NUL + + struct SavedNetwork { + char ssid[SSID_CAP]; + char psk[PSK_CAP]; + }; + + struct SavedList { + SavedNetwork items[MAX_SAVED]; + int count; + bool autoconnect; + }; + + // ---- helpers ----------------------------------------------------------- + + inline void copy_str(char* dst, int cap, const char* src) { + int i = 0; + for (; src && src[i] && i < cap - 1; i++) dst[i] = src[i]; + dst[i] = '\0'; + } + + // "network.." + inline void network_key(char* out, int cap, int index, const char* field) { + char idx[8]; + int n = 0; + if (index == 0) { + idx[n++] = '0'; + } else { + char tmp[8]; + int t = 0; + for (int v = index; v > 0 && t < (int)sizeof(tmp); v /= 10) + tmp[t++] = (char)('0' + (v % 10)); + while (t > 0) idx[n++] = tmp[--t]; + } + idx[n] = '\0'; + + int p = 0; + const char* prefix = "network."; + while (*prefix && p < cap - 1) out[p++] = *prefix++; + for (int i = 0; i < n && p < cap - 1; i++) out[p++] = idx[i]; + if (p < cap - 1) out[p++] = '.'; + while (field && *field && p < cap - 1) out[p++] = *field++; + out[p] = '\0'; + } + + // ---- load / store ------------------------------------------------------ + + inline void saved_load(SavedList* out) { + if (!out) return; + out->count = 0; + out->autoconnect = true; + + auto doc = montauk::config::load("wifi"); + out->autoconnect = doc.get_bool("wifi.autoconnect", true); + + for (int i = 0; i < MAX_SAVED; i++) { + char key[64]; + network_key(key, sizeof(key), i, "ssid"); + const char* ssid = doc.get_string(key, nullptr); + if (!ssid || !ssid[0]) break; // entries are written contiguously + + network_key(key, sizeof(key), i, "psk"); + const char* psk = doc.get_string(key, ""); + + SavedNetwork& n = out->items[out->count++]; + copy_str(n.ssid, SSID_CAP, ssid); + copy_str(n.psk, PSK_CAP, psk); + } + + doc.destroy(); + } + + // Rewrite the file from the list. Returns 0 on success. + inline int saved_store(const SavedList* list) { + if (!list) return -1; + + montauk::toml::Doc doc; + doc.init(); + montauk::config::set_bool(&doc, "wifi.autoconnect", list->autoconnect); + + for (int i = 0; i < list->count && i < MAX_SAVED; i++) { + char key[64]; + network_key(key, sizeof(key), i, "ssid"); + montauk::config::set_string(&doc, key, list->items[i].ssid); + network_key(key, sizeof(key), i, "psk"); + montauk::config::set_string(&doc, key, list->items[i].psk); + } + + int rc = montauk::config::save("wifi", &doc); + doc.destroy(); + return rc; + } + + inline int saved_index_of(const SavedList* list, const char* ssid) { + if (!list || !ssid) return -1; + for (int i = 0; i < list->count; i++) { + if (montauk::streq(list->items[i].ssid, ssid)) return i; + } + return -1; + } + + inline const SavedNetwork* saved_find(const SavedList* list, const char* ssid) { + int idx = saved_index_of(list, ssid); + return idx < 0 ? nullptr : &list->items[idx]; + } + + // Add or update an entry in memory. Returns false when the list is full. + inline bool saved_set(SavedList* list, const char* ssid, const char* psk) { + if (!list || !ssid || !ssid[0]) return false; + int idx = saved_index_of(list, ssid); + if (idx < 0) { + if (list->count >= MAX_SAVED) return false; + idx = list->count++; + copy_str(list->items[idx].ssid, SSID_CAP, ssid); + } + copy_str(list->items[idx].psk, PSK_CAP, psk ? psk : ""); + return true; + } + + inline bool saved_remove(SavedList* list, const char* ssid) { + int idx = saved_index_of(list, ssid); + if (idx < 0) return false; + for (int i = idx; i < list->count - 1; i++) list->items[i] = list->items[i + 1]; + list->count--; + return true; + } + + // Convenience wrappers that touch the file directly. + + inline bool remember(const char* ssid, const char* psk) { + SavedList list; + saved_load(&list); + if (!saved_set(&list, ssid, psk)) return false; + return saved_store(&list) == 0; + } + + inline bool forget(const char* ssid) { + SavedList list; + saved_load(&list); + if (!saved_remove(&list, ssid)) return false; + return saved_store(&list) == 0; + } + + // Copy the saved passphrase for `ssid` into out. False when not saved. + inline bool lookup(const char* ssid, char* out, int cap) { + SavedList list; + saved_load(&list); + const SavedNetwork* n = saved_find(&list, ssid); + if (!n) return false; + copy_str(out, cap, n->psk); + return true; + } + + // ---- presentation ------------------------------------------------------ + + inline const char* security_name(uint8_t security) { + switch (security) { + case montauk::abi::WIFI_SEC_OPEN: return "Open"; + case montauk::abi::WIFI_SEC_WEP: return "WEP"; + case montauk::abi::WIFI_SEC_WPA: return "WPA"; + case montauk::abi::WIFI_SEC_WPA2: return "WPA2"; + case montauk::abi::WIFI_SEC_WPA3: return "WPA3"; + default: return "Unknown"; + } + } + + inline bool needs_key(uint8_t security) { + return security != montauk::abi::WIFI_SEC_OPEN; + } + + // 0-4 bars from an RSSI in dBm. + inline int signal_bars(int8_t rssi) { + if (rssi >= -55) return 4; + if (rssi >= -67) return 3; + if (rssi >= -75) return 2; + if (rssi >= -85) return 1; + return 0; + } + + inline const char* state_name(uint8_t state) { + switch (state) { + case montauk::abi::WIFI_STATE_ABSENT: return "No adapter"; + case montauk::abi::WIFI_STATE_DETECTED: return "Loading firmware"; + case montauk::abi::WIFI_STATE_BOOTING: return "Starting"; + case montauk::abi::WIFI_STATE_RUNNING: return "Ready"; + case montauk::abi::WIFI_STATE_ERROR: return "Adapter error"; + case montauk::abi::WIFI_STATE_RFKILL: return "Radio off"; + default: return "Unknown"; + } + } + + // What the join is doing right now, for a progress line. + inline const char* conn_state_name(uint32_t connState) { + switch (connState) { + case montauk::abi::WIFI_CONN_IDLE: return "Not connected"; + case montauk::abi::WIFI_CONN_CONTEXTS_UP: return "Preparing radio..."; + case montauk::abi::WIFI_CONN_AUTHENTICATING: return "Authenticating..."; + case montauk::abi::WIFI_CONN_AUTHENTICATED: return "Authenticated"; + case montauk::abi::WIFI_CONN_ASSOCIATING: return "Associating..."; + case montauk::abi::WIFI_CONN_ASSOCIATED: return "Associated"; + case montauk::abi::WIFI_CONN_HANDSHAKING: return "Exchanging keys..."; + case montauk::abi::WIFI_CONN_CONNECTED: return "Connected"; + case montauk::abi::WIFI_CONN_FAILED: return "Connection failed"; + default: return ""; + } + } + + inline const char* error_message(int err) { + switch (err) { + case 0: return ""; + case montauk::abi::WIFI_ERR_NO_ADAPTER: return "No Wi-Fi adapter is ready"; + case montauk::abi::WIFI_ERR_NOT_FOUND: return "That network is out of range"; + case montauk::abi::WIFI_ERR_NEED_KEY: return "This network needs a password"; + case montauk::abi::WIFI_ERR_UNSUPPORTED: return "This security type is not supported"; + case montauk::abi::WIFI_ERR_AUTH: return "Wrong password"; + case montauk::abi::WIFI_ERR_TIMEOUT: return "The network did not respond"; + default: return "Could not join the network"; + } + } + +} // namespace wifi +} // namespace montauk diff --git a/template/sysroot/lib/crt1.o b/template/sysroot/lib/crt1.o index 5186a5a72e544f84c9ec281e5ba6bbde7df87384..ef5b27cf1f260a7ce048e3d2f5e668722a4461d0 100644 GIT binary patch literal 2832 zcmbW3U1%It6vyvm^JPssYpS5Mu-g~$iy<2YQB-!w)=VIz#Te;JStr}wCg5(u>}>jh zV9IW_gGLG#EWYTIBEIy+y2V&ix26`rGRL z#~S8z!#T6Df)!z&lZx89S$l(~Pb4qk*=2(Bh$_47HF$|7G0*KYoa(x<@>kOTt#;MV zJ3;SPq@Yys-#^>*cSW8@>1nM3{lEquW!)fo?W$ROhju#Ei-vRj&nA-8&d@dZnzC2k zwSzBjoC@zhnY-j%?){wBq-jm}w|xrq6E0z&!cLd(Dwv53^?!#`ZK(EpH@8Se%tUlO z6I{E{$`@RgX)~(@60T@YCDlFZYZO_uJK2Zlgh}P?KiOd$dQ`&^W{Wnbp{9 zy%SaWoAG+oLEbBFwhPn*v5qHOh#2jN8jgF9rkAy{C?@_}!QHfGAkS?qqJs=(t)(cltzC3FYKCAW}pQtrZE>B8UY z!apG1>Aw5BaFO%;l$8@%uTshu7D@|Q*Yn(?!u1Z#RSMRaSaSV&L@A(UI$2pwmd%$A zZUtrE^?c#_`6CNHtt@$Y%ISwmv*BWthB-ROGz3l2T_{nfL)%79{nQ`>A*L^)5Li?8 z00&W?XAW%)F(*45<$NwqYg|8lZ!_;9k39d-%LzNr9QW1pUu53WbN+9cN9*b12X5tI zU9k&aW{x}<%3jp;df%#=9yKXD$sBLy5DoQQtD1gTiOHYV@^9Dp`(5~1Ef4)!XxT@a zUO&fIy6|tbJhIQ6={x4A{|*|e{!NXKXgo`n9h4}N~NtoImfCk^y0O+&X)KJ@#!IXFW|r9P|p{=)910j~cR;E(D}1z?MmBOW4- zoYDPhwIBbUXqMDsgee-xiSM{>YQO&OVqM)|`82apENZrg@4rK12-QC2rs&wn4(S%%|1a@}P7VM7 delta 571 zcmbOrc7l6?M*RsU1~_m4vm6+B7`PcicSr(x9-U7eLmc-oFfuR%d-Trz@b~|JkH$9) zKs_Fv_kB8_@w0jyZ~gG^|9_Cq)&u-~EkJq~h-Z0$-~W)ujAJdVAWaPX+gd?%%Yl+q z8wHQv-XB0c9-TM#YXVJXC>7zC2dW5Q^k_c7*zilCG_b`M5{#R**Y9j<+0zx#tB?1}JsI@Bq-U z{H&85m?Z2#LO>t_p%@sef*2Sp1Q?}x*f}NuO_BkMgD?oZ1QCvW0&PqlylhTA%CtqMPn{37`G>MBvR0tF>PykjAWb;oBWD%d-z`|)#T#{H+!jPMonFm&)4^_ww zq?w^?5Oo{MW&zTylWW^e YrOhb-RVOleBbPa60hAXp`6Gus0NAFE)c^nh diff --git a/template/sysroot/lib/libjpeg.a b/template/sysroot/lib/libjpeg.a index f1b0586b74a809940d5da5fe4a102377a2aa7e75..1b4f7280fb0460cd8f05a383520230f7d941ba3d 100644 GIT binary patch delta 2422 zcmZWr3v5$m6yAUSTW*=5y|;{(mRHrtAdzlE+-B5lLDLpcHcc3)xD5m|C9aA%8WXog zja`hgjycYZ!K%S^LfnI}{TnJp8S9v6H)kLQqcai3=a`Q%g@Na`*TsuXnlJx(eCM3+ zf3$6l8Alp3!Y*_E&G|Ry=4Y?yyly#6FB&(33vSA741$LhJ{N^h6qtwtoZdFyw|1TT zj+`8K&I-?RPqus6W0iqr+qZ66_H>|fD=v5v4vOkZXpltWNmv5%V}Z&Yo1WT+e?J8e z;C)qayLhDv4(7p@S?F(rDtN|$u3%iWbLs0Mfd3MVCf@F+(-iF0pRP$9Uc65OUwktSFYj!slbb|xXf*ZQQ z%g$h=8=~wi`ULpEtzuYE3x_a9OE!XX55%a=cn_4ZKQOZwvLK}5hF&6m--1p=xl{}yr?a21=+f|m?9dX^$KTgBvAsN$>=13!?yOEIB=U1a?T zWgH%{i107)-8HaM5y8J;g0T@1xCmAkw)KG-Uzo!c=U!cm-M1Kr9K-fG+`%R=K8O3* zAX@F*0bN$H#Lm|;sI-WngI9v9S=Z2Ut1-P%-Q(gdu+oC9F1`z78|KgBUiK5#%;WWJ zD_ZCCC|ieOJ}-0i>$_*Y&B$@R4YL<;JM2}3e*u3Tz^mf9MZATLW4)W28phKE;8M|j zEw{5Nk#{W*n8+Jlob-+nI9+;!V+BU?DN&S}ijLjL9j;;hrfgqn3?uUMHzvvDWjw}- zJf58-x90qZ{KTEctSJ%5;bn6)nTEV16Ev$>xQa$;q?5mjdo^9&Sj9_8mp!YKviMf> zGLj{_Iw?!h8eR|m793r}?XK(f*1Y!^i;yhJUA&ZQvOHdtl*Lh;?5&JodhploHKt98 zSTQfqWV*608SN`ci5M8NF?$%xHgd-_`TJM7r$qfmUL+Cu*X4A2 zhVepqio9@Bl6?Ipe!+vg1eirhn4gzmp;h>s_~mQZ=jeNrmjTQct#4|IEw|v@eWXy0 zg28><18xOdwe>Lt&2N)}FDY0|nC{ol@4OnU9pHBCew)8R&&B!{60!lWv{3nKIzLhV z6=UNf^BpcTvS;m2*@OG}!;D^;uMhBqiR|s2Df?t6pCN1cJ$imGKQ)t`!$rsF7CfzB z{xM#qVF%&#{r~V~qgM1z6!y`kN9dvK;~tiUp+0^Z9#paXIPL2i{bVkzH>S<9VfZ*N zW%F_DIM0GQ3%dJ>C8lD2KM7G{!3si}5&L-zTIfW@xySjr{uF-FU}Sq1BXR0EPsN@% z$#%bO&j2sQ*W>ikK0Lq`*IRnt6OG2aP6flHC*)eOl`>$j4b3NL=tpc=OX%v-yU%Sh z<{eg18KgoxXy`#|^Ggg3@+{{=`W+v>D*RJS4AQpyEh774?O~zx+LKhaOAR%j}N0MhoO`I1zvIWLhl8|5hi zKZZ02qI{VkEJ7NvqfN4@2FwSV>P$yih43*`4JW#|WU65u=rfsO#469ARS-iORG>^5 zTCcLn$DEjuO?l~E+aW2w3jW!HqA$qlaD%- fXHrOUT`J@|36)g~$ZhH8e)Srd}=A`i-8iSHC delta 2386 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%0D;h$QGK=d@Hcm*O_LbTXh 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 literal 388078 zcmeFa4SW>Uxj#OW7uak$J4+D{JP=?8~Olnw6cMmNuOqnSayL@O+lY zafelsq$ee5=oR1neW!!}Uwt6@!%HOmttwgNTz#*ETXl(xZmTM5O6cxfwVH2tR!P;a zs`8Qwx>c{{o71=h2B{SUi#s9WsJJ_eTp}v&PJsy}h%v5SQFS+GDylBg^H(@SVP|D| zIX%XIx|LK_RaQx5)kWp2?<~T-xNK#ai{lmURk&BLE^?OO?q0pRgy706Yx%yqs;q+G zR=X>lbYEj+bXGtpB&@6|T79SFsuUSrl_KM+qVn=eClM}oua-)yN=hWBNUACk_o5Zm zQaSw;xhl&D5Lv~|T`sLGag|imNYE(Vz~Ix9XCTyQNZB2{c+-TJEmClO$q5uvD|cT`HB9RMHPC*;!svMEaIWA%>Gci;77i z1c@dD5fS`Wa}xcMnoFgcV89T>Rb1&N$@F+t3AS||EMX6Nebe2?COUx#-AcPT;m9t3BJB!Pzn6=tsn6k*_f+OEeY1JD6ie0oTU=HnmD3N5t6LS1$i}b@o4QykCF4UmYgPj^_zMk} zS3)Ld2^nKmC9>TE+|^|jlCu&XQ@IirUoEY!td(3v6%@aWezK+$$ejcOXDmXf2)~y} zMU=6Ka#CEXs&s=#Fa=`~nNp^M2zUW!poHHtC=WRhDsjPyi=bI?b5@d1maVKPDu=LT za1iLidAH;$gDr~7N=rGgauwe=3x`(S%{R!c1C+QZp|Z5N2*tg;q=d?TF*yuwU>9WX zLVZwPS+SDtQhC`5=hVtjn`v$>v!+i+QyKid;))q*X_wEsJo_?kFYmCf&v(p=kYzNQ z;*zCnq)^OAm83NfL#}RF+O?D0D3_{zY%8?A^ygPzdBwJ5vFcm? z7uEA=O3xdLy{oizsxq#gbDDyUT9>xRt~~Ry+InO{o-e&FU;ZdB^RTC5dhX^K!?itH ztEzQ-&cxW1Cw8iyzJy%mkq*_+vZ!;uV%g)7;hq7s*KA6$DSjyU5f>^QEK)Zt~9%MxYD$`U-d1J9oc)U4yvA= zDOoMI*lx8gU*4{a1N_dEW)f9TvS}?uKJY`9?ChVcmcS%4QIi&Q`Yp2R>CJWBqH1kw z>!B#s`!Hcq8}_+ARGO@+cBX!pJ2fzKa3qZmtz%Za`+22l0noI`4&OW(@P4J~M!9~U z`{(US)2P6y=6+o5XER51ylhuho2h2^R^O{Ov?%rKkZ$Ocxbo&&knV~GMAdP1Lmwe> zU8j1j4quejk=^MULyu!rUmTEhxo%ap`4&~X&%*kWKUXy?J^x(w&9JK3C)`KWNDJdc zx;BTP$PNNSGB9+zhHfJgfj_E|(>WxdX9oVgv=K>0?D6SYmt8*Ndsobym2I0dH!pvl z6iO&s;VdpGB`n`^6*dz9`R$G5xfCJK^?aJvuq)47lHgsQ;!yIBs12u-hC5;Rj{KJC z&$Fw0A*6R{LiU@AuZMDJZ_V+|m|)A?otNFA_~u4PXbJ{%c~dI+Q+9W?qIySlL`9^j zI!e{kB6~VyQll;V#QJyg8$MDRE(gMVIK^dIr%V_cP5Gf1!ICL|UiN~bG~%XX-8pDwGK!2Th;IQ7o7>>C5C^0W4ta-XFZe>}m!^z~K* z0=KJ3`DZH!zC&2G6?ito6vR;d9Fo0(62-@z&P%p#J=n*L0Pt z$~p+0FoM$5ZB((sw0dr!uIv&;dkr`^t?uq?Fp)XabXuGsOk~K%#X@j}-qc#E90!d{K>H@$O3PZ!+6~%Q*F?glBo=bL4Q?k=7&(Dy z*~=$YWl9TvE_nm0f1@S}QoUnHgu9M?3Z%KIU2Q!$1JB_%H$n2CfBe1SKa-twx;u?K z*`Wcoo{TKT++zWU=;zwtqwa?_m>V%T!cJ%@oYqXDZ;zxLoP!NcZTx zvhK`VA8dQR0#LTr-4V78eHpht7*U&A)?Cq&(cJH0@G1w5|dm6%~1R zcBlI#&sXxge{nbucmi@k*}icNf$JSwx4pIRVzsq*qFvjm^|tnovt{nIWwzPM+8T+7 z)@Qr>m13Lrwda#aBxkkQGG7U#5I?Fju7QN%qA2gCKFT;xuk8M$q2G1w^JDP@je`5l z&|~)p`DJe>#7mNIUVnje*DdPf`FFSE*lt}8WvOK?b=S*Mf%EMGXNyCA!y)6h3k4z< zSUz$+%UC>ZV~NW1L}f$Z6TLpp+UHM)r%Q#+sl<);G4<|_Q14Fd%;y58YVRx2N>jn7 zN>gD#X>uP`{C;u}YroH(VGYHQyxTcHY7E>X+cNV>&#XgjkFbu0> znSRB058}_0(M=|AMALVwHPSt%td&f=gG?U1sQJR^)~<-F+34wIr|HRtPEP$x)P{aC zeXjCwXRgxJuWslhwcp^du@Zi94eviM)QWs+X`|fkBPCjei~)d?8&7{#eRFwN48E|LM$77uU&ghZS_Y2 zAW|)BB`-tMTWV7(Ua`GMAf?KR-D+m5+N^CBm)i4z+Iln^op>O73l%|>3VXL6i~tlB zUDQY`P}!EZ+LT1u?%bKR3$52UlY#QQ$vz+k+pTh*j0UsSHN|rp{-59Qw(CCIHYx!P z{q8qH&)o-2*&BV1%lhq}mZ)1GP^#KI(f)8c{kQiVacC{*)py#`fI%XEvMoo~M%~*7 zXM#t*&dHhG4s?Js+kA6|Dr&b)yFP`Q2J}}!{Q;uMrKQ#NICxST7NUd%KIZ3OPig1` z4&3W^5CzYfRAtNG5NbwG4$)&{&9?CsQ){m6EI=j27RlF&63iw!#wO7|ho$U2hoQ~G zcI|b$w%^|R8SHT^(q8s5*@KL+2TFYfMIdAbd(WW)?e&l)j+!iC&wRPmu2j5?O!yO) z;2!5HaO7{n57iG1{Yrf{#Df?Yth*B2r?MFSt>_uhbNdD^e5$Xi-~ZUxlqUL@T;Tkl zB1P!U_9A>x@{gdpgf6>w8bcko5-1hVf2n$o%C4K>>Yjr`-55K-=k}?d4|;8$Lqj1z zq|0V9Z)u~$8`a^^qB_~Fv)kOq3(!X%v%r^}DDWvwQSqCj6f%{Xz1Mxhp>?%q?Fzif z#F>7t)&o}{CMNo~BgKTC$9pYu?&dX+`zT7(Px1Weq3)x0V(V2@0H@UFW&Ki>XFj(x zqaO-J*EoF1-JV0}hPJv=RG*45(R7R{UFLpxqpD?rw**zYrx(xMCko(LSAl&?f&3=w zX5>{f_j*2%T_bZhL+$9)X1BSb`EdEhm~ zRbS3Xd-hj~hy2p+iyBTsU*Z2PVkV(YLCCK(ErjgHF}OS;Z?*_6tR-AptLKBE?t`dc zp$ye?7zX-4$h}tWK}kOp$>qS|%~N_ETArmBqYR6hz1#g+LBlTB)oRavF^tkqnh2VL zdb^`EVPFp9VMu~;%2C;#y{kIl(B43!RR=btLWlM~8Uk)zki5J->r{ZOWLz|kz6yiz zG!tr5uu=CL7Y06YLuSPdbsZbs;^~rsUifd;fX)Tg!9ba!%6)-2-9ONeb`8y)HQjx( zy>TFX-E7Hq0Wul*h+f?+$=#!7_JW8s5MF6g>UX)WM3WhxpVg9&@Ff_{?ZxtLKUqx9%?IfK{EGv4$H3L9Wu9Lc<4gsu8uS&k|H{6{TL&o{I{E#!*lo#xtw;|70>(= zt-5UZPu4z;6UmWaB9ZC_B7H0zfyi&ydR+>`ll^SufTx65Q_eNBr1O`*IW8#Qa1e`w ze3)Od%U!k(NosGjV{(lGBnw@bZvwyd`&<`lp9kVgVc$itZwn@)Tye8VJ{@n?zJ^n( zk{>p<6BxpO*?(Nw+UH?b3h94VtE%K3FuaK5C%kzF(Boh~7IZ{B9J7c)q@iFKnsZ&L zG+mQh-|xy%wa>M_V0pV9{I@dyd9%>I=>CL!xhxpzd=Gn99bpWX??6Bh_?BN~JCzTI z|0jSmJ6K4_547=4o$G6sgLby}j6kw%oe@F*aq+uIw)Nbj-WBCzBI8s2!WDY>$fct=R#H(KS2k zTWA(3&_?x-QC`DKYdCF+{j=+rx@)Msxt6I~k6zw7Wt1VRuX|8zTvwwVv=xW+(_3^!yDd&IHEsd_BP|5hwU~(f%2W7b?Q2A++PWp`f+pZUb|(1CK8CS&ciwB z+qQ$4WJ2M7={oNe#`gj##^L3eHGB5Ec4h9n=*%niKSu_!AYfj;(c2LRMhB#y`vGLv z+U(xT>|UtlEd6{9va0q5%@t!z`&Vi`>yQQG*Im4BqD`f_1?@V^0^f|O=t)-{=9M9= zOcnUzBK)H84-|&faqQBpvt?hDX z{j7PrhG;*gKS=Lh!07U_pg#aMA!tglzh%g`8qdB-zB_=+(5inpbIbCnJ`eGwUM<*q z4kD`)i7UJIvR&(7rW~m21V>iph@BGFRqFdNx)U6oV-YLppWT2Q;Gh0mkY?LXqg{U& z4-@mK6u?m>f3p$C>`|ALD@i-($Nxr9pSJBNMUubKfU|8U04&fD*u0Q@w(aD&{yPmg zHS;~YFK4tcPk;xabpXJ;45g0|>0dB3r`xsZl>bn_(!S|U5yVedLaK)?ITMq58LFN_= zVK7x^T3>kq7LfwG@FOm2eyr+-^(UyLyX1B(ru3@5r6*M1YE)f^QFWb3Q#Nh^FhaRC zcc>fA)FGm5qz(omRK%5yG(w_C1Vw(XN4VPhr5>?JQq3*TQ;BRA)z%pb*NjoM13V>e z)c4QlA_Fgos@lR6syFUs#8E(!_4`5^<-qRGry&`Agg5na_q+rO&7}K42eqHi*CEJL z3V|k$6Qj-KD}jF(aq!wqJ`s3S#1U>L+Xpt{CwK{aEG9K!yxptz99}&|MIS|Z=Dm>W zcarLdLG@Xu49z>VgQzQ)W9 zUb`=<^uhp70R=7VDjWYqp{wigt8AoknR>M_0TDr%Eug1EB4y)ONZ&@f#;8(N z=5_;yG(|tEG9NQji$Uhl30k`;B>xKcrv;8|jE;{Amr|8^uO0z9RC1s29TmC-9;Po{ zi}oN5&rL|HerwlmP46wxJ{$;KPne|UT>Jzu!=$(O1cknmK*^qgM|qOO^zyUb4m68+ za~ zl*N9mltUj%(~z9BnwylS?wm`NdMXDwlU;Xtj!w_&_w>amn;t_-)~-L&C?aAs7cumiDKn3uQjv$b-u?zxa?E)c;^t1zZ?S!&j_ATnE>x)u0 z{wJV=^mEdb`o~EsNS6jY=xpaqR_db>1g{NL>UPi;$eig)eF_rm(KcR#I~qRcUjnI( zo+Hzpwh4X8KnU zuxOV({Rv9LY{Vf*!%RwVx?Vn6A0Z&glq@O9IOJ2T~yZtdI!p!(ZcO3%64eWH0JFl*FK!MAo=cqfjjQ;zcVk?U=AzzjB( z`YS;!AA^Ar1g~#BGP>Wn5+qUTM*&22V&(OCi|r+Z(HOmHZ~Y+3?&W1@#$^uMl*|pp zfeoWu*H{OpTtoX8Lj9AI4&Pm}Lpv_AYn|Lb;l?D`N}>nNI@EaBYOBjhmz4S!f!U!Q z66M+4-wDUZ{hjH-#)+u}@llnA0R?KXAyk}0{Gqq3wtg5zqH+#Q_wrrI^iY=OIQ;mx za#^pV1hhCKP1!NO+m8C^t6bOXIxW@1yi4wnRp;A&8s-QU`fmVXleP3cQ{+sOg`LyU zxeHuiLQee<(|l|)*;|aG$mjw=maya9dFppw?7R z@3{4&e3&29a;V)x_fX@(AB{7OEG+gCtc)-AjwKi@s;?OX%Ng|hli#Ie73mFc-}|}U z`A${~k8F9zZ$@UC6T68^im6HDtC0JUs23K$Kye1mYT;fshchbN<>i^u^fdxp!m9I0 zoP${Jm{@tpSr$@?hmte*`7}Wc;LdI{m>X*>W<~55%G+Gm8Q#)K3&;F$nDCL zr>bXB+`Z5C{B+9xcBs7MX$NU6+3*fk5xx@9*E6aQ^vjU%5~ZQYg37=<%F{PZ*)#@_ z+I~-)#U5FkVADqBd8aB(@BlI<5l4=xO8o<%gHX9rPvb#^?o{e~Md&`I{%|CxO;qaR zfX#Dk8359hP2&*B#UfdM`d+NWs*VA2 ztUxYZzKO17I?uJ01NgOjYca`kqs6W*wfH{>)iXLCXkn=dkJ2E4mZ^#$`zsIF0HWJJ zFx8=%R}#oSqUUr(T|Ck5bEK1pV2N9Kz#(!(G?HIsfGlz9e=#&}Ey#XNd7u-0=Q$mD zqp)@mZfcW1_~vy}-mHC?>V?v00hgIS=--$8FzW%@_a&3^^N@$2YYzEi01k$2-kF{= z@ReVJpiMjB>97<;&WE$iEbyY%Mi=v4@Jq`IO8o&?6`N8GeN#6VtqzTUJK&d_p9Em` zNyYaQ+-ZiP7p13R-@4hDR`R7@Y~oDs2VUokn`F;!t&YjZr0R66MqEx9vrn!+jCmaS zOG5GQ;4v@zsN(xK3TVe1+6S5Y9r9uB=Rup*Q5{iA8g=N@?9biDY@W|#TjuM~^g>_p z6xsh{Lqt&=o~7cjmy5$fDh{eQiyPH#RMH%oAB3@(D}|MBEW{;`#{~CLRa^V4D4VKp z?Xy|C99rSim?7UrT}+2o(yVIvEqC?bB}Pd%sou5EI=o2r=C^zjyfNQYl1f9|G$IOZx<{U@AQ#zfltXkMLZK6jq)V zhvsJ-@+Tcb2fA@E^Upw?3yJ?ZWU+e}qD)>d%H$W&Q&_^aNGQNgSi5gEAZjiCW#DVh z<|P-){$C-puB448Ah;RK?wk$_p+>(qM(ugercY@MuCzYr=!UFatSj3hXwH8*h#KmD zdSD^R_oif>s=Eqn3L$j4$`7$`L3je$fwu76pnVQp>?C)=P2Tgr3(n0vG8t2GykH=u z0&hy-hbDlj0|DR(y!XP;LMObi^`XC(ln*0|b(Ik16-YN6u z!+F;}3s25(LE*REL_Un7d$A+)ur2$qSaDXi90IAZ!czw=V$MA-naXIits^QCg%n%+ zvtM8D&(n@(?aDlfqK-BHyzFDDvT$#KuQ-7!NdFP=a|4Ve8pxp6itdWeitQGimzxJ+ zWhq$E>z8xT=NSp2p?w~*M>v6?us5@y5ZELvwRc>8&V~O1lrU6UYjg57KRRKy%)JG2 zc->M@>cK2f2i0FQp&D%b;rh$x?$6v0Wz3LKp+49SW%!>w4GWn|JbQ}Su>KEV)o>8& zK<`>IFkuhn@y>)p`JbfnzmGiiArQ=O_)K|l4n**0f-<%11d~AFrr`zJPJcdv)NH$A zJ-e~d`7{4(J+$HZGyj#n#>@_j_ubET8u(5FCJoSdN8RvgHsC51O28jwuW2; zRbRs*PS3$Fb!k+sK}C@3Hf+G1CQYoKT2fw$8~)Qj{l` z!NlS#0V9OM&ivyTkOkLZ7ehD;`hH4gLpRzI+zyJ{32~E9E2K}qkG+IN z)ZR7bSX~$9OjCZ?LQNZ1ylpWZN>jws?<@5-JkN=7zeh6&9TAelw?fSL%C%!n&7@6f z%1f7N@SsyX28u_CVhr*(-wx(&FV*4K;d_Y`iun>OTPe}#ly5#xl0gC_tG-9};)WX7 zr*_w!#z5d5dWiiRM=<_!(D)1NZO0amDJmH17f>}Hef1W z1otx}sJ?44xG-^S3JUx@zGx-vzT`!iAL}Y%`bNkK3x zvV?Fli(xtKT4zJcn(q}j`&m`IyzD1wxc|WuIsS`4eZb6={~3UU@EX*JPumXSO!Rh` zk06KT=gc!0^fl)Nod9U+SJc;mxC7M`ILV{V5!4Xkb1*vlNw;RJlHS;^3=JflADDEs zx^)KPVol=pFfbyN1rL<`dk_tcr-O9D4XjV#!i9;7FzA|X4(AcwCdZX%e-7B>X}nUA$_&-+7V|Gf^^lE1ZkO5nGHBZ$)jXFCUau7PZ!WZ*vR4a=n=Q(N&RP7OJB*57+ z+|U3KpAOL+p#hqJ3YwZEv_W-fgU}8w{~6jKv_q>}&<3F$T6>V&AgymZtz?SAqBTrM z9vjUTquA+O`ZAE#*+9P*=5h)`I+w&cn@DTtNjt1!Vw=K-ox~_FV$uX@Xg-ftd8wM< zMd24{7;DrT=2uG!`7t#}OB~Km7Bf!FnW0Kq-frX9{L3mU(&$b9lHx1Vq_iSeniQNo z1Wwl1jo6KV%ZACpztMc`my2J(Kp6Difc3#6-C=R@I;j!kJ0#FGKro20fP_rHc)oWg zDi58IvGhz!&Cdb`>zg2&Qzd5Pa%z?OUm-Q8NNJ$ghIv6<(B^$Mc%gtAf@kok?y4$R zVS)40X3{f*?8ra}FOo0|m=IR#{{snG`|`1aQd^c#s#J=pS|#tTJS|O0%uPYEMX!8~ z{X}T(Ohk#?y+1a!0N4*5%)s?gbAds=`4S_kDtt)cRXjdpktx@i9XQr*eD^V8rY4K3~w4(G)>0&*#RN#PcZDB%UoAQXC8nzQQr z$7u5y7CznGDt11tCw~L-T1*+Zs%Q`7Dz7cguJzcpR~*VSeIQfqaArBYWtQel@B`0K zwfC_6QK0ov1GnaTcJGpO3l^p1HoJz9J$p%dLUlBS6Yyqe56EBv$kf)cUTmFt>8WrJ zG}r@HheUtqI^T@hwwK6oSWR(d+O-2FYoI;GzSRj_OOfE17j$HQ;l_@EK0=3OToXB= zLlSWW6bEtQJ$*7>KI@BcSs|@e=t;$-v5Hxi&iT~Il-dzD!jz0fpMF;?z45E>KT|tf z!t{H#y)faq37A;%!ux5uoi<*$ve{Mf3LPz96nf`^E7uix@3%54oeEQ8B!%Q@`oC zMK!bsv(o;O5ombA9S^?d`82^hKON=kR?qr0*;V84uCzF=TA84DUWY$8oYU~8^a0+i zW8)15DB}+86c*-mhf}uv25_opt_A(je0rZd!DMZtf805rUh~6rbJ){R`7+K6xQgqB z>I~{bD@`oADpqd>WYzUzdoc;x5uT}v)|J#{3JU+lJ{&lv*aV0wY0HBE^7IZ<>i?Ak z7V&<$r+2-0znso#@bp?;DYTX}*V7y0x<=h_x*vkpJjdll)p$2ss8Q&6fav1n0W=l5 zFn-=&And2ZZ)T_*i)*gZgo>Ivra`udW^lg;M}BP55A9eaeDt!4u8|i=SGMgI{2EP! zZAY3B>jz?^;Sh2YPXG6SqjN58;Sg(Kewx?xbcJdrJirz1CZYOCss9C@qN!kqdK6*a zt>ohXSftSq7P3%T(bg!>y=i#6X*1Fs-g|MZ0rqibcUCWUWcK1!P{s2{Jm|DBFK939 z&Zuo4}sLk;r< zs>ULRbCScm&f@T`I>5EP*yaj0F&Kk(3NQgmb$*fRT`a16RYM?qaYA(zLJ9P~rt1>Y z2iK}lNlIAR%tgDue5c-ZfhT(NaT>}17%{%>oXf2PqoJ3;h|dc={r#?y+PZYDw{xDT zri1nfZ|&eu=xF@31eq#MA%of)E7qYcNuQz{Z2yfm-@p>rcZQ&3(*N)j!tk1I>D>Z33j6sytbP< zzz*MUrXQz%^hxdqqi>#uC@N<3f2lK!u6qg+IveO0l+jC1MQA!gfXzUV<#0F;QgIk6 z8oaT3XBZt!bcTsx(SM}_YDyca`}PqADXay%Vt)y98&j6=R7zdvO38#nk!U-r(JHnRa&YhzjOj5PyhXEQ zxY6Ila`#k9shte`&DSA2i6U}&K2TVD1eu4uurd(%JdR3{($D%nKbN!F;h4-pkZR)M2RYF6jDP!zU8RIK3$}0 zg{bfg&}8NxbZ7-fu%S}bmSg2{c}r*h({LGxy1bbdwLiv04oWe$S7h~Xrw0M-Ny7es z1wD1AqiV|Q`qEr0uf!fe+Pp}G91~$TphjuKn^;PHCK71}#a>RW>%(&XV)U5`d~;-M z0K*fd{uabNN0$MQvgs($D^2_Q(_crxyab0|q!LtsZ9owu6p7lS6B%>eVEkr3;fH4-* zE2;SyR|lFnf6J7f9S-jRJmaD5(4mdaW<6oQVFFB4gGt+ijm%v9g|nC_KXz z?4K!5?#+Xus}_>V13x2Iw2AFei2LU;hiG?|CRO%)@P6HCY{wk|B!I(?o0bUw5ln{~ zFMayyP@=4U|9t?kd5*^7T`{@lKm2ziO0s`}l-ct8Tglk?HC4E|lnvzREU5EAb&aS;qb+)OUQ7;?D<~fGY{y~7O zd-oQtn;H|UKRh4ix?c=2tv?CX2O$4}o;au^)WF}#jS^4V;EW70L8pD?>7QL=!-&G+ z>_)*#zF7z8hQ_T1yqQA#iGX~bVv>(+{4eRgBQ}ICTZ+qyY^R__er98 zpZhQ@5c4;vZI9q(r6a*}5ea58!CYhSd>PeMKVF%C4|&0gLrZ5a0FFk;I7bobks`f7 z`#2EX&k1;*p=AHH%Qz3^}qUxZv3fgIM6Q>?|) zA7Q-QN;p9X{ad3``1ka0mHKzIRGgg8Oum_D70qKx?$jm{0hsP+v}0r98;g_}pUJfu}Ne+G!oNGTV*ch0pS z0-1tUlGrLt<1`%mqjjh_sK#}$0jv{@(J{MFQcOs@CHg|*VD2qHFT^3-gNh1f+36P(~aIGIS)Cr=L* z=T~&(lKQ*d#|2kdn+Ru&=h2t9H`3umI8Z{@BhxVSm>Z_YT-IaYS)-qUOwc1ne>kSa z^fNlSTuG!w7~XNhe=8n@oP3ZPP#0XkncZ^3rxI!! zc(_tO3B7;3NwFgiN6#K1P#TWVUVfUdsUL=?G%CfOA-&8|=BYqbBPPlU zs2pAjh&grlU(r9!jvbY*Bh6<>Sf7sw+p{}V?Jo{x{t4{JXKgf3!_$1cMpimUtN$Ls z@X9E?2cY`?U;!?%LDB9!ZQ_;cnT6gZJA2W!bLG$?D5Ez)ls00k=adL+Q zFBu5)EOfGix}k$&^=(Q+1(0Bpg3YeKcStn?J28&v*iY{G$xwDo94jW?RKuaNT{!Rv z$HTcU!t638$00S;i}NBbF^OXMwNaO}C7|x;)gf`lEPMAEsxHXi=m^cwSxT3fJ^eU( zqb9tjAn7yQ|CRbyK!>Nf-|_z!5)FQE${yw~GVy8|8qE&jc-X|J)bl!WqezJmuGrx| z;r}D|sA;j@SR;j-GuzB|Tw0i`@??x&gEr1M_k1lsWbOuli%Gbc*!cz|{-6B*O{lfLg$qUy^7ytYTp z?4XlzMh6wu>U!BsCtP2xuBXHF*XlkPcvK&!XSUk4U3Q$7pzX1tinDtT;gG;%@G0sb zz^6`t4xPj`l~3X_e5%!Tg_)d5E@QtC?T{t);Eb+Lo9J)Zs8VI4(@R3% zTp1Jm(9Zc>)sZH+J`SFT@ft1s&mwy?z>!{h8gyu@Qo}A)d+u3|VA@w{V2h!(RyK-S ztAVzc*tI`nlJaL1+VC|O|4j;E@-`3}6`N9UBjo|A8A07nUXD1tFVSPvjt=>t4uB>A z=ecPwby0ThJv&YvbI`D($JYNkEd~7uxbPYi)-~O4Ixtu1eU6A@flDWecUs?3z0VOX zT5Of|g>CO6L}`I9X9BIiX7&N>HC7&SqRuwTkLnQ&t6m>T4xE$?=UFQc&@`|vW37tn zj1E~xbB$#L~WQnQ? z(4^d961#!UXe1pNW?`wdz5Y^49|!r2sTkz-U#|l@e7Q0-loL~X7xIl2mdh}|=51yr z#EUuAu%B)_YWH>9p?OMR+tFdaH@!+W%!#QvfP>}ybC|0-BB(Si4ZhBO0?CF8slNFU z8&`X4E8wx0< zM_XVnabht!4!g@ndI`dB5Ao}(+QCT1d5+Eu_zZZEZuMUbC{%9sBo=%Sj(QLQ_R>X4 z!*If3c2$xKRi?j1=t8LWJ8-uCsBT_{}Y1{euSEoOV)4%t5cGF5f3Aykeq8&_I6{>j>tFZB&Is4DLFa@p zhXX$+o$)#BBmbjm}X*2;Oft6^YR#<6yCHTdt; z-Ss8f@*{wIfZ> z+iDVw7+@flkiwt?jPP&z#zFAhdW8Q;-!)+S8XB4Qma_3?lp9b&Q;h%%jTl?P=)jSw z$wt+9Wqc|>fn}`xl0hMTzbfUz#Hd#RxcFwm-Ox&X`}#7LD*V-aKex=!&Z;}yY> zXlH5P?dAg@hN%VoVignsCJ`{d2|L}?rS2uVE$-vga8G78x$Ogj^zU0A~xhI|0t_ z07J|r^fC>zgdf-5h`{%Vy78oT7WnMYRU;lD0gYEi2O49}Tx-|8_f~4y4EuJ{dZAG+ z48e`(pt7g-Js@CO;0X54=m`7~dbygYlSVsHG2<5E7wxjZK7el{nfSlX;JRLE!W@PKJ15JaLvpKNt0eIS0;`4VHiMN0OK5ohJHc z$$NaC(ZAYlbZ%Uu;cjigSS<3j>HSOKdm9h`1YgWUcpv#8RvaKBo2+ z{XxAxUW#7UZQ{Tf;6ayQ>8pf~iYM%b9MsJeqrFb}qPQo3;QAWh>j^xhrV*ceO?F&O@-AD*Rw0Oz=XPH!L3Mphwav^wpzXs-wr zfB)!B(mVM6Q}tZHOeAD@`X}z=m}+xVFjzllV2F%9m9h-|pqpOd{trtn>B~ho?ff?WbUn0I zUsxE@_)_f46cd&x7E9aV3Bj#@z^j)7?!t<13ZdrxwtlRk2h49{B%##PycPKaUas`1 zw1o_3>LH1a%cB=D>A1XIxM!WB)A9}wa1%G;Vh>U+)wArACJTLQkm%w4NfO!k{vK3> z<0R4sQE;JWFI(*IF7Q#<&&GKqA6P|_#qP!UAarsjjmb8iG zt{Wl@K58vctpB%oXeh^|SMUSDAtuh;0jvH5?z7a9uNioZTw(iojBs>Mx_uAH&VJ(e=D@X+w&uAA^4UFs zEHmq&#uD~6`~~kt*>JFpoeoaJrn_CXSuL&)xuMKMXx6qxz}1XDDKOED3;XZZ;k-3` zJ;|ra^zi_EM}!VE>&l(g=lT$@aq(ve(C*c(k#VdmE;<~jn+lI;-#Lv^=ov1iVMGwe ze1oU=!rBzo89W|d)n@#SKv9hv`%37%xq)w15lAq9hU)CZ@$xTI61{9tqfnQ(znd12 zxLwA3OMD-=nxyXx4st}&t*Z+8BVkO5-e~cT!$#=Gbp+xgsn`YK%lTKq0QNaEzFmXiBYuPD zHF8T)9*G44uaf7Tk5@0gqFs<0ol$M`<EnSAx~9wRjiD5l&i{_RZG1yJi?gvigc&tl^vjOmTjlfY}?RAUoJC$$1bg`hd*i7!JjnS6Sd)t{zovjizi|hezOP3sz+%Pv2N&3w;dDb98Ly+yqERZ1eHo zK&3j*pp* z>IGHO_-0}SR^&&qlygJd-LMMGvz?*<3p}Rx=kk1wtMG_v=V=FQFWv$G_EYRx5ihqr z9{TcKfv>y)8%OAsH>Bc(YQwwjzWX21H^$)2#n8^VMlg-lJZis0{mS|cJYw^lzDn70 z1E%lc$Fj*qu{@VUK8>XYfUG~^znEY=y?|bfkk4iYCja*w$$?K3c<)u~KWA{y=~2p- zxdc5&_V`oa(%3b9der(a_}FyVPuWsPPuIw$IL|K*E{ykn&cx#REF|mu0sWW5B(?%+ zQGryrWG<8mu++22-N)m#FuTo`S_Sk9=f4{ z#Wv46ORVzXHVY2nwgIcCbi4(?fOB0l_XILbx~2M;>V4bhCDB;pSrShtrPcQhA%43Y z*$1l*;eDJI?1jq+TP>~wUeKP4rbek>53P8POw1{)9f5b0`qR0F7WE?6$HW|V!GQZi z)qg-Bf;pW?(O)V}*Fu;=2;+#BjrkUwdg$+id)U0!S;P@^yvy{sO?-CFSJ=hgNZmSm zi^+sv@hl`0ktVK0?kJH1xrv5q&Uo>_j3X(5IMFl52ivz0Ss*efH{1w#K25RF?wp6u z4$lx`H;p6(W;6DX%D-n)xj88GbC&+^gCPz5``5B2<|T(~A_@i|fqa{(ay7WBUj*PV zeaxoZB+{ie@4SSw^a0YryvYOe()oQx-s|E>IpVNcn1x6Sh6GBJP}u)~Ey3cAEtsg` zQefeaCD@&YIdzR36} z4kU^?LuWl>{DtHj=zWmP zpUBUyOA#NKFqH|k26Q%PUYGwE)4dK~PYjh9dg_0DfL%y4_+%b!RP+FVYD{b`3yf)0 z(Pp839D(}TF{GcK-$l>0Hq@Y1hy2TNm>sFlOn^#&-=#}zen8cRklJ5}$9UDJi`$27 zg?L~z5aDN-4&r}r5wPAq_}|4jc{XpnnF^EtD;uq_D~~@G?7{a^(SmteCUYTt8kzF( z$zW>3_)Dws8ymj4h;m7YP=Dkr>H%{Nlc(*?)9{Lz@=O=L-#Sq%croZ6w*Z;9;KjRJ zbG!x3F!i-z8c74Q!rcB9dT-jf@V^|=y_1X>i7Q=DD`$x@jONm zDzxTOL`5SlR5Xa~pwOk1A=I)eo8U%h+XaQ^>9@ zx6r2-MjFEgoFPHgyVUcBO^d>p{7k37gz2qNG|rd=!J9<_7r&}xyiir`b~;O{tEI}- zB~?W(I!mge(v^nDsxp_Wq*$t`OasWBX{9(|Dy_0At+=eJ#ObQ6x>qWzSnYOAt8}|? zOA}uQTv1dk0ETi3G7w?pC2Pv6UDXU< zWF}Lc78b3jDJm~3PAjTf>0VV*;gYIKs@>(TH1LsDS(;W=RI#!|Ds@*lgR+#ZT3ud3 zpdd6=T2NHsE&>ztN>)^HprEMAd8cGsT~$^t6%^eoEpS(q;HUgv$>v_^u69X_OIEu| zR;?(hk`_X6x?fjWBNFmToFV|Mgb@g;2o)hA9E5`MQ%=eyA%nEoT~Q1=1(keT;x4JC z=+ctn3K3dzr@M-i=2ex^lf^|YcNGVK6uBg1A#e$R5?}}*%;eui{s9e)L*7*U(fz^! z_rcNt!q;5=PQAT<1=2UceA6@Hi+XF{1oKVLC_l;fO)zN$ge#YSF^{a@ zIU|LM0lK*M!rj6#iO${o?)t|ya3Np6ot?#O%wCfBmC-A9!na%B6Wrz8de>@26)U&+a=j zXX@eEXC9bbGJiym4u5d=n4ih`f644o<*g&jTW{3qZ_x2RrQcsR@b3_GpV#Bh>i6_q zL4Ue_Ke$8CADt`iF~+?`#82-S*L?XWUwm@cOEIgS9zHMW@YNsX{dHyj;hV3S(Rt%j zACB$3Mwh2wzpv+e+Qt^XXKc*nyR=BerA4{%A5R+bFMoPl@RO!L&)BFx7tqG9sT|Z9 zX}Fui(v_G3jFx2f@|y{W=C}vFCRw5=eNvRTSuoRs*EXV}%h1S8vS5-(k|xm4(EX!k z;Wn9A)YB&=h&(c;@{kx20=7&|*+@x(fyoqRB$lkvKV?v+GAW90;>pa>jQ+n2(3p%1 zb|HK#{v7{g{2kxd+l5|PcrCj!c{g0jh)^`}rL-XWt2Cdm?)Xrc9Y28>IJr7+|r ziY&sg*n}epr+kJVXD@!{GZD_45j{GgC_>L0J!UvR$yr@lEk$22oU~_QtkJ0)NysUw zER~|iGFXauav?`jMPwXD(nKVUBk6*P=<$(6dM>dRJt6*H&`cr1A|aggV1BF^N5OozJMbZ9Xd6V)`(auO8h8t z0})OfMOh}FF&+*X7EcMqu2oX{kVMPBMe3XkNirE|$Z(T^h9q-DsB4H|IF7j=A{Z7M z1%`}buw?Nhg(E_dLq>B%D00Xcj-*MGF97MeU`0x>#*uN@YAK1sEgu>@B_^j8R@2|1dohk#)oFLpUObx`^_6jzo$lbsW)Md;>?K#ghj(GDMW8{{~)B zp7c`16cgkhSl$Uz^a;Ngxwmk{BBfH!PmSEOr7U9Vp)r&z#C|Fro|A|giIHOgm4F@| zAtS{EbOoaH$3R1*6j~=_$R8$83WMat#|I>)QdYN-Jyzu0np|>r&YurRREe8}Wf2_! z&8P595%E;>iO9_&f)`6EDPeiUX9n195y>I2+apHO1B;YGXi}R28-aR>Wf>|>K8eIJ zgrGQL6s4QQrfipnKpE=Jh|y!ipg6TGf{};va_U_X?6#rQ)b+?pf4a6)>Jcts*bgF= z%CqqviX4D}MNPRq>SMs#SGY^1JEDZ`?No0SMa30B5{rFR(Hd!mh$IL%I)x02s8YlC zo0;HM(ZdKyh&fIzjvhue(}2{{K~wJ>n3}gLENkAn@aXr$qTE>g04z^KYGVw;wiUrrRf2A>r;3fL$Tc0@n(o&7yn#`!3}D_Ws8lL zXPQ!Fn<)Z@LFHPJ!X%TDqq%*e2e>@qCdpe9tT=zvRU=J~y@-OwWp&+$q?sd3NztpS zr73#dr-NN=fYX)_y1ozYX%SkQ5j)IGFKOOZN)?xE1SIAYu^F)=FI#HjKUh5mA>=O- z<&|cLdrT2vh%;vw8k!QTybN~2vN6HP@VrUe*>wl~03HRB3n(x`JY}3C!y)6QuwDqtaNJR`Np}ErNDiLJ^G%QC8%&WQ%PFF8 zKKy))Z66VGVT2pMOCs*a@AV{RgnUEvL;tn@hfxvoGWoIy`KE|_0USTLvl*`Zq&2<| z4E$pb7bci+=>M=L+2X!cFqs822hLe;AVLdH2>Fe`lJq(Q~u;XKst{n!el4*_W+$IiZD-4~Gtk0VCFwmWs zbn#~^h&Z`GXXk5c(#PkG(rhC`EJ<3b2Vb-%RmX$NptWSOmT}2sHRJjpvfk^~q;15( zHvku;r~XRS%?#Lf9Q!nX+6WKFt0@qbUiL27}R*rdmagYyw;5Hzu|N$;OG znzN0+!JmDiS!Z1tsZTw&M?eO2;qYvpuN}1i%#2p_YJJw!^Fc{8z(fe86lWgZh{r#+ zrgnnWjI#&91INvS4S>%zU^0fBKe47yhHlR>U^1WvQW`MI4vv(UVre4i1&c8)KqI

6*Z9W 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 diff --git a/template/sysroot/lib/libtls.a b/template/sysroot/lib/libtls.a index 3143169c76bb9af35eaec5a309a71f0498f5a490..b4e9ae80dafd906a4ed970780b6e691d61381af0 100644 GIT binary patch delta 37973 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 delta 36997 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 diff --git a/tests/wifi/ap_mlme.py b/tests/wifi/ap_mlme.py index 9c11df0..e538ef4 100644 --- a/tests/wifi/ap_mlme.py +++ b/tests/wifi/ap_mlme.py @@ -633,6 +633,64 @@ check(link_rm is not None and mac_rm is not None and link_rm < mac_rm, "the link is removed before the MAC that owns it") d7.p.stdin.close() +# ============================================================================= +# An access point that stops acknowledging brings the link down. +# +# IwxLinkUp() used to be nothing but "the state machine reached Connected", and +# only an explicit deauthentication frame moved it off that state. A hotspot +# that simply went away -- slept, changed channel, dropped the station without +# saying so -- therefore left the link reported as up forever: NetIf kept +# choosing wlan0, every packet vanished, and the desktop showed a healthy +# connection while nothing worked. Beacons cannot be used to notice this +# (MacConfigCmd stops asking for them once associated), so the driver watches +# its own frames going unacknowledged instead. +# ============================================================================= + +print("\n=== a silent access point takes the link down ===") +d8 = Driver() +d8.cmd("MAC " + STA.hex()) +d8.cmd(f"CONNECT {AP.hex()} {CHANNEL} 0 OpenNet - -") +d8.cmd("RXMGMT " + mgmt(0xb0, STA, AP, AP, struct.pack(' -- report N transmit outcomes, the way + // IwxTxComplete does off the firmware's TX response. Drives the + // link supervision that notices an access point which stopped + // acknowledging without ever deauthenticating. + unsigned acked, count; + sscanf(line.c_str(), "TXSTATUS %u %u", &acked, &count); + for (unsigned i = 0; i < count; i++) IwxConnectNoteTx(acked != 0); + printf("DONE STATE %d LINK %d\n", IwxConnectState(), IwxLinkUp() ? 1 : 0); } else if (line.rfind("SERVICE", 0) == 0) { IwxConnectService(); printf("DONE STATE %d LINK %d\n", IwxConnectState(), IwxLinkUp() ? 1 : 0);

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