FelipeMdeO opened a new pull request, #19725:
URL: https://github.com/apache/nuttx/pull/19725

   # arch/[risc-v|xtensa]: fix Wi-Fi station reconnection after AP-side 
disconnect
   
   ## Summary
   
   An ESP32 Wi-Fi station running NuttX never recovers from an AP-initiated
   disconnection. Once the access point goes away — a reboot, a brief outage, a
   deauthentication — the station drops the link and stays down forever. The
   interface remains `UP` without `RUNNING` and only a manual
   `wapi psk` + `wapi essid` brings it back. Nothing is logged, so in the field
   this looks like the board "losing the network" for no reason.
   
   This is a regression. The disconnect handler in `esp_wifi_event_handler.c`
   reconnects only when the reported reason is `WIFI_REASON_ASSOC_LEAVE`:
   
   ```c
   case WIFI_EVENT_STA_DISCONNECTED:
       wlinfo("Wi-Fi station disconnected, reason: %u\n", reason);
       esp_wlan_sta_disconnect_hook();
       if (reason == WIFI_REASON_ASSOC_LEAVE)
         {
           work_queue(LPWORK, &g_wifi_reconnect_work, esp_reconnect_work_cb, 
NULL, 0);
         }
       break;
   ```
   
   The `reason` field carries an 802.11 reason code, extended by ESP-IDF with
   vendor values above 200 for locally detected conditions
   (`wifi_err_reason_t` in `esp_wifi_types_generic.h`):
   
   ```c
   WIFI_REASON_AUTH_EXPIRE    = 2,    /* Authentication expired          */
   WIFI_REASON_AUTH_LEAVE     = 3,    /* Deauthentication due to leaving */
   WIFI_REASON_ASSOC_EXPIRE   = 4,    /* Association expired             */
   WIFI_REASON_ASSOC_LEAVE    = 8,    /* Deassociated due to leaving     */
   WIFI_REASON_BEACON_TIMEOUT = 200,  /* Beacon timeout                  */
   WIFI_REASON_NO_AP_FOUND    = 201,  /* No AP found                     */
   ```
   
   `WIFI_REASON_ASSOC_LEAVE` is the value the stack reports for a *locally
   initiated* disconnect — confirmed on hardware, since running `wapi essid` 
logs
   `reason: 8` because the tool disconnects before associating. So the surviving
   branch covers the one case where reconnecting is not needed, and every
   AP-initiated reason is left unhandled.
   
   Before the Wi-Fi driver refactors the same handler used an explicit intent
   flag, which is the rule ESP-IDF documents — reconnect on any reason unless 
the
   disconnection was requested locally:
   
   ```c
   case WIFI_ADPT_EVT_STA_DISCONNECT:
       g_sta_connected = false;
       esp_wlan_sta_set_linkstatus(false);
       if (g_sta_reconnect)
         {
           ret = esp_wifi_connect();
         }
       break;
   ```
   
   `g_sta_reconnect` was set true by `esp_wifi_sta_connect()` and false by
   `esp_wifi_sta_disconnect()`, so the driver always knew whether it had asked 
to
   leave. The refactors replaced that flag with the reason-code test:
   
   | Commit | Date | Chips |
   |---|---|---|
   | `1f7c3a32e5` | 2025-08-21 | ESP32-C3 / C6 |
   | `20ff68bd650` | 2025-09-01 | ESP32 / S2 / S3 |
   
   The intent cannot be derived from the reason code: the reason is what the
   *other* end reports, while the intent is state this driver owns. The correct
   logic is still present in tree today, in
   `arch/risc-v/src/esp32c3-legacy/esp32c3_wifi_adapter.c`, so the same chip
   currently ships two drivers with opposite behaviour.
   
   This PR restores the intent flag on both architectures. 
`esp_reconnect_work_cb`
   re-checks the flag because the user may request `ifdown` between the work 
being
   queued and the work running. Retry cadence is left to the event loop, 
matching
   ESP-IDF: a failed attempt raises another `STA_DISCONNECTED` (reason 201) 
