diff --git a/include/infuse/dfu/helpers.h b/include/infuse/dfu/helpers.h index 610fbf227..12a8e3fd4 100644 --- a/include/infuse/dfu/helpers.h +++ b/include/infuse/dfu/helpers.h @@ -26,6 +26,26 @@ extern "C" { * @{ */ +/** + * @brief Prepare a flash area for write/erase + * + * Prepare to erase or write to a flash area. + * + * @note MUST call @ref infuse_dfu_write_erase_finish when complete + * + * @param fa Flash area (must be already opened) + */ +void infuse_dfu_write_erase_start(const struct flash_area *fa); + +/** + * @brief Finalise a flash area write/erase + * + * Cleanup the @ref infuse_dfu_write_erase_start call once complete. + * + * @param fa Flash area (must be already opened) + */ +void infuse_dfu_write_erase_finish(const struct flash_area *fa); + /** * @brief Erase a flash area to be ready for a new image * @@ -57,6 +77,17 @@ int infuse_dfu_nrf91_modem_delta_prepare(void); */ int infuse_dfu_nrf91_modem_delta_finish(void); +#ifdef CONFIG_ZTEST + +/** + * @brief Get the current balance count of the start/finish helpers + * + * @return int Number of start calls minus finish calls + */ +int infuse_dfu_write_erase_call_count(void); + +#endif /* CONFIG_ZTEST */ + /** * @} */ diff --git a/include/infuse/epacket/interface/epacket_bt_adv.h b/include/infuse/epacket/interface/epacket_bt_adv.h index 59141472c..66cefc817 100644 --- a/include/infuse/epacket/interface/epacket_bt_adv.h +++ b/include/infuse/epacket/interface/epacket_bt_adv.h @@ -28,6 +28,31 @@ extern "C" { #define epacket_bt_adv_frame epacket_v0_versioned_frame_format +/** + * @brief Request Bluetooth scanning to be suspended + * + * After calling, any active Bluetooth scanning is suspended and any future + * scanning is disabled until @ref epacket_bt_adv_scan_resume is called. + * + * A call to @ref epacket_receive received between these two function calls will + * be actioned once @ref epacket_bt_adv_scan_resume is called for the remaining + * duration. + * + * The duration of any suspension should be tightly bounded to much less than + * CONFIG_EPACKET_INTERFACE_BT_ADV_SCAN_WATCHDOG_SEC (default 10 minutes). + * + * This can be useful when performing actions where performance is typically degraded + * by Bluetooth activity, for example writing to internal flash. + */ +void epacket_bt_adv_scan_suspend(void); + +/** + * @brief Release a request for Bluetooth scanning to be suspended + * + * Release the constraint of Bluetooth scanning created by @ref epacket_bt_adv_scan_suspend. + */ +void epacket_bt_adv_scan_resume(void); + /** * @} */ diff --git a/subsys/dfu/dfu_helpers.c b/subsys/dfu/dfu_helpers.c index b458f6ff4..69e0b48a4 100644 --- a/subsys/dfu/dfu_helpers.c +++ b/subsys/dfu/dfu_helpers.c @@ -12,14 +12,52 @@ #include #include +#include #ifdef CONFIG_NRF_MODEM_LIB #include "nrf_modem_delta_dfu.h" #endif /* CONFIG_NRF_MODEM_LIB */ +#define INTERNAL_FLASH DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)) + +#ifdef CONFIG_ZTEST +static int write_erase_call_count; + +int infuse_dfu_write_erase_call_count(void) +{ + return write_erase_call_count; +} + +#endif /* CONFIG_ZTEST */ + +void infuse_dfu_write_erase_start(const struct flash_area *fa) +{ +#ifdef CONFIG_EPACKET_INTERFACE_BT_ADV + if (fa->fa_dev == INTERNAL_FLASH) { + /* Erasing/writing internal flash while Bluetooth scanning is very slow */ + epacket_bt_adv_scan_suspend(); + } +#endif /* CONFIG_EPACKET_INTERFACE_BT_ADV */ +#ifdef CONFIG_ZTEST + write_erase_call_count += 1; +#endif /* CONFIG_ZTEST */ +} + +void infuse_dfu_write_erase_finish(const struct flash_area *fa) +{ +#ifdef CONFIG_EPACKET_INTERFACE_BT_ADV + if (fa->fa_dev == INTERNAL_FLASH) { + epacket_bt_adv_scan_resume(); + } +#endif /* CONFIG_EPACKET_INTERFACE_BT_ADV */ +#ifdef CONFIG_ZTEST + write_erase_call_count -= 1; +#endif /* CONFIG_ZTEST */ +} + /* Implementation taken from zephyr_img_mgmt.c */ -int infuse_dfu_image_erase(const struct flash_area *fa, size_t image_len, - infuse_progress_cb_t progress_cb, bool mcuboot_trailer) +int infuse_dfu_image_erase_wrapped(const struct flash_area *fa, size_t image_len, + infuse_progress_cb_t progress_cb, bool mcuboot_trailer) { const struct device *dev = flash_area_get_device(fa); struct flash_pages_info page; @@ -96,6 +134,18 @@ int infuse_dfu_image_erase(const struct flash_area *fa, size_t image_len, #endif } +int infuse_dfu_image_erase(const struct flash_area *fa, size_t image_len, + infuse_progress_cb_t progress_cb, bool mcuboot_trailer) +{ + int rc; + + infuse_dfu_write_erase_start(fa); + rc = infuse_dfu_image_erase_wrapped(fa, image_len, progress_cb, mcuboot_trailer); + infuse_dfu_write_erase_finish(fa); + + return rc; +} + #ifdef CONFIG_NRF_MODEM_LIB int infuse_dfu_nrf91_modem_delta_prepare(void) { diff --git a/subsys/epacket/interfaces/epacket_bt_adv.c b/subsys/epacket/interfaces/epacket_bt_adv.c index 4ac322c2e..3db1ee216 100644 --- a/subsys/epacket/interfaces/epacket_bt_adv.c +++ b/subsys/epacket/interfaces/epacket_bt_adv.c @@ -49,6 +49,8 @@ static struct net_buf *adv_set_bufs[CONFIG_BT_EXT_ADV_MAX_ADV_SET]; static K_FIFO_DEFINE(tx_buf_queue); static struct bt_le_ext_adv *adv_set; static bool adv_set_active; +static uint8_t scan_suspended; +static K_SEM_DEFINE(scan_control, 1, 1); static void bt_adv_broadcast(const struct device *dev, struct net_buf *pkt) { @@ -218,15 +220,23 @@ static void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, static int epacket_bt_adv_receive_control(const struct device *dev, bool enable) { - int rc; + int rc = 0; + k_sem_take(&scan_control, K_FOREVER); if (enable) { - rc = bt_le_scan_start(&scan_param, scan_cb); + if (scan_suspended == 0) { + /* Scanning has been temporarily blocked */ + rc = bt_le_scan_start(&scan_param, scan_cb); + } infuse_work_reschedule(&scan_watchdog_work, SCAN_WDOG_TIMEOUT); } else { k_work_cancel_delayable(&scan_watchdog_work); - rc = bt_le_scan_stop(); + if (scan_suspended == 0) { + /* Scanning has already been stopped */ + rc = bt_le_scan_stop(); + } } + k_sem_give(&scan_control); return rc; } @@ -262,6 +272,40 @@ static void scan_rx_watchdog_expired(struct k_work *work) } } +void epacket_bt_adv_scan_suspend(void) +{ + int rc; + + k_sem_take(&scan_control, K_FOREVER); + if (k_work_delayable_is_pending(&scan_watchdog_work) && (scan_suspended == 0)) { + /* Scanning is currently ongoing, cancel it */ + LOG_INF("Suspending scanning"); + rc = bt_le_scan_stop(); + if (rc != 0) { + LOG_ERR("Failed to stop scanning (%d)", rc); + } + } + scan_suspended += 1; + k_sem_give(&scan_control); +} + +void epacket_bt_adv_scan_resume(void) +{ + int rc; + + k_sem_take(&scan_control, K_FOREVER); + scan_suspended -= 1; + if (k_work_delayable_is_pending(&scan_watchdog_work) && (scan_suspended == 0)) { + /* Scanning is still desired by the application */ + LOG_INF("Resuming scanning"); + rc = bt_le_scan_start(&scan_param, scan_cb); + if (rc != 0) { + LOG_ERR("Failed to restart scanning (%d)", rc); + } + } + k_sem_give(&scan_control); +} + static int epacket_bt_adv_init(const struct device *dev) { k_work_init_delayable(&scan_watchdog_work, scan_rx_watchdog_expired); diff --git a/subsys/rpc/commands/common_file_actions.c b/subsys/rpc/commands/common_file_actions.c index 7a772cdbb..99676ea01 100644 --- a/subsys/rpc/commands/common_file_actions.c +++ b/subsys/rpc/commands/common_file_actions.c @@ -99,6 +99,7 @@ int rpc_common_file_actions_start(struct rpc_common_file_actions_ctx *ctx, ctx->action = action; ctx->received = 0; ctx->crc = 0; + ctx->needs_cleanup = false; switch (ctx->action) { case RPC_ENUM_FILE_ACTION_DISCARD: @@ -142,6 +143,17 @@ int rpc_common_file_actions_start(struct rpc_common_file_actions_ctx *ctx, default: rc = -EINVAL; } + +#ifdef CONFIG_INFUSE_DFU_HELPERS + if (ctx->fa && (rc == 0)) { + /* Write should proceed, closed by either: + * rpc_common_file_actions_finish or + * rpc_common_file_actions_error_cleanup + */ + infuse_dfu_write_erase_start(ctx->fa); + ctx->needs_cleanup = true; + } +#endif /* CONFIG_INFUSE_DFU_HELPERS */ return rc; } @@ -255,6 +267,7 @@ static int finish_cpatch(struct rpc_common_file_actions_ctx *ctx) flash_area_open(FIXED_PARTITION_ID(slot0_partition), &fa_original); flash_area_open(FIXED_PARTITION_ID(slot1_partition), &fa_output); + infuse_dfu_write_erase_start(fa_output); /* Start patch process */ rc = cpatch_patch_start(fa_original, ctx->fa, &header); @@ -295,6 +308,7 @@ static int finish_cpatch(struct rpc_common_file_actions_ctx *ctx) cleanup: /* Cleanup files */ + infuse_dfu_write_erase_finish(fa_output); flash_area_close(fa_output); flash_area_close(fa_original); @@ -314,6 +328,11 @@ int rpc_common_file_actions_finish(struct rpc_common_file_actions_ctx *ctx, uint size_t mem_size; uint8_t *mem; + if (ctx->needs_cleanup) { + infuse_dfu_write_erase_finish(ctx->fa); + ctx->needs_cleanup = false; + } + /* Temporary memory buffer */ mem = rpc_server_command_working_mem(&mem_size); @@ -425,6 +444,12 @@ int rpc_common_file_actions_error_cleanup(struct rpc_common_file_actions_ctx *ct { int rc = 0; +#ifdef CONFIG_INFUSE_DFU_HELPERS + if (ctx->needs_cleanup) { + infuse_dfu_write_erase_finish(ctx->fa); + } +#endif /* CONFIG_INFUSE_DFU_HELPERS */ + switch (ctx->action) { #ifdef SUPPORT_APP_IMG #ifdef SUPPORT_APP_CPATCH diff --git a/subsys/rpc/commands/common_file_actions.h b/subsys/rpc/commands/common_file_actions.h index 6f10a92da..942d05929 100644 --- a/subsys/rpc/commands/common_file_actions.h +++ b/subsys/rpc/commands/common_file_actions.h @@ -18,9 +18,10 @@ struct rpc_common_file_actions_ctx { const struct flash_area *fa; uint32_t client_ctx; }; - enum rpc_enum_file_action action; uint32_t received; uint32_t crc; + enum rpc_enum_file_action action; + bool needs_cleanup; }; #define FILE_ALREADY_PRESENT 1 diff --git a/tests/bsim/bluetooth/epacket/gateway/src/main.c b/tests/bsim/bluetooth/epacket/gateway/src/main.c index 433b75fb4..25ee0c035 100644 --- a/tests/bsim/bluetooth/epacket/gateway/src/main.c +++ b/tests/bsim/bluetooth/epacket/gateway/src/main.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -172,6 +173,71 @@ static void main_gateway_scan(void) } } +static void main_gateway_scan_suspend(void) +{ + const struct device *epacket_bt_adv = DEVICE_DT_GET(DT_NODELABEL(epacket_bt_adv)); + int rc; + + common_init(); + epacket_set_receive_handler(epacket_bt_adv, epacket_bt_adv_receive_handler); + + /* Suspend and resume while not scanning does nothing */ + epacket_bt_adv_scan_suspend(); + epacket_bt_adv_scan_resume(); + + /* Start scanning while suspended */ + epacket_bt_adv_scan_suspend(); + rc = epacket_receive(epacket_bt_adv, K_FOREVER); + if (rc < 0) { + FAIL("Failed to start ePacket receive (%d)\n", rc); + return; + } + + /* No data received to start with */ + if (k_sem_take(&epacket_adv_received, K_MSEC(1100)) != -EAGAIN) { + FAIL("Received ePacket while suspended\n"); + return; + } + + /* Reference counted API, so the resume should not actually resume */ + epacket_bt_adv_scan_suspend(); + epacket_bt_adv_scan_resume(); + if (k_sem_take(&epacket_adv_received, K_MSEC(1100)) != -EAGAIN) { + FAIL("Received ePacket while suspended\n"); + return; + } + + /* Unblock the scanning, data received */ + epacket_bt_adv_scan_resume(); + if (k_sem_take(&epacket_adv_received, K_MSEC(1100)) != 0) { + FAIL("Failed to receive ePacket after resume\n"); + return; + } + + /* Suspend the scanning again */ + epacket_bt_adv_scan_suspend(); + if (k_sem_take(&epacket_adv_received, K_MSEC(1100)) != -EAGAIN) { + FAIL("Received ePacket while suspended\n"); + return; + } + + /* Halt scanning manually */ + rc = epacket_receive(epacket_bt_adv, K_NO_WAIT); + if (rc < 0) { + FAIL("Failed to stop ePacket receive (%d)\n", rc); + return; + } + + /* Unblock the scanning, but still no packets should be received */ + epacket_bt_adv_scan_resume(); + if (k_sem_take(&epacket_adv_received, K_MSEC(1100)) != -EAGAIN) { + FAIL("Received ePacket after halt\n"); + return; + } + + PASS("Scan suspend passed\n"); +} + static void main_gateway_scan_wdog(void) { const struct device *epacket_bt_adv = DEVICE_DT_GET(DT_NODELABEL(epacket_bt_adv)); @@ -2326,6 +2392,13 @@ static const struct bst_test_instance epacket_gateway[] = { .test_tick_f = test_tick, .test_main_f = main_gateway_scan, }, + { + .test_id = "epacket_bt_gateway_scan_suspend", + .test_descr = "Test suspending scanning", + .test_pre_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = main_gateway_scan_suspend, + }, { .test_id = "epacket_bt_gateway_scan_wdog", .test_descr = "Check Bluetooth scan watchdog", diff --git a/tests/bsim/bluetooth/epacket/tests_scripts/scan_suspend.sh b/tests/bsim/bluetooth/epacket/tests_scripts/scan_suspend.sh new file mode 100755 index 000000000..67d7316b8 --- /dev/null +++ b/tests/bsim/bluetooth/epacket/tests_scripts/scan_suspend.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Copyright (c) 2024 Embeint Holdings Pty Ltd +# SPDX-License-Identifier: Apache-2.0 + +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +simulation_id="epacket_scan_suspend" +verbosity_level=2 + +cd ${BSIM_OUT_PATH}/bin + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_epacket_device_prj_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -RealEncryption=0 \ + -testid=epacket_bt_device -rs=23 + +Execute ./bs_${BOARD_TS}_tests_bsim_bluetooth_epacket_gateway_prj_conf \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -RealEncryption=0 \ + -testid=epacket_bt_gateway_scan_suspend -rs=6 + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=10e6 $@ + +wait_for_background_jobs diff --git a/tests/subsys/rpc/commands/coap_download/src/main.c b/tests/subsys/rpc/commands/coap_download/src/main.c index 16741661e..7bde32d1d 100644 --- a/tests/subsys/rpc/commands/coap_download/src/main.c +++ b/tests/subsys/rpc/commands/coap_download/src/main.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -232,6 +233,9 @@ ZTEST(rpc_command_coap_download, test_download) send_download_command(20, "coap.dev.infuse-iot.com", 5684, 0, RPC_ENUM_FILE_ACTION_APP_IMG, "file/small_file", UINT32_MAX / 2, UINT32_MAX); expect_coap_download_response(20, -EINVAL, 0, 0); + + /* Balanced call count */ + zassert_equal(0, infuse_dfu_write_erase_call_count()); } ZTEST(rpc_command_coap_download, test_download_bt_ctlr) @@ -315,6 +319,9 @@ ZTEST(rpc_command_coap_download, test_download_cpatch) RPC_ENUM_FILE_ACTION_APP_CPATCH, "file/hello_world-validate", 333, UINT32_MAX); expect_coap_download_response(22, 0, 333, 0x8451810D); + + /* Balanced call count */ + zassert_equal(0, infuse_dfu_write_erase_call_count()); } ZTEST_SUITE(rpc_command_coap_download, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/subsys/rpc/commands/file_write_basic/src/main.c b/tests/subsys/rpc/commands/file_write_basic/src/main.c index dbcd50e4c..66e464be8 100644 --- a/tests/subsys/rpc/commands/file_write_basic/src/main.c +++ b/tests/subsys/rpc/commands/file_write_basic/src/main.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -346,6 +347,9 @@ ZTEST(rpc_command_file_write_basic, test_file_write_dfu) zassert_equal(fixed_payload_crc, ret.cmd_crc); ret.cmd_len = sizeof(fixed_payload); validate_flash_area(&ret, FIXED_PARTITION_ID(slot1_partition)); + + /* Balanced call count */ + zassert_equal(0, infuse_dfu_write_erase_call_count()); } #else ZTEST(rpc_command_file_write_basic, test_file_write_dfu) @@ -440,6 +444,9 @@ ZTEST(rpc_command_file_write_basic, test_file_write_dfu_cpatch) 0, 0, false, false, (void *)&hardcoded_patch); zassert_equal(-EINVAL, ret.cmd_rc); zassert_equal(sizeof(hardcoded_patch), ret.cmd_len); + + /* Balanced call count */ + zassert_equal(0, infuse_dfu_write_erase_call_count()); } #else