From 90eada8402c123b50a98fcba61b2f83fe20fcd74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20J=C3=B8lsgard?= Date: Wed, 5 Jul 2023 12:33:14 +0200 Subject: [PATCH] samples: cellular: http_update: remove application reboot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * With changes to the modem firmware and integration it is not necessary to reboot the application to perform a modem firmware update. This commit refactors the full and delta modem firmware update samples to only reinitialize the modem and modem library when performing the update. * With changes to full and delta modem firmware update samples, this was the only sample using the common folder so this is merged into the application. Restructure the sample to align with the modem firmware update samples. Signed-off-by: Eivind Jølsgard --- .../working_with_nrf/nrf91/nrf91_features.rst | 2 +- .../releases/release-notes-1.5.0.rst | 2 +- .../releases/release-notes-1.7.0.rst | 2 +- .../releases/release-notes-2.4.0.rst | 2 +- .../releases/release-notes-changelog.rst | 2 +- .../application_update/CMakeLists.txt | 2 - .../http_update/application_update/README.rst | 10 +- .../cert/AmazonRootCA1 | 0 .../application_update/sample.yaml | 6 +- .../http_update/application_update/src/main.c | 416 ++++++++++-- .../http_update/common/include/update.h | 68 -- .../cellular/http_update/common/src/update.c | 245 -------- .../boards/nrf9160dk_nrf9160_ns.conf | 7 - .../http_update/full_modem_update/src/main.c | 267 -------- .../modem_delta_update/CMakeLists.txt | 2 - .../http_update/modem_delta_update/README.rst | 8 +- .../boards/nrf9160dk_nrf9160_ns.conf | 9 + .../boards/nrf9161dk_nrf9161_ns.conf | 9 + .../modem_delta_update/cert/AmazonRootCA1 | 20 + .../http_update/modem_delta_update/prj.conf | 8 +- .../modem_delta_update/sample.yaml | 3 +- .../http_update/modem_delta_update/src/main.c | 487 ++++++++++++-- .../CMakeLists.txt | 2 - .../Kconfig | 0 .../README.rst | 18 +- .../boards/nrf9160dk_nrf9160_ns.conf | 13 + .../boards/nrf9160dk_nrf9160_ns.overlay | 0 .../boards/nrf9161dk_nrf9161_ns.conf | 14 + .../boards/nrf9161dk_nrf9161_ns.overlay | 16 + .../boards/nrf9161dk_nrf9161_ns_0_7_0.overlay | 21 + .../modem_full_update/cert/AmazonRootCA1 | 20 + .../prj.conf | 4 - .../sample.yaml | 3 +- .../http_update/modem_full_update/src/main.c | 592 ++++++++++++++++++ .../dfu_target/src/dfu_target_full_modem.c | 3 + 35 files changed, 1567 insertions(+), 716 deletions(-) rename samples/cellular/http_update/{common => application_update}/cert/AmazonRootCA1 (100%) delete mode 100644 samples/cellular/http_update/common/include/update.h delete mode 100644 samples/cellular/http_update/common/src/update.c delete mode 100644 samples/cellular/http_update/full_modem_update/boards/nrf9160dk_nrf9160_ns.conf delete mode 100644 samples/cellular/http_update/full_modem_update/src/main.c create mode 100644 samples/cellular/http_update/modem_delta_update/boards/nrf9160dk_nrf9160_ns.conf create mode 100644 samples/cellular/http_update/modem_delta_update/boards/nrf9161dk_nrf9161_ns.conf create mode 100644 samples/cellular/http_update/modem_delta_update/cert/AmazonRootCA1 rename samples/cellular/http_update/{full_modem_update => modem_full_update}/CMakeLists.txt (74%) rename samples/cellular/http_update/{full_modem_update => modem_full_update}/Kconfig (100%) rename samples/cellular/http_update/{full_modem_update => modem_full_update}/README.rst (80%) create mode 100644 samples/cellular/http_update/modem_full_update/boards/nrf9160dk_nrf9160_ns.conf rename samples/cellular/http_update/{full_modem_update => modem_full_update}/boards/nrf9160dk_nrf9160_ns.overlay (100%) create mode 100644 samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.conf create mode 100644 samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.overlay create mode 100644 samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns_0_7_0.overlay create mode 100644 samples/cellular/http_update/modem_full_update/cert/AmazonRootCA1 rename samples/cellular/http_update/{full_modem_update => modem_full_update}/prj.conf (92%) rename samples/cellular/http_update/{full_modem_update => modem_full_update}/sample.yaml (68%) create mode 100644 samples/cellular/http_update/modem_full_update/src/main.c diff --git a/doc/nrf/device_guides/working_with_nrf/nrf91/nrf91_features.rst b/doc/nrf/device_guides/working_with_nrf/nrf91/nrf91_features.rst index 5754a0c65fee..e49e7f8dfe51 100644 --- a/doc/nrf/device_guides/working_with_nrf/nrf91/nrf91_features.rst +++ b/doc/nrf/device_guides/working_with_nrf/nrf91/nrf91_features.rst @@ -240,7 +240,7 @@ You can hardcode the information in the application, or you can use a functional Samples and applications implementing FOTA ------------------------------------------ -* :ref:`http_full_modem_update_sample` sample - performs a full firmware OTA update of the modem. +* :ref:`http_modem_full_update_sample` sample - performs a full firmware OTA update of the modem. * :ref:`http_modem_delta_update_sample` sample - performs a delta OTA update of the modem firmware. * :ref:`http_application_update_sample` sample - performs a basic application FOTA update. * :ref:`aws_iot` sample - performs a FOTA update using MQTT and HTTP, where the firmware download is triggered through an AWS IoT job. diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-1.5.0.rst b/doc/nrf/releases_and_maturity/releases/release-notes-1.5.0.rst index c2348cbcca68..d729df0a174d 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-1.5.0.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-1.5.0.rst @@ -63,7 +63,7 @@ nRF9160 * :ref:`asset_tracker_v2` application, which is a low-power asset tracking example for nRF9160 DK and Thingy:91. * :ref:`fmfu_smp_svr_sample` sample, which shows how to add the full modem serial update functionality to an application using SMP Server. - * :ref:`http_full_modem_update_sample` sample, which demonstrates how to add full modem upgrade support to an application. Note that this requires an external flash memory of a minimum of 2 MB to work, hence the sample will only work on the nRF9160 DK v0.14.0 or later. + * :ref:`http_modem_full_update_sample` sample, which demonstrates how to add full modem upgrade support to an application. Note that this requires an external flash memory of a minimum of 2 MB to work, hence the sample will only work on the nRF9160 DK v0.14.0 or later. * :ref:`http_modem_delta_update_sample` sample, which demonstrates how to add delta modem upgrade support to an application. * :ref:`lib_fmfu_mgmt`, which is a new library that implements parts of the mcumgr management protocol for doing full modem serial updates. diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-1.7.0.rst b/doc/nrf/releases_and_maturity/releases/release-notes-1.7.0.rst index 3aa7acdc14fa..e8229d041cc0 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-1.7.0.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-1.7.0.rst @@ -566,7 +566,7 @@ In addition to documentation related to the changes listed above, the following * nRF9160 samples: - * :ref:`http_full_modem_update_sample` - Updated with the description of its customization options for firmware files. + * :ref:`http_modem_full_update_sample` - Updated with the description of its customization options for firmware files. * :ref:`zigbee_samples`: diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst b/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst index 112197c0f7c9..3ef87f2306be 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-2.4.0.rst @@ -543,7 +543,7 @@ Multicore samples nRF9160 samples --------------- -* :ref:`http_full_modem_update_sample` sample: +* :ref:`http_modem_full_update_sample` sample: * The sample now uses modem firmware versions 1.3.3 and 1.3.4. * Enabled external flash in the nRF9160 DK devicetree overlays for v0.14.0 or later versions, as it is now disabled in the Zephyr board definition. 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 2b522151f6a1..fb464245d8c4 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -426,7 +426,7 @@ Cellular samples (renamed from nRF9160 samples) * Updated credentials for the HTTPS connection. -* :ref:`http_full_modem_update_sample` sample: +* :ref:`http_modem_full_update_sample` sample: * Updated credentials for the HTTPS connection. diff --git a/samples/cellular/http_update/application_update/CMakeLists.txt b/samples/cellular/http_update/application_update/CMakeLists.txt index 7d44dfdc81c2..c479df86be92 100644 --- a/samples/cellular/http_update/application_update/CMakeLists.txt +++ b/samples/cellular/http_update/application_update/CMakeLists.txt @@ -11,6 +11,4 @@ project(application_update) # NORDIC SDK APP START target_sources(app PRIVATE src/main.c) -target_sources(app PRIVATE ../common/src/update.c) -target_include_directories(app PRIVATE ../common/include) # NORDIC SDK APP END diff --git a/samples/cellular/http_update/application_update/README.rst b/samples/cellular/http_update/application_update/README.rst index 56c2f2de2e4c..ee0360658b68 100644 --- a/samples/cellular/http_update/application_update/README.rst +++ b/samples/cellular/http_update/application_update/README.rst @@ -13,7 +13,7 @@ It uses the :ref:`lib_fota_download` library to download two image files from a Requirements ************ -The sample supports the following development kit: +The sample supports the following development kits: .. table-from-sample-yaml:: @@ -111,7 +111,7 @@ After programming the sample to your development kit, test it by performing the Remember to rename the file to match the ``CONFIG_DOWNLOAD_FILE_V1`` configuration option. #. Configure the application image version to be 2 and rebuild the application. #. Upload the file :file:`app_update.bin` with the application image version 2 to the server you have chosen. -#. Reset your nRF9160 DK to start the application. +#. Reset your nRF91 Series DK to start the application. #. Open a terminal emulator and observe that an output similar to the following is printed: .. code-block:: @@ -120,9 +120,9 @@ After programming the sample to your development kit, test it by performing the #. Observe that **LED 1** is lit. This indicates that version 1 of the application is running. -#. Press **Button 1** on the nRF9160 DK or type "download" in the terminal emulator to start the download process, and wait until ``Download complete`` is printed in the terminal. -#. Press the **RESET** button on the kit or type "reset" in the terminal emulator. - MCUboot will now detect the downloaded image and program it to flash memory. +#. Press **Button 1** on the nRF91 Series DK or type "download" in the terminal emulator to start the download process, and wait until ``Download complete`` is printed in the terminal. +#. Press **Button 1** on the nRF91 Series DK or type "apply" in the terminal emulator to reboot and apply the update. + MCUboot now detects the downloaded image and program it to flash memory. This can take up to a minute. Nothing is printed in the terminal while the writing is in progress. #. Observe that **LED 1** and **LED 2** are lit. diff --git a/samples/cellular/http_update/common/cert/AmazonRootCA1 b/samples/cellular/http_update/application_update/cert/AmazonRootCA1 similarity index 100% rename from samples/cellular/http_update/common/cert/AmazonRootCA1 rename to samples/cellular/http_update/application_update/cert/AmazonRootCA1 diff --git a/samples/cellular/http_update/application_update/sample.yaml b/samples/cellular/http_update/application_update/sample.yaml index 26d5d798a0bd..6c54788df6ee 100644 --- a/samples/cellular/http_update/application_update/sample.yaml +++ b/samples/cellular/http_update/application_update/sample.yaml @@ -5,12 +5,14 @@ tests: build_only: true integration_platforms: - nrf9160dk_nrf9160_ns - platform_allow: nrf9160dk_nrf9160_ns + - nrf9161dk_nrf9161_ns + platform_allow: nrf9160dk_nrf9160_ns nrf9161dk_nrf9161_ns tags: ci_build sample.cellular.http_update.application_update.lwm2m_carrier: build_only: true extra_args: OVERLAY_CONFIG=overlay-carrier.conf - platform_allow: nrf9160dk_nrf9160_ns integration_platforms: - nrf9160dk_nrf9160_ns + - nrf9161dk_nrf9161_ns + platform_allow: nrf9160dk_nrf9160_ns nrf9161dk_nrf9161_ns tags: ci_build diff --git a/samples/cellular/http_update/application_update/src/main.c b/samples/cellular/http_update/application_update/src/main.c index 5b14e262832b..28259a34d974 100644 --- a/samples/cellular/http_update/application_update/src/main.c +++ b/samples/cellular/http_update/application_update/src/main.c @@ -1,19 +1,27 @@ /* - * Copyright (c) 2019 Nordic Semiconductor ASA + * Copyright (c) 2019-2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#include #include #include +#include +#include +#include +#include +#include +#include + #include #include +#include +#include #include -#include -#include "update.h" +#include #if defined(CONFIG_LWM2M_CARRIER) #include -#include #endif #if CONFIG_APPLICATION_VERSION == 2 @@ -22,33 +30,357 @@ #define NUM_LEDS 1 #endif -static const char *get_file(void) -{ -#if CONFIG_APPLICATION_VERSION == 2 - return CONFIG_DOWNLOAD_FILE_V1; +#define TLS_SEC_TAG 42 + +#ifdef CONFIG_USE_HTTPS +#define SEC_TAG (TLS_SEC_TAG) #else - return CONFIG_DOWNLOAD_FILE_V2; +#define SEC_TAG (-1) +#endif + +enum fota_state { IDLE, CONNECTED, UPDATE_DOWNLOAD, UPDATE_PENDING, UPDATE_APPLY }; +static enum fota_state state = IDLE; + +static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); +static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); +static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); +static struct gpio_callback sw0_cb; +static struct k_work fota_work; + +static void fota_work_cb(struct k_work *work); +static void apply_state(enum fota_state new_state); +static int modem_configure_and_connect(void); +static int update_download(void); + +/** + * @brief Handler for LTE link control events + */ +static void lte_lc_handler(const struct lte_lc_evt *const evt) +{ + static bool connected; + + switch (evt->type) { + case LTE_LC_EVT_NW_REG_STATUS: + if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) && + (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) { + if (!connected) { + break; + } + + printk("LTE network is disconnected.\n"); + connected = false; + if (state == CONNECTED) { + apply_state(IDLE); + } + break; + } + + connected = true; + + if (state == IDLE) { + printk("LTE Link Connected!\n"); + apply_state(CONNECTED); + } + break; + default: + break; + } +} + +static int leds_init(void) +{ + if (!device_is_ready(led0.port)) { + printk("Led0 GPIO port not ready\n"); + return -ENODEV; + } + + if (!device_is_ready(led1.port)) { + printk("Led1 GPIO port not ready\n"); + return -ENODEV; + } + + return 0; +} + +/* We use the LEDs to indicate which version of the application we have installed */ +static int leds_set(int num_leds) +{ + switch (num_leds) { + case 0: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE); + break; + case 1: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE); + break; + case 2: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void app_dfu_btn_irq_disable(void) +{ + gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_DISABLE); +} + +static void app_dfu_btn_irq_enable(void) +{ + gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_EDGE_TO_ACTIVE); +} + +static void app_dfu_button_pressed(const struct device *gpiob, struct gpio_callback *cb, + uint32_t pins) +{ + switch (state) { + case CONNECTED: + apply_state(UPDATE_DOWNLOAD); + break; + case UPDATE_PENDING: + apply_state(UPDATE_APPLY); + break; + default: + break; + } + +} + +static int button_init(void) +{ + int err; + + if (!device_is_ready(sw0.port)) { + printk("SW0 GPIO port device not ready\n"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&sw0, GPIO_INPUT); + if (err < 0) { + return err; + } + + gpio_init_callback(&sw0_cb, app_dfu_button_pressed, BIT(sw0.pin)); + + err = gpio_add_callback(sw0.port, &sw0_cb); + if (err < 0) { + printk("Unable to configure SW0 GPIO pin!\n"); + return err; + } + + return 0; +} + +static void apply_state(enum fota_state new_state) +{ + __ASSERT(state != new_state, "State already set: %d", state); + + state = new_state; + + switch (new_state) { + case IDLE: + app_dfu_btn_irq_disable(); + modem_configure_and_connect(); + break; + case CONNECTED: + app_dfu_btn_irq_enable(); + printk("Press Button 1 or enter 'download' to download new application firmware\n"); + break; + case UPDATE_DOWNLOAD: + app_dfu_btn_irq_disable(); + k_work_submit(&fota_work); + break; + case UPDATE_PENDING: + app_dfu_btn_irq_enable(); + printk("Press Button 1 or enter 'apply' to apply new application firmware\n"); + break; + case UPDATE_APPLY: + app_dfu_btn_irq_disable(); + k_work_submit(&fota_work); + break; + } +} + +#if defined(CONFIG_USE_HTTPS) +static int cert_provision(void) +{ + static const char cert[] = { + #include "../cert/AmazonRootCA1" + }; + BUILD_ASSERT(sizeof(cert) < KB(4), "Certificate too large"); + + int err; + bool exists; + + err = modem_key_mgmt_exists(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); + if (err) { + printk("Failed to check for certificates err %d\n", err); + return err; + } + + if (exists) { + /* For the sake of simplicity we delete what is provisioned + * with our security tag and reprovision our certificate. + */ + err = modem_key_mgmt_delete(TLS_SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN); + if (err) { + printk("Failed to delete existing certificate, err %d\n", + err); + } + } + + printk("Provisioning certificate\n"); + + /* Provision certificate to the modem */ + err = modem_key_mgmt_write(TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, + sizeof(cert) - 1); + if (err) { + printk("Failed to provision certificate, err %d\n", err); + return err; + } + + return 0; +} +#endif /* defined(CONFIG_USE_HTTPS) */ + +/** + * @brief Configures modem to provide LTE link. + */ +static int modem_configure_and_connect(void) +{ + BUILD_ASSERT(!IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT), + "This sample does not support auto init and connect"); + +#if !defined(CONFIG_LWM2M_CARRIER) + int err; + +#if defined(CONFIG_USE_HTTPS) + err = cert_provision(); + if (err) { + printk("Could not provision root CA to %d", TLS_SEC_TAG); + return err; + } + +#endif /* CONFIG_USE_HTTPS */ + + printk("LTE Link Connecting ...\n"); + err = lte_lc_init_and_connect_async(lte_lc_handler); + if (err) { + printk("LTE link could not be established."); + return err; + } +#endif /* !CONFIG_LWM2M_CARRIER */ + + return 0; +} + +static void fota_work_cb(struct k_work *work) +{ + int err; + + ARG_UNUSED(work); + + switch (state) { + case UPDATE_DOWNLOAD: + err = update_download(); + if (err) { + printk("Download failed, err %d\n", err); + apply_state(CONNECTED); + } + break; + case UPDATE_APPLY: +#if !defined(CONFIG_LWM2M_CARRIER) + lte_lc_deinit(); #endif + sys_reboot(SYS_REBOOT_WARM); + break; + default: + break; + } + +} + +static int shell_download(const struct shell *shell, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + if (state != CONNECTED) { + return -EPERM; + } + + apply_state(UPDATE_DOWNLOAD); + + return 0; } +SHELL_CMD_REGISTER(download, NULL, "For downloading app firmware update", shell_download); + +static int shell_apply(const struct shell *shell, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + if (state != UPDATE_PENDING) { + return -EPERM; + } + + apply_state(UPDATE_APPLY); + + return 0; +} + +SHELL_CMD_REGISTER(apply, NULL, "For applying app firmware update", shell_apply); + static void fota_dl_handler(const struct fota_download_evt *evt) { switch (evt->id) { case FOTA_DOWNLOAD_EVT_ERROR: printk("Received error from fota_download\n"); - update_sample_stop(); + apply_state(CONNECTED); break; - case FOTA_DOWNLOAD_EVT_FINISHED: - update_sample_done(); - printk("Press 'Reset' button or enter 'reset' to apply new firmware\n"); + apply_state(UPDATE_PENDING); break; - default: break; } } +static int update_download(void) +{ + int err; + const char *file; + + err = fota_download_init(fota_dl_handler); + if (err) { + printk("fota_download_init() failed, err %d\n", err); + return 0; + } + +#if CONFIG_APPLICATION_VERSION == 2 + file = CONFIG_DOWNLOAD_FILE_V1; +#else + file = CONFIG_DOWNLOAD_FILE_V2; +#endif + + /* Functions for getting the host and file */ + err = fota_download_start(CONFIG_DOWNLOAD_HOST, file, SEC_TAG, 0, 0); + if (err) { + app_dfu_btn_irq_enable(); + printk("fota_download_start() failed, err %d\n", err); + return err; + } + + return 0; +} + #if defined(CONFIG_LWM2M_CARRIER) NRF_MODEM_LIB_ON_INIT(carrier_on_modem_lib_init, on_modem_lib_init, NULL); @@ -56,21 +388,20 @@ static void on_modem_lib_init(int ret, void *ctx) { ARG_UNUSED(ctx); - if (ret != 0) { + if (ret) { + /* Modem initialization failed. */ return; } /* LTE LC is uninitialized on every modem shutdown. */ - lte_lc_init(); -} - -static void lte_event_handler(const struct lte_lc_evt *const evt) -{ - /* This event handler is not in use here. */ - ARG_UNUSED(evt); + ret = lte_lc_init(); + if (ret != 0) { + printk("failed to initialize LTE connection"); + return; + } } -void print_err(const lwm2m_carrier_event_t *evt) +static void print_err(const lwm2m_carrier_event_t *evt) { const lwm2m_carrier_event_error_t *err = evt->data.error; @@ -96,7 +427,7 @@ void print_err(const lwm2m_carrier_event_t *evt) printk("%s, reason %d\n", strerr[err->type], err->value); } -void print_deferred(const lwm2m_carrier_event_t *evt) +static void print_deferred(const lwm2m_carrier_event_t *evt) { const lwm2m_carrier_event_deferred_t *def = evt->data.deferred; @@ -134,7 +465,7 @@ int lwm2m_carrier_event_handler(const lwm2m_carrier_event_t *event) switch (event->type) { case LWM2M_CARRIER_EVENT_LTE_LINK_UP: printk("LWM2M_CARRIER_EVENT_LTE_LINK_UP\n"); - err = lte_lc_connect_async(lte_event_handler); + err = lte_lc_connect_async(lte_lc_handler); break; case LWM2M_CARRIER_EVENT_LTE_LINK_DOWN: printk("LWM2M_CARRIER_EVENT_LTE_LINK_DOWN\n"); @@ -188,32 +519,35 @@ int main(void) printk("HTTP application update sample started\n"); printk("Using version %d\n", CONFIG_APPLICATION_VERSION); + /* This is needed so that MCUBoot won't revert the update */ + boot_write_img_confirmed(); + err = nrf_modem_lib_init(); if (err < 0) { printk("Failed to initialize modem library!\n"); - return 0; + return err; } - /* This is needed so that MCUBoot won't revert the update */ - boot_write_img_confirmed(); + k_work_init(&fota_work, fota_work_cb); - err = fota_download_init(fota_dl_handler); - if (err != 0) { - printk("fota_download_init() failed, err %d\n", err); - return 0; + err = button_init(); + if (err) { + return err; } - err = update_sample_init(&(struct update_sample_init_params){ - .update_start = update_sample_start, - .num_leds = NUM_LEDS, - .filename = get_file() - }); - if (err != 0) { - printk("update_sample_init() failed, err %d\n", err); - return 0; + err = leds_init(); + if (err) { + return err; + } + + err = modem_configure_and_connect(); + if (err) { + printk("Modem configuration failed: %d\n", err); + return err; } - printk("Press Button 1 or enter 'download' to download firmware update\n"); + leds_set(NUM_LEDS); + app_dfu_btn_irq_enable(); return 0; } diff --git a/samples/cellular/http_update/common/include/update.h b/samples/cellular/http_update/common/include/update.h deleted file mode 100644 index 8bb7155dfd0f..000000000000 --- a/samples/cellular/http_update/common/include/update.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#ifndef UPDATE_H__ -#define UPDATE_H__ - -#define TLS_SEC_TAG 42 - -#ifndef CONFIG_USE_HTTPS -#define SEC_TAG (-1) -#else -#define SEC_TAG (TLS_SEC_TAG) -#endif - -/** - * @typedef update_start_cb - * - * @brief Signature for callback invoked when button is pressed. - */ -typedef void (*update_start_cb)(void); - -/** - * @brief Initialization struct. - */ -struct update_sample_init_params { - /* Callback invoked when the button is pressed */ - update_start_cb update_start; - - /* Number of LEDs to enable. Must be 0, 1 or 2. */ - int num_leds; - - /* Update file to download */ - const char *filename; -}; - -/** Initialize common update sample library. - * - * This will initialize one button with the given callback, and enable - * one or two LEDs based on the result of `params->two_leds` - * - * @param[in] params Pointer to initiazilation parameters. - * - * @return non-negative on success, negative errno code on fail - */ -int update_sample_init(struct update_sample_init_params *params); - -/** Begin downloading specified FOTA update - */ -void update_sample_start(void); - -/** Notify the library that the update has been stopped. - * - * This will re-enable the button. Making it possible to - * restart or continue the update. - */ -void update_sample_stop(void); - -/** Notify the library that the update has been completed. - * - * This will send the modem to power off mode, while waiting - * for a reset. - */ -void update_sample_done(void); - -#endif /* UPDATE_H__ */ diff --git a/samples/cellular/http_update/common/src/update.c b/samples/cellular/http_update/common/src/update.c deleted file mode 100644 index 0ba61f44fcfd..000000000000 --- a/samples/cellular/http_update/common/src/update.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2020 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "update.h" - -static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); -static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); -static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); -static struct gpio_callback sw0_cb; -static struct k_work fota_work; -static update_start_cb update_start; -static char filename[128] = {0}; - -int cert_provision(void) -{ - static const char cert[] = { - #include "../cert/AmazonRootCA1" - }; - BUILD_ASSERT(sizeof(cert) < KB(4), "Certificate too large"); - - int err; - bool exists; - - err = modem_key_mgmt_exists(TLS_SEC_TAG, - MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); - if (err) { - printk("Failed to check for certificates err %d\n", err); - return err; - } - - if (exists) { - /* For the sake of simplicity we delete what is provisioned - * with our security tag and reprovision our certificate. - */ - err = modem_key_mgmt_delete(TLS_SEC_TAG, - MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN); - if (err) { - printk("Failed to delete existing certificate, err %d\n", - err); - } - } - - printk("Provisioning certificate\n"); - - /* Provision certificate to the modem */ - err = modem_key_mgmt_write(TLS_SEC_TAG, - MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, - sizeof(cert) - 1); - if (err) { - printk("Failed to provision certificate, err %d\n", err); - return err; - } - - return 0; -} - -static int led_init(int num_leds) -{ - switch (num_leds) { - case 0: - return 0; - case 2: - if (!device_is_ready(led1.port)) { - return -ENODEV; - } - - gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE); - __fallthrough; - case 1: - if (!device_is_ready(led0.port)) { - return -ENODEV; - } - - gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); - return 0; - default: - return -EINVAL; - } -} - -static void button_irq_disable(void) -{ - gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_DISABLE); -} - -static void button_irq_enable(void) -{ - gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_EDGE_TO_ACTIVE); -} - -void dfu_button_pressed(const struct device *gpiob, struct gpio_callback *cb, - uint32_t pins) -{ - k_work_submit(&fota_work); - button_irq_disable(); -} - -static int button_init(void) -{ - int err; - - if (!device_is_ready(sw0.port)) { - printk("SW0 GPIO port device not ready\n"); - return -ENODEV; - } - - err = gpio_pin_configure_dt(&sw0, GPIO_INPUT); - if (err < 0) { - return err; - } - - gpio_init_callback(&sw0_cb, dfu_button_pressed, BIT(sw0.pin)); - - err = gpio_add_callback(sw0.port, &sw0_cb); - if (err < 0) { - printk("Unable to configure SW0 GPIO pin!\n"); - return err; - } - - button_irq_enable(); - - return 0; -} - -/**@brief Configures modem to provide LTE link. - * - * Blocks until link is successfully established. - */ -static void modem_configure(void) -{ -#if defined(CONFIG_LTE_LINK_CONTROL) - BUILD_ASSERT(!IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT), - "This sample does not support auto init and connect"); - int err; - -#if defined(CONFIG_USE_HTTPS) - err = cert_provision(); - __ASSERT(err == 0, "Could not provision root CA to %d", TLS_SEC_TAG); -#endif /* CONFIG_USE_HTTPS */ - - printk("LTE Link Connecting ...\n"); - err = lte_lc_init_and_connect(); - __ASSERT(err == 0, "LTE link could not be established."); - printk("LTE Link Connected!\n"); -#endif /* CONFIG_LTE_LINK_CONTROL */ -} - -static void fota_work_cb(struct k_work *work) -{ - ARG_UNUSED(work); - - if (update_start != NULL) { - update_start(); - } -} - -static int shell_download(const struct shell *shell, size_t argc, char **argv) -{ - ARG_UNUSED(argc); - ARG_UNUSED(argv); - - update_sample_start(); - - return 0; -} - -static int shell_reboot(const struct shell *shell, size_t argc, char **argv) -{ - ARG_UNUSED(argc); - ARG_UNUSED(argv); - - shell_print(shell, "Device will now reboot"); - sys_reboot(SYS_REBOOT_WARM); - - return 0; -} - -SHELL_CMD_REGISTER(reset, NULL, "For rebooting device", shell_reboot); -SHELL_CMD_REGISTER(download, NULL, "For downloading modem firmware", shell_download); - -int update_sample_init(struct update_sample_init_params *params) -{ - int err; - - if (params == NULL || params->update_start == NULL || params->filename == NULL) { - return -EINVAL; - } - - k_work_init(&fota_work, fota_work_cb); - - update_start = params->update_start; - strcpy(filename, params->filename); - - modem_configure(); - - err = button_init(); - if (err != 0) { - return err; - } - - err = led_init(params->num_leds); - if (err != 0) { - return err; - } - - return 0; -} - -void update_sample_start(void) -{ - int err; - - /* Functions for getting the host and file */ - err = fota_download_start(CONFIG_DOWNLOAD_HOST, filename, SEC_TAG, 0, 0); - if (err != 0) { - update_sample_stop(); - printk("fota_download_start() failed, err %d\n", err); - } -} - -void update_sample_stop(void) -{ - button_irq_enable(); -} - -void update_sample_done(void) -{ -#if !defined(CONFIG_LWM2M_CARRIER) - lte_lc_deinit(); -#endif -} diff --git a/samples/cellular/http_update/full_modem_update/boards/nrf9160dk_nrf9160_ns.conf b/samples/cellular/http_update/full_modem_update/boards/nrf9160dk_nrf9160_ns.conf deleted file mode 100644 index 0856d9cd18e1..000000000000 --- a/samples/cellular/http_update/full_modem_update/boards/nrf9160dk_nrf9160_ns.conf +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -CONFIG_SPI=y -CONFIG_SPI_NOR=y diff --git a/samples/cellular/http_update/full_modem_update/src/main.c b/samples/cellular/http_update/full_modem_update/src/main.c deleted file mode 100644 index 21955094ce60..000000000000 --- a/samples/cellular/http_update/full_modem_update/src/main.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2021 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* We assume that modem version strings (not UUID) will not be more than this */ -#define MAX_MODEM_VERSION_LEN 256 - -static struct k_work fmfu_work; -static const struct gpio_dt_spec sw1 = GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios); -static struct gpio_callback sw1_cb; -static const struct device *flash_dev = DEVICE_DT_GET_ONE(jedec_spi_nor); -static char modem_version[MAX_MODEM_VERSION_LEN]; - -/* Buffer used as temporary storage when downloading the modem firmware, and - * when loading the modem firmware from external flash to the modem. - */ -#define FMFU_BUF_SIZE (0x1000) -static uint8_t fmfu_buf[FMFU_BUF_SIZE]; - -static void fmfu_button_irq_disable(void) -{ - gpio_pin_interrupt_configure_dt(&sw1, GPIO_INT_DISABLE); -} - -static void fmfu_button_irq_enable(void) -{ - gpio_pin_interrupt_configure_dt(&sw1, GPIO_INT_EDGE_TO_ACTIVE); -} - -void fmfu_button_pressed(const struct device *gpiob, struct gpio_callback *cb, - uint32_t pins) -{ - k_work_submit(&fmfu_work); - fmfu_button_irq_disable(); -} - -static void apply_fmfu_from_ext_flash(bool valid_init) -{ - int err; - - printk("Applying full modem firmware update from external flash\n"); - - if (valid_init) { - err = nrf_modem_lib_shutdown(); - if (err != 0) { - printk("nrf_modem_lib_shutdown() failed: %d\n", err); - return; - } - } - - err = nrf_modem_lib_bootloader_init(); - if (err != 0) { - printk("nrf_modem_lib_bootloader_init() failed: %d\n", err); - return; - } - - err = fmfu_fdev_load(fmfu_buf, sizeof(fmfu_buf), flash_dev, 0); - if (err != 0) { - printk("fmfu_fdev_load failed: %d\n", err); - return; - } - - err = nrf_modem_lib_shutdown(); - if (err != 0) { - printk("nrf_modem_lib_shutdown() failed: %d\n", err); - return; - } - - err = nrf_modem_lib_init(); - if (err != 0) { - printk("nrf_modem_lib_init() failed: %d\n", err); - return; - } - - printk("Modem firmware update completed.\n"); - printk("Press 'Reset' button or enter 'reset' to apply new firmware\n"); -} - -static void fmfu_work_cb(struct k_work *work) -{ - ARG_UNUSED(work); - - apply_fmfu_from_ext_flash(true); - - modem_info_string_get(MODEM_INFO_FW_VERSION, modem_version, - MODEM_INFO_MAX_RESPONSE_SIZE); - printk("Current modem firmware version: %s\n", modem_version); -} - -static int button_init(void) -{ - int err; - - if (!device_is_ready(sw1.port)) { - printk("SW1 GPIO port not ready\n"); - return -ENODEV; - } - - err = gpio_pin_configure_dt(&sw1, GPIO_INPUT); - if (err < 0) { - return err; - } - - gpio_init_callback(&sw1_cb, fmfu_button_pressed, BIT(sw1.pin)); - err = gpio_add_callback(sw1.port, &sw1_cb); - if (err < 0) { - printk("Unable to configure SW1 GPIO pin!\n"); - return err; - } - - fmfu_button_irq_enable(); - - return 0; -} - -static bool current_version_is_0(void) -{ - return strncmp(modem_version, CONFIG_DOWNLOAD_MODEM_0_VERSION, - strlen(CONFIG_DOWNLOAD_MODEM_0_VERSION)) == 0; -} - -static const char *get_file(void) -{ - const char *file = CONFIG_DOWNLOAD_MODEM_0_FILE; - - if (current_version_is_0()) { - file = CONFIG_DOWNLOAD_MODEM_1_FILE; - } - - return file; -} - -void fota_dl_handler(const struct fota_download_evt *evt) -{ - switch (evt->id) { - case FOTA_DOWNLOAD_EVT_ERROR: - printk("Received error from fota_download\n"); - /* Fallthrough */ - case FOTA_DOWNLOAD_EVT_FINISHED: - apply_fmfu_from_ext_flash(true); - break; - - default: - break; - } -} - -void update_start(void) -{ - int err; - - /* Functions for getting the host and file */ - err = fota_download_start(CONFIG_DOWNLOAD_HOST, get_file(), SEC_TAG, - 0, 0); - if (err != 0) { - update_sample_stop(); - printk("fota_download_start() failed, err %d\n", err); - } -} - -static int num_leds(void) -{ - return current_version_is_0() ? 1 : 2; -} - -static int shell_flash(const struct shell *shell, size_t argc, char **argv) -{ - ARG_UNUSED(argc); - ARG_UNUSED(argv); - - k_work_submit(&fmfu_work); - fmfu_button_irq_disable(); - - return 0; -} - -SHELL_CMD_REGISTER(flash, NULL, "For rebooting device", shell_flash); - -int main(void) -{ - int err; - - printk("HTTP full modem update sample started\n"); - - if (!device_is_ready(flash_dev)) { - printk("Flash device %s not ready\n", flash_dev->name); - return 0; - } - - err = nrf_modem_lib_init(); - if (err) { - printk("Failed to initialize modem lib, err: %d\n", err); - printk("This could indicate that an earlier update failed\n"); - printk("Trying to apply modem update from ext flash\n"); - apply_fmfu_from_ext_flash(false); - } - - k_work_init(&fmfu_work, fmfu_work_cb); - - err = button_init(); - if (err != 0) { - printk("button_init() failed: %d\n", err); - return 0; - } - - err = fota_download_init(fota_dl_handler); - if (err != 0) { - printk("fota_download_init() failed, err %d\n", err); - return 0; - } - - const struct dfu_target_full_modem_params params = { - .buf = fmfu_buf, - .len = sizeof(fmfu_buf), - .dev = &(struct dfu_target_fmfu_fdev){ .dev = flash_dev, - .offset = 0, - .size = 0 } - }; - - err = dfu_target_full_modem_cfg(¶ms); - if (err != 0) { - printk("dfu_target_full_modem_cfg failed: %d\n", err); - return 0; - } - - err = modem_info_init(); - if (err != 0) { - printk("modem_info_init failed: %d\n", err); - return 0; - } - - modem_info_string_get(MODEM_INFO_FW_VERSION, modem_version, - MODEM_INFO_MAX_RESPONSE_SIZE); - printk("Current modem firmware version: %s\n", modem_version); - - err = update_sample_init(&(struct update_sample_init_params){ - .update_start = update_start, - .num_leds = num_leds(), - .filename = get_file() - }); - if (err != 0) { - printk("update_sample_init() failed, err %d\n", err); - return 0; - } - - printk("Press Button 1 or enter 'download' to download firmware update\n"); - printk("Press Button 2 or enter 'flash' to apply modem firmware update from flash\n"); - - return 0; -} diff --git a/samples/cellular/http_update/modem_delta_update/CMakeLists.txt b/samples/cellular/http_update/modem_delta_update/CMakeLists.txt index 69c4ab6a0bea..e4f3002d5dc8 100644 --- a/samples/cellular/http_update/modem_delta_update/CMakeLists.txt +++ b/samples/cellular/http_update/modem_delta_update/CMakeLists.txt @@ -11,6 +11,4 @@ project(modem_delta_update) # NORDIC SDK APP START target_sources(app PRIVATE src/main.c) -target_sources(app PRIVATE ../common/src/update.c) -target_include_directories(app PRIVATE ../common/include) # NORDIC SDK APP END diff --git a/samples/cellular/http_update/modem_delta_update/README.rst b/samples/cellular/http_update/modem_delta_update/README.rst index 984436ae249d..2aa283310cc6 100644 --- a/samples/cellular/http_update/modem_delta_update/README.rst +++ b/samples/cellular/http_update/modem_delta_update/README.rst @@ -15,7 +15,7 @@ The sample uses the :ref:`lib_fota_download` library to download a file from a r Requirements ************ -The sample supports the following development kit: +The sample supports the following development kits: .. table-from-sample-yaml:: @@ -45,8 +45,10 @@ Testing After programming the sample to your development kit,, test it by performing the following steps: 1. Note the LED pattern (1 or 2 LEDs). -#. Press **Button 1** on the nRF9160 DK or type "download" in the terminal emulator to start the download process of the delta to the alternative firmware version. -#. Once the download has been completed, follow the reboot instructions printed to the UART console. +#. Press **Button 1** on the nRF91 Series DK or type "download" in the terminal emulator to start downloading the delta modem firmware update. + Wait for the download to complete. +#. Press **Button 1** on the nRF91 Series DK or type "apply" in the terminal emulator to apply the delta modem firmware update. + Once the modem update procedure is complete, the modem is reinitialized to run the new firmware. #. Observe that the LED pattern has changed (1 vs 2). #. Start over from point 1, to perform the delta update back to the previous version. diff --git a/samples/cellular/http_update/modem_delta_update/boards/nrf9160dk_nrf9160_ns.conf b/samples/cellular/http_update/modem_delta_update/boards/nrf9160dk_nrf9160_ns.conf new file mode 100644 index 000000000000..b72d0626cc99 --- /dev/null +++ b/samples/cellular/http_update/modem_delta_update/boards/nrf9160dk_nrf9160_ns.conf @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Modem firmware version configuration +CONFIG_SUPPORTED_BASE_VERSION="mfw_nrf9160_1.3.5" +CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST="mfw_nrf9160_update_from_1.3.5_to_1.3.5-FOTA-TEST.bin" +CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE="mfw_nrf9160_update_from_1.3.5-FOTA-TEST_to_1.3.5.bin" diff --git a/samples/cellular/http_update/modem_delta_update/boards/nrf9161dk_nrf9161_ns.conf b/samples/cellular/http_update/modem_delta_update/boards/nrf9161dk_nrf9161_ns.conf new file mode 100644 index 000000000000..d12c0ebc6f74 --- /dev/null +++ b/samples/cellular/http_update/modem_delta_update/boards/nrf9161dk_nrf9161_ns.conf @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Modem firmware version configuration +CONFIG_SUPPORTED_BASE_VERSION="mfw_nrf91x1_2.0.0-77" +CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST="mfw_nrf91x1_update_from_2.0.0-77.beta_to_2.0.0-77.beta-FOTA-TEST.bin" +CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE="mfw_nrf91x1_update_from_2.0.0-77.beta-FOTA-TEST_to_2.0.0-77.beta.bin" diff --git a/samples/cellular/http_update/modem_delta_update/cert/AmazonRootCA1 b/samples/cellular/http_update/modem_delta_update/cert/AmazonRootCA1 new file mode 100644 index 000000000000..262bf3d6e12e --- /dev/null +++ b/samples/cellular/http_update/modem_delta_update/cert/AmazonRootCA1 @@ -0,0 +1,20 @@ +"-----BEGIN CERTIFICATE-----\n" +"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" +"ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" +"b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" +"MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" +"b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" +"ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" +"9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" +"IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" +"VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" +"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" +"jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" +"AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" +"A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" +"U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" +"N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" +"o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" +"5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" +"rqXRfboQnoZsG4q5WTP468SQvvG5\n" +"-----END CERTIFICATE-----\n" diff --git a/samples/cellular/http_update/modem_delta_update/prj.conf b/samples/cellular/http_update/modem_delta_update/prj.conf index bed3e1f0946f..692e4f7450bb 100644 --- a/samples/cellular/http_update/modem_delta_update/prj.conf +++ b/samples/cellular/http_update/modem_delta_update/prj.conf @@ -47,8 +47,8 @@ CONFIG_DFU_TARGET=y # Modem key management CONFIG_MODEM_KEY_MGMT=y -# Sample configuration +# Modem info is used to read the current version +CONFIG_MODEM_INFO=y + +# Download host CONFIG_DOWNLOAD_HOST="nrfconnectsdk.s3.eu-central-1.amazonaws.com" -CONFIG_SUPPORTED_BASE_VERSION="mfw_nrf9160_1.3.4" -CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST="mfw_nrf9160_update_from_1.3.4_to_1.3.4-FOTA-TEST.bin" -CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE="mfw_nrf9160_update_from_1.3.4-FOTA-TEST_to_1.3.4.bin" diff --git a/samples/cellular/http_update/modem_delta_update/sample.yaml b/samples/cellular/http_update/modem_delta_update/sample.yaml index 86aa23af81f9..df9c1c9cddbe 100644 --- a/samples/cellular/http_update/modem_delta_update/sample.yaml +++ b/samples/cellular/http_update/modem_delta_update/sample.yaml @@ -5,5 +5,6 @@ tests: build_only: true integration_platforms: - nrf9160dk_nrf9160_ns - platform_allow: nrf9160dk_nrf9160_ns + - nrf9161dk_nrf9161_ns + platform_allow: nrf9160dk_nrf9160_ns nrf9161dk_nrf9161_ns tags: ci_build diff --git a/samples/cellular/http_update/modem_delta_update/src/main.c b/samples/cellular/http_update/modem_delta_update/src/main.c index d808d4886844..240580297a25 100644 --- a/samples/cellular/http_update/modem_delta_update/src/main.c +++ b/samples/cellular/http_update/modem_delta_update/src/main.c @@ -1,53 +1,197 @@ /* - * Copyright (c) 2020 Nordic Semiconductor ASA + * Copyright (c) 2020-2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ -#include #include #include +#include +#include +#include +#include +#include +#include +#include + +#include #include +#include +#include +#include +#include +#include #include #include -#include -#include "update.h" #define FOTA_TEST "FOTA-TEST" -static char version[256]; +#define TLS_SEC_TAG 42 + +#ifdef CONFIG_USE_HTTPS +#define SEC_TAG (TLS_SEC_TAG) +#else +#define SEC_TAG (-1) +#endif + +/* We assume that modem version strings (not UUID) will not be more than this */ +#define MAX_MODEM_VERSION_LEN 256 +static char modem_version[MAX_MODEM_VERSION_LEN]; + +enum fota_state { IDLE, CONNECTED, UPDATE_DOWNLOAD, UPDATE_PENDING, UPDATE_APPLY, ERROR }; +static volatile enum fota_state state = IDLE; + +static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); +static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); +static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); +static struct gpio_callback sw0_cb; +static struct k_work fota_work; + +BUILD_ASSERT(strlen(CONFIG_SUPPORTED_BASE_VERSION), "CONFIG_SUPPORTED_BASE_VERSION not set"); +BUILD_ASSERT(strlen(CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE), + "CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE not set"); +BUILD_ASSERT(strlen(CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST), + "CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST not set"); + +static int apply_state(enum fota_state new_state); static bool is_test_firmware(void) { - static bool version_read; - int err; + return strstr(modem_version, FOTA_TEST) != NULL; +} + +static void lte_lc_handler(const struct lte_lc_evt *const evt) +{ + static bool connected; + + switch (evt->type) { + case LTE_LC_EVT_NW_REG_STATUS: + if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) && + (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) { + if (!connected) { + break; + } - if (!version_read) { - err = nrf_modem_at_cmd(version, sizeof(version), "AT+CGMR"); - if (err != 0) { - printk("Unable to read modem version: %d\n", err); - return false; + printk("LTE network is disconnected.\n"); + connected = false; + if (state == CONNECTED) { + apply_state(IDLE); + } + break; } - if (strstr(version, CONFIG_SUPPORTED_BASE_VERSION) == NULL) { - printk("Unsupported base modem version: %s\n", version); - printk("Supported base version (set in prj.conf): %s\n", - CONFIG_SUPPORTED_BASE_VERSION); - return false; + connected = true; + + if (state == IDLE) { + printk("LTE Link Connected!\n"); + apply_state(CONNECTED); } + break; + default: + break; + } +} - version_read = true; +static int leds_init(void) +{ + if (!device_is_ready(led0.port)) { + printk("Led0 GPIO port not ready\n"); + return -ENODEV; + } + + if (!device_is_ready(led1.port)) { + printk("Led1 GPIO port not ready\n"); + return -ENODEV; } - return strstr(version, FOTA_TEST) != NULL; + return 0; } -static const char *get_file(void) +static int leds_set(int num) { - if (is_test_firmware()) { - return CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE; - } else { - return CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST; + switch (num) { + case 0: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE); + break; + case 1: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE); + break; + case 2: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE); + break; + default: + return -EINVAL; + } + return 0; +} + +static void dfu_button_irq_disable(void) +{ + gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_DISABLE); +} + +static void dfu_button_irq_enable(void) +{ + gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_EDGE_TO_ACTIVE); +} + +static void dfu_button_pressed(const struct device *gpiob, struct gpio_callback *cb, + uint32_t pins) +{ + switch (state) { + case CONNECTED: + apply_state(UPDATE_DOWNLOAD); + break; + case UPDATE_PENDING: + apply_state(UPDATE_APPLY); + break; + default: + break; + } +} + +static int button_init(void) +{ + int err; + + if (!device_is_ready(sw0.port)) { + printk("SW0 GPIO port device not ready\n"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&sw0, GPIO_INPUT); + if (err < 0) { + return err; + } + + gpio_init_callback(&sw0_cb, dfu_button_pressed, BIT(sw0.pin)); + + err = gpio_add_callback(sw0.port, &sw0_cb); + if (err < 0) { + printk("Unable to configure SW0 GPIO pin!\n"); + return err; + } + + return 0; +} + +static void current_version_display(void) +{ + int err; + int num_leds; + + err = modem_info_string_get(MODEM_INFO_FW_VERSION, modem_version, + MAX_MODEM_VERSION_LEN); + if (err < 0) { + printk("Failed to get modem version\n"); + return; } + + num_leds = is_test_firmware() ? 1 : 2; + leds_set(num_leds); + printk("Current modem firmware version: %s\n", modem_version); } void fota_dl_handler(const struct fota_download_evt *evt) @@ -55,12 +199,11 @@ void fota_dl_handler(const struct fota_download_evt *evt) switch (evt->id) { case FOTA_DOWNLOAD_EVT_ERROR: printk("Received error from fota_download\n"); - update_sample_stop(); + apply_state(CONNECTED); break; case FOTA_DOWNLOAD_EVT_FINISHED: - update_sample_done(); - printk("Press 'Reset' button or enter 'reset' to apply new firmware\n"); + apply_state(UPDATE_PENDING); break; default: @@ -68,25 +211,241 @@ void fota_dl_handler(const struct fota_download_evt *evt) } } -static int num_leds(void) +#if defined(CONFIG_USE_HTTPS) +static int cert_provision(void) +{ + static const char cert[] = { + #include "../cert/AmazonRootCA1" + }; + BUILD_ASSERT(sizeof(cert) < KB(4), "Certificate too large"); + + int err; + bool exists; + + err = modem_key_mgmt_exists(TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); + if (err) { + printk("Failed to check for certificates err %d\n", err); + return err; + } + + if (exists) { + /* For the sake of simplicity we delete what is provisioned + * with our security tag and reprovision our certificate. + */ + err = modem_key_mgmt_delete(TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN); + if (err) { + printk("Failed to delete existing certificate, err %d\n", + err); + } + } + + printk("Provisioning certificate\n"); + + /* Provision certificate to the modem */ + err = modem_key_mgmt_write(TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, + sizeof(cert) - 1); + if (err) { + printk("Failed to provision certificate, err %d\n", err); + return err; + } + + return 0; +} +#endif /* CONFIG_USE_HTTPS */ + +/** + * @brief Configures modem to provide LTE link. + */ +static int modem_configure_and_connect(void) +{ + BUILD_ASSERT(!IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT), + "This sample does not support auto init and connect"); + int err; + +#if defined(CONFIG_USE_HTTPS) + err = cert_provision(); + if (err) { + printk("Could not provision root CA to %d", TLS_SEC_TAG); + return err; + } + +#endif /* CONFIG_USE_HTTPS */ + + printk("LTE Link Connecting ...\n"); + err = lte_lc_init_and_connect_async(lte_lc_handler); + if (err) { + printk("LTE link could not be established."); + return err; + } + + return 0; +} + +static int update_download(void) { + int err; + const char *file; + + err = modem_info_string_get(MODEM_INFO_FW_VERSION, modem_version, + MAX_MODEM_VERSION_LEN); + if (err < 0) { + printk("Failed to get modem version\n"); + return false; + } + if (is_test_firmware()) { - return 2; + file = CONFIG_DOWNLOAD_FILE_FOTA_TEST_TO_BASE; } else { - return 1; + file = CONFIG_DOWNLOAD_FILE_BASE_TO_FOTA_TEST; + } + + err = fota_download_init(fota_dl_handler); + if (err) { + printk("fota_download_init() failed, err %d\n", err); + return err; + } + + /* Functions for getting the host and file */ + err = fota_download_start(CONFIG_DOWNLOAD_HOST, file, SEC_TAG, 0, 0); + if (err) { + printk("fota_download_start() failed, err %d\n", err); + return err; + } + + return 0; +} + +static int apply_state(enum fota_state new_state) +{ + __ASSERT(state != new_state, "State already set: %d", state); + + state = new_state; + + switch (state) { + case IDLE: + dfu_button_irq_disable(); + modem_configure_and_connect(); + break; + case CONNECTED: + dfu_button_irq_enable(); + printk("Press Button 1 or enter 'download' to download firmware update\n"); + break; + case UPDATE_DOWNLOAD: + dfu_button_irq_disable(); + k_work_submit(&fota_work); + break; + case UPDATE_PENDING: + dfu_button_irq_enable(); + printk("Press Button 1 or enter 'apply' to apply the firmware update\n"); + break; + case UPDATE_APPLY: + dfu_button_irq_disable(); + k_work_submit(&fota_work); + break; + case ERROR: + break; + } + + return 0; +} + +static int shell_download(const struct shell *shell, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + if (state != CONNECTED) { + printk("Not allowed\n"); + return -EPERM; + } + + apply_state(UPDATE_DOWNLOAD); + + return 0; +} + +SHELL_CMD_REGISTER(download, NULL, "Download modem firmware", shell_download); + +static int shell_apply(const struct shell *shell, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + if (state != UPDATE_PENDING) { + printk("Not allowed\n"); + return -EPERM; + } + + apply_state(UPDATE_APPLY); + + return 0; +} + +SHELL_CMD_REGISTER(apply, NULL, "Apply modem firmware", shell_apply); + +static void fota_work_cb(struct k_work *work) +{ + int err; + + ARG_UNUSED(work); + + switch (state) { + case UPDATE_DOWNLOAD: + err = update_download(); + if (err) { + apply_state(CONNECTED); + } + break; + case UPDATE_APPLY: + printk("Applying firmware update. This can take a while.\n"); + lte_lc_deinit(); + /* Re-initialize the modem to apply the update. */ + err = nrf_modem_lib_shutdown(); + if (err) { + printk("Failed to shutdown modem, err %d\n", err); + } + + err = nrf_modem_lib_init(); + if (err) { + printk("Modem initialization failed, err %d\n", err); + switch (err) { + case -NRF_EPERM: + printk("Modem is already initialized"); + break; + case -NRF_EAGAIN: + printk("Modem update failed due to low voltage. Try again later\n"); + apply_state(UPDATE_PENDING); + return; + default: + printk("Reprogram the full modem firmware to recover\n"); + apply_state(ERROR); + return; + } + } + + current_version_display(); + + apply_state(IDLE); + break; + default: + break; } } -static void on_modem_lib_dfu(int32_t dfu_res, void *ctx) +static void on_modem_lib_dfu(int dfu_res, void *ctx) { switch (dfu_res) { case NRF_MODEM_DFU_RESULT_OK: - printk("Modem firmware updated\n"); + printk("Modem firmware update successful!\n"); + printk("Modem is initializing with the new firmware\n"); break; case NRF_MODEM_DFU_RESULT_UUID_ERROR: case NRF_MODEM_DFU_RESULT_AUTH_ERROR: printk("Modem firmware update failed\n"); - printk("Modem is running the old firmware.\n"); + printk("Modem is initializing with the previous firmware\n"); break; case NRF_MODEM_DFU_RESULT_HARDWARE_ERROR: case NRF_MODEM_DFU_RESULT_INTERNAL_ERROR: @@ -98,6 +457,7 @@ static void on_modem_lib_dfu(int32_t dfu_res, void *ctx) printk("Please reboot once you have sufficient power for the DFU.\n"); break; default: + printk("Unknown error.\n"); break; } } @@ -110,35 +470,64 @@ int main(void) printk("HTTP delta modem update sample started\n"); + k_work_init(&fota_work, fota_work_cb); + + err = button_init(); + if (err) { + return err; + } + + err = leds_init(); + if (err) { + return err; + } + printk("Initializing modem library\n"); err = nrf_modem_lib_init(); if (err) { - printk("Could not initialize momdem library, err %d\n", err); - return 0; + printk("Modem library initialization failed, err %d\n", err); + switch (err) { + case -NRF_EPERM: + printk("Modem is already initialized"); + break; + case -NRF_EAGAIN: + printk("Modem update failed due to low voltage. Try again later\n"); + apply_state(UPDATE_PENDING); + return 0; + default: + printk("Reprogram the full modem firmware to recover\n"); + apply_state(ERROR); + return err; + } } printk("Initialized modem library\n"); - err = fota_download_init(fota_dl_handler); - if (err != 0) { - printk("fota_download_init() failed, err %d\n", err); - return 0; + err = modem_info_init(); + if (err) { + printk("modem_info_init failed: %d\n", err); + return err; } - err = update_sample_init(&(struct update_sample_init_params){ - .update_start = update_sample_start, - .num_leds = num_leds(), - .filename = get_file() - }); - if (err != 0) { - printk("update_sample_init() failed, err %d\n", err); - return 0; + current_version_display(); + + if (strstr(modem_version, CONFIG_SUPPORTED_BASE_VERSION) == NULL) { + printk("Unsupported base modem version: %s\n", modem_version); + printk("Supported base version (set in prj.conf): %s\n", + CONFIG_SUPPORTED_BASE_VERSION); + return -EINVAL; } - printk("Current modem firmware version: %s\n", version); + err = modem_configure_and_connect(); + if (err) { + printk("Modem configuration failed: %d\n", err); + return err; + } - printk("Press Button 1 for enter 'download' to download modem delta update\n"); + while (1) { + k_sleep(K_FOREVER); + } return 0; } diff --git a/samples/cellular/http_update/full_modem_update/CMakeLists.txt b/samples/cellular/http_update/modem_full_update/CMakeLists.txt similarity index 74% rename from samples/cellular/http_update/full_modem_update/CMakeLists.txt rename to samples/cellular/http_update/modem_full_update/CMakeLists.txt index 332bbc69b594..c47698debaf8 100644 --- a/samples/cellular/http_update/full_modem_update/CMakeLists.txt +++ b/samples/cellular/http_update/modem_full_update/CMakeLists.txt @@ -11,6 +11,4 @@ project(full_modem_update) # NORDIC SDK APP START target_sources(app PRIVATE src/main.c) -target_sources(app PRIVATE ../common/src/update.c) -target_include_directories(app PRIVATE ../common/include) # NORDIC SDK APP END diff --git a/samples/cellular/http_update/full_modem_update/Kconfig b/samples/cellular/http_update/modem_full_update/Kconfig similarity index 100% rename from samples/cellular/http_update/full_modem_update/Kconfig rename to samples/cellular/http_update/modem_full_update/Kconfig diff --git a/samples/cellular/http_update/full_modem_update/README.rst b/samples/cellular/http_update/modem_full_update/README.rst similarity index 80% rename from samples/cellular/http_update/full_modem_update/README.rst rename to samples/cellular/http_update/modem_full_update/README.rst index 282eb50ecfa7..520690a1fc08 100644 --- a/samples/cellular/http_update/full_modem_update/README.rst +++ b/samples/cellular/http_update/modem_full_update/README.rst @@ -1,4 +1,4 @@ -.. _http_full_modem_update_sample: +.. _http_modem_full_update_sample: Cellular: HTTP full modem update ################################ @@ -13,7 +13,7 @@ The sample downloads the modem firmware signed by Nordic Semiconductor and updat Requirements ************ -The sample supports the following development kit, version 0.14.0 or higher: +The sample supports the following development kits: .. table-from-sample-yaml:: @@ -25,7 +25,8 @@ Overview ******** An |external_flash_size| of free space is required to perform a full modem update. -Hence, only versions 0.14.0 and later of the nrf9160 DK support this sample as the earlier versions do not have any external flash memory. +Hence, for the nRF9160 DK, version 0.14.0 or higher is supported by the sample as the earlier versions do not have any external flash memory. +The nRF9161 DK has external flash memory. The sample proceeds as follows: @@ -34,8 +35,8 @@ The sample proceeds as follows: #. It prevalidates the update if the firmware supports the prevalidation process. #. It then programs the update to the modem, using the :ref:`lib_fmfu_fdev` library. -The current version of this sample downloads two different versions of the firmware, namely 1.3.3 and 1.3.4. -The sample then selects the version which is currently not installed. +The current version of this sample downloads two different versions of the firmware. +The sample then selects the version to install based on the currently installed version. Configuration ************* @@ -88,9 +89,10 @@ Testing |test_sample| 1. Start the application and wait for a prompt for pressing a button. -#. Press the button or type "download" in the terminal emulator to start the update procedure. -#. Once the download has completed, the modem update procedure begins automatically. -#. Press the **Reset** button or type "reset" in the terminal emulator to reset the development kit. +#. Press the **Button 1** button or type "download" in the terminal emulator to start downloading the update. + Wait for the download to complete. +#. Press the **Button 2** button or type "apply" in the terminal emulator to apply the update. + Once the modem update procedure is complete, the modem is reinitialized to run the new firmware. #. Observe that the LED pattern has changed. Dependencies diff --git a/samples/cellular/http_update/modem_full_update/boards/nrf9160dk_nrf9160_ns.conf b/samples/cellular/http_update/modem_full_update/boards/nrf9160dk_nrf9160_ns.conf new file mode 100644 index 000000000000..62f094f93483 --- /dev/null +++ b/samples/cellular/http_update/modem_full_update/boards/nrf9160dk_nrf9160_ns.conf @@ -0,0 +1,13 @@ +# Copyright (c) 2021-2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SPI=y +CONFIG_SPI_NOR=y + +# Modem firmware version configuration +CONFIG_DOWNLOAD_MODEM_0_FILE="fmfu_1.3.4.cbor" +CONFIG_DOWNLOAD_MODEM_0_VERSION="mfw_nrf9160_1.3.4" + +CONFIG_DOWNLOAD_MODEM_1_FILE="fmfu_1.3.5.cbor" diff --git a/samples/cellular/http_update/full_modem_update/boards/nrf9160dk_nrf9160_ns.overlay b/samples/cellular/http_update/modem_full_update/boards/nrf9160dk_nrf9160_ns.overlay similarity index 100% rename from samples/cellular/http_update/full_modem_update/boards/nrf9160dk_nrf9160_ns.overlay rename to samples/cellular/http_update/modem_full_update/boards/nrf9160dk_nrf9160_ns.overlay diff --git a/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.conf b/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.conf new file mode 100644 index 000000000000..68634b4b9097 --- /dev/null +++ b/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.conf @@ -0,0 +1,14 @@ +# Copyright (c) 2021-2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SPI=y +CONFIG_SPI_NOR=y + +# Modem firmware version configuration +CONFIG_DOWNLOAD_MODEM_0_FILE="fmfu_2.0.0-77.beta.cbor" +CONFIG_DOWNLOAD_MODEM_0_VERSION="mfw_nrf91x1_2.0.0-77" + +# There is only one released version, so it is the same +CONFIG_DOWNLOAD_MODEM_1_FILE="fmfu_2.0.0-77.beta.cbor" diff --git a/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.overlay b/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.overlay new file mode 100644 index 000000000000..478e966ee691 --- /dev/null +++ b/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { +/* Configure partition manager to use gd25wb256 as the external flash */ + chosen { + nordic,pm-ext-flash = &gd25wb256; + }; +}; + +&gd25wb256 { + status = "okay"; +}; diff --git a/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns_0_7_0.overlay b/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns_0_7_0.overlay new file mode 100644 index 000000000000..09168893d754 --- /dev/null +++ b/samples/cellular/http_update/modem_full_update/boards/nrf9161dk_nrf9161_ns_0_7_0.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/delete-node/ &gd25wb256; + +/ { + chosen { + nordic,pm-ext-flash = &gd25lb256; + }; + + aliases { + ext-flash = &gd25lb256; + }; +}; + +&gd25lb256 { + status = "okay"; +}; diff --git a/samples/cellular/http_update/modem_full_update/cert/AmazonRootCA1 b/samples/cellular/http_update/modem_full_update/cert/AmazonRootCA1 new file mode 100644 index 000000000000..262bf3d6e12e --- /dev/null +++ b/samples/cellular/http_update/modem_full_update/cert/AmazonRootCA1 @@ -0,0 +1,20 @@ +"-----BEGIN CERTIFICATE-----\n" +"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" +"ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" +"b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" +"MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" +"b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" +"ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" +"9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" +"IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" +"VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" +"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" +"jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" +"AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" +"A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" +"U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" +"N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" +"o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" +"5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" +"rqXRfboQnoZsG4q5WTP468SQvvG5\n" +"-----END CERTIFICATE-----\n" diff --git a/samples/cellular/http_update/full_modem_update/prj.conf b/samples/cellular/http_update/modem_full_update/prj.conf similarity index 92% rename from samples/cellular/http_update/full_modem_update/prj.conf rename to samples/cellular/http_update/modem_full_update/prj.conf index 38cb8bd86bd6..78b182f54532 100644 --- a/samples/cellular/http_update/full_modem_update/prj.conf +++ b/samples/cellular/http_update/modem_full_update/prj.conf @@ -80,10 +80,6 @@ CONFIG_MBEDTLS_GCM_C=n CONFIG_MBEDTLS_SHA256_C=y CONFIG_DOWNLOAD_HOST="nrfconnectsdk.s3.eu-central-1.amazonaws.com" -CONFIG_DOWNLOAD_MODEM_0_FILE="fmfu_1.3.3.cbor" -CONFIG_DOWNLOAD_MODEM_0_VERSION="mfw_nrf9160_1.3.3" - -CONFIG_DOWNLOAD_MODEM_1_FILE="fmfu_1.3.4.cbor" # Prevalidation is not yet supported by the modem firmware, so skip it for now. CONFIG_FMFU_FDEV_SKIP_PREVALIDATION=y diff --git a/samples/cellular/http_update/full_modem_update/sample.yaml b/samples/cellular/http_update/modem_full_update/sample.yaml similarity index 68% rename from samples/cellular/http_update/full_modem_update/sample.yaml rename to samples/cellular/http_update/modem_full_update/sample.yaml index 797a28411a81..5b423a4c4aee 100644 --- a/samples/cellular/http_update/full_modem_update/sample.yaml +++ b/samples/cellular/http_update/modem_full_update/sample.yaml @@ -5,5 +5,6 @@ tests: build_only: true integration_platforms: - nrf9160dk_nrf9160_ns - platform_allow: nrf9160dk_nrf9160_ns + - nrf9161dk_nrf9161_ns + platform_allow: nrf9160dk_nrf9160_ns nrf9161dk_nrf9161_ns tags: ci_build diff --git a/samples/cellular/http_update/modem_full_update/src/main.c b/samples/cellular/http_update/modem_full_update/src/main.c new file mode 100644 index 000000000000..8b514e5d7bd5 --- /dev/null +++ b/samples/cellular/http_update/modem_full_update/src/main.c @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TLS_SEC_TAG 42 + +#ifdef CONFIG_USE_HTTPS +#define SEC_TAG (TLS_SEC_TAG) +#else +#define SEC_TAG (-1) +#endif + +/* We assume that modem version strings (not UUID) will not be more than this. */ +#define MAX_MODEM_VERSION_LEN 256 +static char modem_version[MAX_MODEM_VERSION_LEN]; + +enum fota_state { IDLE, CONNECTED, UPDATE_DOWNLOAD, UPDATE_PENDING, UPDATE_APPLY, ERROR }; +static enum fota_state state = IDLE; + +static const struct device *flash_dev = DEVICE_DT_GET_ONE(jedec_spi_nor); +static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); +static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios); +static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); +static const struct gpio_dt_spec sw1 = GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios); +static struct gpio_callback sw0_cb; +static struct gpio_callback sw1_cb; +static struct k_work fota_work; + +static void fota_work_cb(struct k_work *work); +static int apply_state(enum fota_state new_state); +static int modem_configure_and_connect(void); + +/* Buffer used as temporary storage when downloading the modem firmware, and + * when loading the modem firmware from external flash to the modem. + */ +#define FMFU_BUF_SIZE (0x1000) +static uint8_t fmfu_buf[FMFU_BUF_SIZE]; + +BUILD_ASSERT(strlen(CONFIG_DOWNLOAD_MODEM_0_VERSION), "CONFIG_DOWNLOAD_MODEM_0_VERSION not set"); +BUILD_ASSERT(strlen(CONFIG_DOWNLOAD_MODEM_0_FILE), "CONFIG_DOWNLOAD_MODEM_0_FILE not set"); +BUILD_ASSERT(strlen(CONFIG_DOWNLOAD_MODEM_1_FILE), "CONFIG_DOWNLOAD_MODEM_1_FILE not set"); + +static bool current_version_is_0(void) +{ + return strncmp(modem_version, CONFIG_DOWNLOAD_MODEM_0_VERSION, + strlen(CONFIG_DOWNLOAD_MODEM_0_VERSION)) == 0; +} + +static void lte_lc_handler(const struct lte_lc_evt *const evt) +{ + static bool connected; + + switch (evt->type) { + case LTE_LC_EVT_NW_REG_STATUS: + if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) && + (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) { + if (!connected) { + break; + } + + printk("LTE network is disconnected.\n"); + connected = false; + if (state == CONNECTED) { + apply_state(IDLE); + } + break; + } + + connected = true; + + if (state == IDLE) { + printk("LTE Link Connected!\n"); + apply_state(CONNECTED); + } + break; + default: + break; + } +} + +static void dfu_download_btn_irq_enable(void) +{ + gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_EDGE_TO_ACTIVE); +} + +static void dfu_download_btn_irq_disable(void) +{ + gpio_pin_interrupt_configure_dt(&sw0, GPIO_INT_DISABLE); +} + +void dfu_download_btn_pressed(const struct device *gpiob, struct gpio_callback *cb, uint32_t pins) +{ + if (state != CONNECTED) { + return; + } + + apply_state(UPDATE_DOWNLOAD); +} + +static void dfu_apply_btn_irq_disable(void) +{ + gpio_pin_interrupt_configure_dt(&sw1, GPIO_INT_DISABLE); +} + +static void dfu_apply_btn_irq_enable(void) +{ + gpio_pin_interrupt_configure_dt(&sw1, GPIO_INT_EDGE_TO_ACTIVE); +} + +void dfu_apply_btn_pressed(const struct device *gpiob, struct gpio_callback *cb, uint32_t pins) +{ + if (state != IDLE && state != CONNECTED && state != UPDATE_PENDING) { + return; + } + + apply_state(UPDATE_APPLY); +} + +static int button_init(void) +{ + int err; + + if (!device_is_ready(sw0.port)) { + printk("SW0 GPIO port device not ready\n"); + return -ENODEV; + } + + if (!device_is_ready(sw1.port)) { + printk("SW1 GPIO port not ready\n"); + return -ENODEV; + } + + err = gpio_pin_configure_dt(&sw0, GPIO_INPUT); + if (err < 0) { + return err; + } + + err = gpio_pin_configure_dt(&sw1, GPIO_INPUT); + if (err < 0) { + return err; + } + + gpio_init_callback(&sw0_cb, dfu_download_btn_pressed, BIT(sw0.pin)); + gpio_init_callback(&sw1_cb, dfu_apply_btn_pressed, BIT(sw1.pin)); + + err = gpio_add_callback(sw0.port, &sw0_cb); + if (err < 0) { + printk("Unable to configure SW0 GPIO pin!\n"); + return err; + } + + err = gpio_add_callback(sw1.port, &sw1_cb); + if (err < 0) { + printk("Unable to configure SW1 GPIO pin!\n"); + return err; + } + + return 0; +} + +static int leds_init(void) +{ + if (!device_is_ready(led0.port)) { + printk("Led0 GPIO port not ready\n"); + return -ENODEV; + } + + if (!device_is_ready(led1.port)) { + printk("Led1 GPIO port not ready\n"); + return -ENODEV; + } + + return 0; +} + +static int leds_set(int num) +{ + switch (num) { + case 0: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE); + break; + case 1: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE); + break; + case 2: + gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE); + gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE); + break; + default: + return -EINVAL; + } + return 0; +} + +static void current_version_display(void) +{ + int err; + int num_leds; + + err = modem_info_string_get(MODEM_INFO_FW_VERSION, modem_version, + MODEM_INFO_MAX_RESPONSE_SIZE); + if (err < 0) { + printk("Failed to get modem version\n"); + return; + } + + printk("Current modem firmware version: %s\n", modem_version); + + num_leds = current_version_is_0() ? 1 : 2; + leds_set(num_leds); +} + +static int apply_state(enum fota_state new_state) +{ + __ASSERT(state != new_state, "State already set: %d", state); + + state = new_state; + + switch (new_state) { + case IDLE: + dfu_download_btn_irq_disable(); + dfu_apply_btn_irq_enable(); + modem_configure_and_connect(); + break; + case CONNECTED: + dfu_download_btn_irq_enable(); + dfu_apply_btn_irq_enable(); + printk("Press Button 1 or enter 'download' to download firmware update\n"); + printk("Press Button 2 or enter 'apply' to apply modem firmware update " + "from flash\n"); + break; + case UPDATE_DOWNLOAD: + dfu_download_btn_irq_disable(); + dfu_apply_btn_irq_disable(); + k_work_submit(&fota_work); + break; + case UPDATE_PENDING: + dfu_apply_btn_irq_enable(); + printk("Press Button 2 or enter 'apply' to apply modem firmware update " + "from flash\n"); + break; + case UPDATE_APPLY: + dfu_download_btn_irq_disable(); + dfu_apply_btn_irq_disable(); + k_work_submit(&fota_work); + break; + case ERROR: + break; + } + + return 0; +} + +static int apply_fmfu_from_ext_flash(bool valid_init) +{ + int err; + + printk("Applying full modem firmware update from external flash\n"); + + if (valid_init) { + err = nrf_modem_lib_shutdown(); + if (err != 0) { + printk("nrf_modem_lib_shutdown() failed: %d\n", err); + return err; + } + } + + err = nrf_modem_lib_bootloader_init(); + if (err != 0) { + printk("nrf_modem_lib_bootloader_init() failed: %d\n", err); + return err; + } + + err = fmfu_fdev_load(fmfu_buf, sizeof(fmfu_buf), flash_dev, 0); + if (err != 0) { + printk("fmfu_fdev_load failed: %d\n", err); + return err; + } + + err = nrf_modem_lib_shutdown(); + if (err != 0) { + printk("nrf_modem_lib_shutdown() failed: %d\n", err); + return err; + } + + err = nrf_modem_lib_init(); + if (err) { + printk("Modem library initialization failed, err %d\n", err); + switch (err) { + case -NRF_EPERM: + printk("Modem is already initialized"); + break; + default: + printk("Reprogram the full modem firmware to recover\n"); + apply_state(UPDATE_PENDING); + return err; + } + } + + printk("Modem firmware update completed.\n"); + + current_version_display(); + + return 0; +} + +#if defined(CONFIG_USE_HTTPS) +static int cert_provision(void) +{ + static const char cert[] = { + #include "../cert/AmazonRootCA1" + }; + BUILD_ASSERT(sizeof(cert) < KB(4), "Certificate too large"); + + int err; + bool exists; + + err = modem_key_mgmt_exists(TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, &exists); + if (err) { + printk("Failed to check for certificates err %d\n", err); + return err; + } + + if (exists) { + /* For the sake of simplicity we delete what is provisioned + * with our security tag and reprovision our certificate. + */ + err = modem_key_mgmt_delete(TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN); + if (err) { + printk("Failed to delete existing certificate, err %d\n", + err); + } + } + + printk("Provisioning certificate\n"); + + /* Provision certificate to the modem */ + err = modem_key_mgmt_write(TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, cert, + sizeof(cert) - 1); + if (err) { + printk("Failed to provision certificate, err %d\n", err); + return err; + } + + return 0; +} +#endif /* defined(CONFIG_USE_HTTPS) */ + +/** + * @brief Configures modem to provide LTE link. + */ +static int modem_configure_and_connect(void) +{ + BUILD_ASSERT(!IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT), + "This sample does not support auto init and connect"); + int err; + +#if defined(CONFIG_USE_HTTPS) + err = cert_provision(); + if (err) { + printk("Could not provision root CA to %d", TLS_SEC_TAG); + return err; + } + +#endif /* CONFIG_USE_HTTPS */ + + printk("LTE Link Connecting ...\n"); + err = lte_lc_init_and_connect_async(lte_lc_handler); + if (err) { + printk("LTE link could not be established."); + return err; + } + + return 0; +} + +void fota_dl_handler(const struct fota_download_evt *evt) +{ + switch (evt->id) { + case FOTA_DOWNLOAD_EVT_ERROR: + printk("Received error from fota_download\n"); + /* Fallthrough */ + case FOTA_DOWNLOAD_EVT_FINISHED: + apply_state(UPDATE_PENDING); + break; + + default: + break; + } +} + +static int update_download(void) +{ + int err; + const char *file; + + err = fota_download_init(fota_dl_handler); + if (err != 0) { + printk("fota_download_init() failed, err %d\n", err); + return err; + } + + const struct dfu_target_full_modem_params params = { + .buf = fmfu_buf, + .len = sizeof(fmfu_buf), + .dev = &(struct dfu_target_fmfu_fdev){ + .dev = flash_dev, + .offset = 0, + .size = 0 + } + }; + + err = dfu_target_full_modem_cfg(¶ms); + if (err != 0) { + printk("dfu_target_full_modem_cfg failed: %d\n", err); + return err; + } + + err = modem_info_string_get(MODEM_INFO_FW_VERSION, modem_version, + MODEM_INFO_MAX_RESPONSE_SIZE); + if (err < 0) { + printk("Failed to get modem version\n"); + return err; + } + + if (current_version_is_0()) { + file = CONFIG_DOWNLOAD_MODEM_1_FILE; + } else { + file = CONFIG_DOWNLOAD_MODEM_0_FILE; + } + + /* Functions for getting the host and file */ + err = fota_download_start(CONFIG_DOWNLOAD_HOST, file, SEC_TAG, 0, 0); + if (err != 0) { + printk("fota_download_start() failed, err %d\n", err); + return err; + } + + return 0; +} + +static int shell_download(const struct shell *shell, size_t argc, char **argv) +{ + int err; + + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + err = apply_state(UPDATE_DOWNLOAD); + if (err) { + printk("Could not start download, err %d\n", err); + return err; + } + + return 0; +} + +static int shell_apply(const struct shell *shell, size_t argc, char **argv) +{ + int err; + + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + err = apply_state(UPDATE_APPLY); + if (err) { + printk("Could not start fota, err %d\n", err); + return err; + } + + return 0; +} + +SHELL_CMD_REGISTER(download, NULL, "Download modem DFU", shell_download); +SHELL_CMD_REGISTER(apply, NULL, "Apply modem DFU", shell_apply); + +static void fota_work_cb(struct k_work *work) +{ + int err; + + ARG_UNUSED(work); + + switch (state) { + case UPDATE_DOWNLOAD: + dfu_target_full_modem_reset(); + err = update_download(); + if (err) { + printk("Download failed, err %d\n", err); + apply_state(CONNECTED); + } + break; + case UPDATE_APPLY: + err = apply_fmfu_from_ext_flash(true); + if (err) { + printk("FMFU failed, err %d\n", err); + } + + apply_state(IDLE); + break; + default: + break; + } +} + +int main(void) +{ + int err; + + printk("HTTP full modem update sample started\n"); + + if (!device_is_ready(flash_dev)) { + printk("Flash device %s not ready\n", flash_dev->name); + return -ENODEV; + } + + k_work_init(&fota_work, fota_work_cb); + + err = button_init(); + if (err != 0) { + printk("button_init() failed: %d\n", err); + return err; + } + + err = leds_init(); + if (err != 0) { + printk("leds_init() failed: %d\n", err); + return err; + } + + err = nrf_modem_lib_init(); + if (err) { + printk("Modem initialization failed, err %d\n", err); + switch (err) { + case -NRF_EPERM: + printk("Modem is already initialized"); + break; + default: + printk("Failed to initialize modem lib, err: %d\n", err); + printk("This could indicate that an earlier update failed\n"); + printk("Reprogram the full modem firmware to recover\n"); + apply_state(UPDATE_PENDING); + return err; + } + } + + err = modem_info_init(); + if (err != 0) { + printk("modem_info_init failed: %d\n", err); + return err; + } + + current_version_display(); + + err = modem_configure_and_connect(); + if (err) { + printk("Modem configuration failed: %d\n", err); + return err; + } + + dfu_apply_btn_irq_enable(); + + return 0; +} diff --git a/subsys/dfu/dfu_target/src/dfu_target_full_modem.c b/subsys/dfu/dfu_target/src/dfu_target_full_modem.c index 25e02d601d19..bb942cae2607 100644 --- a/subsys/dfu/dfu_target/src/dfu_target_full_modem.c +++ b/subsys/dfu/dfu_target/src/dfu_target_full_modem.c @@ -150,5 +150,8 @@ int dfu_target_full_modem_reset(void) if (!configured) { return -EPERM; } + + configured = false; + return dfu_target_stream_reset(); }