Browse Source

mgmt: hawkbit: resume firmware downloads

save download progress, to resume failed
firmware downloads.

Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
pull/83756/head
Fin Maaß 1 year ago committed by Benjamin Cabé
parent
commit
4cd44e7da2
  1. 4
      samples/subsys/mgmt/hawkbit/sample.yaml
  2. 11
      subsys/mgmt/hawkbit/Kconfig
  3. 118
      subsys/mgmt/hawkbit/hawkbit.c

4
samples/subsys/mgmt/hawkbit/sample.yaml

@ -34,3 +34,7 @@ tests: @@ -34,3 +34,7 @@ tests:
sample.net.hawkbit.set_settings_runtime:
extra_configs:
- CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME=y
sample.net.hawkbit.save_progress:
extra_configs:
- CONFIG_HAWKBIT_SAVE_PROGRESS=y
- CONFIG_STREAM_FLASH_PROGRESS=y

11
subsys/mgmt/hawkbit/Kconfig

@ -188,6 +188,17 @@ config HAWKBIT_EVENT_CALLBACKS @@ -188,6 +188,17 @@ config HAWKBIT_EVENT_CALLBACKS
help
Be able to set event callbacks for hawkBit.
config HAWKBIT_SAVE_PROGRESS
bool "Save the hawkBit update download progress"
depends on STREAM_FLASH_PROGRESS
help
Regularly save progress of hawkBit update download operation.
When enabled, the download progress is periodically saved to
non-volatile storage. If a download is interrupted, it can be resumed from
the last saved point rather than starting over, saving bandwidth and time.
This is especially useful for large updates over unreliable networks or in
resource-constrained environments.
module = HAWKBIT
module-str = Log Level for hawkbit
module-help = Enables logging for hawkBit code.

118
subsys/mgmt/hawkbit/hawkbit.c

