diff --git a/hal/network/lwip/esp32/esp32ncpnetif.cpp b/hal/network/lwip/esp32/esp32ncpnetif.cpp index 14e1f4e05b..bd70ef68bf 100644 --- a/hal/network/lwip/esp32/esp32ncpnetif.cpp +++ b/hal/network/lwip/esp32/esp32ncpnetif.cpp @@ -322,7 +322,9 @@ int Esp32NcpNetif::upImpl() { // Ensure that we are disconnected downImpl(); r = wifiMan_->connect(); - if (r) { + // FIXME: with just cleared configuration and no 'NetifEvent::Down' issued from SystemNetworkManager + // we are still attempting to connect. For now simply suppress the log. + if (r && wifiMan_->hasNetworkConfig()) { LOG(TRACE, "Failed to connect to WiFi: %d", r); } return r; diff --git a/hal/network/lwip/realtek/rtlncpnetif.cpp b/hal/network/lwip/realtek/rtlncpnetif.cpp index 5a91681c4a..2c30fea818 100644 --- a/hal/network/lwip/realtek/rtlncpnetif.cpp +++ b/hal/network/lwip/realtek/rtlncpnetif.cpp @@ -287,7 +287,9 @@ int RealtekNcpNetif::upImpl() { // Ensure that we are disconnected downImpl(); r = wifiMan_->connect(); - if (r) { + // FIXME: with just cleared configuration and no 'NetifEvent::Down' issued from SystemNetworkManager + // we are still attempting to connect. For now simply suppress the log. + if (r && wifiMan_->hasNetworkConfig()) { LOG(TRACE, "Failed to connect to WiFi: %d", r); } return r; diff --git a/hal/network/ncp/wifi/wifi_network_manager.cpp b/hal/network/ncp/wifi/wifi_network_manager.cpp index 6830a21d85..3e54c89fbe 100644 --- a/hal/network/ncp/wifi/wifi_network_manager.cpp +++ b/hal/network/ncp/wifi/wifi_network_manager.cpp @@ -33,10 +33,6 @@ #include "network_config.pb.h" -#include "network/ncp/wifi/ncp.h" -#include "ifapi.h" -#include "system_network.h" - #define PB(_name) particle_firmware_##_name #define PB_WIFI(_name) particle_ctrl_wifi_##_name @@ -198,6 +194,10 @@ WifiNetworkManager::WifiNetworkManager(WifiNcpClient* client) : WifiNetworkManager::~WifiNetworkManager() { } +int WifiNetworkManager::connect(WifiNetworkConfig conf) { + return client_->connect(conf.ssid(), conf.bssid(), conf.security(), conf.credentials()); +} + int WifiNetworkManager::connect(const char* ssid) { // Get known networks Vector networks; @@ -220,7 +220,7 @@ int WifiNetworkManager::connect(const char* ssid) { // Perform a network scan on ESP32 devices because ESP32 doesn't support 802.11v/k/r int r = SYSTEM_ERROR_INTERNAL; if (client_->ncpId() != PlatformNCPIdentifier::PLATFORM_NCP_ESP32) { - r = client_->connect(network->ssid(), network->bssid(), network->security(), network->credentials()); + r = connect(*network); } if (r < 0) { // Perform network scan @@ -250,7 +250,7 @@ int WifiNetworkManager::connect(const char* ssid) { } else if (strcmp(ssid, ap.ssid()) != 0) { continue; } - r = client_->connect(network->ssid(), ap.bssid(), network->security(), network->credentials()); + r = connect(*network); if (r == 0) { if (network->bssid() != ap.bssid()) { // Update BSSID @@ -267,7 +267,7 @@ int WifiNetworkManager::connect(const char* ssid) { for (; index < networks.size(); ++index) { network = &networks.at(index); if (network->hidden()) { - r = client_->connect(network->ssid(), network->bssid(), network->security(), network->credentials()); + r = connect(*network); if (r == 0) { connected = true; break; @@ -299,11 +299,43 @@ int WifiNetworkManager::connect(const char* ssid) { return 0; } -int WifiNetworkManager::setNetworkConfig(WifiNetworkConfig conf, bool validate) { +int WifiNetworkManager::setNetworkConfig(WifiNetworkConfig conf, WifiNetworkConfigFlags flags) { CHECK_TRUE(conf.ssid(), SYSTEM_ERROR_INVALID_ARGUMENT); + NcpClientLock lock(client_); + if (flags & WifiNetworkConfigFlag::VALIDATE) { + if (conf.credentials().type() == WifiCredentials::Type::PASSWORD && conf.security() == WifiSecurity::NONE) { + Vector networks; + CHECK(client_->scan([](WifiScanResult network, void* data) -> int { + const auto networks = (Vector*)data; + CHECK_TRUE(networks->append(std::move(network)), SYSTEM_ERROR_NO_MEMORY); + return 0; + }, &networks)); + for (auto network : networks) { + if (!strcmp(conf.ssid(), network.ssid())) { + conf.security((WifiSecurity)network.security()); + break; + } + } + } + client_->disconnect(); + bool state = true; + if (flags & WifiNetworkConfigFlag::TURN_ON) { + state = client_->ncpPowerState() == NcpPowerState::ON; + CHECK(client_->on()); + client_->disable(); + CHECK(client_->enable()); + CHECK(client_->on()); + } + SCOPE_GUARD({ + if (!state) { + client_->off(); + } + }); + CHECK(connect(conf)); + } else { + lock.unlock(); + } Vector networks; - WifiNetworkConfig oldConfig = {}; - bool restoreOldConfig = false; CHECK(loadConfig(&networks)); int index = networkIndexForSsid(conf.ssid(), networks); if (index < 0) { @@ -312,77 +344,9 @@ int WifiNetworkManager::setNetworkConfig(WifiNetworkConfig conf, bool validate) CHECK_TRUE(networks.resize(networks.size() + 1), SYSTEM_ERROR_NO_MEMORY); } index = networks.size() - 1; - } else if (validate) { - // Has old config - oldConfig = networks[index]; } networks[index] = std::move(conf); CHECK(saveConfig(networks)); - if (validate) { - restoreOldConfig = true; - const auto mgr = wifiNetworkManager(); - CHECK_TRUE(mgr, SYSTEM_ERROR_UNKNOWN); - auto ncpClient = mgr->ncpClient(); - CHECK_TRUE(ncpClient, SYSTEM_ERROR_UNKNOWN); - const NcpClientLock lock(ncpClient); - - NcpPowerState ncpPwrState = ncpClient->ncpPowerState(); - bool networkOn = network_is_on(NETWORK_INTERFACE_WIFI_STA, nullptr); - bool needToConnect = network_connecting(NETWORK_INTERFACE_WIFI_STA, 0, nullptr) || network_ready(NETWORK_INTERFACE_WIFI_STA, NETWORK_READY_TYPE_ANY, nullptr); - if_t iface = nullptr; - CHECK(if_get_by_index(NETWORK_INTERFACE_WIFI_STA, &iface)); - CHECK_TRUE(iface, SYSTEM_ERROR_INVALID_STATE); - - SCOPE_GUARD ({ - if (!needToConnect) { - ncpClient->disconnect(); - network_disconnect(NETWORK_INTERFACE_WIFI_STA, NETWORK_DISCONNECT_REASON_USER, nullptr); - // network_disconnect() will disable the NCP client - ncpClient->enable(); - if (!networkOn) { - network_off(NETWORK_INTERFACE_WIFI_STA, 0, 0, nullptr); - if_req_power pwr = {}; - pwr.state = IF_POWER_STATE_DOWN; - if_request(iface, IF_REQ_POWER_STATE, &pwr, sizeof(pwr), nullptr); - if (ncpPwrState != NcpPowerState::ON && ncpPwrState != NcpPowerState::TRANSIENT_ON) { - ncpClient->off(); - } - } -#if HAL_PLATFORM_NRF52840 - else { - // The above enable() puts the NCP client into disabled and powered off state - // The following enable() will actually enable the NCP client and put it to OFF state - ncpClient->enable(); - ncpClient->on(); - } -#endif - } - if (restoreOldConfig) { - if (oldConfig.ssid() == nullptr) { - networks.removeAt(index); - } else { - networks[index] = oldConfig; - } - saveConfig(networks); - } - }); - - // Connect to the network - CHECK(ncpClient->on()); - // To unblock - ncpClient->disable(); - CHECK(ncpClient->enable()); - // These two are in sync now - ncpClient->disconnect(); // ignore the error - network_disconnect(NETWORK_INTERFACE_WIFI_STA, NETWORK_DISCONNECT_REASON_USER, nullptr); - // FIXME: We are wiating for ncpNetif to potentially fully disconnect - // FIXME: synchronize NCP client / NcpNetif and system network manager state - CHECK(ncpClient->enable()); - CHECK(ncpClient->on()); - network_connect(NETWORK_INTERFACE_WIFI_STA, 0, 0, nullptr); - CHECK(mgr->connect(conf.ssid())); - restoreOldConfig = false; - } return 0; } diff --git a/hal/network/ncp/wifi/wifi_network_manager.h b/hal/network/ncp/wifi/wifi_network_manager.h index bebdc4b518..4270ee375a 100644 --- a/hal/network/ncp/wifi/wifi_network_manager.h +++ b/hal/network/ncp/wifi/wifi_network_manager.h @@ -21,6 +21,7 @@ #include "c_string.h" #include +#include "enumflags.h" namespace particle { @@ -45,6 +46,16 @@ enum class WifiSecurity { WPA2_WPA3_PSK = 6 }; +enum class WifiNetworkConfigFlag { + NONE = 0x00, + VALIDATE = 0x01, + TURN_ON = 0x02 +}; + +typedef EnumFlags WifiNetworkConfigFlags; + +ENABLE_ENUM_CLASS_BITWISE(WifiNetworkConfigFlag); + class WifiCredentials { public: enum Type { @@ -152,9 +163,10 @@ class WifiNetworkManager { ~WifiNetworkManager(); int connect(const char* ssid); + int connect(WifiNetworkConfig conf); int connect(); - static int setNetworkConfig(WifiNetworkConfig conf, bool validate = false); + int setNetworkConfig(WifiNetworkConfig conf, WifiNetworkConfigFlags flags = WifiNetworkConfigFlag::NONE); static int getNetworkConfig(const char* ssid, WifiNetworkConfig* conf); static int getNetworkConfig(GetNetworkConfigCallback callback, void* data); static void removeNetworkConfig(const char* ssid); diff --git a/hal/network/ncp/wifi/wlan_hal.cpp b/hal/network/ncp/wifi/wlan_hal.cpp index e6da537af8..4fb26d5f6c 100644 --- a/hal/network/ncp/wifi/wlan_hal.cpp +++ b/hal/network/ncp/wifi/wlan_hal.cpp @@ -182,7 +182,12 @@ int wlan_set_credentials(WLanCredentials* halCred) { conf.credentials(std::move(cred)); const auto mgr = wifiNetworkManager(); CHECK_TRUE(mgr, SYSTEM_ERROR_UNKNOWN); - CHECK(mgr->setNetworkConfig(std::move(conf), halCred->flags & WLAN_SET_CREDENTIALS_FLAGS_VALIDATE)); + WifiNetworkConfigFlags flags = WifiNetworkConfigFlag::NONE; + if (halCred->flags & WLAN_SET_CREDENTIALS_FLAGS_VALIDATE) { + flags |= WifiNetworkConfigFlag::VALIDATE; + flags |= WifiNetworkConfigFlag::TURN_ON; + } + CHECK(mgr->setNetworkConfig(std::move(conf), flags)); return 0; } diff --git a/hal/network/ncp_client/realtek/rtl_ncp_client.cpp b/hal/network/ncp_client/realtek/rtl_ncp_client.cpp index 06e4156d64..c4072a3ebe 100644 --- a/hal/network/ncp_client/realtek/rtl_ncp_client.cpp +++ b/hal/network/ncp_client/realtek/rtl_ncp_client.cpp @@ -235,11 +235,6 @@ int RealtekNcpClient::on() { CHECK(rltkOff()); CHECK(rltkOn()); ncpState(NcpState::ON); - wifi_reg_event_handler(WIFI_EVENT_DISCONNECT, [](char* buf, int buf_len, int flags, void* userdata) -> void { - LOG(INFO, "disconnected"); - RealtekNcpClient* client = (RealtekNcpClient*)userdata; - client->connectionState(NcpConnectionState::DISCONNECTED); - }, (void*)this); return SYSTEM_ERROR_NONE; } @@ -304,12 +299,19 @@ NcpConnectionState RealtekNcpClient::connectionState() { } int RealtekNcpClient::connect(const char* ssid, const MacAddress& bssid, WifiSecurity sec, const WifiCredentials& cred) { + CHECK_FALSE(needsReset_, SYSTEM_ERROR_BUSY); int rtlError = RTW_ERROR; for (int i = 0; i < 2; i++) { { const NcpClientLock lock(this); CHECK_TRUE(connState_ == NcpConnectionState::DISCONNECTED, SYSTEM_ERROR_INVALID_STATE); + wifi_reg_event_handler(WIFI_EVENT_DISCONNECT, [](char* buf, int buf_len, int flags, void* userdata) -> void { + LOG(INFO, "disconnected"); + RealtekNcpClient* client = (RealtekNcpClient*)userdata; + client->connectionState(NcpConnectionState::DISCONNECTED); + }, (void*)this); + LOG(INFO, "Try to connect to ssid: %s", ssid); rtlError = wifi_connect((char*)ssid, wifiSecurityToRtlSecurity(sec), @@ -332,8 +334,8 @@ int RealtekNcpClient::connect(const char* ssid, const MacAddress& bssid, WifiSec } int RealtekNcpClient::getNetworkInfo(WifiNetworkInfo* info) { - const NcpClientLock lock(this); CHECK_TRUE(connState_ == NcpConnectionState::CONNECTED, SYSTEM_ERROR_INVALID_STATE); + const NcpClientLock lock(this); int rtlError = 0; // LOG(INFO, "RNCP getNetworkInfo"); @@ -380,9 +382,10 @@ int RealtekNcpClient::getNetworkInfo(WifiNetworkInfo* info) { } int RealtekNcpClient::scan(WifiScanCallback callback, void* data) { - const NcpClientLock lock(this); - + CHECK_FALSE(needsReset_, SYSTEM_ERROR_BUSY); CHECK_TRUE(ncpState_ == NcpState::ON, SYSTEM_ERROR_INVALID_STATE); + + const NcpClientLock lock(this); struct Context { WifiScanCallback callback = nullptr; void* data = nullptr; @@ -454,17 +457,19 @@ int RealtekNcpClient::scan(WifiScanCallback callback, void* data) { for (int i = 0; i < ctx.results.size(); i++) { callback(ctx.results[i], data); } - if ((rtlError && ctx.results.size() == 0) || rtlError == RTW_TIMEOUT) { + + // XXX: + if ((rtlError /* keeping this for now */ && ctx.results.size() == 0) || rtlError == RTW_TIMEOUT) { // Workaround for a weird state we might enter where the wifi driver // is not returning any results - rtwRadioReset(); - return rtl_error_to_system(rtlError); + needsReset_ = true; } return rtl_error_to_system(rtlError); } int RealtekNcpClient::getMacAddress(MacAddress* addr) { + const NcpClientLock lock(this); char mac[6*2 + 5 + 1] = {}; wifi_get_mac_address(mac); CHECK_TRUE(macAddressFromString(addr, mac), SYSTEM_ERROR_UNKNOWN); @@ -564,8 +569,14 @@ int RealtekNcpClient::dataChannelWrite(int id, const uint8_t* data, size_t size) int RealtekNcpClient::dataChannelFlowControl(bool state) { return SYSTEM_ERROR_NONE; } + void RealtekNcpClient::processEvents() { + if (needsReset_) { + rtwRadioReset(); + needsReset_ = false; + } } + int RealtekNcpClient::checkParser() { return SYSTEM_ERROR_NONE; } diff --git a/hal/network/ncp_client/realtek/rtl_ncp_client.h b/hal/network/ncp_client/realtek/rtl_ncp_client.h index 9a890875b4..a642a83f49 100644 --- a/hal/network/ncp_client/realtek/rtl_ncp_client.h +++ b/hal/network/ncp_client/realtek/rtl_ncp_client.h @@ -73,6 +73,7 @@ class RealtekNcpClient: public WifiNcpClient { volatile NcpState prevNcpState_; volatile NcpConnectionState connState_; volatile NcpPowerState pwrState_; + volatile bool needsReset_ = false; void ncpState(NcpState state); void ncpPowerState(NcpPowerState state); diff --git a/hal/src/rtl872x/lwip/lwip_rtlk.cpp b/hal/src/rtl872x/lwip/lwip_rtlk.cpp index 69643ff5c0..35d4e3e0f5 100644 --- a/hal/src/rtl872x/lwip/lwip_rtlk.cpp +++ b/hal/src/rtl872x/lwip/lwip_rtlk.cpp @@ -18,44 +18,44 @@ extern void restore_flags(void); } unsigned char is_promisc_enabled(void) { - LOG(INFO, "is_promisc_enabled"); + // LOG(INFO, "is_promisc_enabled"); return 0; } int promisc_set(rtw_rcr_level_t enabled, void (*callback)(unsigned char*, unsigned int, void*), unsigned char len_used) { - LOG(INFO, "promisc_set TODO"); + // LOG(INFO, "promisc_set TODO"); return -1; } void promisc_deinit(void *padapter) { - LOG(INFO, "promisc_deinit TODO"); + // LOG(INFO, "promisc_deinit TODO"); } int promisc_recv_func(void *padapter, void *rframe) { - LOG(INFO, "promisc_recv_func TODO"); + // LOG(INFO, "promisc_recv_func TODO"); return 0; } void eap_autoreconnect_hdl(uint8_t method_id) { - LOG(INFO, "eap_autoreconnect_hdl"); + // LOG(INFO, "eap_autoreconnect_hdl"); } int get_eap_phase(void) { - LOG(INFO, "get_eap_phase"); + // LOG(INFO, "get_eap_phase"); return 0; } int get_eap_method(void) { - LOG(INFO, "get_eap_method"); + // LOG(INFO, "get_eap_method"); return 0; } void netif_post_sleep_processing(void) { - LOG(INFO, "netif_post_sleep_processing TODO"); + // LOG(INFO, "netif_post_sleep_processing TODO"); } void netif_pre_sleep_processing(void) { - LOG(INFO, "netif_pre_sleep_processing TODO"); + // LOG(INFO, "netif_pre_sleep_processing TODO"); } int netif_get_idx(struct netif* pnetif) { diff --git a/hal/src/rtl872x/rtl_sdk_support.cpp b/hal/src/rtl872x/rtl_sdk_support.cpp index 4dc394a66d..5793507d8f 100644 --- a/hal/src/rtl872x/rtl_sdk_support.cpp +++ b/hal/src/rtl872x/rtl_sdk_support.cpp @@ -157,8 +157,9 @@ void rtwCoexStop() { } void rtwRadioReset() { - std::lock_guard lk(radioMutex); + // XXX: the order of the locks has to be BLE -> radioMutex hal_ble_lock(nullptr); + std::unique_lock lk(radioMutex); bool bleInitialized = hal_ble_is_initialized(nullptr); bool advertising = hal_ble_gap_is_advertising(nullptr) || hal_ble_gap_is_connecting(nullptr, nullptr) || @@ -175,6 +176,7 @@ void rtwRadioReset() { hal_ble_gap_start_advertising(nullptr); } } + lk.unlock(); hal_ble_unlock(nullptr); } diff --git a/hal/src/rtl872x/usbd_driver.cpp b/hal/src/rtl872x/usbd_driver.cpp index 19e12ea68a..1ee937d803 100644 --- a/hal/src/rtl872x/usbd_driver.cpp +++ b/hal/src/rtl872x/usbd_driver.cpp @@ -86,6 +86,8 @@ uint8_t endpointTypeToRtl(EndpointType type) { const size_t RTL_USB_DEV_PCD_OFFSET = offsetof(usb_dev_t, pcd); const size_t RTL_USB_PCD_SPINLOCK_OFFSET = 0x180; +const unsigned int RTL_USB_RESET_COUNT_THRESHOLD = 100; + } // anonymous extern "C" { @@ -189,6 +191,7 @@ int RtlUsbDriver::detach() { usbd_unregister_class(); usbd_deinit(); initialized_ = false; + resetCount_ = 0; return 0; } @@ -197,6 +200,13 @@ void RtlUsbDriver::reset() { config_ = 0; usb_hal_disable_global_interrupt(); needsReset_ = true; + resetCount_ = 0; + } else { + if (resetCount_++ >= RTL_USB_RESET_COUNT_THRESHOLD) { + usb_hal_disable_global_interrupt(); + needsReset_ = true; + resetCount_ = 0; + } } } @@ -437,6 +447,7 @@ uint8_t RtlUsbDriver::setConfigCb(usb_dev_t* dev, uint8_t config) { std::lock_guard lk(*self); self->setDevReference(dev); self->config_ = (int)config; + self->resetCount_ = 0; return CHECK_RTL_USB(self->setConfig(config)); } diff --git a/hal/src/rtl872x/usbd_driver.h b/hal/src/rtl872x/usbd_driver.h index e7ff8ab57f..c9fdc65c10 100644 --- a/hal/src/rtl872x/usbd_driver.h +++ b/hal/src/rtl872x/usbd_driver.h @@ -144,6 +144,7 @@ class RtlUsbDriver : public DeviceDriver { volatile bool initialized_ = false; volatile bool needsReset_ = false; volatile int config_ = 0; + volatile unsigned int resetCount_ = 0; }; } // namespace usbd diff --git a/system/src/control/wifi_new.cpp b/system/src/control/wifi_new.cpp index c4e456c696..3f1745bfc3 100644 --- a/system/src/control/wifi_new.cpp +++ b/system/src/control/wifi_new.cpp @@ -117,10 +117,33 @@ int joinNewNetwork(ctrl_request* req) { conf.security((WifiSecurity)pbReq.security); #endif conf.credentials(std::move(cred)); + // Connect to the network + CHECK(ncpClient->on()); + // FIXME: synchronize NCP client / NcpNetif and system network manager state + bool needToConnect = network_connecting(NETWORK_INTERFACE_WIFI_STA, 0, nullptr) || + network_ready(NETWORK_INTERFACE_WIFI_STA, NETWORK_READY_TYPE_ANY, nullptr); + // To unblock + ncpClient->disable(); + CHECK(ncpClient->enable()); + // These two are in sync now + ncpClient->disconnect(); // ignore the error + network_disconnect(NETWORK_INTERFACE_WIFI_STA, NETWORK_DISCONNECT_REASON_USER, nullptr); + // FIXME: We are wiating for ncpNetif to potentially fully disconnect + // FIXME: synchronize NCP client / NcpNetif and system network manager state + CHECK(ncpClient->enable()); + CHECK(ncpClient->on()); + network_connect(NETWORK_INTERFACE_WIFI_STA, 0, 0, nullptr); + NAMED_SCOPE_GUARD(networkDisconnectGuard, { + // FIXME: synchronize NCP client / NcpNetif and system network manager state + if (!needToConnect) { + network_disconnect(NETWORK_INTERFACE_WIFI_STA, NETWORK_DISCONNECT_REASON_USER, nullptr); + } + }); // Set new configuration - CHECK(wifiMgr->setNetworkConfig(conf, true)); + CHECK(wifiMgr->setNetworkConfig(conf, WifiNetworkConfigFlag::VALIDATE)); // TODO: Not adding NetworkCredentials for now as this object needs to be allocated on heap and then cleaned up system_notify_event(network_credentials, network_credentials_added); + networkDisconnectGuard.dismiss(); return 0; } diff --git a/user/tests/wiring/wifi_cred_check/wifi_cred_check.cpp b/user/tests/wiring/wifi_cred_check/wifi_cred_check.cpp index a95e6f78aa..93d9bc8a61 100644 --- a/user/tests/wiring/wifi_cred_check/wifi_cred_check.cpp +++ b/user/tests/wiring/wifi_cred_check/wifi_cred_check.cpp @@ -25,7 +25,7 @@ test(Test_00_Init) { credentials.setValidate(true); } -test(Test_01_WiFi_Credentials_Check_When_WiFi_Is_Off) { +test(Test_01_WiFi_Credentials_Check_And_WiFi_State_Consistency) { for (uint8_t test = 0; test < WiFiState::MAX; test++) { Serial.printlnf(" > Set WiFi state to %s", (test ? (test == 1 ? "ON" : "CONNECTED") : "OFF")); switch (test) { diff --git a/wiring/inc/spark_wiring_wifi.h b/wiring/inc/spark_wiring_wifi.h index ce7e5d20ea..0855487118 100644 --- a/wiring/inc/spark_wiring_wifi.h +++ b/wiring/inc/spark_wiring_wifi.h @@ -243,16 +243,16 @@ class WiFiClass : public NetworkClass return (network_set_credentials(*this, 0, &creds, NULL) == 0); } - void setCredentials(const char* ssid, WiFiCredentials credentials) { + bool setCredentials(const char* ssid, WiFiCredentials credentials) { WLanCredentials creds = credentials.getHalCredentials(); creds.ssid = ssid; creds.ssid_len = ssid ? strlen(ssid) : 0; - network_set_credentials(*this, 0, &creds, NULL); + return network_set_credentials(*this, 0, &creds, NULL) == 0; } - void setCredentials(WiFiCredentials credentials) { + bool setCredentials(WiFiCredentials credentials) { WLanCredentials creds = credentials.getHalCredentials(); - network_set_credentials(*this, 0, &creds, NULL); + return network_set_credentials(*this, 0, &creds, NULL) == 0; } bool hasCredentials(void) {