Give native USB time to recover after scheduled light sleep
This commit is contained in:
1 parent
5cbff19463
commit
be6d636c79
3 files changed
+70
-8
No files matched your search
@@ -1432,11 +1432,23 @@ bool ESP32Board::emergencySleepMilliseconds(uint32_t millisecs) {
|
||||
#endif
|
||||
const TickType_t usb_ticks = usb_held ? pdMS_TO_TICKS(millisecs) : 0;
|
||||
if (usb_held && usb_ticks == 0) return false;
|
||||
TickType_t usb_service_ticks = 0;
|
||||
#if MESH_ESP32_USB_CONSOLE_COOPERATIVE
|
||||
if (!usb_held) {
|
||||
// SOF/connection state is sampled by RTOS hooks. Reserve awake service
|
||||
// time after a timer wake so a newly attached host can become visible
|
||||
// before the next sleep attempt. Keep the whole wait within this budget.
|
||||
const uint32_t service_ms = millisecs > 100UL ? 100UL : millisecs;
|
||||
usb_service_ticks = pdMS_TO_TICKS(service_ms);
|
||||
if (usb_service_ticks == 0) return false;
|
||||
millisecs -= service_ms;
|
||||
}
|
||||
#endif
|
||||
if (watched && esp_task_wdt_reset() != ESP_OK) return false;
|
||||
if (usb_held) {
|
||||
if (usb_held || millisecs == 0) {
|
||||
// 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);
|
||||
vTaskDelay(usb_held ? usb_ticks : usb_service_ticks);
|
||||
return !watched || esp_task_wdt_reset() == ESP_OK;
|
||||
}
|
||||
if (esp_sleep_enable_timer_wakeup(uint64_t(millisecs) * 1000ULL) != ESP_OK)
|
||||
@@ -1445,6 +1457,8 @@ bool ESP32Board::emergencySleepMilliseconds(uint32_t millisecs) {
|
||||
// normal sleep path installs its own next timer; never leave ours armed.
|
||||
const esp_err_t slept = esp_light_sleep_start();
|
||||
const esp_err_t cleared = esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER);
|
||||
if (slept == ESP_OK && cleared == ESP_OK && usb_service_ticks != 0)
|
||||
vTaskDelay(usb_service_ticks);
|
||||
const bool watchdog_ok = !watched || esp_task_wdt_reset() == ESP_OK;
|
||||
return slept == ESP_OK && cleared == ESP_OK && watchdog_ok;
|
||||
}
|
||||
|
||||
@@ -223,9 +223,7 @@ int main() {
|
||||
|
||||
|
||||
class EmergencySleepBackendTests(unittest.TestCase):
|
||||
def test_actual_platform_waits_and_cleanup(self):
|
||||
compiler = shutil.which("g++") or shutil.which("clang++")
|
||||
self.assertIsNotNone(compiler)
|
||||
def platform_code(self):
|
||||
sources = {name: (ROOT / path).read_text() for name, path in (
|
||||
("core", "src/MeshCore.h"),
|
||||
("nrf", "src/helpers/NRF52Board.cpp"),
|
||||
@@ -242,17 +240,63 @@ class EmergencySleepBackendTests(unittest.TestCase):
|
||||
("NRF_SLEEP", "nrf", "bool NRF52Board::emergencySleepMilliseconds("),
|
||||
("ESP_SLEEP", "esp", "bool ESP32Board::emergencySleepMilliseconds(")):
|
||||
code = code.replace("@" + marker + "@", extract_braced(sources[source], signature))
|
||||
return code
|
||||
|
||||
def run_code(self, code, flags=()):
|
||||
compiler = shutil.which("g++") or shutil.which("clang++")
|
||||
self.assertIsNotNone(compiler)
|
||||
with tempfile.TemporaryDirectory(prefix="emergency-sleep-backends-") as directory:
|
||||
work = Path(directory)
|
||||
cpp, binary = work / "test.cpp", work / "test"
|
||||
cpp.write_text(code)
|
||||
subprocess.run([compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror",
|
||||
"-DCONFIG_ESP_TASK_WDT_TIMEOUT_S=4", *SANITIZERS,
|
||||
"-DCONFIG_ESP_TASK_WDT_TIMEOUT_S=4", *flags, *SANITIZERS,
|
||||
str(cpp), "-o", str(binary)], check=True,
|
||||
capture_output=True, text=True, timeout=60)
|
||||
subprocess.run([str(binary)], check=True, capture_output=True,
|
||||
text=True, timeout=20)
|
||||
|
||||
def test_actual_platform_waits_and_cleanup(self):
|
||||
self.run_code(self.platform_code())
|
||||
|
||||
def test_native_usb_timer_wake_reserves_enumeration_service_without_extending_budget(self):
|
||||
code = self.platform_code().split("int main() {", 1)[0] + r'''
|
||||
int main() {
|
||||
ESP32Board esp;
|
||||
reset(); assert(esp.emergencySleepMilliseconds(10000));
|
||||
assert(last_armed_us == 9900000ULL && delayed_ticks == 100);
|
||||
assert((events == std::vector<std::string>{"timer", "sleep", "clear_timer", "delay"}));
|
||||
assert(timer_us == 0 && other_wake_sources == 0x71);
|
||||
reset(); task_watchdog_status = ESP_OK;
|
||||
assert(esp.emergencySleepMilliseconds(10000));
|
||||
assert(last_armed_us == 1900000ULL && delayed_ticks == 100 && task_watchdog_feeds == 2);
|
||||
assert((events == std::vector<std::string>{"task_feed", "timer", "sleep", "clear_timer", "delay", "task_feed"}));
|
||||
reset(); assert(esp.emergencySleepMilliseconds(125));
|
||||
assert(last_armed_us == 25000 && delayed_ticks == 100);
|
||||
reset(); assert(esp.emergencySleepMilliseconds(30));
|
||||
assert(delayed_ticks == 30 && events == std::vector<std::string>{"delay"});
|
||||
assert(!timer_calls && !sleep_calls && !clear_calls);
|
||||
reset(); tick_ms = 10;
|
||||
assert(esp.emergencySleepMilliseconds(129));
|
||||
assert(last_armed_us == 29000 && delayed_ticks == 10); // Timer + floored RTOS wait <=129ms.
|
||||
reset(); tick_ms = 1000;
|
||||
assert(!esp.emergencySleepMilliseconds(10000) && events.empty());
|
||||
for (int* failed : {&sleep_error, &clear_error}) {
|
||||
reset(); *failed = 9;
|
||||
assert(!esp.emergencySleepMilliseconds(10000));
|
||||
assert(!delayed_ticks && clear_calls == 1);
|
||||
assert((events == std::vector<std::string>{"timer", "sleep", "clear_timer"}));
|
||||
}
|
||||
reset(); timer_error = 9;
|
||||
assert(!esp.emergencySleepMilliseconds(10000));
|
||||
assert(!delayed_ticks && !sleep_calls && !clear_calls);
|
||||
reset(); usb_held = true; wireless_ready = false;
|
||||
assert(esp.emergencySleepMilliseconds(10000));
|
||||
assert(delayed_ticks == 100 && !sleep_calls); // Existing host still bypasses physical USB sleep.
|
||||
}
|
||||
'''
|
||||
self.run_code(code, ("-DMESH_ESP32_USB_CONSOLE_COOPERATIVE=1",))
|
||||
|
||||
def test_disabled_feature_has_no_new_base_virtuals(self):
|
||||
compiler = shutil.which("g++") or shutil.which("clang++")
|
||||
self.assertIsNotNone(compiler)
|
||||
|
||||
@@ -182,8 +182,12 @@ int main(){
|
||||
def test_actual_infrastructure_loop_services_saved_choice_and_quiet_deadline(self):
|
||||
for role in ("simple_repeater","simple_room_server","simple_sensor"):
|
||||
source=(ROOT/"examples"/role/"main.cpp").read_text()
|
||||
start=source.index(" board.servicePowerTestControls();",source.index("void loop()"))
|
||||
end=source.index("#endif",start)
|
||||
loop_start=source.index("void loop()")
|
||||
clock_service=source.index("mesh::esp32::serviceCpuFrequencyPolicy(",loop_start)
|
||||
# Emergency maintenance also services UART restoration before its
|
||||
# early return. Keep this fixture anchored to the clock policy.
|
||||
start=source.rindex(" board.servicePowerTestControls();",loop_start,clock_service)
|
||||
end=source.index("#endif",clock_service)
|
||||
service=source[start:end]
|
||||
tests="void role_service(){\n"+service+"}\n"+r'''
|
||||
int main(){
|
||||
|
||||
Reference in new issue
Block a user