Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: nrf_cloud_fota_poll: Add API to apply FOTA images #20545

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/net/nrf_cloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ enum nrf_cloud_fota_status {
NRF_CLOUD_FOTA_CANCELED = 5,
NRF_CLOUD_FOTA_REJECTED = 6,
NRF_CLOUD_FOTA_DOWNLOADING = 7,

/* Validation for Full modem FOTA needed, disconnect from LTE network and call
* nrf_cloud_fota_poll_update_apply(). This event is not reported to nRF Cloud and is only
* used internally to signal the application to apply the full modem FOTA update.
*/
NRF_CLOUD_FOTA_FMFU_VALIDATION_NEEDED = 8,
};

/** @brief FOTA update type. */
Expand Down
10 changes: 10 additions & 0 deletions include/net/nrf_cloud_fota_poll.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ int nrf_cloud_fota_poll_process_pending(struct nrf_cloud_fota_poll_ctx *ctx);
*/
int nrf_cloud_fota_poll_process(struct nrf_cloud_fota_poll_ctx *ctx);

/**
* @brief Apply downloaded image. For full modem FOTA this must be called after the network has
* been disconnected. Only applicable in non-blocking mode.
*
* @param[in] ctx Pointer to context used for FOTA polling operations.
*
* @return 0 on success, negative value on failure.
*/
int nrf_cloud_fota_poll_update_apply(struct nrf_cloud_fota_poll_ctx *ctx);

/** @} */

#ifdef __cplusplus
Expand Down
94 changes: 72 additions & 22 deletions subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ static enum nrf_cloud_fota_status fota_status = NRF_CLOUD_FOTA_QUEUED;
static char const *fota_status_details = FOTA_STATUS_DETAILS_SUCCESS;

/* Forward-declarations */
static void handle_download_succeeded_and_reboot(struct nrf_cloud_fota_poll_ctx *ctx);
static int handle_downloaded_image(struct nrf_cloud_fota_poll_ctx *ctx, bool cloud_disconnect);
static int update_job_status(struct nrf_cloud_fota_poll_ctx *ctx);

/****************************************************/
Expand Down Expand Up @@ -103,7 +103,7 @@ static int disconnect(struct nrf_cloud_fota_poll_ctx *ctx)
err = -ENOTSUP;
#endif
#if defined(CONFIG_LTE_LINK_CONTROL)
(void)lte_lc_power_off();
err = lte_lc_power_off();
#endif

return err;
Expand All @@ -118,6 +118,21 @@ static void cleanup(void)
#endif
}

static void fota_status_callback(struct nrf_cloud_fota_poll_ctx *ctx,
enum nrf_cloud_fota_status status)
{ if (ctx->status_fn) {
ctx->status_fn(status, NULL);
}
}

static void fota_reboot_callback(struct nrf_cloud_fota_poll_ctx *ctx,
enum nrf_cloud_fota_reboot_status status)
{
if (ctx->reboot_fn) {
ctx->reboot_fn(status);
}
}

/****************************************************/
/* End of transport-specific wrappers. */
/****************************************************/
Expand All @@ -139,10 +154,30 @@ static void http_fota_dl_handler(const struct fota_download_evt *evt)

if (ctx_ptr->is_nonblocking) {
k_work_cancel_delayable(&ctx_ptr->timeout_work);
handle_download_succeeded_and_reboot(ctx_ptr);

/* If the update is a full modem FOTA we tell the application to apply the
* image via nrf_cloud_fota_poll_update_apply() once it has disconnected
* from the network.
*
* If its not a full modem FOTA, we can just apply the update directly here
* as its not dependent on the network state.
*/
if (job.type == NRF_CLOUD_FOTA_MODEM_FULL) {
fota_status_callback(ctx_ptr,
NRF_CLOUD_FOTA_FMFU_VALIDATION_NEEDED);
} else {
int err = handle_downloaded_image(ctx_ptr, false);

if (err) {
LOG_ERR("handle_downloaded_image() failed: %d", err);
nrf_cloud_download_cancel();
}
}

} else {
k_sem_give(&fota_download_sem);
}

break;
case FOTA_DOWNLOAD_EVT_ERASE_PENDING:
case FOTA_DOWNLOAD_EVT_ERASE_TIMEOUT:
Expand Down Expand Up @@ -233,9 +268,7 @@ static void process_pending_job(struct nrf_cloud_fota_poll_ctx *ctx)
/* Save validate status and reboot */
(void)nrf_cloud_fota_settings_save(&pending_job);

if (ctx->reboot_fn) {
ctx->reboot_fn(FOTA_REBOOT_REQUIRED);
}
fota_reboot_callback(ctx, FOTA_REBOOT_REQUIRED);
}
}

