diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 77d04661b0b6..7c0f62d33ff7 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -858,6 +858,7 @@ Libraries for networking * FOTA status callback. * The :kconfig:option:`CONFIG_NRF_CLOUD_COAP_DISCONNECT_ON_FAILED_REQUEST` Kconfig option to disconnect the CoAP client on a failed request. + * The :kconfig:option:`CONFIG_NRF_CLOUD_FOTA_SMP` Kconfig option to enable experimental support for SMP FOTA using MQTT. * Updated: diff --git a/include/net/nrf_cloud.h b/include/net/nrf_cloud.h index 21962e6bd77c..063ea90326a4 100644 --- a/include/net/nrf_cloud.h +++ b/include/net/nrf_cloud.h @@ -310,6 +310,8 @@ enum nrf_cloud_fota_type { /** Full modem update */ NRF_CLOUD_FOTA_MODEM_FULL = 5, + /** Auxiliary device updated using SMP */ + NRF_CLOUD_FOTA_SMP = 6, NRF_CLOUD_FOTA_TYPE__INVALID }; @@ -438,9 +440,11 @@ struct nrf_cloud_svc_info_fota { uint8_t application:1; /** Flag to indicate if full modem image updates are supported */ uint8_t modem_full:1; + /** Flag to indicate if smp updates are supported */ + uint8_t smp:1; /** Reserved for future use */ - uint8_t _rsvd:4; + uint8_t _rsvd:3; }; /** @brief DEPRECATED - No longer used by nRF Cloud */ @@ -664,6 +668,12 @@ struct nrf_cloud_init_param { * @kconfig{CONFIG_NRF_CLOUD_SEND_DEVICE_STATUS} is enabled. */ const char *application_version; + + /** Callback of type @ref dfu_target_reset_cb_t for resetting the SMP device to enter + * MCUboot recovery mode. + * Used if @kconfig{CONFIG_NRF_CLOUD_FOTA_SMP} is enabled. + */ + void *smp_reset_cb; }; /** @@ -1078,6 +1088,38 @@ bool nrf_cloud_fota_is_type_enabled(const enum nrf_cloud_fota_type type); */ int nrf_cloud_fota_job_start(void); +/** + * @brief Install a downloaded SMP FOTA job. + * Called automatically if @kconfig{CONFIG_NRF_CLOUD_FOTA} is enabled (MQTT FOTA). + * + * @retval 0 SMP update installed successfully. + * @retval -ENOTSUP Error; @kconfig{CONFIG_NRF_CLOUD_FOTA_SMP} is not enabled. + * @retval -EIO Error; failed to schedule image installation. + * @retval -EPROTO Error; failed to reset SMP device. + * @return A negative value indicates an error. + */ +int nrf_cloud_fota_smp_install(void); + +/** + * @brief Read the image info from the SMP device to obtain the current version. + * + * @retval 0 Success; call @ref nrf_cloud_fota_smp_version_get to get the version string. + * @retval -ENOBUFS Error; internal buffer is too small to hold version string. + * @return A negative value indicates an error. + */ +int nrf_cloud_fota_smp_version_read(void); + +/** + * @brief Get the current version string of the SMP device. + * An empty string will be returned if @ref nrf_cloud_fota_smp_version_read has + * not been successfully called. + * + * @retval 0 Success. + * @retval -ENOBUFS Error; internal buffer is too small to hold version string. + * @return A negative value indicates an error. + */ +int nrf_cloud_fota_smp_version_get(char **smp_ver_out); + /** * @brief Check if credentials exist in the configured location. * diff --git a/include/net/nrf_cloud_defs.h b/include/net/nrf_cloud_defs.h index 59db68070199..158e72974d88 100644 --- a/include/net/nrf_cloud_defs.h +++ b/include/net/nrf_cloud_defs.h @@ -119,6 +119,8 @@ #define NRF_CLOUD_FOTA_TYPE_MODEM_FULL "MDM_FULL" #define NRF_CLOUD_FOTA_TYPE_BOOT "BOOT" #define NRF_CLOUD_FOTA_TYPE_APP "APP" +#define NRF_CLOUD_FOTA_TYPE_SMP "SMP" +#define NRF_CLOUD_FOTA_TYPE_CUSTOM "CUSTOM" #define NRF_CLOUD_FOTA_REST_KEY_JOB_DOC "jobDocument" #define NRF_CLOUD_FOTA_REST_KEY_JOB_ID "jobId" #define NRF_CLOUD_FOTA_REST_KEY_PATH "path" @@ -196,6 +198,7 @@ #define NRF_CLOUD_JSON_KEY_KEEPALIVE "keepalive" #define NRF_CLOUD_JSON_KEY_CONN "connection" #define NRF_CLOUD_JSON_KEY_APP_VER "appVersion" +#define NRF_CLOUD_JSON_KEY_SMP_APP_VER "smpDevAppVer" #define NRF_CLOUD_JSON_KEY_CONN_INFO "connectionInfo" #define NRF_CLOUD_JSON_KEY_PROTOCOL "protocol" #define NRF_CLOUD_JSON_KEY_METHOD "method" diff --git a/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota b/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota index 3c3d974e23c5..7b7f69bc9a28 100644 --- a/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota +++ b/subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota @@ -110,6 +110,14 @@ config NRF_CLOUD_FOTA_FULL_MODEM_UPDATE_BUF_SIZE default 4096 endif # NRF_CLOUD_FOTA_FULL_MODEM_UPDATE +config NRF_CLOUD_FOTA_SMP + bool "SMP FOTA updates" + depends on NRF_CLOUD_FOTA + select NRF_MCUMGR_SMP_CLIENT + select EXPERIMENTAL + help + Enables FOTA updates to an auxiliary MCU using SMP. + config NRF_CLOUD_FOTA_TRANSPORT_ENABLED bool default y if (NRF_CLOUD_REST || NRF_CLOUD_COAP || NRF_CLOUD_FOTA) @@ -142,6 +150,12 @@ config NRF_CLOUD_FOTA_TYPE_MODEM_FULL_SUPPORTED help This symbol is y when full modem FOTA is supported by the configuration. +config NRF_CLOUD_FOTA_TYPE_SMP_SUPPORTED + bool + default y if NRF_CLOUD_FOTA_SMP + help + This symbol is y when SMP FOTA is supported by the configuration. + config NRF_CLOUD_FOTA_POLL bool "Enable FOTA job polling helpers" depends on FOTA_DOWNLOAD diff --git a/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h b/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h index 5af78b40b184..8115e5ad6064 100644 --- a/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h +++ b/subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h @@ -29,6 +29,8 @@ struct nrf_cloud_download_fota { /* FOTA update type */ enum dfu_target_image_type expected_type; int img_sz; + /* fota_download callback: fota_download_callback_t */ + void *cb; }; struct nrf_cloud_download_data { diff --git a/subsys/net/lib/nrf_cloud/include/nrf_cloud_fota.h b/subsys/net/lib/nrf_cloud/include/nrf_cloud_fota.h index 794ec0aaf332..82a6039cbe9b 100644 --- a/subsys/net/lib/nrf_cloud/include/nrf_cloud_fota.h +++ b/subsys/net/lib/nrf_cloud/include/nrf_cloud_fota.h @@ -102,19 +102,25 @@ typedef void (*nrf_cloud_fota_callback_t) typedef void (*nrf_cloud_fota_ble_callback_t) (const struct nrf_cloud_fota_ble_job * const ble_job); +struct nrf_cloud_fota_init_param { + nrf_cloud_fota_callback_t evt_cb; + /* Callback of type @ref dfu_target_reset_cb_t */ + void *smp_reset_cb; +}; + /** * @brief Initialize the nRF Cloud FOTA module. * * @note This API must be called prior to using nRF Cloud FOTA and it must * return successfully. * - * @param[in] cb FOTA event handler. + * @param[in] init Initialization parameters. * * @retval 0 If successful. * 1 If successful and a prior FOTA event has been completed. * Otherwise, a (negative) error code is returned. */ -int nrf_cloud_fota_init(nrf_cloud_fota_callback_t cb); +int nrf_cloud_fota_init(struct nrf_cloud_fota_init_param const *const init); /** * @brief Uninitialize nRF Cloud FOTA; cleans up allocated memory. If a FOTA diff --git a/subsys/net/lib/nrf_cloud/include/nrf_cloud_transport.h b/subsys/net/lib/nrf_cloud/include/nrf_cloud_transport.h index ca36c8997b23..3e04a46e4ba9 100644 --- a/subsys/net/lib/nrf_cloud/include/nrf_cloud_transport.h +++ b/subsys/net/lib/nrf_cloud/include/nrf_cloud_transport.h @@ -105,7 +105,7 @@ struct nct_dc_endpoints { int nct_socket_get(void); /** @brief Initialization routine for the transport. */ -int nct_initialize(const char * const client_id); +int nct_initialize(const struct nrf_cloud_init_param *param); /** @brief Unintialize the transport; reset state and free allocated memory */ void nct_uninit(void); diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud.c index 1cc0ee89629b..efdede1cb0ee 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud.c @@ -129,7 +129,7 @@ int nrf_cloud_init(const struct nrf_cloud_init_param *param) #endif /* Initialize the transport. */ - err = nct_initialize(param->client_id); + err = nct_initialize(param); if (err) { return err; } diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c index c7aba1796a43..90e01ad01d5e 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c @@ -377,8 +377,9 @@ static int enabled_info_sections_get(struct nrf_cloud_device_status *const ds) if (fota && IS_ENABLED(CONFIG_NRF_CLOUD_SEND_SERVICE_INFO_FOTA)) { fota->bootloader = IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_BOOT_SUPPORTED); fota->application = IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_APP_SUPPORTED); - fota->modem = IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_MODEM_DELTA_SUPPORTED); + fota->modem = IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_MODEM_DELTA_SUPPORTED); fota->modem_full = IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_MODEM_FULL_SUPPORTED); + fota->smp = IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_SMP_SUPPORTED); } return 0; @@ -1043,8 +1044,7 @@ static int nrf_cloud_encode_service_info_fota(const struct nrf_cloud_svc_info_fo /* Add the FOTA array to the serviceInfo object */ int item_cnt = 0; - cJSON *array = cJSON_AddArrayToObjectCS(svc_inf_obj, - NRF_CLOUD_JSON_KEY_SRVC_INFO_FOTA); + cJSON *array = cJSON_AddArrayToObjectCS(svc_inf_obj, NRF_CLOUD_JSON_KEY_SRVC_INFO_FOTA); if (!array) { return -ENOMEM; @@ -1054,8 +1054,7 @@ static int nrf_cloud_encode_service_info_fota(const struct nrf_cloud_svc_info_fo ++item_cnt; } if (fota->modem) { - cJSON_AddItemToArray(array, - cJSON_CreateString(NRF_CLOUD_FOTA_TYPE_MODEM_DELTA)); + cJSON_AddItemToArray(array, cJSON_CreateString(NRF_CLOUD_FOTA_TYPE_MODEM_DELTA)); ++item_cnt; } if (fota->application) { @@ -1063,8 +1062,11 @@ static int nrf_cloud_encode_service_info_fota(const struct nrf_cloud_svc_info_fo ++item_cnt; } if (fota->modem_full) { - cJSON_AddItemToArray(array, - cJSON_CreateString(NRF_CLOUD_FOTA_TYPE_MODEM_FULL)); + cJSON_AddItemToArray(array, cJSON_CreateString(NRF_CLOUD_FOTA_TYPE_MODEM_FULL)); + ++item_cnt; + } + if (fota->smp) { + cJSON_AddItemToArray(array, cJSON_CreateString(NRF_CLOUD_FOTA_TYPE_SMP)); ++item_cnt; } @@ -1459,6 +1461,19 @@ static int encode_modem_info_json_object(struct modem_param_info *modem, cJSON * ret = json_add_str_cs(device_obj, NRF_CLOUD_JSON_KEY_APP_VER, app_ver); } +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + if (!ret) { + char *smp_ver = NULL; + + (void)nrf_cloud_fota_smp_version_get(&smp_ver); + + if (smp_ver) { + ret = json_add_str_cs(device_obj, NRF_CLOUD_JSON_KEY_SMP_APP_VER, + smp_ver); + } + } +#endif /* CONFIG_NRF_CLOUD_FOTA_SMP */ + if (ret) { cJSON_Delete(device_obj); return -ENOMEM; @@ -1915,7 +1930,7 @@ int nrf_cloud_fota_job_decode(struct nrf_cloud_fota_job_info *const job_info, goto cleanup; } - if (IS_ENABLED(CONFIG_NRF_CLOUD_LOG_LEVEL_DGB)) { + if (IS_ENABLED(CONFIG_NRF_CLOUD_LOG_LEVEL_DBG)) { char *temp = cJSON_PrintUnformatted(array); if (temp) { diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c index e5ddd4263142..5cb9405dcaed 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c @@ -12,6 +12,10 @@ #if defined(CONFIG_NRF_CLOUD_COAP) #include "../coap/include/nrf_cloud_coap_transport.h" #endif +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) +#include +#include +#endif #include "nrf_cloud_download.h" LOG_MODULE_REGISTER(nrf_cloud_download, CONFIG_NRF_CLOUD_LOG_LEVEL); @@ -362,17 +366,47 @@ static void resume_work_fn(struct k_work *unused) } #endif /* NRF_CLOUD_COAP_DOWNLOADS */ +#if defined(CONFIG_FOTA_DOWNLOAD) +static int smp_fota_dl_cancel(void) +{ +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + return mcumgr_smp_client_download_cancel(); +#else + return -ENOTSUP; +#endif /* CONFIG_NRF_CLOUD_FOTA_SMP */ +} + +static void smp_fota_dl_util_init(const struct nrf_cloud_download_fota *const fota) +{ +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + fota_download_util_client_init(fota->cb, true); +#endif +} +#endif /* CONFIG_FOTA_DOWNLOAD */ + static int fota_start(struct nrf_cloud_download_data *const dl) { #if defined(CONFIG_FOTA_DOWNLOAD) + + /* Download using CoAP if enabled */ #if defined(CONFIG_NRF_CLOUD_COAP_DOWNLOADS) return coap_dl(dl); -#else +#endif /* CONFIG_NRF_CLOUD_COAP_DOWNLOADS */ + + if (dl->fota.expected_type == DFU_TARGET_IMAGE_TYPE_SMP) { + /* This needs to be called to set the SMP flag in fota_download */ + smp_fota_dl_util_init(&dl->fota); + } else { + /* This needs to be called to clear the SMP flag */ + (void)fota_download_init(dl->fota.cb); + } + return fota_download_start_with_image_type(dl->host, dl->path, dl->dl_cfg.sec_tag_count ? dl->dl_cfg.sec_tag_list[0] : -1, dl->dl_cfg.pdn_id, dl->dl_cfg.frag_size_override, dl->fota.expected_type); -#endif /* CONFIG_NRF_CLOUD_COAP_DOWNLOADS */ + #endif /* CONFIG_FOTA_DOWNLOAD */ + return -ENOTSUP; } @@ -403,6 +437,20 @@ static void active_dl_reset(void) active_dl.type = NRF_CLOUD_DL_TYPE_NONE; } +static int fota_dl_cancel(struct nrf_cloud_download_data *const dl) +{ + int ret = -ENOTSUP; + +#if defined(CONFIG_FOTA_DOWNLOAD) + if (dl->fota.expected_type == DFU_TARGET_IMAGE_TYPE_SMP) { + ret = smp_fota_dl_cancel(); + } else { + ret = fota_download_cancel(); + } +#endif /* CONFIG_FOTA_DOWNLOAD */ + return ret; +} + void nrf_cloud_download_cancel(void) { int ret = 0; @@ -410,9 +458,7 @@ void nrf_cloud_download_cancel(void) k_mutex_lock(&active_dl_mutex, K_FOREVER); if (active_dl.type == NRF_CLOUD_DL_TYPE_FOTA) { -#if defined(CONFIG_FOTA_DOWNLOAD) - ret = fota_download_cancel(); -#endif + ret = fota_dl_cancel(&active_dl); } else if (active_dl.type == NRF_CLOUD_DL_TYPE_DL_CLIENT) { ret = dlc_disconnect(&active_dl); } else { @@ -454,8 +500,17 @@ int nrf_cloud_download_start(struct nrf_cloud_download_data *const dl) return -EINVAL; } - if (!IS_ENABLED(CONFIG_FOTA_DOWNLOAD) && (dl->type == NRF_CLOUD_DL_TYPE_FOTA)) { - return -ENOTSUP; + if (dl->type == NRF_CLOUD_DL_TYPE_FOTA) { + if (!IS_ENABLED(CONFIG_FOTA_DOWNLOAD)) { + return -ENOTSUP; + } + if (!dl->fota.cb) { + return -ENOEXEC; + } + if ((dl->fota.expected_type == DFU_TARGET_IMAGE_TYPE_SMP) && + !IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_SMP)) { + return -ENOSYS; + } } if (!check_fota_file_path_len(dl->path)) { diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c index b3ff7ae16972..b73a75509cd1 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota.c @@ -25,15 +25,16 @@ #include #include #include - #if defined(CONFIG_BOOTLOADER_MCUBOOT) #include #endif - #if defined(CONFIG_NRF_MODEM_LIB) #include #include #endif +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) +#include +#endif LOG_MODULE_REGISTER(nrf_cloud_fota, CONFIG_NRF_CLOUD_FOTA_LOG_LEVEL); @@ -67,6 +68,7 @@ static int save_validate_status(const char *const job_id, const enum nrf_cloud_fota_validate_status status); static int publish_validated_job_status(void); static void reset_topics(void); +static void install_smp_update(struct nrf_cloud_fota_job *fota); static struct mqtt_client *client_mqtt; static nrf_cloud_fota_callback_t event_cb; @@ -142,6 +144,10 @@ static void send_fota_done_event_if_done(void) */ if ((current_fota.status == NRF_CLOUD_FOTA_IN_PROGRESS) && (saved_job.validate == NRF_CLOUD_FOTA_VALIDATE_PENDING)) { + /* SMP updates can be installed immediately */ + if (current_fota.info.type == NRF_CLOUD_FOTA_SMP) { + install_smp_update(¤t_fota); + } send_event(NRF_CLOUD_FOTA_EVT_DONE, ¤t_fota); } } @@ -157,7 +163,7 @@ static int pending_fota_job_validate(void) } /* Do not enforce a reboot for modem updates since they can also be handled by - * reinitializing the modem lib + * reinitializing the modem lib. */ if (!nrf_cloud_fota_is_type_modem(saved_job.type)) { reboot_on_init = reboot; @@ -211,7 +217,7 @@ static void fota_reboot(void) sys_reboot(SYS_REBOOT_COLD); } -int nrf_cloud_fota_init(nrf_cloud_fota_callback_t cb) +int nrf_cloud_fota_init(struct nrf_cloud_fota_init_param const *const init) { int ret; @@ -219,12 +225,22 @@ int nrf_cloud_fota_init(nrf_cloud_fota_callback_t cb) fota_reboot(); } - if (cb == NULL) { + if (init == NULL) { LOG_ERR("Invalid parameter"); return -EINVAL; } - event_cb = cb; + event_cb = init->evt_cb; + +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + ret = mcumgr_smp_client_init((dfu_target_reset_cb_t)init->smp_reset_cb); + if (ret != 0) { + LOG_ERR("Failed to init SMP client, error: %d", ret); + return ret; + } + + (void)nrf_cloud_fota_smp_version_read(); +#endif /* CONFIG_NRF_CLOUD_FOTA_SMP */ if (initialized) { return 0; @@ -239,6 +255,7 @@ int nrf_cloud_fota_init(nrf_cloud_fota_callback_t cb) LOG_ERR("fota_download_init error: %d", ret); return ret; } + fota_dl_initialized = true; } @@ -255,13 +272,14 @@ int nrf_cloud_fota_init(nrf_cloud_fota_callback_t cb) /* Indicate that a FOTA update has occurred */ ret = 1; } else if (ret == -ENODEV) { - /* No job was pending validation, check for already validated modem job */ - if (nrf_cloud_fota_is_type_modem(saved_job.type) && - (saved_job.validate == NRF_CLOUD_FOTA_VALIDATE_PASS || + /* No job was pending validation, check for already validated job */ + if ((saved_job.validate == NRF_CLOUD_FOTA_VALIDATE_PASS || saved_job.validate == NRF_CLOUD_FOTA_VALIDATE_FAIL || saved_job.validate == NRF_CLOUD_FOTA_VALIDATE_UNKNOWN)) { - /* Device has just rebooted from a modem FOTA */ - LOG_INF("FOTA updated modem"); + if (nrf_cloud_fota_is_type_modem(saved_job.type)) { + /* Device has just rebooted from a modem FOTA */ + LOG_INF("FOTA updated modem"); + } ret = 1; } else { ret = 0; @@ -621,6 +639,24 @@ static int save_validate_status(const char *const job_id, return ret; } +static void install_smp_update(struct nrf_cloud_fota_job *fota) +{ + /* TODO: after installation, read the version number. + * This does not work for some reason. + * There seems to be some sort of crash/error in dfu_target_smp_image_list_get() + * when called after image installation. + * For now, just use the standard FOTA flow: reboot and then read the version on startup. + */ + int ret = pending_fota_job_validate(); + + if (ret < 0) { + current_fota.status = NRF_CLOUD_FOTA_FAILED; + current_fota.error = NRF_CLOUD_FOTA_ERROR_APPLY_FAIL; + } else { + current_fota.status = NRF_CLOUD_FOTA_SUCCEEDED; + } +} + static void http_fota_handler(const struct fota_download_evt *evt) { __ASSERT_NO_MSG(evt != NULL); @@ -648,7 +684,9 @@ static void http_fota_handler(const struct fota_download_evt *evt) save_validate_status(current_fota.info.id, current_fota.info.type, NRF_CLOUD_FOTA_VALIDATE_PENDING); + ret = publish_job_status(¤t_fota); + break; case FOTA_DOWNLOAD_EVT_ERASE_PENDING: @@ -816,6 +854,14 @@ static int start_job(struct nrf_cloud_fota_job *const job, const bool send_evt) ret = -EFTYPE; } break; + case NRF_CLOUD_FOTA_SMP: + if (IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_SMP)) { + img_type = DFU_TARGET_IMAGE_TYPE_SMP; + } else { + LOG_ERR("Not configured for SMP FOTA"); + ret = -EFTYPE; + } + break; default: LOG_ERR("Unhandled FOTA type: %d", job->info.type); ret = -EFTYPE; @@ -839,7 +885,11 @@ static int start_job(struct nrf_cloud_fota_job *const job, const bool send_evt) .pdn_id = 0, .frag_size_override = CONFIG_NRF_CLOUD_FOTA_DOWNLOAD_FRAGMENT_SIZE, }, - .fota = { .expected_type = img_type } + .fota = { + .expected_type = img_type, + .img_sz = job->info.file_size, + .cb = (void *)http_fota_handler + } }; ret = nrf_cloud_download_start(&dl); diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_common.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_common.c index 482d1b18a8f8..b31680614a56 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_common.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_common.c @@ -20,6 +20,9 @@ #if defined(CONFIG_FOTA_DOWNLOAD) #include #endif +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) +#include +#endif #include #include "nrf_cloud_fota.h" @@ -49,6 +52,10 @@ LOG_MODULE_REGISTER(nrf_cloud_fota_common, CONFIG_NRF_CLOUD_FOTA_LOG_LEVEL); #define FOTA_SETTINGS_FULL FOTA_SETTINGS_NAME "/" FOTA_SETTINGS_KEY_PENDING_JOB #endif +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) +static char smp_ver[IMG_MGMT_VER_MAX_STR_LEN + 1]; +#endif + /* Pending job info used with the settings library */ static struct nrf_cloud_settings_fota_job *pending_job; static K_MUTEX_DEFINE(pending_job_mutex); @@ -372,6 +379,35 @@ static enum nrf_cloud_fota_validate_status modem_full_fota_validate_get(void) NRF_CLOUD_FOTA_VALIDATE_PASS); } +static enum nrf_cloud_fota_validate_status smp_fota_validate_get(void) +{ +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + return nrf_cloud_fota_smp_install() ? + NRF_CLOUD_FOTA_VALIDATE_FAIL : NRF_CLOUD_FOTA_VALIDATE_PASS; +#endif + return NRF_CLOUD_FOTA_VALIDATE_UNKNOWN; +} + +int nrf_cloud_fota_smp_install(void) +{ + int err = -ENOTSUP; + +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + err = mcumgr_smp_client_update(); + if (err) { + LOG_ERR("Failed to install SMP update, error: %d", err); + return -EIO; + } + + err = mcumgr_smp_client_reset(); + if (err) { + LOG_WRN("mcumgr_smp_client_reset error: %d", err); + return -EPROTO; + } +#endif + return err; +} + bool nrf_cloud_fota_is_type_modem(const enum nrf_cloud_fota_type type) { return ((type == NRF_CLOUD_FOTA_MODEM_DELTA) || @@ -460,6 +496,10 @@ int nrf_cloud_pending_fota_job_process(struct nrf_cloud_settings_fota_job * cons } job->validate = boot_fota_validate_get(job->bl_flags); + } else if (job->type == NRF_CLOUD_FOTA_SMP) { + job->validate = smp_fota_validate_get(); + /* TODO: reboot for now, but this should not be necessary */ + *reboot_required = true; } else { LOG_ERR("Unknown FOTA job type: %d", job->type); return -ENOENT; @@ -479,8 +519,65 @@ bool nrf_cloud_fota_is_type_enabled(const enum nrf_cloud_fota_type type) return IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_MODEM_DELTA_SUPPORTED); case NRF_CLOUD_FOTA_MODEM_FULL: return IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_MODEM_FULL_SUPPORTED); + case NRF_CLOUD_FOTA_SMP: + return IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_TYPE_SMP_SUPPORTED); default: LOG_WRN("Unhandled FOTA type: %d", type); return false; } } + +int nrf_cloud_fota_smp_version_read(void) +{ +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + struct mcumgr_image_state image_list = {0}; + struct mcumgr_image_data *list; + size_t ver_len; + int ret = dfu_target_smp_image_list_get(&image_list); + + if (ret) { + LOG_WRN("Failed to read SMP image list, error: %d", ret); + return -ENODATA; + } + + /* Set the return value in case no active image is found */ + ret = -ENODEV; + + list = image_list.image_list; + for (int i = 0; i < image_list.image_list_length; ++i, ++list) { + LOG_DBG("%s Image(%d) slot(%d)", + list->flags.active ? "Primary" : "Secondary", + list->img_num, + list->slot_num); + LOG_DBG(" Version: %s", list->version); + LOG_DBG(" Bootable(%d) Pending(%d) Confirmed(%d)", + list->flags.bootable, list->flags.pending, list->flags.confirmed); + + if (list->flags.active) { + ver_len = strlen(list->version); + if (ver_len >= sizeof(smp_ver)) { + return -ENOBUFS; + } + memcpy(smp_ver, list->version, ver_len + 1); + ret = 0; + } + } + + return ret; +#endif + return -ENOTSUP; +} + +int nrf_cloud_fota_smp_version_get(char **smp_ver_out) +{ +#if defined(CONFIG_NRF_CLOUD_FOTA_SMP) + if (!smp_ver_out) { + return -EINVAL; + } + + *smp_ver_out = smp_ver; + + return 0; +#endif + return -ENOTSUP; +} diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c index 68edd30353ed..57edba913d32 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_fota_poll.c @@ -463,7 +463,8 @@ static int start_download(void) }, .fota = { .expected_type = ctx_ptr->img_type, - .img_sz = job.file_size + .img_sz = job.file_size, + .cb = http_fota_dl_handler } }; diff --git a/subsys/net/lib/nrf_cloud/src/nrf_cloud_transport.c b/subsys/net/lib/nrf_cloud/src/nrf_cloud_transport.c index 36b37f4179ec..c4af93b329f1 100644 --- a/subsys/net/lib/nrf_cloud/src/nrf_cloud_transport.c +++ b/subsys/net/lib/nrf_cloud/src/nrf_cloud_transport.c @@ -820,7 +820,7 @@ static void nct_mqtt_evt_handler(struct mqtt_client *const mqtt_client, } } -int nct_initialize(const char * const client_id) +int nct_initialize(const struct nrf_cloud_init_param *param) { int err; @@ -833,7 +833,12 @@ int nct_initialize(const char * const client_id) } #if defined(CONFIG_NRF_CLOUD_FOTA) - err = nrf_cloud_fota_init(nrf_cloud_fota_cb_handler); + struct nrf_cloud_fota_init_param fota_init = { + .evt_cb = nrf_cloud_fota_cb_handler, + .smp_reset_cb = param->smp_reset_cb + }; + + err = nrf_cloud_fota_init(&fota_init); if (err < 0) { return err; } else if (err && persistent_session) { @@ -841,7 +846,8 @@ int nct_initialize(const char * const client_id) nct_save_session_state(0); } #endif - err = nct_client_id_set(client_id); + + err = nct_client_id_set(param->client_id); if (err) { return err; } diff --git a/tests/subsys/net/lib/nrf_cloud/cloud/src/fakes.h b/tests/subsys/net/lib/nrf_cloud/cloud/src/fakes.h index 8061a3119625..ca6cacb81d39 100644 --- a/tests/subsys/net/lib/nrf_cloud/cloud/src/fakes.h +++ b/tests/subsys/net/lib/nrf_cloud/cloud/src/fakes.h @@ -17,7 +17,7 @@ DEFINE_FFF_GLOBALS; int poll(struct zsock_pollfd *fds, int nfds, int timeout); /* Fake functions declaration */ -FAKE_VALUE_FUNC(int, nct_initialize, const char *); +FAKE_VALUE_FUNC(int, nct_initialize, const struct nrf_cloud_init_param *); FAKE_VALUE_FUNC(int, nfsm_init); FAKE_VALUE_FUNC(int, nrf_cloud_codec_init, struct nrf_cloud_os_mem_hooks *); FAKE_VOID_FUNC(nrf_cloud_set_app_version, const char *const); @@ -47,15 +47,15 @@ FAKE_VALUE_FUNC(int, nrf_cloud_obj_cloud_encode, struct nrf_cloud_obj *const); FAKE_VALUE_FUNC(int, nrf_cloud_obj_cloud_encoded_free, struct nrf_cloud_obj *const); /* Custom fakes implementation */ -int fake_nct_initialize__succeeds(const char *const client_id) +int fake_nct_initialize__succeeds(const struct nrf_cloud_init_param *param) { - ARG_UNUSED(client_id); + ARG_UNUSED(param); return 0; } -int fake_nct_initialize__fails(const char *const client_id) +int fake_nct_initialize__fails(const struct nrf_cloud_init_param *param) { - ARG_UNUSED(client_id); + ARG_UNUSED(param); return -ENODEV; }