@ -44,6 +44,7 @@ LOG_MODULE_REGISTER(hawkbit, CONFIG_HAWKBIT_LOG_LEVEL); @@ -44,6 +44,7 @@ LOG_MODULE_REGISTER(hawkbit, CONFIG_HAWKBIT_LOG_LEVEL);
#define SHA256_HASH_SIZE 32
#define RESPONSE_BUFFER_SIZE 1100
#define DDI_SECURITY_TOKEN_SIZE 32
#define RANGE_HEADER_SIZE 50
#define HAWKBIT_RECV_TIMEOUT (300 * MSEC_PER_SEC)
#define HAWKBIT_SET_SERVER_TIMEOUT K_MSEC(300)
@ -115,8 +116,6 @@ static struct hawkbit_config { @@ -115,8 +116,6 @@ static struct hawkbit_config {
#endif /* CONFIG_HAWKBIT_USE_DYNAMIC_CERT_TAG */
struct hawkbit_download {
int download_status;
int download_progress;
size_t downloaded_size;
size_t http_content_size;
uint8_t file_hash[SHA256_HASH_SIZE];
@ -343,6 +342,13 @@ static int hawkbit_settings_set(const char *name, size_t len, settings_read_cb r @@ -343,6 +342,13 @@ static int hawkbit_settings_set(const char *name, size_t len, settings_read_cb r
return 0;
}
#endif /* CONFIG_HAWKBIT_SET_SETTINGS_RUNTIME */
/* This is to omit the error message, as that is fetched in stream_flash_progress_load()
* and we don't need to get it here.
*/
if (IS_ENABLED(CONFIG_HAWKBIT_SAVE_PROGRESS) &&
settings_name_steq(name, "flash_progress", NULL)) {
return 0;
}
return -ENOENT;
}
@ -864,26 +870,13 @@ int hawkbit_init(void) @@ -864,26 +870,13 @@ int hawkbit_init(void)
return ret;
}
static void response_cb(struct http_response *rsp, enum http_final_call final_data, void *userdata)
static void response_json_cb(struct http_response *rsp, enum http_final_call final_data,
struct hawkbit_context *hb_context)
{
struct hawkbit_context *hb_context = userdata;
size_t body_len;
int ret, downloaded;
int ret;
uint8_t *body_data = NULL, *rsp_tmp = NULL;
if (rsp->http_status_code != 200) {
LOG_ERR("HTTP request denied: %d", rsp->http_status_code);
if (rsp->http_status_code == 401 || rsp->http_status_code == 403) {
hb_context->code_status = HAWKBIT_PERMISSION_ERROR;
} else {
hb_context->code_status = HAWKBIT_METADATA_ERROR;
}
return;
}
switch (hb_context->type) {
case HAWKBIT_PROBE:
case HAWKBIT_PROBE_DEPLOYMENT_BASE:
if (hb_context->dl.http_content_size == 0) {
hb_context->dl.http_content_size = rsp->content_length;
}
@ -901,7 +894,7 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da @@ -901,7 +894,7 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da
if (rsp_tmp == NULL) {
LOG_ERR("Failed to realloc memory");
hb_context->code_status = HAWKBIT_ALLOC_ERROR;
break;
return;
}
hb_context->response_data = rsp_tmp;
@ -917,7 +910,7 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da @@ -917,7 +910,7 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da
hb_context->dl.http_content_size,
hb_context->dl.downloaded_size);
hb_context->code_status = HAWKBIT_METADATA_ERROR;
break;
return;
}
hb_context->response_data[hb_context->dl.downloaded_size] = '\0';
@ -942,12 +935,22 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da @@ -942,12 +935,22 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da
}
}
}
}
break;
static void response_download_cb(struct http_response *rsp, enum http_final_call final_data,
struct hawkbit_context *hb_context)
{
size_t body_len;
int ret, downloaded;
uint8_t *body_data = NULL;
static uint8_t download_progress;
case HAWKBIT_DOWNLOAD:
if (hb_context->dl.http_content_size == 0) {
hb_context->dl.http_content_size = rsp->content_length;
download_progress = 0;
if (IS_ENABLED(CONFIG_HAWKBIT_SAVE_PROGRESS) && rsp->http_status_code != 206) {
hb_context->flash_ctx.stream.bytes_written = 0;
}
}
if (rsp->body_found) {
@ -959,28 +962,54 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da @@ -959,28 +962,54 @@ static void response_cb(struct http_response *rsp, enum http_final_call final_da
if (ret < 0) {
LOG_ERR("Failed to write flash: %d", ret);
hb_context->code_status = HAWKBIT_DOWNLOAD_ERROR;
break;
return;
}
#ifdef CONFIG_HAWKBIT_SAVE_PROGRESS
stream_flash_progress_save(&hb_context->flash_ctx.stream, "hawkbit/flash_progress");
#endif
}
hb_context->dl.downloaded_size = flash_img_bytes_written(&hb_context->flash_ctx);
downloaded =
hb_context->dl.downloaded_size * 100 / hb_context->dl.http_content_size;
downloaded = hb_context->dl.downloaded_size * 100 / hb_context->dl.file_size;
if (downloaded > hb_context->dl.download_progress) {
hb_context->dl.download_progress = downloaded;
LOG_DBG("Downloaded: %d%% ", hb_context->dl.download_progress);
if (downloaded != download_progress) {
download_progress = downloaded;
LOG_DBG("Downloaded: %u%% (%u / %u)", download_progress,
hb_context->dl.downloaded_size, hb_context->dl.file_size);
}
if (final_data == HTTP_DATA_FINAL) {
hb_context->final_data_received = true;
}
}
static void response_cb(struct http_response *rsp, enum http_final_call final_data, void *userdata)
{
struct hawkbit_context *hb_context = userdata;
if (!IN_RANGE(rsp->http_status_code, 200, 299)) {
LOG_ERR("HTTP request denied: %d", rsp->http_status_code);
if (rsp->http_status_code == 401 || rsp->http_status_code == 403) {
hb_context->code_status = HAWKBIT_PERMISSION_ERROR;
} else {
hb_context->code_status = HAWKBIT_METADATA_ERROR;
}
return;
}
switch (hb_context->type) {
case HAWKBIT_PROBE:
case HAWKBIT_PROBE_DEPLOYMENT_BASE:
response_json_cb(rsp, final_data, hb_context);
break;
default:
case HAWKBIT_DOWNLOAD:
response_download_cb(rsp, final_data, hb_context);
break;
default:
break;
}
}
@ -1055,6 +1084,13 @@ static bool send_request(struct hawkbit_context *hb_context, enum hawkbit_http_r @@ -1055,6 +1084,13 @@ static bool send_request(struct hawkbit_context *hb_context, enum hawkbit_http_r
* Resource for software module (Deployment Base)
* GET: /{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}
*/
http_req.method = HTTP_GET;
hb_context->dl.http_content_size = 0;
hb_context->dl.downloaded_size = 0;
break;
case HAWKBIT_DOWNLOAD:
/*
* Resource for software module (Deployment Base)
@ -1063,7 +1099,23 @@ static bool send_request(struct hawkbit_context *hb_context, enum hawkbit_http_r @@ -1063,7 +1099,23 @@ static bool send_request(struct hawkbit_context *hb_context, enum hawkbit_http_r
*/
http_req.method = HTTP_GET;
hb_context->dl.http_content_size = 0;
#ifdef CONFIG_HAWKBIT_SAVE_PROGRESS
hb_context->dl.downloaded_size = flash_img_bytes_written(&hb_context->flash_ctx);
if (IN_RANGE(hb_context->dl.downloaded_size, 1, hb_context->dl.file_size)) {
char header_range[RANGE_HEADER_SIZE] = {0};
snprintf(header_range, sizeof(header_range), "Range: bytes=%u-" HTTP_CRLF,
hb_context->dl.downloaded_size);
const char *const headers_range[] = {header_range, NULL};
http_req.optional_headers = (const char **)headers_range;
LOG_DBG("optional header: %s", header_range);
LOG_INF("Resuming download from %d bytes", hb_context->dl.downloaded_size);
}
#else
hb_context->dl.downloaded_size = 0;
#endif
break;
@ -1459,6 +1511,10 @@ static void s_download(void *o) @@ -1459,6 +1511,10 @@ static void s_download(void *o)
*/
flash_area_ptr = s->hb_context.flash_ctx.flash_area;
#ifdef CONFIG_HAWKBIT_SAVE_PROGRESS
stream_flash_progress_load(&s->hb_context.flash_ctx.stream, "hawkbit/flash_progress");
#endif
if (!send_request(&s->hb_context, HAWKBIT_DOWNLOAD, url_buffer, NULL)) {
LOG_ERR("Send request failed (%s)", "HAWKBIT_DOWNLOAD");
smf_set_state(SMF_CTX(s), &hawkbit_states[S_HAWKBIT_TERMINATE]);
@ -1473,6 +1529,10 @@ static void s_download(void *o) @@ -1473,6 +1529,10 @@ static void s_download(void *o)
return;
}
#ifdef CONFIG_HAWKBIT_SAVE_PROGRESS
stream_flash_progress_clear(&s->hb_context.flash_ctx.stream, "hawkbit/flash_progress");
#endif
/* Verify the hash of the stored firmware */
fic.match = s->hb_context.dl.file_hash;
fic.clen = s->hb_context.dl.downloaded_size;

Loading…
Cancel
Save