feat: wi-fi - expand support and fix issues, add GUI components
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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); }
|
||||
|
||||
|
||||
@@ -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 <montauk/config.h>
|
||||
#include <montauk/string.h>
|
||||
#include <montauk/syscall.h>
|
||||
|
||||
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.<index>.<field>"
|
||||
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
|
||||
Reference in New Issue
Block a user