From ed807e48ab53f70f444d5389d9c101f41461d45e Mon Sep 17 00:00:00 2001 From: Li Junru Date: Tue, 24 Dec 2024 19:06:07 +0800 Subject: [PATCH] feat(esp_msc_ota): add skip_msc_connect_wait config Closes https://github.com/espressif/esp-iot-solution/issues/429 --- components/.build-rules.yml | 2 +- components/usb/esp_msc_ota/CHANGELOG.md | 4 + components/usb/esp_msc_ota/esp_msc_host.c | 56 +++++++++----- components/usb/esp_msc_ota/esp_msc_ota.c | 74 +++++++++++++++---- components/usb/esp_msc_ota/idf_component.yml | 2 +- .../usb/esp_msc_ota/include/esp_msc_ota.h | 1 + .../test_apps/main/idf_component.yml | 3 +- .../esp_msc_ota/test_apps/main/test_msc_ota.c | 19 +++++ 8 files changed, 122 insertions(+), 39 deletions(-) diff --git a/components/.build-rules.yml b/components/.build-rules.yml index 676ae1ff3..cac1a3a4a 100644 --- a/components/.build-rules.yml +++ b/components/.build-rules.yml @@ -12,7 +12,7 @@ components/touch/touch_proximity_sensor/test_apps: components/usb/esp_msc_ota/test_apps: enable: - - if: SOC_USB_OTG_SUPPORTED == 1 + - if: IDF_TARGET in ["esp32s3"] components/usb/usb_stream/test_apps: enable: diff --git a/components/usb/esp_msc_ota/CHANGELOG.md b/components/usb/esp_msc_ota/CHANGELOG.md index 6010a28fc..dde87f336 100644 --- a/components/usb/esp_msc_ota/CHANGELOG.md +++ b/components/usb/esp_msc_ota/CHANGELOG.md @@ -1,5 +1,9 @@ # changelog +## v1.1.0 - 2024-12-24 + +* Add the `skip_msc_connect_wait` configuration to allow users to mount the file system manually. + ## v1.0.0 - 2024-8-15 * Publish the official version diff --git a/components/usb/esp_msc_ota/esp_msc_host.c b/components/usb/esp_msc_ota/esp_msc_host.c index b2e5f71b9..d5604b6e6 100644 --- a/components/usb/esp_msc_ota/esp_msc_host.c +++ b/components/usb/esp_msc_ota/esp_msc_host.c @@ -17,8 +17,9 @@ static const char *TAG = "esp_msc_host"; ESP_EVENT_DEFINE_BASE(ESP_MSC_HOST_EVENT); SemaphoreHandle_t msc_read_semaphore; - typedef struct { + usb_host_config_t host_config; + TaskHandle_t task_handle; EventGroupHandle_t usb_flags; const char *base_path; esp_vfs_fat_mount_config_t mount_config; @@ -150,24 +151,41 @@ static void msc_host_task(void *args) static void usb_event_task(void *args) { esp_msc_host_t *msc_host = (esp_msc_host_t *)args; + + // Install USB Host driver. Should only be called once in entire application + esp_err_t err = usb_host_install(&msc_host->host_config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to install USB Host library"); + vTaskDelete(NULL); + } + xTaskNotifyGive(msc_host->task_handle); + bool has_clients = true; - while (1) { + bool has_devices = false; + while (has_clients) { uint32_t event_flags; - usb_host_lib_handle_events(portMAX_DELAY, &event_flags); - - // Release devices once all clients has deregistered + ESP_ERROR_CHECK(usb_host_lib_handle_events(portMAX_DELAY, &event_flags)); if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { - has_clients = false; - if (usb_host_device_free_all() == ESP_OK) { - break; + ESP_LOGI(TAG, "Get FLAGS_NO_CLIENTS"); + if (ESP_OK == usb_host_device_free_all()) { + ESP_LOGI(TAG, "All devices marked as free, no need to wait FLAGS_ALL_FREE event"); + has_clients = false; + } else { + ESP_LOGI(TAG, "Wait for the FLAGS_ALL_FREE"); + has_devices = true; } } - // can be deinitialized, and terminate this task. - if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE && !has_clients) { - break; + if (has_devices && event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "Get FLAGS_ALL_FREE"); + has_clients = false; } } + ESP_LOGI(TAG, "No more clients and devices, uninstall USB Host library"); + // Clean up USB Host + vTaskDelay(100); // Short delay to allow clients clean-up + usb_host_uninstall(); + ESP_LOGD(TAG, "USB Host library is uninstalled"); xEventGroupSetBits(msc_host->usb_flags, HOST_UNINSTALL); vTaskDelete(NULL); } @@ -192,8 +210,8 @@ esp_err_t esp_msc_host_install(esp_msc_host_config_t *config, esp_msc_host_handl msc_host->usb_flags = xEventGroupCreate(); MSC_OTA_CHECK_GOTO(msc_host->usb_flags != NULL, "Failed to create event group", install_fail); - err = usb_host_install(&config->host_config); - MSC_OTA_CHECK_GOTO(err == ESP_OK, "Failed to install USB host", install_fail); + msc_host->host_config = config->host_config; + msc_host->task_handle = xTaskGetCurrentTaskHandle(); MSC_OTA_CHECK_CONTINUE(config->host_driver_config.callback == NULL, "The specified callback will be overridden by the internal callback function"); msc_host_driver_config_t msc_config = {0}; @@ -202,10 +220,12 @@ esp_err_t esp_msc_host_install(esp_msc_host_config_t *config, esp_msc_host_handl msc_config.callback_arg = msc_host; BaseType_t task_created = xTaskCreate(usb_event_task, "usb_event", 4096, msc_host, 2, NULL); - MSC_OTA_CHECK_GOTO(task_created == pdPASS, "Failed to create USB events task", usb_install_fail); + MSC_OTA_CHECK_GOTO(task_created == pdPASS, "Failed to create USB events task", install_fail); + uint32_t notify_value = ulTaskNotifyTake(false, pdMS_TO_TICKS(1000)); + MSC_OTA_CHECK_GOTO(notify_value != 0, "USB host library not installed", install_fail); err = msc_host_install(&msc_config); - MSC_OTA_CHECK_GOTO(err == ESP_OK, "Failed to install MSC host", usb_install_fail); + MSC_OTA_CHECK_GOTO(err == ESP_OK, "Failed to install MSC host", install_fail); task_created = xTaskCreate(msc_host_task, "msc_host", 4096, msc_host, 5, NULL); MSC_OTA_CHECK_GOTO(task_created == pdPASS, "Failed to create MSC host task", msc_install_fail); @@ -213,16 +233,13 @@ esp_err_t esp_msc_host_install(esp_msc_host_config_t *config, esp_msc_host_handl *handle = (esp_msc_host_handle_t)msc_host; q_msc_host = msc_host; - ESP_LOGI(TAG, "MSC Host Install Done\n"); + ESP_LOGI(TAG, "MSC Host Install Done"); return ESP_OK; msc_install_fail: msc_host_uninstall(); wait_for_event(msc_host->usb_flags, HOST_UNINSTALL, portMAX_DELAY); -usb_install_fail: - usb_host_uninstall(); - install_fail: if (msc_host->usb_flags != NULL) { vEventGroupDelete(msc_host->usb_flags); @@ -243,7 +260,6 @@ esp_err_t esp_msc_host_uninstall(esp_msc_host_handle_t handle) ESP_LOGI(TAG, "Please remove the USB flash drive."); xEventGroupSetBits(msc_host->usb_flags, HOST_TOBE_UNINSTALL); wait_for_event(msc_host->usb_flags, HOST_UNINSTALL, portMAX_DELAY); - usb_host_uninstall(); if (msc_host->usb_flags != NULL) { vEventGroupDelete(msc_host->usb_flags); } diff --git a/components/usb/esp_msc_ota/esp_msc_ota.c b/components/usb/esp_msc_ota/esp_msc_ota.c index 72784b409..6c50f1746 100644 --- a/components/usb/esp_msc_ota/esp_msc_ota.c +++ b/components/usb/esp_msc_ota/esp_msc_ota.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -29,6 +29,29 @@ _Static_assert(DEFAULT_OTA_BUF_SIZE > (sizeof(esp_image_header_t) + sizeof(esp_i /* Setting event group */ #define MSC_CONNECT (1 << 0) // MSC device connect +/** + * @brief To prevent the VFS from being unregistered during file read and write operations. + * + * @param semaphore Semaphore to take. + * @return esp_err_t + * - ESP_OK on success or if the semaphore is NULL. + * - ESP_FAIL if taking the semaphore fails. + */ +#define SEMAPHORE_TAKE(semaphore) \ + ((semaphore == NULL) ? ESP_OK : \ + ((xSemaphoreTake(semaphore, pdMS_TO_TICKS(10)) == pdTRUE) ? ESP_OK : ESP_FAIL)) + +/** + * @brief To prevent the VFS from being unregistered during file read and write operations. + * + * @param semaphore Semaphore to give. + * @return esp_err_t + * - ESP_OK on success or if the semaphore is NULL. + */ +#define SEMAPHORE_GIVE(semaphore) \ + ((semaphore == NULL) ? ESP_OK : \ + (xSemaphoreGive(semaphore), ESP_OK)) + typedef struct { EventGroupHandle_t mscEventGroup; bool bulk_flash_erase; @@ -107,6 +130,7 @@ esp_msc_ota_status_t esp_msc_ota_get_status(esp_msc_ota_handle_t handle) static esp_err_t _read_header(esp_msc_ota_t *msc_ota) { + esp_err_t err = ESP_OK; EventBits_t bits = xEventGroupWaitBits(msc_ota->mscEventGroup, MSC_CONNECT, pdFALSE, pdFALSE, 0); MSC_OTA_CHECK(bits & MSC_CONNECT, "msc can't be disconnect", ESP_ERR_INVALID_STATE); /* @@ -115,6 +139,8 @@ static esp_err_t _read_header(esp_msc_ota_t *msc_ota) int data_read_size = IMAGE_HEADER_SIZE; int data_read = 0; + err = SEMAPHORE_TAKE(msc_read_semaphore); + MSC_OTA_CHECK(err == ESP_OK, "take msc_read_semaphore failed", ESP_FAIL); FILE *file = fopen(msc_ota->ota_bin_path, "rb"); MSC_OTA_CHECK(file != NULL, "Failed to open file for reading", ESP_ERR_NOT_FOUND); @@ -125,27 +151,28 @@ static esp_err_t _read_header(esp_msc_ota_t *msc_ota) data_read_size = data_read_size > fileLength ? fileLength : data_read_size; ESP_LOGI(TAG, "Reading file %s, size: %d, total size: %"PRIu32"", msc_ota->ota_bin_path, data_read_size, fileLength); - BaseType_t ret = xSemaphoreTake(msc_read_semaphore, pdMS_TO_TICKS(10)); - MSC_OTA_CHECK(ret == pdTRUE, "take msc_read_semaphore failed", ESP_FAIL); data_read = fread(msc_ota->ota_upgrade_buf, 1, data_read_size, file); - xSemaphoreGive(msc_read_semaphore); if (data_read == data_read_size) { msc_ota->binary_file_read_len = data_read; - fclose(file); - return ESP_OK; + err = ESP_OK; + goto file_close; } else if (data_read > 0 && data_read < data_read_size) { msc_ota->binary_file_read_len = data_read; - fclose(file); - return ESP_OK; + err = ESP_OK; + goto file_close; } else if (ferror(file)) { + err = ESP_FAIL; ESP_LOGI(TAG, "Error reading from file"); } else if (feof(file)) { - ESP_LOGI(TAG, "End of file reached.\n"); + err = ESP_FAIL; + ESP_LOGI(TAG, "End of file reached."); } +file_close: fclose(file); - return ESP_FAIL; + SEMAPHORE_GIVE(msc_read_semaphore); + return err; } static esp_err_t _ota_verify_chip_id(const void *arg) @@ -198,9 +225,14 @@ esp_err_t esp_msc_ota_begin(esp_msc_ota_config_t *config, esp_msc_ota_handle_t * esp_event_handler_register(ESP_MSC_HOST_EVENT, ESP_EVENT_ANY_ID, &_esp_msc_host_handler, msc_ota); - ESP_LOGI(TAG, "Waiting for MSC device to connect..."); - EventBits_t bits = xEventGroupWaitBits(msc_ota->mscEventGroup, MSC_CONNECT, pdFALSE, pdFALSE, config->wait_msc_connect); - MSC_OTA_CHECK_GOTO(bits & MSC_CONNECT, "TIMEOUT: MSC device not connected", msc_cleanup); + if (!config->skip_msc_connect_wait) { + ESP_LOGI(TAG, "Waiting for MSC device to connect..."); + EventBits_t bits = xEventGroupWaitBits(msc_ota->mscEventGroup, MSC_CONNECT, pdFALSE, pdFALSE, config->wait_msc_connect); + MSC_OTA_CHECK_GOTO(bits & MSC_CONNECT, "TIMEOUT: MSC device not connected", msc_cleanup); + } else { + xEventGroupSetBits(msc_ota->mscEventGroup, MSC_CONNECT); + ESP_LOGI(TAG, "skip_msc_connect_wait is true, set MSC_CONNECT event directly"); + } msc_ota->update_partition = NULL; ESP_LOGI(TAG, "Starting OTA..."); @@ -272,9 +304,12 @@ esp_err_t esp_msc_ota_perform(esp_msc_ota_handle_t handle) MSC_OTA_CHECK(bits & MSC_CONNECT, "msc can't be disconnect", ESP_ERR_INVALID_STATE); if (msc_ota->file == NULL) { + err = SEMAPHORE_TAKE(msc_read_semaphore); + MSC_OTA_CHECK(err == ESP_OK, "take msc_read_semaphore failed", ESP_FAIL); FILE *file = fopen(msc_ota->ota_bin_path, "rb"); MSC_OTA_CHECK(file != NULL, "Failed to open file for reading", ESP_ERR_NOT_FOUND); fseek(file, msc_ota->binary_file_read_len, SEEK_CUR); + SEMAPHORE_GIVE(msc_read_semaphore); msc_ota->file = file; } uint32_t *fileLength = &msc_ota->binary_file_read_len; @@ -282,10 +317,10 @@ esp_err_t esp_msc_ota_perform(esp_msc_ota_handle_t handle) if (*fileLength < *totalLength) { size_t readLength = *fileLength > msc_ota->ota_upgrade_buf_size ? msc_ota->ota_upgrade_buf_size : *fileLength; - BaseType_t ret = xSemaphoreTake(msc_read_semaphore, pdMS_TO_TICKS(10)); - MSC_OTA_CHECK(ret == pdTRUE, "take msc_read_semaphore failed", ESP_FAIL); + err = SEMAPHORE_TAKE(msc_read_semaphore); + MSC_OTA_CHECK(err == ESP_OK, "take msc_read_semaphore failed", ESP_FAIL); data_read = fread(msc_ota->ota_upgrade_buf, 1, readLength, msc_ota->file); - xSemaphoreGive(msc_read_semaphore); + SEMAPHORE_GIVE(msc_read_semaphore); MSC_OTA_CHECK(data_read > 0, "Failed to read file", ESP_ERR_INVALID_SIZE); err = esp_ota_write(msc_ota->update_handle, (const void *)msc_ota->ota_upgrade_buf, data_read); @@ -299,7 +334,10 @@ esp_err_t esp_msc_ota_perform(esp_msc_ota_handle_t handle) } if (*fileLength >= *totalLength) { msc_ota->status = ESP_MSC_OTA_SUCCESS; + err = SEMAPHORE_TAKE(msc_read_semaphore); + MSC_OTA_CHECK(err == ESP_OK, "take msc_read_semaphore failed", ESP_FAIL); fclose(msc_ota->file); + SEMAPHORE_GIVE(msc_read_semaphore); return ESP_OK; } return ESP_OK; @@ -338,6 +376,7 @@ esp_err_t esp_msc_ota_end(esp_msc_ota_handle_t handle) } } esp_event_handler_unregister(ESP_MSC_HOST_EVENT, ESP_EVENT_ANY_ID, &_esp_msc_host_handler); + vEventGroupDelete(msc_ota->mscEventGroup); free(msc_ota); esp_msc_ota_dispatch_event(ESP_MSC_OTA_FINISH, NULL, 0); return err; @@ -353,7 +392,10 @@ esp_err_t esp_msc_ota_abort(esp_msc_ota_handle_t handle) case ESP_MSC_OTA_SUCCESS: case ESP_MSC_OTA_IN_PROGRESS: if (msc_ota->file) { + err = SEMAPHORE_TAKE(msc_read_semaphore); + MSC_OTA_CHECK(err == ESP_OK, "take msc_read_semaphore failed", ESP_FAIL); fclose(msc_ota->file); + SEMAPHORE_GIVE(msc_read_semaphore); } err = esp_ota_abort(msc_ota->update_handle); [[fallthrough]]; diff --git a/components/usb/esp_msc_ota/idf_component.yml b/components/usb/esp_msc_ota/idf_component.yml index 81fbb71aa..2accd4a3f 100644 --- a/components/usb/esp_msc_ota/idf_component.yml +++ b/components/usb/esp_msc_ota/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.0.0" +version: "1.1.0" targets: - esp32s2 - esp32s3 diff --git a/components/usb/esp_msc_ota/include/esp_msc_ota.h b/components/usb/esp_msc_ota/include/esp_msc_ota.h index 8dccd0072..8f8cc53b3 100644 --- a/components/usb/esp_msc_ota/include/esp_msc_ota.h +++ b/components/usb/esp_msc_ota/include/esp_msc_ota.h @@ -52,6 +52,7 @@ typedef enum { typedef struct { const char *ota_bin_path; /*!< OTA binary name, must be an exact match. Note: By default file names cannot exceed 11 bytes e.g. "/usb/ota.bin" */ bool bulk_flash_erase; /*!< Erase entire flash partition during initialization. By default flash partition is erased during write operation and in chunk of 4K sector size */ + bool skip_msc_connect_wait; /*!< Skip waiting for MSC device to connect, if true means MSC device is already connected */ TickType_t wait_msc_connect; /*!< Wait time for MSC device to connect */ size_t buffer_size; /*!< Buffer size for OTA write operation, must larger than 1024 */ } esp_msc_ota_config_t; diff --git a/components/usb/esp_msc_ota/test_apps/main/idf_component.yml b/components/usb/esp_msc_ota/test_apps/main/idf_component.yml index e6a86d849..126b4192f 100644 --- a/components/usb/esp_msc_ota/test_apps/main/idf_component.yml +++ b/components/usb/esp_msc_ota/test_apps/main/idf_component.yml @@ -1,2 +1,3 @@ dependencies: - idf: ">=5.0" + idf: '>=5.0' + espressif/esp32_s3_usb_otg: ^1.6.1 diff --git a/components/usb/esp_msc_ota/test_apps/main/test_msc_ota.c b/components/usb/esp_msc_ota/test_apps/main/test_msc_ota.c index 315627c45..d092e962b 100644 --- a/components/usb/esp_msc_ota/test_apps/main/test_msc_ota.c +++ b/components/usb/esp_msc_ota/test_apps/main/test_msc_ota.c @@ -18,6 +18,7 @@ #include "esp_msc_host.h" #include "esp_msc_ota.h" #include "usb/usb_host.h" +#include "bsp/esp-bsp.h" static const char *TAG = "esp_msc_ota_test"; @@ -207,3 +208,21 @@ TEST_CASE("Auto MSC OTA", "[MSC OTA][Auto]") TEST_ESP_OK(esp_msc_host_uninstall(host_handle)); vTaskDelay(1000 / portTICK_PERIOD_MS); } + +TEST_CASE("Test msc ota without msc host", "[MSC OTA][WITHOUT MSC HOST]") +{ + esp_event_loop_create_default(); + bsp_sdcard_mount(); + + char path[256]; + sprintf(path, "%s/ota_test.bin", CONFIG_BSP_uSD_MOUNT_POINT); + + esp_msc_ota_handle_t msc_ota_handle = NULL; + esp_msc_ota_config_t config = { + .ota_bin_path = path, + .skip_msc_connect_wait = true, + }; + TEST_ESP_OK(esp_msc_ota_begin(&config, &msc_ota_handle)); + TEST_ESP_OK(esp_msc_ota_abort(msc_ota_handle)); + bsp_sdcard_unmount(); +}