which
   schedules the next one, self-throttled by the radio's scan time (~2.4 s
   measured).
   
   The `failure_retry_cnt` writes in `esp_wifi_sta_disconnect()` are left
   untouched, so this PR only adds the flag there. A comment marks them as
   having no documented effect: ESP-IDF states the field applies only when
   `scan_method` is `WIFI_ALL_CHANNEL_SCAN`, and NuttX never selects it — the
   only assignment of `scan_method` anywhere in the tree is
   `WIFI_FAST_SCAN` in the legacy ESP32-C3 driver, and neither the common driver
   nor the bundled HAL ever writes the field. Removing the now-redundant writes,
   and revisiting whether that field is the right mechanism at all, is better
   handled as a follow-up PR so this one stays focused on the regression.
   
   ## Impact
   
   * Impact on user: yes, and this is the point. A station that loses its AP now
     reconnects by itself instead of staying offline until rebooted or manually
     re-associated.
   * Impact on hardware: all Espressif chips with Wi-Fi station support, on both
     `arch/risc-v` and `arch/xtensa`. Validated on ESP32-C3.
   * Impact on compatibility: a user-requested disconnect (`ifdown`,
     `esp_wifi_sta_disconnect(false)` from the ioctl path) still does not
     reconnect — that path is what the flag protects. No call sites changed:
     `esp_wlan_netdev.c` already passes `false` for the user disconnect and the
     four internal disconnects in `esp_wifi_api.c` already pass `true`.
   
   ## Testing
   
   Two boards on the same access point, in the same time window, so the trigger 
is
   identical for both:
   
   | | Device under test | Reference |
   |---|---|---|
   | Board | ESP32-C3-DevKit | ESP32-C6-DevKit |
   | Firmware | NuttX, stock `esp32c3-devkit:wifi` defconfig | ESP-IDF v6.0.1, 
`examples/wifi/getting_started/station` |
   | Only changes | `CONFIG_DEBUG_WIRELESS_INFO=y` + Wi-Fi credentials | log 
the reason code; retry without a limit |
   
   The NuttX side runs the unmodified board defconfig — no application on top, 
so
   the Wi-Fi driver is the only thing under test.
   
   **Reproduction: power the access point off for 5 minutes, then back on.**
   
   NuttX, ESP32-C3 — the AP disappears:
   
   ```
   I (14465) wifi:bcn_timeout,ap_probe_send_start
   I (14482) wifi:ap_probe_send over, reset wifi status to disassoc
   I (14482) wifi:state: run -> init (0xc800)
   esp_wifi_event_handler: Wi-Fi station disconnected, reason: 200
   I (14483) wifi:<ba-del>idx:0, tid:0
   I (14483) wifi:<ba-del>idx:1, tid:7
   ```
   
   Nothing follows. No scan, no association attempt, no further log line — the
   remaining entries are the radio tearing down block-ack state at the same
   millisecond. The AP came back a few minutes later; 21 minutes after that the
   board was still offline:
   
   ```
   nsh> ifconfig
   wlan0        Link encap:Ethernet HWaddr 80:65:99:2d:4e:3c at UP mtu 1500
        inet addr:192.168.15.144 DRaddr:192.168.15.1 Mask:255.255.255.0
   ```
   
   `UP` without `RUNNING`: carrier down, no recovery.
   
   ESP-IDF, ESP32-C6, same outage — note the **same reason 200**:
   
   ```
   W (1121797) wifi station: STA_DISCONNECTED, reason: 200
   I (1121797) wifi station: retry to connect to the AP (attempt 1)
   W (1124217) wifi station: STA_DISCONNECTED, reason: 201
   I (1124217) wifi station: retry to connect to the AP (attempt 2)
   ...
   W (1473207) wifi station: STA_DISCONNECTED, reason: 201
   I (1473207) wifi station: retry to connect to the AP (attempt 147)
   I (1494777) wifi station: got ip:192.168.15.161
   ```
   
   147 attempts spaced ~2.4 s apart, then an address as soon as the AP was 
serving
   again. Both stacks received the same event; only the handling differs.
   
   **With this patch applied** — this is why the PR is a draft. The patched
   firmware is built and running on the ESP32-C3, and manual association,
   `ifdown` and user-requested disconnect all still behave as before, but the
   access-point outage has not been repeated yet against the fixed build. That
   run will be added here before the PR leaves draft. Expected result: the same
   `reason: 200`, followed by reconnection attempts and a restored link.
   
   Reviewers who want to reproduce either side need only two boards and a power
   switch on their access point.
   
   **Related reports that may share this root cause, though neither was 
diagnosed:**
   https://github.com/apache/nuttx/issues/19137
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to