Keep native USB responsive during ESP32 emergency radio sleep
This commit is contained in:
1 parent
3385f2acaa
commit
5cbff19463
2 files changed
+41
-4
No files matched your search
@@ -1413,12 +1413,15 @@ void ESP32Board::powerOff() {
|
||||
|
||||
#if MESH_ENABLE_EMERGENCY_POWER
|
||||
bool ESP32Board::emergencySleepMilliseconds(uint32_t millisecs) {
|
||||
// The caller owns radio suspension and reversible wireless shutdown. Never
|
||||
// force a live WiFi/Bluetooth stack through explicit light sleep.
|
||||
if (millisecs == 0 || isRadioTestActive() || xPortInIsrContext()
|
||||
|| xTaskGetSchedulerState() != taskSCHEDULER_RUNNING
|
||||
|| !mesh::esp32::manualLightSleepReady()) return false;
|
||||
|| xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) return false;
|
||||
// Native USB loses its connection in light sleep, even without an open CDC
|
||||
// reader. Retain the ordinary physical-host/loss-grace policy here too.
|
||||
// RF remains suspended, but an attached host gets frequent command service.
|
||||
const bool usb_held = isUsbSleepHeld();
|
||||
if (!usb_held && !mesh::esp32::manualLightSleepReady()) return false;
|
||||
if (millisecs > 10000UL) millisecs = 10000UL;
|
||||
if (usb_held && millisecs > 100UL) millisecs = 100UL;
|
||||
const esp_err_t watchdog_status = esp_task_wdt_status(nullptr);
|
||||
const bool watched = watchdog_status == ESP_OK;
|
||||
if (!watched && watchdog_status != ESP_ERR_NOT_FOUND
|
||||
@@ -1427,7 +1430,15 @@ bool ESP32Board::emergencySleepMilliseconds(uint32_t millisecs) {
|
||||
if (watched && millisecs > uint32_t(CONFIG_ESP_TASK_WDT_TIMEOUT_S) * 500UL)
|
||||
millisecs = uint32_t(CONFIG_ESP_TASK_WDT_TIMEOUT_S) * 500UL;
|
||||
#endif
|
||||
const TickType_t usb_ticks = usb_held ? pdMS_TO_TICKS(millisecs) : 0;
|
||||
if (usb_held && usb_ticks == 0) return false;
|
||||
if (watched && esp_task_wdt_reset() != ESP_OK) return false;
|
||||
if (usb_held) {
|
||||
// This also remains safe during asynchronous wireless teardown. Do not
|
||||
// arm or clear wake sources while yielding with the USB peripheral live.
|
||||
vTaskDelay(usb_ticks);
|
||||
return !watched || esp_task_wdt_reset() == ESP_OK;
|
||||
}
|
||||
if (esp_sleep_enable_timer_wakeup(uint64_t(millisecs) * 1000ULL) != ESP_OK)
|
||||
return false;
|
||||
// Add no radio/GPIO wake. Existing button sources remain untouched. The
|
||||
|
||||
@@ -22,6 +22,7 @@ HARNESS = r'''
|
||||
#include <vector>
|
||||
static std::vector<std::string> events;
|
||||
static bool radio_test = false, interrupt_context = false, wireless_ready = true;
|
||||
static bool usb_held = false;
|
||||
static unsigned primask = 0, basepri = 0, faultmask = 0;
|
||||
static unsigned __get_IPSR() { return interrupt_context; }
|
||||
static unsigned __get_PRIMASK() { return primask; }
|
||||
@@ -102,6 +103,7 @@ struct NRF52Board : Base {
|
||||
};
|
||||
struct ESP32Board : Base {
|
||||
bool isRadioTestActive() const { return radio_test; }
|
||||
bool isUsbSleepHeld() const { return usb_held; }
|
||||
@ESP_SUPPORT@
|
||||
bool emergencySleepMilliseconds(uint32_t millisecs) override;
|
||||
};
|
||||
@@ -109,6 +111,7 @@ struct ESP32Board : Base {
|
||||
@ESP_SLEEP@
|
||||
static void reset() {
|
||||
events.clear(); radio_test = interrupt_context = false; wireless_ready = true;
|
||||
usb_held = false;
|
||||
primask = basepri = faultmask = 0; nrf_nvic_state.__cr_flag = 0;
|
||||
scheduler = taskSCHEDULER_RUNNING;
|
||||
tick_ms = 1; delayed_ticks = nrf_feeds = 0;
|
||||
@@ -165,6 +168,29 @@ int main() {
|
||||
|
||||
reset(); wireless_ready = false;
|
||||
assert(!esp.emergencySleepMilliseconds(100)); assert(events.empty());
|
||||
reset(); usb_held = true; wireless_ready = false; timer_us = 555;
|
||||
assert(esp.emergencySleepMilliseconds(UINT32_MAX));
|
||||
assert(delayed_ticks == 100 && events == std::vector<std::string>{"delay"});
|
||||
assert(!timer_calls && !sleep_calls && !clear_calls && timer_us == 555);
|
||||
assert(other_wake_sources == 0x71); // USB, RF and existing wake ownership are untouched.
|
||||
usb_held = false; events.clear();
|
||||
assert(!esp.emergencySleepMilliseconds(100) && events.empty()); // Teardown is still live.
|
||||
wireless_ready = true;
|
||||
assert(esp.emergencySleepMilliseconds(100)); // Recheck the host on every call; detach permits light sleep.
|
||||
assert(last_armed_us == 100000 && sleep_calls == 1 && !timer_us);
|
||||
reset(); usb_held = true; task_watchdog_status = ESP_OK;
|
||||
assert(esp.emergencySleepMilliseconds(250));
|
||||
assert(delayed_ticks == 100 && task_watchdog_feeds == 2);
|
||||
assert((events == std::vector<std::string>{"task_feed", "delay", "task_feed"}));
|
||||
assert(!timer_calls && !sleep_calls && !clear_calls);
|
||||
reset(); usb_held = true; tick_ms = 1000;
|
||||
assert(!esp.emergencySleepMilliseconds(100) && events.empty());
|
||||
for (unsigned failure : {1U, 2U}) {
|
||||
reset(); usb_held = true; task_watchdog_status = ESP_OK; fail_watchdog_feed = failure;
|
||||
assert(!esp.emergencySleepMilliseconds(100));
|
||||
assert(delayed_ticks == (failure == 1 ? 0U : 100U));
|
||||
assert(!timer_calls && !sleep_calls && !clear_calls);
|
||||
}
|
||||
reset(); task_watchdog_status = 99;
|
||||
assert(!esp.emergencySleepMilliseconds(100)); assert(events.empty());
|
||||
reset(); assert(esp.emergencySleepMilliseconds(UINT32_MAX));
|
||||
|
||||
Reference in new issue
Block a user