Expand Down Expand Up @@ -494,7 +527,7 @@ static int wait_for_download(void)
return 0;
}

static void handle_download_succeeded_and_reboot(struct nrf_cloud_fota_poll_ctx *ctx)
static int handle_downloaded_image(struct nrf_cloud_fota_poll_ctx *ctx, bool cloud_disconnect)
{
int err;

Expand All @@ -506,17 +539,26 @@ static void handle_download_succeeded_and_reboot(struct nrf_cloud_fota_poll_ctx

err = nrf_cloud_bootloader_fota_slot_set(&pending_job);
if (err) {
LOG_WRN("Failed to set B1 slot flag, BOOT FOTA validation may be incorrect");
LOG_ERR("Failed to set B1 slot flag, BOOT FOTA validation may be incorrect");
return err;
}

disconnect(ctx);
if (cloud_disconnect) {
err = disconnect(ctx);
if (err) {
LOG_ERR("Failed to disconnect from nRF Cloud, error: %d", err);
return err;
}
}

#if defined(CONFIG_NRF_CLOUD_FOTA_FULL_MODEM_UPDATE)
if (job.type == NRF_CLOUD_FOTA_MODEM_FULL) {
LOG_INF("Applying full modem FOTA update...");

err = nrf_cloud_fota_fmfu_apply();
if (err) {
LOG_ERR("Failed to apply full modem FOTA update %d", err);

pending_job.validate = NRF_CLOUD_FOTA_VALIDATE_FAIL;
} else {
pending_job.validate = NRF_CLOUD_FOTA_VALIDATE_PASS;
Expand All @@ -529,9 +571,11 @@ static void handle_download_succeeded_and_reboot(struct nrf_cloud_fota_poll_ctx
bool reboot_required = false;

LOG_INF("Installing SMP FOTA update...");

err = nrf_cloud_pending_fota_job_process(&pending_job, &reboot_required);
if (err < 0) {
LOG_ERR("Failed to install SMP FOTA update %d", err);

pending_job.validate = NRF_CLOUD_FOTA_VALIDATE_FAIL;
} else {
pending_job.validate = NRF_CLOUD_FOTA_VALIDATE_PASS;
Expand All @@ -541,18 +585,14 @@ static void handle_download_succeeded_and_reboot(struct nrf_cloud_fota_poll_ctx

err = nrf_cloud_fota_settings_save(&pending_job);
if (err) {
LOG_WRN("FOTA job will be marked as successful without validation");
fota_status_details = FOTA_STATUS_DETAILS_NO_VALIDATE;
(void)update_job_status(ctx);
LOG_ERR("Failed saving pending FOTA job, error: %d", err);
return err;
}

if (ctx_ptr->status_fn) {
ctx_ptr->status_fn(NRF_CLOUD_FOTA_SUCCEEDED, NULL);
}
fota_status_callback(ctx, NRF_CLOUD_FOTA_SUCCEEDED);
fota_reboot_callback(ctx, FOTA_REBOOT_SUCCESS);

if (ctx->reboot_fn) {
ctx->reboot_fn(FOTA_REBOOT_SUCCESS);
}
return 0;
}

static void wait_after_job_update(void)
Expand All @@ -563,6 +603,18 @@ static void wait_after_job_update(void)
k_sleep(K_SECONDS(JOB_WAIT_S));
}

int nrf_cloud_fota_poll_update_apply(struct nrf_cloud_fota_poll_ctx *ctx)
{
int err = handle_downloaded_image(ctx, false);

if (err) {
LOG_ERR("handle_downloaded_image, error: %d", err);
return err;
}

return 0;
}

int nrf_cloud_fota_poll_process(struct nrf_cloud_fota_poll_ctx *ctx)
{
int err;
Expand Down Expand Up @@ -592,9 +644,7 @@ int nrf_cloud_fota_poll_process(struct nrf_cloud_fota_poll_ctx *ctx)
return -EAGAIN;
}

if (ctx_ptr->status_fn) {
ctx_ptr->status_fn(NRF_CLOUD_FOTA_DOWNLOADING, NULL);
}
fota_status_callback(ctx, NRF_CLOUD_FOTA_DOWNLOADING);

/* Start the FOTA download process and wait for completion (or timeout) */
err = start_download();
Expand Down Expand Up @@ -624,7 +674,7 @@ int nrf_cloud_fota_poll_process(struct nrf_cloud_fota_poll_ctx *ctx)
* Job status will be sent to nRF Cloud after reboot and validation.
*/
if (fota_status == NRF_CLOUD_FOTA_SUCCEEDED) {
handle_download_succeeded_and_reboot(ctx);
handle_downloaded_image(ctx, true);
/* Application was expected to reboot... */
return -EBUSY;
}
Expand Down