Skip to content

Commit

Permalink
net: lib: nrf_cloud_fota: enable SMP FOTA type
Browse files Browse the repository at this point in the history
Enable SMP FOTA type for MQTT-based FOTA.

IRIS-9480
IRIS-9482

Signed-off-by: Justin Morton <justin.morton@nordicsemi.no>
  • Loading branch information
jayteemo committed Sep 27, 2024
1 parent e74b3ce commit 5cc1fde
Show file tree
Hide file tree
Showing 15 changed files with 333 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
44 changes: 43 additions & 1 deletion include/net/nrf_cloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -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.
*
Expand Down
3 changes: 3 additions & 0 deletions include/net/nrf_cloud_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
14 changes: 14 additions & 0 deletions subsys/net/lib/nrf_cloud/Kconfig.nrf_cloud_fota
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions subsys/net/lib/nrf_cloud/include/nrf_cloud_download.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 8 additions & 2 deletions subsys/net/lib/nrf_cloud/include/nrf_cloud_fota.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion subsys/net/lib/nrf_cloud/include/nrf_cloud_transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion subsys/net/lib/nrf_cloud/src/nrf_cloud.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
31 changes: 23 additions & 8 deletions subsys/net/lib/nrf_cloud/src/nrf_cloud_codec_internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -1054,17 +1054,19 @@ 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) {
cJSON_AddItemToArray(array, cJSON_CreateString(NRF_CLOUD_FOTA_TYPE_APP));
++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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
69 changes: 62 additions & 7 deletions subsys/net/lib/nrf_cloud/src/nrf_cloud_download.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <mcumgr_smp_client.h>
#include <fota_download_util.h>
#endif
#include "nrf_cloud_download.h"

LOG_MODULE_REGISTER(nrf_cloud_download, CONFIG_NRF_CLOUD_LOG_LEVEL);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -403,16 +437,28 @@ 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;

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 {
Expand Down Expand Up @@ -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)) {
Expand Down
Loading

0 comments on commit 5cc1fde

Please sign in to comment.