#include #include #include #include #include "ql_fota.h" #include "ql_fs.h" #include "ql_power.h" #include "ql_rtos.h" #include "webclient.h" #include "ota_baseline.h" #include "ota_fullfota2.h" #define BANBAN_FULLOTA2_PACKAGE_PATH "U:/FotaFile.bin" #define BANBAN_FULLOTA2_URL_MAX 256U #define BANBAN_FULLOTA2_READ_BUFFER_SIZE 4096U #define BANBAN_FULLOTA2_TASK_STACK_SIZE (12U * 1024U) #define BANBAN_FULLOTA2_TASK_PRIORITY 100U #define BANBAN_FULLOTA2_HTTP_TIMEOUT_MS 30000 #define BANBAN_FULLOTA2_FBF_OVERHEAD 8192U typedef struct { char url[BANBAN_FULLOTA2_URL_MAX]; banban_fullfota2_finished_cb_t finished_cb; } banban_fullfota2_request_t; static ql_task_t g_fullfota2_task = NULL; static volatile int g_fullfota2_running = 0; static banban_fullfota2_request_t g_fullfota2_request; static void banban_fullfota2_finish(int result, struct webclient_session *session, uint8_t *buffer, qlFotaImgProcCtxPtr fota) { banban_fullfota2_finished_cb_t finished_cb = g_fullfota2_request.finished_cb; if (session != NULL) { webclient_close(session); } if (buffer != NULL) { web_free(buffer); } if (fota != NULL) { ql_fota_deinit(fota); } if (result != BANBAN_FULLOTA2_OK) { ql_fota_fixed_internal_flash_upgrade(0); if (ql_access(BANBAN_FULLOTA2_PACKAGE_PATH, 0) == 0) { ql_remove(BANBAN_FULLOTA2_PACKAGE_PATH); } } memset(&g_fullfota2_request, 0, sizeof(g_fullfota2_request)); g_fullfota2_running = 0; g_fullfota2_task = NULL; if (finished_cb != NULL) { finished_cb(result); } } static void banban_fullfota2_task(void *argument) { struct webclient_session *session = NULL; qlFotaImgProcCtxPtr fota = NULL; uint8_t *buffer = NULL; int status; int content_length; int free_size; int read_length; int written = 0; int next_progress = 10; int result = BANBAN_FULLOTA2_ERR_HTTP; struct lfs_info staged_info; (void)argument; printf("[FULLOTA2] start url=%s path=%s\r\n", g_fullfota2_request.url, BANBAN_FULLOTA2_PACKAGE_PATH); if (ql_access(BANBAN_FULLOTA2_PACKAGE_PATH, 0) == 0) { ql_remove(BANBAN_FULLOTA2_PACKAGE_PATH); } session = webclient_session_create(2048); if (session == NULL) { result = BANBAN_FULLOTA2_ERR_HTTP; goto failed; } webclient_set_timeout(session, BANBAN_FULLOTA2_HTTP_TIMEOUT_MS); status = webclient_get(session, g_fullfota2_request.url); if (status != 200) { printf("[FULLOTA2] HTTP GET failed status=%d\r\n", status); result = BANBAN_FULLOTA2_ERR_HTTP; goto failed; } content_length = session->content_length; if (content_length <= (int)BANBAN_FULLOTA2_FBF_OVERHEAD || content_length > (int)BANBAN_OTA_APP_PARTITION_SIZE) { printf("[FULLOTA2] invalid Content-Length=%d\r\n", content_length); result = BANBAN_FULLOTA2_ERR_LENGTH; goto failed; } free_size = ql_fs_free_size('U'); if (free_size < 0 || (uint32_t)free_size < (uint32_t)content_length + BANBAN_OTA_STAGING_RESERVE_BYTES) { printf("[FULLOTA2] insufficient space free=%d required=%lu\r\n", free_size, (unsigned long)((uint32_t)content_length + BANBAN_OTA_STAGING_RESERVE_BYTES)); result = BANBAN_FULLOTA2_ERR_SPACE; goto failed; } ql_fota_fixed_internal_flash_upgrade(1); if (ql_fota_set_package_path(BANBAN_FULLOTA2_PACKAGE_PATH) != 0) { printf("[FULLOTA2] set package path failed\r\n"); result = BANBAN_FULLOTA2_ERR_INIT; goto failed; } fota = ql_fota_init(); if (fota == NULL) { printf("[FULLOTA2] ql_fota_init failed\r\n"); result = BANBAN_FULLOTA2_ERR_INIT; goto failed; } buffer = web_malloc(BANBAN_FULLOTA2_READ_BUFFER_SIZE); if (buffer == NULL) { printf("[FULLOTA2] allocate read buffer failed\r\n"); result = BANBAN_FULLOTA2_ERR_INIT; goto failed; } while (written < content_length) { int request_length = content_length - written; int progress; if (request_length > (int)BANBAN_FULLOTA2_READ_BUFFER_SIZE) { request_length = BANBAN_FULLOTA2_READ_BUFFER_SIZE; } read_length = webclient_read(session, buffer, request_length); if (read_length <= 0) { printf("[FULLOTA2] body read failed ret=%d received=%d expected=%d\r\n", read_length, written, content_length); result = BANBAN_FULLOTA2_ERR_HTTP; goto failed; } if (ql_fota_image_write(fota, buffer, read_length) != 0) { printf("[FULLOTA2] image write failed received=%d chunk=%d\r\n", written, read_length); result = BANBAN_FULLOTA2_ERR_WRITE; goto failed; } written += read_length; progress = (written * 100) / content_length; if (progress >= next_progress || written == content_length) { printf("[FULLOTA2] download progress=%d%% bytes=%d/%d\r\n", progress, written, content_length); while (next_progress <= progress) { next_progress += 10; } } } if (written != content_length) { result = BANBAN_FULLOTA2_ERR_LENGTH; goto failed; } if (ql_fota_image_flush(fota) != 0) { printf("[FULLOTA2] image flush failed\r\n"); result = BANBAN_FULLOTA2_ERR_FLUSH; goto failed; } memset(&staged_info, 0, sizeof(staged_info)); if (ql_stat(BANBAN_FULLOTA2_PACKAGE_PATH, &staged_info) != 0 || staged_info.size != (uint32_t)content_length) { printf("[FULLOTA2] staged length mismatch file=%lu expected=%d\r\n", (unsigned long)staged_info.size, content_length); result = BANBAN_FULLOTA2_ERR_LENGTH; goto failed; } if (ql_fota_image_verify(fota) != 0) { printf("[FULLOTA2] FBF verify/setflag failed\r\n"); result = BANBAN_FULLOTA2_ERR_VERIFY; goto failed; } printf("[FULLOTA2] package verified; rebooting into updater\r\n"); banban_fullfota2_finish(BANBAN_FULLOTA2_OK, session, buffer, fota); ql_rtos_task_sleep_ms(200); ql_power_reset(); /* ql_power_reset() is not expected to return. Keep the task quiesced if it does. */ while (1) { ql_rtos_task_sleep_s(60); } failed: printf("[FULLOTA2] failed result=%d\r\n", result); banban_fullfota2_finish(result, session, buffer, fota); ql_rtos_task_delete(NULL); } int banban_fullfota2_start(const char *url, banban_fullfota2_finished_cb_t finished_cb) { int ret; size_t url_length; if (url == NULL || url[0] == '\0') { return BANBAN_FULLOTA2_ERR_ARGUMENT; } url_length = strlen(url); if (url_length >= sizeof(g_fullfota2_request.url)) { return BANBAN_FULLOTA2_ERR_ARGUMENT; } if (g_fullfota2_running) { return BANBAN_FULLOTA2_ERR_BUSY; } memset(&g_fullfota2_request, 0, sizeof(g_fullfota2_request)); memcpy(g_fullfota2_request.url, url, url_length + 1); g_fullfota2_request.finished_cb = finished_cb; g_fullfota2_running = 1; ret = ql_rtos_task_create(&g_fullfota2_task, BANBAN_FULLOTA2_TASK_STACK_SIZE, BANBAN_FULLOTA2_TASK_PRIORITY, "ota_fullfota2", banban_fullfota2_task, NULL); if (ret != 0) { memset(&g_fullfota2_request, 0, sizeof(g_fullfota2_request)); g_fullfota2_running = 0; g_fullfota2_task = NULL; return BANBAN_FULLOTA2_ERR_TASK; } return BANBAN_FULLOTA2_OK; } int banban_fullfota2_is_running(void) { return g_fullfota2_running ? 1 : 0; } void banban_fullfota2_discard_staged_package(void) { if (!g_fullfota2_running && ql_access(BANBAN_FULLOTA2_PACKAGE_PATH, 0) == 0) { ql_remove(BANBAN_FULLOTA2_PACKAGE_PATH); } }