From 456b427f0d0af5a99f8d6d0462619335a5246859 Mon Sep 17 00:00:00 2001 From: Winford Date: Sun, 21 Apr 2024 15:54:40 -0700 Subject: [PATCH] Completly stop driver and free all resources with network:stop/0 on ESP32 Adds a destroy callback to the ESP32 network driver to completely stop the driver and free all network resources when network:stop/0 is used. Previosly the driver was not being stopped internally and resources were not freed when the gen_server was stopped, causing instability, and possible crashes when event callbacks were triggered, but there was no process alive to handle them. Closes #643 Signed-off-by: Winford --- CHANGELOG.md | 2 + libs/eavmlib/src/network.erl | 2 + .../components/avm_builtins/network_driver.c | 56 ++++++++++++++++++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f21faa90a..44ed36e50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix several uses of free on prevously released memory on ESP32, under certain error condition using `network:start/1`, that would lead to a hard crash of the VM. +- Fix a bug in ESP32 network driver where the low level driver was not being stopped and resoureces were not freed +when `network:stop/0` was used, see issue [#643](https://github.com/atomvm/AtomVM/issues/643) ## [0.6.2] - 25-05-2024 diff --git a/libs/eavmlib/src/network.erl b/libs/eavmlib/src/network.erl index 887eb090d..d7326d36a 100644 --- a/libs/eavmlib/src/network.erl +++ b/libs/eavmlib/src/network.erl @@ -332,6 +332,8 @@ handle_info(Msg, State) -> %% @hidden terminate(_Reason, _State) -> + Ref = make_ref(), + network_port ! {?SERVER, Ref, stop}, ok. %% diff --git a/src/platforms/esp32/components/avm_builtins/network_driver.c b/src/platforms/esp32/components/avm_builtins/network_driver.c index e87a21277..392d112ec 100644 --- a/src/platforms/esp32/components/avm_builtins/network_driver.c +++ b/src/platforms/esp32/components/avm_builtins/network_driver.c @@ -87,12 +87,14 @@ enum network_cmd NetworkInvalidCmd = 0, // TODO add support for scan, ifconfig NetworkStartCmd, - NetworkRssiCmd + NetworkRssiCmd, + NetworkStopCmd }; static const AtomStringIntPair cmd_table[] = { { ATOM_STR("\x5", "start"), NetworkStartCmd }, { ATOM_STR("\x4", "rssi"), NetworkRssiCmd }, + { ATOM_STR("\x4", "stop"), NetworkStopCmd }, SELECT_INT_DEFAULT(NetworkInvalidCmd) }; @@ -758,12 +760,51 @@ static void start_network(Context *ctx, term pid, term ref, term config) if (!IS_NULL_PTR(ap_wifi_config)) { set_dhcp_hostname(ap_wifi_interface, "AP", interop_kv_get_value(ap_config, dhcp_hostname_atom, ctx->global)); } + // // Done -- send an ok so the FSM can proceed // port_send_reply(ctx, pid, ref, OK_ATOM); } +static void stop_network(Context *ctx) +{ + // Stop unregister event callbacks so they dont trigger during shutdown. + esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler); + + esp_netif_t *sta_wifi_interface = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + esp_netif_t *ap_wifi_interface = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"); + + // Disconnect STA if connected to access point + if ((sta_wifi_interface != NULL) && (esp_netif_is_netif_up(sta_wifi_interface))) { + esp_err_t err = esp_wifi_disconnect(); + if (UNLIKELY(err == ESP_FAIL)) { + ESP_LOGE(TAG, "ESP FAIL error while disconnecting from AP, continuing network shutdown..."); + } + } + + // Stop and deinit the WiFi driver, these only return OK, or not init error (fine to ignore). + esp_wifi_stop(); + esp_wifi_deinit(); + + // Stop sntp (ignore OK, or not configured error) + esp_sntp_stop(); + + // Delete network event loop + esp_err_t err = esp_event_loop_delete_default(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Invalid state error while deleting event loop, continuing network shutdown..."); + } + + // Destroy existing netif interfaces + if (ap_wifi_interface != NULL) { + esp_netif_destroy_default_wifi(ap_wifi_interface); + } + if (sta_wifi_interface != NULL) { + esp_netif_destroy_default_wifi(sta_wifi_interface); + } +} + static void get_sta_rssi(Context *ctx, term pid, term ref) { size_t tuple_reply_size = PORT_REPLY_SIZE + TUPLE_SIZE(2); @@ -784,11 +825,11 @@ static void get_sta_rssi(Context *ctx, term pid, term ref) port_ensure_available(ctx, tuple_reply_size); term reply = port_create_tuple2(ctx, make_atom(ctx->global, ATOM_STR("\x4", "rssi")), rssi); port_send_reply(ctx, pid, ref, reply); - } static NativeHandlerResult consume_mailbox(Context *ctx) { + bool cmd_terminate = false; Message *message = mailbox_first(&ctx->mailbox); term msg = message->message; @@ -821,6 +862,10 @@ static NativeHandlerResult consume_mailbox(Context *ctx) case NetworkRssiCmd: get_sta_rssi(ctx, pid, ref); break; + case NetworkStopCmd: + cmd_terminate = true; + stop_network(ctx); + break; default: { ESP_LOGE(TAG, "Unrecognized command: %x", cmd); @@ -847,7 +892,7 @@ static NativeHandlerResult consume_mailbox(Context *ctx) mailbox_remove_message(&ctx->mailbox, &ctx->heap); - return NativeContinue; + return cmd_terminate ? NativeTerminate : NativeContinue; } // @@ -883,6 +928,11 @@ Context *network_driver_create_port(GlobalContext *global, term opts) return ctx; } +// +// Destructor +// + + #ifdef CONFIG_AVM_ENABLE_NETWORK_PORT_DRIVER REGISTER_PORT_DRIVER(network, network_driver_init, NULL, network_driver_create_port)