From a03612b64db258a31dc0eab23cbf10a76b1b9877 Mon Sep 17 00:00:00 2001 From: Marcin Gasiorek Date: Wed, 28 Aug 2024 13:51:15 +0200 Subject: [PATCH] samples: Add support for nRF54L Update NCS version and add nRF54L support. Signed-off-by: Marcin Gasiorek --- CMakeLists.txt | 2 +- README.md | 39 +- pics/board_configurator_nrf54l15_0_3_0.png | Bin 0 -> 119857 bytes samples/SWTL001/CMakeLists.txt | 34 +- samples/SWTL001/Kconfig | 57 ++- samples/SWTL001/Kconfig.sensor_monitoring | 36 ++ samples/SWTL001/Kconfig.sysbuild | 54 ++ .../boards/nrf52840dk_nrf52840.overlay | 31 ++ .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 31 ++ .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf | 13 + .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay | 107 ++++ .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf | 7 + .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay | 100 ++++ ...4l15pdk_nrf54l15_cpuapp_release_0_2_1.conf | 13 + ...4l15pdk_nrf54l15_cpuapp_release_0_3_0.conf | 7 + .../SWTL001/child_image/hci_ipc/Kconfig.root | 69 --- samples/SWTL001/child_image/hci_ipc/prj.conf | 8 - .../child_image/hci_ipc/prj_no_dfu.conf | 8 - .../child_image/hci_ipc/prj_release.conf | 9 - .../SWTL001/child_image/mcuboot/Kconfig.root | 127 ----- .../boards/nrf52840dk_nrf52840.overlay | 11 - .../nrf52840dk_nrf52840_release.overlay | 11 - .../boards/nrf5340dk_nrf5340_cpuapp.conf | 33 -- .../nrf5340dk_nrf5340_cpuapp_release.conf | 33 -- .../nrf5340dk_nrf5340_cpuapp_release.overlay | 11 - samples/SWTL001/child_image/mcuboot/prj.conf | 22 - .../child_image/mcuboot/prj_release.conf | 22 - .../configuration/pm_static_no_dfu.yml | 5 - samples/SWTL001/include/app.h | 5 + samples/SWTL001/pm_static.yml | 10 - ....yml => pm_static_nrf52840dk_nrf52840.yml} | 0 ...pm_static_nrf52840dk_nrf52840_release.yml} | 0 ...=> pm_static_nrf5340dk_nrf5340_cpuapp.yml} | 0 ...atic_nrf5340dk_nrf5340_cpuapp_release.yml} | 0 .../pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml | 71 +++ ...ic_nrf54l15pdk_nrf54l15_cpuapp_release.yml | 71 +++ ... => pm_static_thingy53_nrf5340_cpuapp.yml} | 0 samples/SWTL001/prj_no_dfu.conf | 22 - samples/SWTL001/sample.yaml | 68 +-- samples/SWTL001/src/main.c | 3 - samples/SWTL001/src/swtl001/app.c | 1 + samples/SWTL001/sysbuild/ipc_radio/prj.conf | 35 ++ .../mcuboot/boards/nrf52840dk_nrf52840.conf | 10 + .../boards/nrf52840dk_nrf52840.overlay} | 3 +- .../boards/nrf5340dk_nrf5340_cpuapp.conf | 28 ++ .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 3 +- .../boards/nrf54l15pdk_nrf54l15_cpuapp.conf | 13 + .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay | 7 + .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay | 11 + .../boards/thingy53_nrf5340_cpuapp.conf | 3 - .../boards/thingy53_nrf5340_cpuapp.overlay} | 3 +- samples/SWTL001/sysbuild/mcuboot/prj.conf | 39 ++ samples/lbm_sid_end_device/CMakeLists.txt | 24 +- samples/lbm_sid_end_device/Kconfig | 30 +- samples/lbm_sid_end_device/Kconfig.sysbuild | 54 ++ .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 31 ++ .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf | 13 + .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay | 104 ++++ .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf | 7 + .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay | 100 ++++ ...4l15pdk_nrf54l15_cpuapp_release_0_2_1.conf | 13 + ...4l15pdk_nrf54l15_cpuapp_release_0_3_0.conf | 7 + .../child_image/hci_ipc/Kconfig.root | 69 --- .../child_image/hci_ipc/prj.conf | 8 - .../child_image/hci_ipc/prj_no_dfu.conf | 8 - .../child_image/hci_ipc/prj_release.conf | 9 - .../child_image/mcuboot/Kconfig.root | 127 ----- .../boards/nrf52840dk_nrf52840.overlay | 11 - .../nrf52840dk_nrf52840_release.overlay | 11 - .../boards/nrf5340dk_nrf5340_cpuapp.conf | 33 -- .../nrf5340dk_nrf5340_cpuapp_release.conf | 33 -- .../nrf5340dk_nrf5340_cpuapp_release.overlay | 11 - .../child_image/mcuboot/prj.conf | 22 - .../child_image/mcuboot/prj_release.conf | 22 - .../configuration/pm_static_no_dfu.yml | 5 - samples/lbm_sid_end_device/include/app.h | 5 + .../include/file_transfer.h | 17 - .../include/sbdt/file_transfer.h | 26 + .../include/sbdt/scratch_buffer.h | 37 ++ samples/lbm_sid_end_device/include/sidewalk.h | 14 + samples/lbm_sid_end_device/pm_static.yml | 10 - .../pm_static_nrf52840dk_nrf52840.yml} | 0 .../pm_static_nrf52840dk_nrf52840_release.yml | 73 +++ .../pm_static_nrf5340dk_nrf5340_cpuapp.yml} | 0 ...tatic_nrf5340dk_nrf5340_cpuapp_release.yml | 119 +++++ .../pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml | 71 +++ ...ic_nrf54l15pdk_nrf54l15_cpuapp_release.yml | 71 +++ ... => pm_static_thingy53_nrf5340_cpuapp.yml} | 0 samples/lbm_sid_end_device/prj_no_dfu.conf | 22 - samples/lbm_sid_end_device/sample.yaml | 68 +-- samples/lbm_sid_end_device/src/cli/app.c | 17 +- samples/lbm_sid_end_device/src/cli/app_dut.c | 12 +- .../lbm_sid_end_device/src/cli/app_shell.c | 29 +- .../src/cli/sid_on_dev_cert_cli.c | 467 ++++++++++++++++++ samples/lbm_sid_end_device/src/hello/app.c | 31 +- .../src/lbm/app_lbm_sidewalk.c | 6 +- .../lbm_sid_end_device/src/lbm/app_nav3_lbm.c | 4 +- .../src/lbm/app_nav3_sidewalk.c | 7 +- .../src/lbm/main_geolocation_lbm.c | 2 + .../src/lbm/main_geolocation_sidewalk.c | 2 + .../src/lbm/main_periodical_uplink.c | 2 + samples/lbm_sid_end_device/src/main.c | 3 - .../src/sensor_monitoring/app.c | 29 +- .../src/sensor_monitoring/app_tx.c | 29 +- samples/lbm_sid_end_device/src/sidewalk.c | 206 ++++++-- .../sysbuild/ipc_radio/prj.conf | 35 ++ .../mcuboot/boards/nrf52840dk_nrf52840.conf | 10 + .../boards/nrf52840dk_nrf52840.overlay | 12 + .../boards/nrf5340dk_nrf5340_cpuapp.conf | 28 ++ .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 3 +- .../boards/nrf54l15pdk_nrf54l15_cpuapp.conf | 13 + .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay | 7 + .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay | 11 + .../boards/thingy53_nrf5340_cpuapp.conf | 3 - .../boards/thingy53_nrf5340_cpuapp.overlay | 12 + .../sysbuild/mcuboot/prj.conf | 39 ++ samples/sid_end_device/CMakeLists.txt | 24 +- samples/sid_end_device/Kconfig | 38 +- samples/sid_end_device/Kconfig.sysbuild | 54 ++ samples/sid_end_device/VERSION | 5 + .../boards/nrf5340dk_nrf5340_cpuapp.conf | 19 - .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 31 ++ .../nrf5340dk_nrf5340_cpuapp_release.conf | 19 - .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf | 13 + .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay | 107 ++++ .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf | 7 + .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay | 100 ++++ ...4l15pdk_nrf54l15_cpuapp_release_0_2_1.conf | 13 + ...4l15pdk_nrf54l15_cpuapp_release_0_3_0.conf | 7 + .../thingy53_nrf5340_cpuapp_no_dfu.conf | 10 - .../child_image/hci_ipc/Kconfig.root | 69 --- .../child_image/hci_ipc/prj.conf | 8 - .../child_image/hci_ipc/prj_no_dfu.conf | 8 - .../child_image/hci_ipc/prj_release.conf | 9 - .../child_image/mcuboot/Kconfig.root | 127 ----- .../boards/nrf52840dk_nrf52840.overlay | 11 - .../nrf52840dk_nrf52840_release.overlay | 11 - .../boards/nrf5340dk_nrf5340_cpuapp.conf | 33 -- .../nrf5340dk_nrf5340_cpuapp_release.conf | 33 -- .../nrf5340dk_nrf5340_cpuapp_release.overlay | 11 - .../thingy53_nrf5340dk_nrf5340_cpuapp.overlay | 11 - .../child_image/mcuboot/prj.conf | 22 - .../child_image/mcuboot/prj_release.conf | 22 - .../configuration/pm_static_no_dfu.yml | 5 - samples/sid_end_device/include/app.h | 5 + .../sid_end_device/include/file_transfer.h | 17 - .../include/sbdt/file_transfer.h | 26 + .../include/sbdt/scratch_buffer.h | 37 ++ samples/sid_end_device/include/sidewalk.h | 14 + samples/sid_end_device/overlay-dut.conf | 3 + samples/sid_end_device/pm_static.yml | 10 - .../pm_static_nrf52840dk_nrf52840.yml | 73 +++ .../pm_static_nrf52840dk_nrf52840_release.yml | 73 +++ .../pm_static_nrf5340dk_nrf5340_cpuapp.yml | 119 +++++ ...tatic_nrf5340dk_nrf5340_cpuapp_release.yml | 119 +++++ .../pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml | 71 +++ ...ic_nrf54l15pdk_nrf54l15_cpuapp_release.yml | 71 +++ ... => pm_static_thingy53_nrf5340_cpuapp.yml} | 0 samples/sid_end_device/prj_no_dfu.conf | 22 - samples/sid_end_device/sample.yaml | 68 +-- samples/sid_end_device/src/cli/app.c | 17 +- samples/sid_end_device/src/cli/app_dut.c | 12 +- samples/sid_end_device/src/cli/app_shell.c | 29 +- .../src/cli/sid_on_dev_cert_cli.c | 467 ++++++++++++++++++ samples/sid_end_device/src/file_transfer.c | 168 ------- samples/sid_end_device/src/hello/app.c | 25 +- samples/sid_end_device/src/main.c | 3 - .../src/sbdt}/file_transfer.c | 145 +++--- .../sid_end_device/src/sbdt/scratch_buffer.c | 82 +++ .../src/sensor_monitoring/app.c | 29 +- .../src/sensor_monitoring/app_tx.c | 29 +- samples/sid_end_device/src/sidewalk.c | 200 ++++++-- .../sysbuild/ipc_radio/prj.conf | 35 ++ .../mcuboot/boards/nrf52840dk_nrf52840.conf | 10 + .../boards/nrf52840dk_nrf52840.overlay | 12 + .../boards/nrf5340dk_nrf5340_cpuapp.conf | 28 ++ .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 3 +- .../boards/nrf54l15pdk_nrf54l15_cpuapp.conf | 13 + .../nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay | 7 + .../nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay | 11 + .../boards/thingy53_nrf5340_cpuapp.conf | 3 - .../boards/thingy53_nrf5340_cpuapp.overlay | 12 + .../sid_end_device/sysbuild/mcuboot/prj.conf | 39 ++ v261_nRF52840_sidewalk_lr11xx.diff | 106 ---- v270_sidewalk_lr11xx.diff | 89 ++++ west.yml | 4 +- 186 files changed, 4682 insertions(+), 2061 deletions(-) create mode 100644 pics/board_configurator_nrf54l15_0_3_0.png create mode 100644 samples/SWTL001/Kconfig.sensor_monitoring create mode 100644 samples/SWTL001/Kconfig.sysbuild create mode 100644 samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf create mode 100644 samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay create mode 100644 samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf create mode 100644 samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay create mode 100644 samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf create mode 100644 samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf delete mode 100644 samples/SWTL001/child_image/hci_ipc/Kconfig.root delete mode 100644 samples/SWTL001/child_image/hci_ipc/prj.conf delete mode 100644 samples/SWTL001/child_image/hci_ipc/prj_no_dfu.conf delete mode 100644 samples/SWTL001/child_image/hci_ipc/prj_release.conf delete mode 100644 samples/SWTL001/child_image/mcuboot/Kconfig.root delete mode 100644 samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay delete mode 100644 samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay delete mode 100644 samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf delete mode 100644 samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf delete mode 100644 samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay delete mode 100644 samples/SWTL001/child_image/mcuboot/prj.conf delete mode 100644 samples/SWTL001/child_image/mcuboot/prj_release.conf delete mode 100644 samples/SWTL001/configuration/pm_static_no_dfu.yml delete mode 100644 samples/SWTL001/pm_static.yml rename samples/SWTL001/{configuration/nrf52840dk_nrf52840/pm_static_dfu.yml => pm_static_nrf52840dk_nrf52840.yml} (100%) rename samples/{lbm_sid_end_device/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml => SWTL001/pm_static_nrf52840dk_nrf52840_release.yml} (100%) rename samples/SWTL001/{configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml => pm_static_nrf5340dk_nrf5340_cpuapp.yml} (100%) rename samples/{lbm_sid_end_device/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml => SWTL001/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml} (100%) create mode 100644 samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml create mode 100644 samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml rename samples/SWTL001/{configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml => pm_static_thingy53_nrf5340_cpuapp.yml} (100%) delete mode 100644 samples/SWTL001/prj_no_dfu.conf create mode 100644 samples/SWTL001/sysbuild/ipc_radio/prj.conf create mode 100644 samples/SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf rename samples/{lbm_sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay => SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay} (77%) create mode 100644 samples/SWTL001/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf rename samples/{lbm_sid_end_device/child_image => SWTL001/sysbuild}/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay (77%) create mode 100644 samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf create mode 100644 samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay create mode 100644 samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay rename samples/{lbm_sid_end_device/child_image => SWTL001/sysbuild}/mcuboot/boards/thingy53_nrf5340_cpuapp.conf (93%) rename samples/SWTL001/{child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay => sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay} (77%) create mode 100644 samples/SWTL001/sysbuild/mcuboot/prj.conf create mode 100644 samples/lbm_sid_end_device/Kconfig.sysbuild create mode 100644 samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf create mode 100644 samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay create mode 100644 samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf create mode 100644 samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay create mode 100644 samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf create mode 100644 samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf delete mode 100644 samples/lbm_sid_end_device/child_image/hci_ipc/Kconfig.root delete mode 100644 samples/lbm_sid_end_device/child_image/hci_ipc/prj.conf delete mode 100644 samples/lbm_sid_end_device/child_image/hci_ipc/prj_no_dfu.conf delete mode 100644 samples/lbm_sid_end_device/child_image/hci_ipc/prj_release.conf delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/Kconfig.root delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/prj.conf delete mode 100644 samples/lbm_sid_end_device/child_image/mcuboot/prj_release.conf delete mode 100644 samples/lbm_sid_end_device/configuration/pm_static_no_dfu.yml delete mode 100644 samples/lbm_sid_end_device/include/file_transfer.h create mode 100644 samples/lbm_sid_end_device/include/sbdt/file_transfer.h create mode 100644 samples/lbm_sid_end_device/include/sbdt/scratch_buffer.h delete mode 100644 samples/lbm_sid_end_device/pm_static.yml rename samples/{sid_end_device/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml => lbm_sid_end_device/pm_static_nrf52840dk_nrf52840.yml} (100%) create mode 100644 samples/lbm_sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml rename samples/{sid_end_device/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml => lbm_sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp.yml} (100%) create mode 100644 samples/lbm_sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml create mode 100644 samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml create mode 100644 samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml rename samples/lbm_sid_end_device/{configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml => pm_static_thingy53_nrf5340_cpuapp.yml} (100%) delete mode 100644 samples/lbm_sid_end_device/prj_no_dfu.conf create mode 100644 samples/lbm_sid_end_device/src/cli/sid_on_dev_cert_cli.c create mode 100644 samples/lbm_sid_end_device/sysbuild/ipc_radio/prj.conf create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf rename samples/{sid_end_device/child_image => lbm_sid_end_device/sysbuild}/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay (77%) create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay rename samples/{sid_end_device/child_image => lbm_sid_end_device/sysbuild}/mcuboot/boards/thingy53_nrf5340_cpuapp.conf (93%) create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay create mode 100644 samples/lbm_sid_end_device/sysbuild/mcuboot/prj.conf create mode 100644 samples/sid_end_device/Kconfig.sysbuild create mode 100644 samples/sid_end_device/VERSION delete mode 100644 samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.conf delete mode 100644 samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp_release.conf create mode 100644 samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf create mode 100644 samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay create mode 100644 samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf create mode 100644 samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay create mode 100644 samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf create mode 100644 samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf delete mode 100644 samples/sid_end_device/boards/thingy53_nrf5340_cpuapp_no_dfu.conf delete mode 100644 samples/sid_end_device/child_image/hci_ipc/Kconfig.root delete mode 100644 samples/sid_end_device/child_image/hci_ipc/prj.conf delete mode 100644 samples/sid_end_device/child_image/hci_ipc/prj_no_dfu.conf delete mode 100644 samples/sid_end_device/child_image/hci_ipc/prj_release.conf delete mode 100644 samples/sid_end_device/child_image/mcuboot/Kconfig.root delete mode 100644 samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay delete mode 100644 samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay delete mode 100644 samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf delete mode 100644 samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf delete mode 100644 samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay delete mode 100644 samples/sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay delete mode 100644 samples/sid_end_device/child_image/mcuboot/prj.conf delete mode 100644 samples/sid_end_device/child_image/mcuboot/prj_release.conf delete mode 100644 samples/sid_end_device/configuration/pm_static_no_dfu.yml delete mode 100644 samples/sid_end_device/include/file_transfer.h create mode 100644 samples/sid_end_device/include/sbdt/file_transfer.h create mode 100644 samples/sid_end_device/include/sbdt/scratch_buffer.h delete mode 100644 samples/sid_end_device/pm_static.yml create mode 100644 samples/sid_end_device/pm_static_nrf52840dk_nrf52840.yml create mode 100644 samples/sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml create mode 100644 samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp.yml create mode 100644 samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml create mode 100644 samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml create mode 100644 samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml rename samples/sid_end_device/{configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml => pm_static_thingy53_nrf5340_cpuapp.yml} (100%) delete mode 100644 samples/sid_end_device/prj_no_dfu.conf create mode 100644 samples/sid_end_device/src/cli/sid_on_dev_cert_cli.c delete mode 100644 samples/sid_end_device/src/file_transfer.c rename samples/{lbm_sid_end_device/src => sid_end_device/src/sbdt}/file_transfer.c (59%) create mode 100644 samples/sid_end_device/src/sbdt/scratch_buffer.c create mode 100644 samples/sid_end_device/sysbuild/ipc_radio/prj.conf create mode 100644 samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf create mode 100644 samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay create mode 100644 samples/sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf rename samples/{SWTL001/child_image => sid_end_device/sysbuild}/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay (77%) create mode 100644 samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf create mode 100644 samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay create mode 100644 samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay rename samples/{SWTL001/child_image => sid_end_device/sysbuild}/mcuboot/boards/thingy53_nrf5340_cpuapp.conf (93%) create mode 100644 samples/sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay create mode 100644 samples/sid_end_device/sysbuild/mcuboot/prj.conf delete mode 100644 v261_nRF52840_sidewalk_lr11xx.diff create mode 100644 v270_sidewalk_lr11xx.diff diff --git a/CMakeLists.txt b/CMakeLists.txt index c2ba1a3..45d5404 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ if(CONFIG_RADIO_LR11XX) message( STATUS "patch needs to be applied? ret=" ${ret}) Set(script_name ${CMAKE_CURRENT_SOURCE_DIR}/scripts/patch_ng.py) Set(args "-p 1") - Set(pfile ${CMAKE_CURRENT_SOURCE_DIR}/v261_nRF52840_sidewalk_lr11xx.diff) + Set(pfile ${CMAKE_CURRENT_SOURCE_DIR}/v270_sidewalk_lr11xx.diff) execute_process( COMMAND ${Python3_EXECUTABLE} ${script_name} ${args} ${pfile} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../sidewalk diff --git a/README.md b/README.md index 84b655b..6d10821 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# SWSD006 - LR11xx Multi-stack Software Development Kit for nRF52840 +# SWSD006 - LR11xx Multi-stack Software Development Kit for nRF52840 & nRF54L15 SWSD006 is a collection of driver, protocol stack and utility software that facilitates development of Sidewalk applications. The software includes numerous examples of how to leverage the unique capabilities of Semtech's LR11xx silicon. -While the software targets the Nordic nRF52840 MCU, it is designed and distributed as a "full-source" offering, it enables users to modify +While the software targets the Nordic SoC, it is designed and distributed as a "full-source" offering, it enables users to modify the contents across multiple layers of software stack. Potential modifications include re-targeting of host MCU and LR11xx silicon, changes to the platform abstraction layer and enhancement of the packet fragmentation scheme. Please note that while software enhancement is enabled and encouraged, validation of described functionality was exclusively performed on the specified silicon variants and software component versions. @@ -11,7 +11,7 @@ This repository implements the following functionality: - LR11xx transceiver silicon support for Sidewalk MAC v1.16 CSS and FSK modulation - Drivers and examples demonstrating WIFI/GNSS NAV3 geolocation features of LR11xx silicon - LoRaWAN Class A multi-stack operation using SWL2001 - LoRa Basics Modem 4.5.0; programmatic control over both LoRaWAN and Sidewalk stacks in one firmware image -- LR11xx transceiver firmware upgrade via SWTL001 port to nRF52840 +- LR11xx transceiver firmware upgrade via SWTL001 port to nRF52840 (or nRF54L15) - LoRaWAN Class A + WIFI/GNSS NAV3 operation example - An example of packet fragmentation and re-assembly (overcomes CSS packet size limitations in Sidewalk) - AWS lambda example code demonstrating End-to-End handling of LoRa EDGE application data: from transceiver to cloud service @@ -39,6 +39,7 @@ west init -m https://github.com/Lora-net/SWSD006 --mr v2.6.1 my-workspace # update nRF Connect SDK modules cd my-workspace west update +west config build.sysbuild True ``` If you intend to build a LoRa Basics Modem project on the windows platform, decide on a workspace location as near the root directory as possible in order to prevent path lengths that exceed the capability of the zephyr build system on windows. This is not a concern on other platforms such as linux. ### configuring project for LR11xx @@ -55,7 +56,34 @@ LR1121 device would typically be used with an XTAL, since it doesnt have GNSS fu All radio other configuration is declared in ``app_subGHz_config_lr11xx.c``, For example if you wanted to use the radio's LDO instead of it's DC-DC, you can modify the ``.regulator_mode = `` to ``LR11XX_SYSTEM_REG_MODE_LDO`` ### Available example applications: all example apps are built using ``west build -b -- -DOVERLAY_CONFIG=foobar.conf``. The app to build is selected by adding ``-- -DOVERLAY_CONFIG=foobar.conf`` - * the full build command, for example: ``west build -b nrf52840dk_nrf52840 -- -DOVERLAY_CONFIG=overlay-nav3sid.conf`` + * the full build command, for example: + | Hardware platforms | Board name | Build command | + | -------------- | ---------- | -------------- | + | nRF52840 DK | nrf52840dk | ``west build -p -b nrf52840dk/nrf52840 -- -DOVERLAY_CONFIG=overlay-nav3sid.conf`` | + | nRF54L15 PDK | nrf54l15pdk | ``west build -p -b nrf54l15pdk/nrf54l15/cpuapp -- -DOVERLAY_CONFIG=overlay-nav3sid.conf`` | + +#### nRF54L15 PDK pinout: +For nRF54L15 PDK revision v0.3.0/0.7.0 use the following GPIO configuration: + +| nRF54L15 PDK | LR1110MB1LCKS | +| -------------- | ---------- | +| P2.06 | SCK | +| P1.11 | MISO | +| P2.08 | MOSI | +| P2.10 | CS | +| P0.02 | LR_NRESET | +| P0.00 | BUSY | +| P0.01 | ACC_INT1 | +| P0.03 | DIO9 | +| P1.12 | LNA_CTRL_MCU | + +> **NOTE** +> +> To use the suggested pins, disable ``VCOM0`` (not used by samples) through the [Board Configurator](https://docs.nordicsemi.com/bundle/nrf-connect-board-configurator/page/index.html) tool in the nRF Connect for Desktop. +> This step is required for the shield to work as some pins are connected to ``VCOM0`` by default. +> See the picture below for ``VCOM0`` suggested configuration: +> ![VCOM0 configuration](./pics/board_configurator_nrf54l15_0_3_0.png) + #### apps provided by Nordic: from the directory ``samples/sid_end_device`` enables LR11xx in ``prj.conf`` --> * hello @@ -71,10 +99,11 @@ from the directory ``samples/lbm_sid_end_device`` --> * NAV3 running simultanously with sidewalk (aka NAV3 in bypass mode): * ``-DOVERLAY_CONFIG=overlay-nav3sid.conf`` * NAV3 running in lora basics modem: - * `-DOVERLAY_CONFIG=overlay-nav3lbm.conf`` + * ``-DOVERLAY_CONFIG=overlay-nav3lbm.conf`` * LR11xx firmware update, and almanac erase: * build in ``samples/SWTL001``directory + ## GNSS Performance Evaluation Notice The included GNSS example source code is provided solely to demonstrate the GNSS scan functionality under ideal conditions. The source code and GNSS scan results are not representative of the optimal configuration or performance characteristics of the silicon. The LR11xx product family is flexible and can be embodied and configured in a multitude of ways to realize various trade-offs regarding performance, battery life, PCB size, cost, etc. The GNSS example included in this release and the corresponding evaluation kits are designed & configured by the included source code in a default capacity which is sufficient to demonstrate functional GNSS scan capability only. Care must be taken if/when attempting to assess performance characteristics of GNSS scan functionality and we strongly encourage those conducting such analysis to contact Semtech via the provided support channels so that we can ensure appropriate configuration settings are employed for a given performance evaluation use-case. diff --git a/pics/board_configurator_nrf54l15_0_3_0.png b/pics/board_configurator_nrf54l15_0_3_0.png new file mode 100644 index 0000000000000000000000000000000000000000..55ab104847cee373161da9f588a77a5b751246c4 GIT binary patch literal 119857 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 i000000QhL&*#8f%L!CKvq%}qW0000; + label = "Application state error"; + }; + state_notifier_dfu: dfu { + gpios = <&gpio1 0x1 0x0>; + label = "Application state dfu"; + }; + state_notifier_sending: sending { + gpios = <&gpio1 0x3 0x0>; + label = "Application state sending"; + }; + state_notifier_receiving: receiving { + gpios = <&gpio1 0x2 0x0>; + label = "Application state receiving"; + }; + }; + + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + state-notifier-error = &state_notifier_error; + state-notifier-dfu = &state_notifier_dfu; + state-notifier-sending = &state_notifier_sending; + state-notifier-receiving = &state_notifier_receiving; + }; + semtech_sx1262_gpios{ compatible = "gpio-keys"; semtech_sx1262_cs: cs { diff --git a/samples/SWTL001/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/SWTL001/boards/nrf5340dk_nrf5340_cpuapp.overlay index 4395ed1..d0a7679 100644 --- a/samples/SWTL001/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/SWTL001/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -20,6 +20,37 @@ }; /{ + state_notifier_gpios{ + compatible = "gpio-keys"; + state_notifier_error: error { + gpios = <&gpio1 0x8 0x0>; + label = "Application state error"; + }; + state_notifier_dfu: dfu { + gpios = <&gpio1 0x2 0x0>; + label = "Application state dfu"; + }; + state_notifier_sending: sending { + gpios = <&gpio1 0x4 0x0>; + label = "Application state sending"; + }; + state_notifier_receiving: receiving { + gpios = <&gpio1 0x3 0x0>; + label = "Application state receiving"; + }; + }; + + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + state-notifier-error = &state_notifier_error; + state-notifier-dfu = &state_notifier_dfu; + state-notifier-sending = &state_notifier_sending; + state-notifier-receiving = &state_notifier_receiving; + }; + semtech_sx1262_gpios{ compatible = "gpio-keys"; semtech_sx1262_cs: cs { diff --git a/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf new file mode 100644 index 0000000..d6a0780 --- /dev/null +++ b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Workaround: +# The nRF54L15 PDK (PCA10156) revisions v0.2.0 AA0-ES2, v0.2.0 AA0-ES3, +# and v0.2.1 AB0-ES5 have Buttons 3 and 4 connected to the GPIO port +# which does not support interrupts. +CONFIG_DK_LIBRARY_BUTTON_NO_ISR=y + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay new file mode 100644 index 0000000..811a033 --- /dev/null +++ b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; +&pinctrl { + spi00_default: spi00_default { + group1 { + psels = , + , + ; + }; + }; + + spi00_sleep: spi00_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + +sid_semtech: &spi00 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi00_default>; + pinctrl-1 = <&spi00_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; +}; + +/{ + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + }; + + semtech_sx1262_gpios{ + compatible = "gpio-keys"; + semtech_sx1262_cs: cs { + gpios = <&gpio2 0xa GPIO_PULL_UP>; + label = "semtech_sx1262 CS"; + }; + semtech_sx1262_reset_gpios: reset { + gpios = <&gpio1 0xb (GPIO_ACTIVE_LOW|GPIO_PULL_UP)>; + label = "semtech_sx1262 Reset"; + }; + semtech_sx1262_busy_gpios: busy { + gpios = <&gpio1 0xc 0x0>; + label = "semtech_sx1262 Busy"; + }; + semtech_sx1262_antenna_enable_gpios: antena_enable { + gpios = <&gpio2 0x7 0x0>; + label = "semtech_sx1262 Antena Enable"; + }; + semtech_sx1262_dio1_gpios: dio1 { + gpios = <&gpio1 0xa 0x0>; + label = "semtech_sx1262 DIO1"; + }; + radio_gnss_lna: gnss_lna { + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + label = "gnss antenna"; + }; + radio_led_sniff: led_sniff { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "yellow LED"; + }; + radio_led_tx: led_tx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "red tx LED"; + }; + radio_led_rx: led_rx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "green rx LED"; + }; + }; + +}; + +&gpiote20 { + status = "okay"; +}; + +&gpiote30 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; diff --git a/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf new file mode 100644 index 0000000..e99da54 --- /dev/null +++ b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay new file mode 100644 index 0000000..5a08ece --- /dev/null +++ b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; + + &pinctrl { + spi21_default: spi21_default { + group1 { + psels = , + , + ; + }; + }; + + spi21_sleep: spi21_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + + sid_semtech: &spi21 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi21_default>; + pinctrl-1 = <&spi21_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; +}; + +/{ + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + }; + + semtech_sx1262_gpios{ + compatible = "gpio-keys"; + semtech_sx1262_cs: cs { + gpios = <&gpio2 0xa GPIO_PULL_UP>; + label = "semtech_sx1262 CS"; + }; + semtech_sx1262_reset_gpios: reset { + gpios = <&gpio0 0x2 (GPIO_ACTIVE_LOW|GPIO_PULL_UP)>; + label = "semtech_sx1262 Reset"; + }; + semtech_sx1262_busy_gpios: busy { + gpios = <&gpio0 0x0 0x0>; + label = "semtech_sx1262 Busy"; + }; + semtech_sx1262_antenna_enable_gpios: antena_enable { + gpios = <&gpio0 0x1 0x0>; + label = "semtech_sx1262 Antena Enable"; + }; + semtech_sx1262_dio1_gpios: dio1 { + gpios = <&gpio0 0x3 0x0>; + label = "semtech_sx1262 DIO1"; + }; + radio_gnss_lna: gnss_lna { + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + label = "gnss antenna"; + }; + radio_led_sniff: led_sniff { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "yellow LED"; + }; + radio_led_tx: led_tx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "red tx LED"; + }; + radio_led_rx: led_rx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "green rx LED"; + }; + }; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; diff --git a/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf new file mode 100644 index 0000000..d6a0780 --- /dev/null +++ b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Workaround: +# The nRF54L15 PDK (PCA10156) revisions v0.2.0 AA0-ES2, v0.2.0 AA0-ES3, +# and v0.2.1 AB0-ES5 have Buttons 3 and 4 connected to the GPIO port +# which does not support interrupts. +CONFIG_DK_LIBRARY_BUTTON_NO_ISR=y + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf new file mode 100644 index 0000000..e99da54 --- /dev/null +++ b/samples/SWTL001/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/SWTL001/child_image/hci_ipc/Kconfig.root b/samples/SWTL001/child_image/hci_ipc/Kconfig.root deleted file mode 100644 index 60e8bd4..0000000 --- a/samples/SWTL001/child_image/hci_ipc/Kconfig.root +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# The purpose of this file is to create a wrapper Kconfig file that will be set as -# hci_ipc_KCONFIG_ROOT and processed before any other Kconfig for hci_ipc child image. - -config HEAP_MEM_POOL_SIZE - default 8192 - -config MAIN_STACK_SIZE - default 2048 - -config SYSTEM_WORKQUEUE_STACK_SIZE - default 2048 - -config BT - default y - -config BT_HCI_RAW - default y - -config BT_MAX_CONN - default 1 - -config BT_PERIPHERAL - default y - -config BT_CENTRAL - default n - -config BT_BUF_ACL_RX_SIZE - default 502 - -config BT_BUF_ACL_TX_SIZE - default 251 - -config BT_CTLR_DATA_LENGTH_MAX - default 251 - -config BT_CTLR_ASSERT_HANDLER - default y - -config BT_HCI_RAW_RESERVE - default 1 - -# Workaround: Unable to allocate command buffer when using K_NO_WAIT since -# Host number of completed commands does not follow normal flow control. -config BT_BUF_CMD_TX_COUNT - default 10 - -config ASSERT - default y - -config DEBUG_INFO - default y - -config EXCEPTION_STACK_TRACE - default y - -config IPC_SERVICE - default y - -config MBOX - default y - -source "Kconfig.zephyr" diff --git a/samples/SWTL001/child_image/hci_ipc/prj.conf b/samples/SWTL001/child_image/hci_ipc/prj.conf deleted file mode 100644 index c89bda6..0000000 --- a/samples/SWTL001/child_image/hci_ipc/prj.conf +++ /dev/null @@ -1,8 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/SWTL001/child_image/hci_ipc/prj_no_dfu.conf b/samples/SWTL001/child_image/hci_ipc/prj_no_dfu.conf deleted file mode 100644 index c89bda6..0000000 --- a/samples/SWTL001/child_image/hci_ipc/prj_no_dfu.conf +++ /dev/null @@ -1,8 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/SWTL001/child_image/hci_ipc/prj_release.conf b/samples/SWTL001/child_image/hci_ipc/prj_release.conf deleted file mode 100644 index 86c0d92..0000000 --- a/samples/SWTL001/child_image/hci_ipc/prj_release.conf +++ /dev/null @@ -1,9 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_RESET_ON_FATAL_ERROR=y diff --git a/samples/SWTL001/child_image/mcuboot/Kconfig.root b/samples/SWTL001/child_image/mcuboot/Kconfig.root deleted file mode 100644 index d6ecb28..0000000 --- a/samples/SWTL001/child_image/mcuboot/Kconfig.root +++ /dev/null @@ -1,127 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# The purpose of this file is to create a wrapper Kconfig file that will be set as -# mcuboot_KCONFIG_ROOT and processed before any other Kconfig for mcuboot child image. - - -config MAIN_STACK_SIZE - default 10240 - -config BOOT_SWAP_SAVE_ENCTLV - default n - -config BOOT_ENCRYPT_RSA - default n - -config BOOT_ENCRYPT_EC256 - default n - -config BOOT_ENCRYPT_X25519 - default n - -config BOOT_BOOTSTRAP - default n - -config PM - default n - -config FLASH - default y - -config FPROTECT - default y - -config NORDIC_QSPI_NOR - default y - -config NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE - default 4096 - -config NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE - default 16 - -config BOOT_MAX_IMG_SECTORS - default 256 - -config LOG - default n - -choice LIBC_IMPLEMENTATION - default MINIMAL_LIBC -endchoice - -config COMMON_LIBC_CALLOC - default y - -config COMMON_LIBC_MALLOC - default y - -config COMMON_LIBC_REALLOCARRAY - default y - -config NCS_SAMPLES_DEFAULTS - default n - -config PRINTK - default n - -config REBOOT - default n - -config NRF_RTC_TIMER - default y if SOC_SERIES_NRF53X - default n - -config CONSOLE - default n - -config CONSOLE_HANDLER - default n - -config GPIO - default n - -config KERNEL_MEM_POOL - default n - -config ASSERT - default n - -config BOOT_BANNER - default n - -config SERIAL - default n - -config UART_CONSOLE - default n - -config TIMESLICING - default n - -config USE_SEGGER_RTT - default n - -config RESET_ON_FATAL_ERROR - default n - -config SECURE_BOOT_DEBUG - default n - -config MULTITHREADING - default n - -config TICKLESS_KERNEL - default n - -config TIMEOUT_64BIT - default n - -config NRF_ENABLE_ICACHE - default n - -source "${ZEPHYR_BASE}/../bootloader/mcuboot/boot/zephyr/Kconfig" diff --git a/samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay b/samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay deleted file mode 100644 index 69bf975..0000000 --- a/samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay b/samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay deleted file mode 100644 index 69bf975..0000000 --- a/samples/SWTL001/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 3664dd3..0000000 --- a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - -# Configure MCUboot features -CONFIG_NRF53_MULTI_IMAGE_UPDATE=y -CONFIG_BOOT_UPGRADE_ONLY=y -CONFIG_BOOT_MAX_IMG_SECTORS=256 -CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Enable flash simulator -CONFIG_PCD_APP=y -CONFIG_FLASH_SIMULATOR=y -CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y -CONFIG_FLASH_SIMULATOR_STATS=n - -# Configure QSPI for external flash -CONFIG_FLASH=y -CONFIG_FPROTECT=y -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf b/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf deleted file mode 100644 index 3664dd3..0000000 --- a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - -# Configure MCUboot features -CONFIG_NRF53_MULTI_IMAGE_UPDATE=y -CONFIG_BOOT_UPGRADE_ONLY=y -CONFIG_BOOT_MAX_IMG_SECTORS=256 -CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Enable flash simulator -CONFIG_PCD_APP=y -CONFIG_FLASH_SIMULATOR=y -CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y -CONFIG_FLASH_SIMULATOR_STATS=n - -# Configure QSPI for external flash -CONFIG_FLASH=y -CONFIG_FPROTECT=y -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay b/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay deleted file mode 100644 index c670799..0000000 --- a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - - / { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/SWTL001/child_image/mcuboot/prj.conf b/samples/SWTL001/child_image/mcuboot/prj.conf deleted file mode 100644 index 78d7622..0000000 --- a/samples/SWTL001/child_image/mcuboot/prj.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" - -CONFIG_BOOT_UPGRADE_ONLY=n -CONFIG_RESET_ON_FATAL_ERROR=y - -CONFIG_LOG=n -CONFIG_PRINTK=n -CONFIG_CONSOLE_HANDLER=n -CONFIG_ASSERT=n -CONFIG_BOOT_BANNER=n -CONFIG_CONSOLE=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_USE_SEGGER_RTT=n -CONFIG_GPIO=n -CONFIG_NO_RUNTIME_CHECKS=y -CONFIG_SIZE_OPTIMIZATIONS=y diff --git a/samples/SWTL001/child_image/mcuboot/prj_release.conf b/samples/SWTL001/child_image/mcuboot/prj_release.conf deleted file mode 100644 index 78d7622..0000000 --- a/samples/SWTL001/child_image/mcuboot/prj_release.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" - -CONFIG_BOOT_UPGRADE_ONLY=n -CONFIG_RESET_ON_FATAL_ERROR=y - -CONFIG_LOG=n -CONFIG_PRINTK=n -CONFIG_CONSOLE_HANDLER=n -CONFIG_ASSERT=n -CONFIG_BOOT_BANNER=n -CONFIG_CONSOLE=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_USE_SEGGER_RTT=n -CONFIG_GPIO=n -CONFIG_NO_RUNTIME_CHECKS=y -CONFIG_SIZE_OPTIMIZATIONS=y diff --git a/samples/SWTL001/configuration/pm_static_no_dfu.yml b/samples/SWTL001/configuration/pm_static_no_dfu.yml deleted file mode 100644 index 658ce75..0000000 --- a/samples/SWTL001/configuration/pm_static_no_dfu.yml +++ /dev/null @@ -1,5 +0,0 @@ -mfg_storage: - address: 0xff000 - end_address: 0x100000 - region: flash_primary - size: 0x1000 diff --git a/samples/SWTL001/include/app.h b/samples/SWTL001/include/app.h index 077f3ca..6eff418 100644 --- a/samples/SWTL001/include/app.h +++ b/samples/SWTL001/include/app.h @@ -4,9 +4,14 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#ifndef SAMPLE_APP_H +#define SAMPLE_APP_H + /** * @brief Start Sidewalk end device application. * * @note This function should never return. */ void app_start(void); + +#endif /* SAMPLE_APP_H */ diff --git a/samples/SWTL001/pm_static.yml b/samples/SWTL001/pm_static.yml deleted file mode 100644 index 55dba59..0000000 --- a/samples/SWTL001/pm_static.yml +++ /dev/null @@ -1,10 +0,0 @@ -mcuboot: - address: 0x0 - end_address: 0x8000 - region: flash_primary - size: 0x8000 -mfg_storage: - address: 0xff000 - end_address: 0x100000 - region: flash_primary - size: 0x1000 diff --git a/samples/SWTL001/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml b/samples/SWTL001/pm_static_nrf52840dk_nrf52840.yml similarity index 100% rename from samples/SWTL001/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml rename to samples/SWTL001/pm_static_nrf52840dk_nrf52840.yml diff --git a/samples/lbm_sid_end_device/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml b/samples/SWTL001/pm_static_nrf52840dk_nrf52840_release.yml similarity index 100% rename from samples/lbm_sid_end_device/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml rename to samples/SWTL001/pm_static_nrf52840dk_nrf52840_release.yml diff --git a/samples/SWTL001/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml b/samples/SWTL001/pm_static_nrf5340dk_nrf5340_cpuapp.yml similarity index 100% rename from samples/SWTL001/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml rename to samples/SWTL001/pm_static_nrf5340dk_nrf5340_cpuapp.yml diff --git a/samples/lbm_sid_end_device/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml b/samples/SWTL001/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml similarity index 100% rename from samples/lbm_sid_end_device/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml rename to samples/SWTL001/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml diff --git a/samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml b/samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml new file mode 100644 index 0000000..2aae80f --- /dev/null +++ b/samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml @@ -0,0 +1,71 @@ +app: + address: 0xc800 + end_address: 0xc3000 + region: flash_primary + size: 0xb6800 +mcuboot: + address: 0x0 + end_address: 0xc000 + placement: + before: + - mcuboot_primary + region: flash_primary + size: 0xc000 +mcuboot_pad: + address: 0xc000 + end_address: 0xc800 + placement: + before: + - mcuboot_primary_app + region: flash_primary + size: 0x800 +mcuboot_primary: + address: 0xc000 + end_address: 0xc3000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + sharers: 0x1 + size: 0xb7000 + span: *id001 +mcuboot_primary_app: + address: 0xc800 + end_address: 0xc3000 + orig_span: &id002 + - app + region: flash_primary + size: 0xb6800 + span: *id002 +mcuboot_secondary: + address: 0xc3000 + end_address: 0x17a000 + placement: + after: + - mcuboot_primary + align: + start: 0x1000 + region: flash_primary + share_size: + - mcuboot_primary + size: 0xb7000 +mfg_storage: + address: 0x17c000 + end_address: 0x17d000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0x17a000 + end_address: 0x17c000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml b/samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml new file mode 100644 index 0000000..2aae80f --- /dev/null +++ b/samples/SWTL001/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml @@ -0,0 +1,71 @@ +app: + address: 0xc800 + end_address: 0xc3000 + region: flash_primary + size: 0xb6800 +mcuboot: + address: 0x0 + end_address: 0xc000 + placement: + before: + - mcuboot_primary + region: flash_primary + size: 0xc000 +mcuboot_pad: + address: 0xc000 + end_address: 0xc800 + placement: + before: + - mcuboot_primary_app + region: flash_primary + size: 0x800 +mcuboot_primary: + address: 0xc000 + end_address: 0xc3000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + sharers: 0x1 + size: 0xb7000 + span: *id001 +mcuboot_primary_app: + address: 0xc800 + end_address: 0xc3000 + orig_span: &id002 + - app + region: flash_primary + size: 0xb6800 + span: *id002 +mcuboot_secondary: + address: 0xc3000 + end_address: 0x17a000 + placement: + after: + - mcuboot_primary + align: + start: 0x1000 + region: flash_primary + share_size: + - mcuboot_primary + size: 0xb7000 +mfg_storage: + address: 0x17c000 + end_address: 0x17d000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0x17a000 + end_address: 0x17c000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/SWTL001/configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml b/samples/SWTL001/pm_static_thingy53_nrf5340_cpuapp.yml similarity index 100% rename from samples/SWTL001/configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml rename to samples/SWTL001/pm_static_thingy53_nrf5340_cpuapp.yml diff --git a/samples/SWTL001/prj_no_dfu.conf b/samples/SWTL001/prj_no_dfu.conf deleted file mode 100644 index 3f14d8c..0000000 --- a/samples/SWTL001/prj_no_dfu.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2023 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Sidewalk -CONFIG_SIDEWALK=y -CONFIG_SIDEWALK_DFU=n -CONFIG_SMF=y - -# Log -CONFIG_LOG=y -CONFIG_LOG_PRINTK=y -CONFIG_LOG_BUFFER_SIZE=2048 -CONFIG_NVS_LOG_LEVEL_WRN=y - -# Bluetooth -CONFIG_BT_DEVICE_NAME="Nordic" - -# Debug -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/SWTL001/sample.yaml b/samples/SWTL001/sample.yaml index a61c19d..e4635fc 100644 --- a/samples/SWTL001/sample.yaml +++ b/samples/SWTL001/sample.yaml @@ -1,96 +1,62 @@ sample: name: Sidewalk end device sample description: Sample implementing Amazon Sidewalk End Device +common: + sysbuild: true + build_only: true + platform_allow: + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54l15pdk/nrf54l15/cpuapp + integration_platforms: + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54l15pdk/nrf54l15/cpuapp tests: sample.sidewalk.hello: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp + - CONFIG_SIDEWALK_FILE_TRANSFER=y tags: Sidewalk hello sample.sidewalk.hello.release: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: - CONF_FILE=prj_release.conf + FILE_SUFFIX=release extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp - tags: Sidewalk hello - - sample.sidewalk.hello.no_dfu: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp - extra_args: - CONF_FILE=prj_no_dfu.conf - extra_configs: - - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello sample.sidewalk.hello.ble_only: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello BLE sample.sidewalk.hello.ble_only.release: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: - CONF_FILE=prj_release.conf + FILE_SUFFIX=release extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello BLE sample.sidewalk.demo: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: OVERLAY_CONFIG="overlay-demo.conf" extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk demo sample.sidewalk.demo.ble_only: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp thingy53_nrf5340_cpuapp + platform_allow: + - thingy53/nrf5340/cpuapp extra_args: OVERLAY_CONFIG="overlay-demo.conf" extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp - - thingy53_nrf5340_cpuapp + - thingy53/nrf5340/cpuapp tags: Sidewalk demo BLE sample.sidewalk.dut: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: OVERLAY_CONFIG="overlay-dut.conf" - extra_configs: - - CONFIG_SIDEWALK_FILE_TRANSFER=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk cli diff --git a/samples/SWTL001/src/main.c b/samples/SWTL001/src/main.c index e050000..e7a910a 100644 --- a/samples/SWTL001/src/main.c +++ b/samples/SWTL001/src/main.c @@ -8,9 +8,6 @@ #include #include -#include -LOG_MODULE_REGISTER(main, CONFIG_SIDEWALK_LOG_LEVEL); - int main(void) { PRINT_SIDEWALK_VERSION(); diff --git a/samples/SWTL001/src/swtl001/app.c b/samples/SWTL001/src/swtl001/app.c index 0f182ac..5ce3112 100644 --- a/samples/SWTL001/src/swtl001/app.c +++ b/samples/SWTL001/src/swtl001/app.c @@ -71,3 +71,4 @@ void app_start(void) break; } } + diff --git a/samples/SWTL001/sysbuild/ipc_radio/prj.conf b/samples/SWTL001/sysbuild/ipc_radio/prj.conf new file mode 100644 index 0000000..654a36b --- /dev/null +++ b/samples/SWTL001/sysbuild/ipc_radio/prj.conf @@ -0,0 +1,35 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Bluetooth +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_CTLR_ASSERT_HANDLER=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=n +CONFIG_BT_MAX_CONN=1 +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 + +# IPC +CONFIG_IPC_SERVICE=y +CONFIG_MBOX=y + +# Debug +CONFIG_LOG=n +CONFIG_SERIAL=n +CONFIG_ASSERT=y +CONFIG_DEBUG_INFO=y +CONFIG_EXCEPTION_STACK_TRACE=y +CONFIG_RESET_ON_FATAL_ERROR=y + +# ipc_radio +CONFIG_IPC_RADIO_BT=y +CONFIG_IPC_RADIO_BT_HCI_IPC=y diff --git a/samples/SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf b/samples/SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 0000000..c333f7b --- /dev/null +++ b/samples/SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,10 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Configure QSPI for external flash +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay b/samples/SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay similarity index 77% rename from samples/lbm_sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay rename to samples/SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay index 055f044..6ea6421 100644 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/SWTL001/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay @@ -4,8 +4,9 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ - / { +/ { chosen { + zephyr,code-partition = &boot_partition; nordic,pm-ext-flash = &mx25r64; }; }; diff --git a/samples/SWTL001/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/SWTL001/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 0000000..96cb33e --- /dev/null +++ b/samples/SWTL001/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,28 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 + +CONFIG_BOOT_SWAP_USING_MOVE=n +# Multi-image updates do not support image swapping yet. +CONFIG_BOOT_UPGRADE_ONLY=y + +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +# Enable custom command to erase settings partition. +CONFIG_ENABLE_MGMT_PERUSER=y +CONFIG_ZCBOR=y +CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/SWTL001/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay similarity index 77% rename from samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay rename to samples/SWTL001/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay index 055f044..6ea6421 100644 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/SWTL001/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -4,8 +4,9 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ - / { +/ { chosen { + zephyr,code-partition = &boot_partition; nordic,pm-ext-flash = &mx25r64; }; }; diff --git a/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf b/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf new file mode 100644 index 0000000..16bd5b0 --- /dev/null +++ b/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n + +# Workaroud: fprotect and watchdog feed +# are not supported in NCS v2.6.0 +CONFIG_FPROTECT=n +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay b/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay new file mode 100644 index 0000000..6220cb2 --- /dev/null +++ b/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* There is no aditional config needed for this version of PDK */ diff --git a/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay b/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay new file mode 100644 index 0000000..8edfb64 --- /dev/null +++ b/samples/SWTL001/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf b/samples/SWTL001/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf similarity index 93% rename from samples/lbm_sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf rename to samples/SWTL001/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf index a0db3d5..561529f 100644 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf +++ b/samples/SWTL001/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf @@ -4,9 +4,6 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - # Configure MCUboot features CONFIG_NRF53_MULTI_IMAGE_UPDATE=y CONFIG_BOOT_UPGRADE_ONLY=y diff --git a/samples/SWTL001/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay b/samples/SWTL001/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay similarity index 77% rename from samples/SWTL001/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay rename to samples/SWTL001/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay index 055f044..6ea6421 100644 --- a/samples/SWTL001/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/SWTL001/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay @@ -4,8 +4,9 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ - / { +/ { chosen { + zephyr,code-partition = &boot_partition; nordic,pm-ext-flash = &mx25r64; }; }; diff --git a/samples/SWTL001/sysbuild/mcuboot/prj.conf b/samples/SWTL001/sysbuild/mcuboot/prj.conf new file mode 100644 index 0000000..0a05853 --- /dev/null +++ b/samples/SWTL001/sysbuild/mcuboot/prj.conf @@ -0,0 +1,39 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +CONFIG_MAIN_STACK_SIZE=10240 + +CONFIG_BOOT_SWAP_SAVE_ENCTLV=n +CONFIG_BOOT_BOOTSTRAP=n +CONFIG_PM=n + +CONFIG_FLASH=y +CONFIG_FPROTECT=y + +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Use minimal C library instead of the Picolib +CONFIG_MINIMAL_LIBC=y + +# Disable logs +CONFIG_NCS_BOOT_BANNER=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n +CONFIG_LOG=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_BOOT_BANNER=n + +# Bootloader size optimization +CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_GPIO=n +CONFIG_TIMESLICING=n +CONFIG_MULTITHREADING=n +CONFIG_TICKLESS_KERNEL=n +CONFIG_TIMEOUT_64BIT=n +CONFIG_NRF_ENABLE_ICACHE=n diff --git a/samples/lbm_sid_end_device/CMakeLists.txt b/samples/lbm_sid_end_device/CMakeLists.txt index 8d853db..1c4847c 100644 --- a/samples/lbm_sid_end_device/CMakeLists.txt +++ b/samples/lbm_sid_end_device/CMakeLists.txt @@ -6,24 +6,6 @@ cmake_minimum_required(VERSION 3.20.0) -# Sidewalk version -include(bootloader_version.cmake) - -# Child images -set(hci_ipc_KCONFIG_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/child_image/hci_ipc/Kconfig.root) -set(mcuboot_KCONFIG_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/child_image/mcuboot/Kconfig.root) - -# Configurations -if(CONF_FILE) - get_filename_component(CONFIG_FILE_NAME ${CONF_FILE} NAME) -endif() - -if("${CONFIG_FILE_NAME}" STREQUAL "prj_no_dfu.conf") - set(PM_STATIC_YML_FILE ${CMAKE_CURRENT_SOURCE_DIR}/configuration/pm_static_no_dfu.yml) -else() - set(PM_STATIC_YML_FILE ${CMAKE_CURRENT_SOURCE_DIR}/configuration/${BOARD}/pm_static_dfu.yml) -endif() - # Zephyr CMake project find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(sidewalk_sid_end_device) @@ -35,7 +17,10 @@ target_sources(app PRIVATE src/sidewalk.c ) -target_sources_ifdef(CONFIG_SIDEWALK_FILE_TRANSFER app PRIVATE src/file_transfer.c) +target_sources_ifdef(CONFIG_SIDEWALK_FILE_TRANSFER app PRIVATE + src/sbdt/file_transfer.c + src/sbdt/scratch_buffer.c +) if(CONFIG_SID_END_DEVICE_SENSOR_MONITORING) target_sources(app PRIVATE @@ -106,6 +91,7 @@ if(CONFIG_SID_END_DEVICE_CLI) src/cli/app_dut.c src/cli/app_shell.c ) + target_sources_ifdef(CONFIG_SIDEWALK_ON_DEV_CERT app PRIVATE src/cli/sid_on_dev_cert_cli.c) endif() if(CONFIG_SMTC_CLI) diff --git a/samples/lbm_sid_end_device/Kconfig b/samples/lbm_sid_end_device/Kconfig index 9b97458..b416aec 100644 --- a/samples/lbm_sid_end_device/Kconfig +++ b/samples/lbm_sid_end_device/Kconfig @@ -90,9 +90,35 @@ config SID_END_DEVICE_EVENT_HEAP_SIZE config SIDEWALK_FILE_TRANSFER select EXPERIMENTAL - bool "Add File transfer capability to the application" + bool "Enable Sidewalk file transfer" help - Include the callbacks necesary to handle file transfer + Add support for Sidewalk Bulk Data Transfer (SBDT) + in application. + +if SIDEWALK_FILE_TRANSFER + +config SIDEWALK_FILE_TRANSFER_HEAP_SIZE + int "Sidewalk file transfer heap size" + default 10240 + help + Heap size in bytes to be allocated + for Sidewalk Bulk Data Transfer (SBDT). + +config SIDEWALK_FILE_TRANSFER_DFU + bool "Sildewak file transfer and dfu" + default SIDEWALK_FILE_TRANSFER + imply SIDEWALK_DFU_IMG_UTILS + imply DFU_MULTI_IMAGE + imply DFU_TARGET + imply DFU_TARGET_MCUBOOT + imply STREAM_FLASH + imply STREAM_FLASH_ERASE + imply SIDEWALK_THREAD_QUEUE_TIMEOUT + help + Save recived data to flash. Expect CBOR manifest. + Autoatically reset device after file transfer. + +endif rsource "Kconfig.defconfig" diff --git a/samples/lbm_sid_end_device/Kconfig.sysbuild b/samples/lbm_sid_end_device/Kconfig.sysbuild new file mode 100644 index 0000000..404f972 --- /dev/null +++ b/samples/lbm_sid_end_device/Kconfig.sysbuild @@ -0,0 +1,54 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config NRF_DEFAULT_IPC_RADIO + default y if BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP + +choice BOOTLOADER + default BOOTLOADER_MCUBOOT +endchoice + +if BOOTLOADER_MCUBOOT + +config DFU_MULTI_IMAGE_PACKAGE_BUILD + default y + +config DFU_MULTI_IMAGE_PACKAGE_APP + default y + +if (BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP) + +config MCUBOOT_UPDATEABLE_IMAGES + default 2 + +choice MCUBOOT_MODE + default MCUBOOT_MODE_OVERWRITE_ONLY +endchoice + +choice BOOT_SIGNATURE_TYPE + default BOOT_SIGNATURE_TYPE_RSA +endchoice + +config SECURE_BOOT + default y + +config SECURE_BOOT_NETCORE + default y + +config NETCORE_APP_UPDATE + default y + +config DFU_MULTI_IMAGE_PACKAGE_NET + default y + +endif # BOOTLOADER_MCUBOOT + +endif # (BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP) + +config PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY + default y if BOARD_NRF52840DK_NRF52840 || BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP + +source "${ZEPHYR_BASE}/share/sysbuild/Kconfig" diff --git a/samples/lbm_sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/lbm_sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay index 4395ed1..d0a7679 100644 --- a/samples/lbm_sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/lbm_sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -20,6 +20,37 @@ }; /{ + state_notifier_gpios{ + compatible = "gpio-keys"; + state_notifier_error: error { + gpios = <&gpio1 0x8 0x0>; + label = "Application state error"; + }; + state_notifier_dfu: dfu { + gpios = <&gpio1 0x2 0x0>; + label = "Application state dfu"; + }; + state_notifier_sending: sending { + gpios = <&gpio1 0x4 0x0>; + label = "Application state sending"; + }; + state_notifier_receiving: receiving { + gpios = <&gpio1 0x3 0x0>; + label = "Application state receiving"; + }; + }; + + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + state-notifier-error = &state_notifier_error; + state-notifier-dfu = &state_notifier_dfu; + state-notifier-sending = &state_notifier_sending; + state-notifier-receiving = &state_notifier_receiving; + }; + semtech_sx1262_gpios{ compatible = "gpio-keys"; semtech_sx1262_cs: cs { diff --git a/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf new file mode 100644 index 0000000..d6a0780 --- /dev/null +++ b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Workaround: +# The nRF54L15 PDK (PCA10156) revisions v0.2.0 AA0-ES2, v0.2.0 AA0-ES3, +# and v0.2.1 AB0-ES5 have Buttons 3 and 4 connected to the GPIO port +# which does not support interrupts. +CONFIG_DK_LIBRARY_BUTTON_NO_ISR=y + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay new file mode 100644 index 0000000..2d0f58c --- /dev/null +++ b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +&pinctrl { + spi00_default: spi00_default { + group1 { + psels = , + , + ; + }; + }; + + spi00_sleep: spi00_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + +sid_semtech: &spi00 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi00_default>; + pinctrl-1 = <&spi00_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; +}; + +/{ + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + }; + + semtech_sx1262_gpios{ + compatible = "gpio-keys"; + semtech_sx1262_cs: cs { + gpios = <&gpio2 0xa GPIO_PULL_UP>; + label = "semtech_sx1262 CS"; + }; + semtech_sx1262_reset_gpios: reset { + gpios = <&gpio1 0xb (GPIO_ACTIVE_LOW|GPIO_PULL_UP)>; + label = "semtech_sx1262 Reset"; + }; + semtech_sx1262_busy_gpios: busy { + gpios = <&gpio1 0xc 0x0>; + label = "semtech_sx1262 Busy"; + }; + semtech_sx1262_antenna_enable_gpios: antena_enable { + gpios = <&gpio2 0x7 0x0>; + label = "semtech_sx1262 Antena Enable"; + }; + semtech_sx1262_dio1_gpios: dio1 { + gpios = <&gpio1 0xa 0x0>; + label = "semtech_sx1262 DIO1"; + }; + radio_gnss_lna: gnss_lna { + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + label = "gnss antenna"; + }; + radio_led_sniff: led_sniff { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "yellow LED"; + }; + radio_led_tx: led_tx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "red tx LED"; + }; + radio_led_rx: led_rx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "green rx LED"; + }; + }; + +}; + +&gpiote20 { + status = "okay"; +}; + +&gpiote30 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; diff --git a/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf new file mode 100644 index 0000000..e99da54 --- /dev/null +++ b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay new file mode 100644 index 0000000..5a08ece --- /dev/null +++ b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; + + &pinctrl { + spi21_default: spi21_default { + group1 { + psels = , + , + ; + }; + }; + + spi21_sleep: spi21_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + + sid_semtech: &spi21 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi21_default>; + pinctrl-1 = <&spi21_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; +}; + +/{ + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + }; + + semtech_sx1262_gpios{ + compatible = "gpio-keys"; + semtech_sx1262_cs: cs { + gpios = <&gpio2 0xa GPIO_PULL_UP>; + label = "semtech_sx1262 CS"; + }; + semtech_sx1262_reset_gpios: reset { + gpios = <&gpio0 0x2 (GPIO_ACTIVE_LOW|GPIO_PULL_UP)>; + label = "semtech_sx1262 Reset"; + }; + semtech_sx1262_busy_gpios: busy { + gpios = <&gpio0 0x0 0x0>; + label = "semtech_sx1262 Busy"; + }; + semtech_sx1262_antenna_enable_gpios: antena_enable { + gpios = <&gpio0 0x1 0x0>; + label = "semtech_sx1262 Antena Enable"; + }; + semtech_sx1262_dio1_gpios: dio1 { + gpios = <&gpio0 0x3 0x0>; + label = "semtech_sx1262 DIO1"; + }; + radio_gnss_lna: gnss_lna { + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + label = "gnss antenna"; + }; + radio_led_sniff: led_sniff { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "yellow LED"; + }; + radio_led_tx: led_tx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "red tx LED"; + }; + radio_led_rx: led_rx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "green rx LED"; + }; + }; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; diff --git a/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf new file mode 100644 index 0000000..d6a0780 --- /dev/null +++ b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Workaround: +# The nRF54L15 PDK (PCA10156) revisions v0.2.0 AA0-ES2, v0.2.0 AA0-ES3, +# and v0.2.1 AB0-ES5 have Buttons 3 and 4 connected to the GPIO port +# which does not support interrupts. +CONFIG_DK_LIBRARY_BUTTON_NO_ISR=y + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf new file mode 100644 index 0000000..e99da54 --- /dev/null +++ b/samples/lbm_sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/lbm_sid_end_device/child_image/hci_ipc/Kconfig.root b/samples/lbm_sid_end_device/child_image/hci_ipc/Kconfig.root deleted file mode 100644 index 60e8bd4..0000000 --- a/samples/lbm_sid_end_device/child_image/hci_ipc/Kconfig.root +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# The purpose of this file is to create a wrapper Kconfig file that will be set as -# hci_ipc_KCONFIG_ROOT and processed before any other Kconfig for hci_ipc child image. - -config HEAP_MEM_POOL_SIZE - default 8192 - -config MAIN_STACK_SIZE - default 2048 - -config SYSTEM_WORKQUEUE_STACK_SIZE - default 2048 - -config BT - default y - -config BT_HCI_RAW - default y - -config BT_MAX_CONN - default 1 - -config BT_PERIPHERAL - default y - -config BT_CENTRAL - default n - -config BT_BUF_ACL_RX_SIZE - default 502 - -config BT_BUF_ACL_TX_SIZE - default 251 - -config BT_CTLR_DATA_LENGTH_MAX - default 251 - -config BT_CTLR_ASSERT_HANDLER - default y - -config BT_HCI_RAW_RESERVE - default 1 - -# Workaround: Unable to allocate command buffer when using K_NO_WAIT since -# Host number of completed commands does not follow normal flow control. -config BT_BUF_CMD_TX_COUNT - default 10 - -config ASSERT - default y - -config DEBUG_INFO - default y - -config EXCEPTION_STACK_TRACE - default y - -config IPC_SERVICE - default y - -config MBOX - default y - -source "Kconfig.zephyr" diff --git a/samples/lbm_sid_end_device/child_image/hci_ipc/prj.conf b/samples/lbm_sid_end_device/child_image/hci_ipc/prj.conf deleted file mode 100644 index c89bda6..0000000 --- a/samples/lbm_sid_end_device/child_image/hci_ipc/prj.conf +++ /dev/null @@ -1,8 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/lbm_sid_end_device/child_image/hci_ipc/prj_no_dfu.conf b/samples/lbm_sid_end_device/child_image/hci_ipc/prj_no_dfu.conf deleted file mode 100644 index c89bda6..0000000 --- a/samples/lbm_sid_end_device/child_image/hci_ipc/prj_no_dfu.conf +++ /dev/null @@ -1,8 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/lbm_sid_end_device/child_image/hci_ipc/prj_release.conf b/samples/lbm_sid_end_device/child_image/hci_ipc/prj_release.conf deleted file mode 100644 index 86c0d92..0000000 --- a/samples/lbm_sid_end_device/child_image/hci_ipc/prj_release.conf +++ /dev/null @@ -1,9 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_RESET_ON_FATAL_ERROR=y diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/Kconfig.root b/samples/lbm_sid_end_device/child_image/mcuboot/Kconfig.root deleted file mode 100644 index d6ecb28..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/Kconfig.root +++ /dev/null @@ -1,127 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# The purpose of this file is to create a wrapper Kconfig file that will be set as -# mcuboot_KCONFIG_ROOT and processed before any other Kconfig for mcuboot child image. - - -config MAIN_STACK_SIZE - default 10240 - -config BOOT_SWAP_SAVE_ENCTLV - default n - -config BOOT_ENCRYPT_RSA - default n - -config BOOT_ENCRYPT_EC256 - default n - -config BOOT_ENCRYPT_X25519 - default n - -config BOOT_BOOTSTRAP - default n - -config PM - default n - -config FLASH - default y - -config FPROTECT - default y - -config NORDIC_QSPI_NOR - default y - -config NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE - default 4096 - -config NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE - default 16 - -config BOOT_MAX_IMG_SECTORS - default 256 - -config LOG - default n - -choice LIBC_IMPLEMENTATION - default MINIMAL_LIBC -endchoice - -config COMMON_LIBC_CALLOC - default y - -config COMMON_LIBC_MALLOC - default y - -config COMMON_LIBC_REALLOCARRAY - default y - -config NCS_SAMPLES_DEFAULTS - default n - -config PRINTK - default n - -config REBOOT - default n - -config NRF_RTC_TIMER - default y if SOC_SERIES_NRF53X - default n - -config CONSOLE - default n - -config CONSOLE_HANDLER - default n - -config GPIO - default n - -config KERNEL_MEM_POOL - default n - -config ASSERT - default n - -config BOOT_BANNER - default n - -config SERIAL - default n - -config UART_CONSOLE - default n - -config TIMESLICING - default n - -config USE_SEGGER_RTT - default n - -config RESET_ON_FATAL_ERROR - default n - -config SECURE_BOOT_DEBUG - default n - -config MULTITHREADING - default n - -config TICKLESS_KERNEL - default n - -config TIMEOUT_64BIT - default n - -config NRF_ENABLE_ICACHE - default n - -source "${ZEPHYR_BASE}/../bootloader/mcuboot/boot/zephyr/Kconfig" diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay b/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay deleted file mode 100644 index 69bf975..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay b/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay deleted file mode 100644 index 69bf975..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 3664dd3..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - -# Configure MCUboot features -CONFIG_NRF53_MULTI_IMAGE_UPDATE=y -CONFIG_BOOT_UPGRADE_ONLY=y -CONFIG_BOOT_MAX_IMG_SECTORS=256 -CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Enable flash simulator -CONFIG_PCD_APP=y -CONFIG_FLASH_SIMULATOR=y -CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y -CONFIG_FLASH_SIMULATOR_STATS=n - -# Configure QSPI for external flash -CONFIG_FLASH=y -CONFIG_FPROTECT=y -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf b/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf deleted file mode 100644 index 3664dd3..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - -# Configure MCUboot features -CONFIG_NRF53_MULTI_IMAGE_UPDATE=y -CONFIG_BOOT_UPGRADE_ONLY=y -CONFIG_BOOT_MAX_IMG_SECTORS=256 -CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Enable flash simulator -CONFIG_PCD_APP=y -CONFIG_FLASH_SIMULATOR=y -CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y -CONFIG_FLASH_SIMULATOR_STATS=n - -# Configure QSPI for external flash -CONFIG_FLASH=y -CONFIG_FPROTECT=y -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay b/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay deleted file mode 100644 index c670799..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - - / { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/prj.conf b/samples/lbm_sid_end_device/child_image/mcuboot/prj.conf deleted file mode 100644 index 78d7622..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/prj.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" - -CONFIG_BOOT_UPGRADE_ONLY=n -CONFIG_RESET_ON_FATAL_ERROR=y - -CONFIG_LOG=n -CONFIG_PRINTK=n -CONFIG_CONSOLE_HANDLER=n -CONFIG_ASSERT=n -CONFIG_BOOT_BANNER=n -CONFIG_CONSOLE=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_USE_SEGGER_RTT=n -CONFIG_GPIO=n -CONFIG_NO_RUNTIME_CHECKS=y -CONFIG_SIZE_OPTIMIZATIONS=y diff --git a/samples/lbm_sid_end_device/child_image/mcuboot/prj_release.conf b/samples/lbm_sid_end_device/child_image/mcuboot/prj_release.conf deleted file mode 100644 index 78d7622..0000000 --- a/samples/lbm_sid_end_device/child_image/mcuboot/prj_release.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" - -CONFIG_BOOT_UPGRADE_ONLY=n -CONFIG_RESET_ON_FATAL_ERROR=y - -CONFIG_LOG=n -CONFIG_PRINTK=n -CONFIG_CONSOLE_HANDLER=n -CONFIG_ASSERT=n -CONFIG_BOOT_BANNER=n -CONFIG_CONSOLE=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_USE_SEGGER_RTT=n -CONFIG_GPIO=n -CONFIG_NO_RUNTIME_CHECKS=y -CONFIG_SIZE_OPTIMIZATIONS=y diff --git a/samples/lbm_sid_end_device/configuration/pm_static_no_dfu.yml b/samples/lbm_sid_end_device/configuration/pm_static_no_dfu.yml deleted file mode 100644 index 658ce75..0000000 --- a/samples/lbm_sid_end_device/configuration/pm_static_no_dfu.yml +++ /dev/null @@ -1,5 +0,0 @@ -mfg_storage: - address: 0xff000 - end_address: 0x100000 - region: flash_primary - size: 0x1000 diff --git a/samples/lbm_sid_end_device/include/app.h b/samples/lbm_sid_end_device/include/app.h index 077f3ca..6eff418 100644 --- a/samples/lbm_sid_end_device/include/app.h +++ b/samples/lbm_sid_end_device/include/app.h @@ -4,9 +4,14 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#ifndef SAMPLE_APP_H +#define SAMPLE_APP_H + /** * @brief Start Sidewalk end device application. * * @note This function should never return. */ void app_start(void); + +#endif /* SAMPLE_APP_H */ diff --git a/samples/lbm_sid_end_device/include/file_transfer.h b/samples/lbm_sid_end_device/include/file_transfer.h deleted file mode 100644 index ad62fc4..0000000 --- a/samples/lbm_sid_end_device/include/file_transfer.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#include - -struct data_received_args { - struct sid_bulk_data_transfer_desc desc; - struct sid_bulk_data_transfer_buffer *buffer; - void *context; -}; - -void app_file_transfer_demo_init(struct sid_handle *handle); - -void app_file_transfer_demo_deinit(struct sid_handle *handle); diff --git a/samples/lbm_sid_end_device/include/sbdt/file_transfer.h b/samples/lbm_sid_end_device/include/sbdt/file_transfer.h new file mode 100644 index 0000000..c51eb1b --- /dev/null +++ b/samples/lbm_sid_end_device/include/sbdt/file_transfer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef FILE_TRANSFER_H +#define FILE_TRANSFER_H + +#include + +/** + * @brief Initilize Sidewalk Bulk Data Transfer module. + * + * @param handle Sidewalk handle given by sid_init. + */ +void app_file_transfer_demo_init(struct sid_handle *handle); + +/** + * @brief Deinitilize Sidewalk Bulk Data Transfer module. + * + * @param handle Sidewalk handle given by sid_init. + */ +void app_file_transfer_demo_deinit(struct sid_handle *handle); + +#endif /* FILE_TRANSFER_H */ diff --git a/samples/lbm_sid_end_device/include/sbdt/scratch_buffer.h b/samples/lbm_sid_end_device/include/sbdt/scratch_buffer.h new file mode 100644 index 0000000..7de934e --- /dev/null +++ b/samples/lbm_sid_end_device/include/sbdt/scratch_buffer.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef SCRATCH_BUFFER_H +#define SCRATCH_BUFFER_H + +#include +#include + +/** + * @brief Init scratch buffer + * + * Inilizing module is neccessary to find files by id. + * + */ +void scratch_buffer_init(void); + +/** + * @brief Create scratch buffor assigned for file id. + * + * @param id file id + * @param size size of file buffer + * @return void* pointer to allocated memory. NULL on error. + */ +void *scratch_buffer_create(uint32_t id, size_t size); + +/** + * @brief Remove scratch buffer for file id. + * + * @param id file id + */ +void scratch_buffer_remove(uint32_t id); + +#endif /* SCRATCH_BUFFER_H */ diff --git a/samples/lbm_sid_end_device/include/sidewalk.h b/samples/lbm_sid_end_device/include/sidewalk.h index 2c30c09..82795d9 100644 --- a/samples/lbm_sid_end_device/include/sidewalk.h +++ b/samples/lbm_sid_end_device/include/sidewalk.h @@ -12,6 +12,9 @@ #include #include +#include +#include + #define LBM_STACK_ID 0 enum state { @@ -42,6 +45,7 @@ typedef enum { #ifdef NAV3_SEND /* */ SID_EVENT_NAV3_SEND, #endif /* !NAV3_SEND */ + /* */ SID_EVENT_REBOOT, /* */ SID_EVENT_LAST, } sidewalk_event_t; @@ -104,6 +108,7 @@ typedef struct sm_s { } sm_t; typedef struct { + sys_snode_t node; struct sid_msg msg; struct sid_msg_desc desc; } sidewalk_msg_t; @@ -114,10 +119,19 @@ typedef struct { size_t data_len; } sidewalk_option_t; +typedef struct { + uint32_t file_id; + size_t file_offset; + void *data; + size_t data_size; +} sidewalk_transfer_t; + void sidewalk_start(sidewalk_ctx_t *context); int sidewalk_event_send(sidewalk_event_t event, void *ctx); +sidewalk_msg_t *get_message_buffer(uint16_t message_id); + extern const struct smf_state sid_states[]; void app_event_lbm(void); #if defined(CONFIG_LBM_END_DEVICE) diff --git a/samples/lbm_sid_end_device/pm_static.yml b/samples/lbm_sid_end_device/pm_static.yml deleted file mode 100644 index 55dba59..0000000 --- a/samples/lbm_sid_end_device/pm_static.yml +++ /dev/null @@ -1,10 +0,0 @@ -mcuboot: - address: 0x0 - end_address: 0x8000 - region: flash_primary - size: 0x8000 -mfg_storage: - address: 0xff000 - end_address: 0x100000 - region: flash_primary - size: 0x1000 diff --git a/samples/sid_end_device/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml b/samples/lbm_sid_end_device/pm_static_nrf52840dk_nrf52840.yml similarity index 100% rename from samples/sid_end_device/configuration/nrf52840dk_nrf52840/pm_static_dfu.yml rename to samples/lbm_sid_end_device/pm_static_nrf52840dk_nrf52840.yml diff --git a/samples/lbm_sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml b/samples/lbm_sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml new file mode 100644 index 0000000..10bf45a --- /dev/null +++ b/samples/lbm_sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml @@ -0,0 +1,73 @@ +app: + address: 0x7200 + end_address: 0xfd000 + region: flash_primary + size: 0xf5e00 +external_flash: + address: 0xf6000 + end_address: 0x800000 + region: external_flash + size: 0x70a000 +mcuboot: + address: 0x0 + end_address: 0x7000 + region: flash_primary + size: 0x7000 +mcuboot_pad: + address: 0x7000 + end_address: 0x7200 + placement: + align: + start: 0x1000 + before: + - mcuboot_primary_app + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x7000 + end_address: 0xfd000 + orig_span: &id001 + - app + - mcuboot_pad + region: flash_primary + size: 0xf6000 + span: *id001 +mcuboot_primary_app: + address: 0x7200 + end_address: 0xfd000 + orig_span: &id002 + - app + region: flash_primary + size: 0xf5e00 + span: *id002 +mcuboot_secondary: + address: 0x0 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0xf6000 + placement: + align: + start: 0x4 + region: external_flash + share_size: + - mcuboot_primary + size: 0xf6000 +mfg_storage: + address: 0xff000 + end_address: 0x100000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0xfd000 + end_address: 0xff000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/sid_end_device/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml b/samples/lbm_sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp.yml similarity index 100% rename from samples/sid_end_device/configuration/nrf5340dk_nrf5340_cpuapp/pm_static_dfu.yml rename to samples/lbm_sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp.yml diff --git a/samples/lbm_sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml b/samples/lbm_sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml new file mode 100644 index 0000000..5764ee5 --- /dev/null +++ b/samples/lbm_sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml @@ -0,0 +1,119 @@ +EMPTY_0: + address: 0xfe000 + end_address: 0xff000 + placement: + after: + - settings_storage + region: flash_primary + size: 0x1000 +app: + address: 0x8200 + end_address: 0xfc000 + region: flash_primary + size: 0xf3e00 +external_flash: + address: 0x134000 + end_address: 0x800000 + region: external_flash + size: 0x6cc000 +mcuboot: + address: 0x0 + end_address: 0x8000 + region: flash_primary + size: 0x8000 +mcuboot_pad: + address: 0x8000 + end_address: 0x8200 + placement: + align: + start: 0x4000 + before: + - mcuboot_primary_app + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x8000 + end_address: 0xfc000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + size: 0xf4000 + span: *id001 +mcuboot_primary_1: + address: 0x0 + device: nordic_ram_flash_controller + end_address: 0x40000 + region: ram_flash + size: 0x40000 +mcuboot_primary_app: + address: 0x8200 + end_address: 0xfc000 + orig_span: &id002 + - app + region: flash_primary + size: 0xf3e00 + span: *id002 +mcuboot_secondary: + address: 0x0 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0xf4000 + placement: + align: + start: 0x4 + region: external_flash + share_size: + - mcuboot_primary + size: 0xf4000 +mcuboot_secondary_1: + address: 0xf4000 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0x134000 + region: external_flash + size: 0x40000 +mfg_storage: + address: 0xff000 + end_address: 0x100000 + region: flash_primary + size: 0x1000 +otp: + address: 0xff8100 + end_address: 0xff83fc + region: otp + size: 0x2fc +pcd_sram: + address: 0x20000000 + end_address: 0x20002000 + placement: + after: + - start + region: sram_primary + size: 0x2000 +ram_flash: + address: 0x40000 + end_address: 0x40000 + region: ram_flash + size: 0x0 +rpmsg_nrf53_sram: + address: 0x20070000 + end_address: 0x20080000 + placement: + before: + - end + region: sram_primary + size: 0x10000 +settings_storage: + address: 0xfc000 + end_address: 0xfe000 + placement: + align: + start: 0x4000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20002000 + end_address: 0x20070000 + region: sram_primary + size: 0x6e000 diff --git a/samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml b/samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml new file mode 100644 index 0000000..2aae80f --- /dev/null +++ b/samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml @@ -0,0 +1,71 @@ +app: + address: 0xc800 + end_address: 0xc3000 + region: flash_primary + size: 0xb6800 +mcuboot: + address: 0x0 + end_address: 0xc000 + placement: + before: + - mcuboot_primary + region: flash_primary + size: 0xc000 +mcuboot_pad: + address: 0xc000 + end_address: 0xc800 + placement: + before: + - mcuboot_primary_app + region: flash_primary + size: 0x800 +mcuboot_primary: + address: 0xc000 + end_address: 0xc3000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + sharers: 0x1 + size: 0xb7000 + span: *id001 +mcuboot_primary_app: + address: 0xc800 + end_address: 0xc3000 + orig_span: &id002 + - app + region: flash_primary + size: 0xb6800 + span: *id002 +mcuboot_secondary: + address: 0xc3000 + end_address: 0x17a000 + placement: + after: + - mcuboot_primary + align: + start: 0x1000 + region: flash_primary + share_size: + - mcuboot_primary + size: 0xb7000 +mfg_storage: + address: 0x17c000 + end_address: 0x17d000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0x17a000 + end_address: 0x17c000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml b/samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml new file mode 100644 index 0000000..2aae80f --- /dev/null +++ b/samples/lbm_sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml @@ -0,0 +1,71 @@ +app: + address: 0xc800 + end_address: 0xc3000 + region: flash_primary + size: 0xb6800 +mcuboot: + address: 0x0 + end_address: 0xc000 + placement: + before: + - mcuboot_primary + region: flash_primary + size: 0xc000 +mcuboot_pad: + address: 0xc000 + end_address: 0xc800 + placement: + before: + - mcuboot_primary_app + region: flash_primary + size: 0x800 +mcuboot_primary: + address: 0xc000 + end_address: 0xc3000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + sharers: 0x1 + size: 0xb7000 + span: *id001 +mcuboot_primary_app: + address: 0xc800 + end_address: 0xc3000 + orig_span: &id002 + - app + region: flash_primary + size: 0xb6800 + span: *id002 +mcuboot_secondary: + address: 0xc3000 + end_address: 0x17a000 + placement: + after: + - mcuboot_primary + align: + start: 0x1000 + region: flash_primary + share_size: + - mcuboot_primary + size: 0xb7000 +mfg_storage: + address: 0x17c000 + end_address: 0x17d000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0x17a000 + end_address: 0x17c000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/lbm_sid_end_device/configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml b/samples/lbm_sid_end_device/pm_static_thingy53_nrf5340_cpuapp.yml similarity index 100% rename from samples/lbm_sid_end_device/configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml rename to samples/lbm_sid_end_device/pm_static_thingy53_nrf5340_cpuapp.yml diff --git a/samples/lbm_sid_end_device/prj_no_dfu.conf b/samples/lbm_sid_end_device/prj_no_dfu.conf deleted file mode 100644 index 3f14d8c..0000000 --- a/samples/lbm_sid_end_device/prj_no_dfu.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2023 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Sidewalk -CONFIG_SIDEWALK=y -CONFIG_SIDEWALK_DFU=n -CONFIG_SMF=y - -# Log -CONFIG_LOG=y -CONFIG_LOG_PRINTK=y -CONFIG_LOG_BUFFER_SIZE=2048 -CONFIG_NVS_LOG_LEVEL_WRN=y - -# Bluetooth -CONFIG_BT_DEVICE_NAME="Nordic" - -# Debug -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/lbm_sid_end_device/sample.yaml b/samples/lbm_sid_end_device/sample.yaml index a61c19d..e4635fc 100644 --- a/samples/lbm_sid_end_device/sample.yaml +++ b/samples/lbm_sid_end_device/sample.yaml @@ -1,96 +1,62 @@ sample: name: Sidewalk end device sample description: Sample implementing Amazon Sidewalk End Device +common: + sysbuild: true + build_only: true + platform_allow: + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54l15pdk/nrf54l15/cpuapp + integration_platforms: + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54l15pdk/nrf54l15/cpuapp tests: sample.sidewalk.hello: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp + - CONFIG_SIDEWALK_FILE_TRANSFER=y tags: Sidewalk hello sample.sidewalk.hello.release: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: - CONF_FILE=prj_release.conf + FILE_SUFFIX=release extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp - tags: Sidewalk hello - - sample.sidewalk.hello.no_dfu: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp - extra_args: - CONF_FILE=prj_no_dfu.conf - extra_configs: - - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello sample.sidewalk.hello.ble_only: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello BLE sample.sidewalk.hello.ble_only.release: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: - CONF_FILE=prj_release.conf + FILE_SUFFIX=release extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello BLE sample.sidewalk.demo: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: OVERLAY_CONFIG="overlay-demo.conf" extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk demo sample.sidewalk.demo.ble_only: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp thingy53_nrf5340_cpuapp + platform_allow: + - thingy53/nrf5340/cpuapp extra_args: OVERLAY_CONFIG="overlay-demo.conf" extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp - - thingy53_nrf5340_cpuapp + - thingy53/nrf5340/cpuapp tags: Sidewalk demo BLE sample.sidewalk.dut: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: OVERLAY_CONFIG="overlay-dut.conf" - extra_configs: - - CONFIG_SIDEWALK_FILE_TRANSFER=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk cli diff --git a/samples/lbm_sid_end_device/src/cli/app.c b/samples/lbm_sid_end_device/src/cli/app.c index 0dad5c5..97eef5e 100644 --- a/samples/lbm_sid_end_device/src/cli/app.c +++ b/samples/lbm_sid_end_device/src/cli/app.c @@ -11,8 +11,7 @@ #include #include #include -#include -#include +#include #include LOG_MODULE_REGISTER(app, CONFIG_SIDEWALK_LOG_LEVEL); @@ -40,6 +39,13 @@ static void on_sidewalk_msg_sent(const struct sid_msg_desc *msg_desc, void *cont LOG_INF("Message send success"); printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( "on_msg_sent", JSON_OBJ(JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0)))))); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc *msg_desc, @@ -50,6 +56,13 @@ static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc "on_send_error", JSON_OBJ(JSON_LIST_2(JSON_VAL_sid_error_t("error", error), JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0))))))); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_factory_reset(void *context) diff --git a/samples/lbm_sid_end_device/src/cli/app_dut.c b/samples/lbm_sid_end_device/src/cli/app_dut.c index fbbe28e..9788694 100644 --- a/samples/lbm_sid_end_device/src/cli/app_dut.c +++ b/samples/lbm_sid_end_device/src/cli/app_dut.c @@ -4,13 +4,14 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#include "sid_error.h" #include #include #include #include #include #include -#include +#include LOG_MODULE_REGISTER(sid_cli, CONFIG_SIDEWALK_LOG_LEVEL); @@ -76,7 +77,7 @@ static void dut_option_get(sidewalk_option_t *p_option, struct sid_handle *handl case SID_OPTION_GET_LINK_POLICY_AUTO_CONNECT_PARAMS: { struct sid_link_auto_connect_params params = { 0 }; memcpy(¶ms.link_type, p_option->data, sizeof(uint32_t)); - sid_error_t e = sid_option(handle, opt, ¶ms, sizeof(params)); + sid_error_t e = sid_option(handle, opt, (void *)¶ms, sizeof(params)); LOG_INF("sid_option returned %d; AC Policy, link %d, enable %d priority %d timeout %d", e, params.link_type, params.enable, params.priority, params.connection_attempt_timeout_seconds); @@ -123,6 +124,9 @@ void app_dut_event_process(sidewalk_ctx_event_t event, sidewalk_ctx_t *sid) sid->config.link_mask = dut_ctx_get_uint32(event.ctx); sid_error_t e = sid_init(&sid->config, &sid->handle); LOG_INF("sid_init returned %d", e); + if (e != SID_ERROR_NONE) { + return; + } #ifdef CONFIG_SIDEWALK_FILE_TRANSFER app_file_transfer_demo_init(sid->handle); #endif @@ -157,13 +161,13 @@ void app_dut_event_process(sidewalk_ctx_event_t event, sidewalk_ctx_t *sid) case DUT_EVENT_GET_MTU: { uint32_t link_mask = dut_ctx_get_uint32(event.ctx); size_t mtu = 0; - sid_error_t e = sid_get_mtu(sid->handle, link_mask, &mtu); + sid_error_t e = sid_get_mtu(sid->handle, (enum sid_link_type)link_mask, &mtu); LOG_INF("sid_get_mtu returned %d, MTU: %d", e, mtu); } break; case DUT_EVENT_GET_TIME: { uint32_t format = dut_ctx_get_uint32(event.ctx); struct sid_timespec curr_time = { 0 }; - sid_error_t e = sid_get_time(sid->handle, format, &curr_time); + sid_error_t e = sid_get_time(sid->handle, (enum sid_time_format)format, &curr_time); LOG_INF("sid_get_time returned %d, SEC: %d NSEC: %d", e, curr_time.tv_sec, curr_time.tv_nsec); } break; diff --git a/samples/lbm_sid_end_device/src/cli/app_shell.c b/samples/lbm_sid_end_device/src/cli/app_shell.c index 5cffbc1..728dd31 100644 --- a/samples/lbm_sid_end_device/src/cli/app_shell.c +++ b/samples/lbm_sid_end_device/src/cli/app_shell.c @@ -227,7 +227,7 @@ static int cmd_sid_option(cli_event_t event, enum sid_option option, void *data, p_opt->data = NULL; } - int err = sidewalk_event_send(event, p_opt); + int err = sidewalk_event_send((sidewalk_event_t)event, p_opt); if (err) { if (p_opt->data) { sid_hal_free(p_opt->data); @@ -262,7 +262,7 @@ static int cmd_sid_simple_param(cli_event_t event, uint32_t *data) } memcpy(event_ctx, data, sizeof(uint32_t)); - int err = sidewalk_event_send(event, event_ctx); + int err = sidewalk_event_send((sidewalk_event_t)event, event_ctx); if (err) { sid_hal_free(event_ctx); return -ENOMSG; @@ -282,10 +282,12 @@ int cmd_sid_init(const struct shell *shell, int32_t argc, const char **argv) uint32_t link_type = 0; if (!IN_RANGE(connection_type, CLI_CMD_OPT_LINK_BLE, CLI_CMD_OPT_LINK_ANY)) { + shell_error(shell, "invalid value"); return -EINVAL; } if (!cli_parse_link_mask_opt(connection_type, &link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } cli_cfg.send_link_type = link_type; @@ -297,7 +299,7 @@ int cmd_sid_deinit(const struct shell *shell, int32_t argc, const char **argv) { CHECK_ARGUMENT_COUNT(argc, CMD_SID_DEINIT_ARG_REQUIRED, CMD_SID_DEINIT_ARG_OPTIONAL); - return sidewalk_event_send(DUT_EVENT_DEINIT, NULL); + return sidewalk_event_send((sidewalk_event_t)DUT_EVENT_DEINIT, NULL); } int cmd_sid_start(const struct shell *shell, int32_t argc, const char **argv) @@ -309,6 +311,7 @@ int cmd_sid_start(const struct shell *shell, int32_t argc, const char **argv) link_type = cli_cfg.send_link_type; } else { if (!cli_parse_link_mask_opt(atoi(argv[1]), &link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } } @@ -325,6 +328,7 @@ int cmd_sid_stop(const struct shell *shell, int32_t argc, const char **argv) link_type = cli_cfg.send_link_type; } else { if (!cli_parse_link_mask_opt(atoi(argv[1]), &link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } } @@ -419,6 +423,7 @@ int cmd_sid_send(const struct shell *shell, int32_t argc, const char **argv) } if (!cli_parse_link_mask_opt(atoi(argv[opt]), &desc.link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } continue; @@ -497,7 +502,7 @@ int cmd_sid_send(const struct shell *shell, int32_t argc, const char **argv) memcpy(&send->msg, &msg, sizeof(struct sid_msg)); memcpy(&send->desc, &desc, sizeof(struct sid_msg_desc)); - int err = sidewalk_event_send(SID_EVENT_SEND_MSG, send); + int err = sidewalk_event_send((sidewalk_event_t)SID_EVENT_SEND_MSG, send); if (err) { sid_hal_free(send->msg.data); sid_hal_free(send); @@ -512,7 +517,7 @@ int cmd_sid_factory_reset(const struct shell *shell, int32_t argc, const char ** CHECK_ARGUMENT_COUNT(argc, CMD_SID_FACTORY_RESET_ARG_REQUIRED, CMD_SID_FACTORY_RESET_ARG_OPTIONAL); - int err = sidewalk_event_send(SID_EVENT_FACTORY_RESET, NULL); + int err = sidewalk_event_send((sidewalk_event_t)SID_EVENT_FACTORY_RESET, NULL); if (err) { shell_error(shell, "event err %d", err); } @@ -537,6 +542,7 @@ int cmd_sid_get_mtu(const struct shell *shell, int32_t argc, const char **argv) link_mask = SID_LINK_TYPE_3; break; default: + shell_error(shell, "invalid value"); return -EINVAL; } @@ -584,6 +590,7 @@ int cmd_sid_option_lp_set(const struct shell *shell, int32_t argc, const char ** data_raw = strtol(argv[1], &end, 0); if (end == argv[1]) { + shell_error(shell, "invalid value"); return -EINVAL; } if (!IN_RANGE(data_raw, 0, UINT8_MAX)) { @@ -885,7 +892,10 @@ int cmd_sid_option_gc(const struct shell *shell, int32_t argc, const char **argv } memset(p_link_mask, 0x0, sizeof(*p_link_mask)); - cli_parse_link_mask_opt((uint8_t)link_type, p_link_mask); + if (!cli_parse_link_mask_opt((uint8_t)link_type, p_link_mask)) { + shell_error(shell, "Can not parse link mask"); + return -EINVAL; + } int err = cmd_sid_option_get_input_data(SID_OPTION_GET_LINK_POLICY_AUTO_CONNECT_PARAMS, p_link_mask, sizeof(uint32_t)); @@ -901,7 +911,9 @@ int cmd_sid_last_status(const struct shell *shell, int32_t argc, const char **ar CHECK_ARGUMENT_COUNT(argc, CMD_SID_LAST_STATUS_ARG_REQUIRED, CMD_SID_LAST_STATUS_ARG_OPTIONAL); - sidewalk_event_send(DUT_EVENT_GET_STATUS, NULL); + if (0 != sidewalk_event_send((sidewalk_event_t)DUT_EVENT_GET_STATUS, NULL)) { + shell_error(shell, "Failed to send Event"); + } return 0; } @@ -919,6 +931,7 @@ int cmd_sid_conn_request(const struct shell *shell, int32_t argc, const char **a conn_req = 0U; break; default: + shell_error(shell, "invalid value"); return -EINVAL; } @@ -930,6 +943,7 @@ int cmd_sid_get_time(const struct shell *shell, int32_t argc, const char **argv) CHECK_ARGUMENT_COUNT(argc, CMD_SID_GET_TIME_ARG_REQUIRED, CMD_SID_GET_TIME_ARG_OPTIONAL); if (argv[1][0] != '0') { + shell_error(shell, "invalid value"); return -EINVAL; } uint32_t time_type = SID_GET_GPS_TIME; @@ -970,6 +984,7 @@ int cmd_sid_set_send_link(const struct shell *shell, int32_t argc, const char ** break; } default: { + shell_error(shell, "invalid value"); return -EINVAL; } } diff --git a/samples/lbm_sid_end_device/src/cli/sid_on_dev_cert_cli.c b/samples/lbm_sid_end_device/src/cli/sid_on_dev_cert_cli.c new file mode 100644 index 0000000..e6d1df5 --- /dev/null +++ b/samples/lbm_sid_end_device/src/cli/sid_on_dev_cert_cli.c @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include + +#include +#include + +#include + +LOG_MODULE_REGISTER(sid_dev_cert_shell, CONFIG_SIDEWALK_LOG_LEVEL); + +#ifdef SYNTAX_ERR +#undef SYNTAX_ERR +#endif +#define SYNTAX_ERR "Syntax err \r\n" + +#define CERT_INIT_H "Initialization of On-Device Certificate Generation library" +#define CERT_DEINIT_H "Deinitialization of On-Device Certificate Generation Library" +#define CERT_SMSN_H "Generate Sidewalk Manufacturing Serial Number (SMSN)" +#define CERT_CSR_H "Generate Certificate Signing Request (CSR)" +#define CERT_CHAIN_H "Write Sidewalk Certificate Chain" +#define CERT_CHAIN_START_H "Start writing of Sidewalk Certificate Chain" +#define CERT_CHAIN_WRITE_H "Write data fragment to Sidewalk Certificate Chain" +#define CERT_CHAIN_COMMIT_H "Commit previously writed Sidewalk Certificate Chain" +#define CERT_APPKEY_H "Write application server ED25519 public key" +#define CERT_APPKEY_START_H "Start writing of app server ED25519 public key" +#define CERT_APPKEY_WRITE_H "Write data fragment to app server ED25519 public key" +#define CERT_APPKEY_COMMIT_H "Commit previously writed app server ED25519 public key" +#define CERT_STORE_H "Verify and store Sidewalk certificates" + +#define CERT_INIT_CMD "cert init" +#define CERT_DEINIT_CMD "cert deinit" +#define CERT_SMSN_CMD "cert smsn []" +#define CERT_CSR_CMD "cert csr " +#define CERT_CHAIN_START_CMD "cert chain start " +#define CERT_CHAIN_WRITE_CMD "cert chain write " +#define CERT_CHAIN_COMMIT_CMD "cert chain commit" +#define CERT_APPKEY_START_CMD "cert app_key start" +#define CERT_APPKEY_WRITE_CMD "cert app_key write " +#define CERT_APPKEY_COMMIT_CMD "cert app_key commit" +#define CERT_STORE_CMD "cert store" + +#define CERT_MSG_OK "{CERT OK}" +#define CERT_MSG_ERROR "{CERT ERROR %d}" +#define CERT_MSG_BASE64 "{CERT <%s>}" +#define CERT_MSG_BASE64_MTU "{CERT BASE64_MTU=%d}" +#define CERT_ED25519_STR "ed25519" +#define CERT_P256R1_STR "p256r1" +#define CERT_DEV_TYPE_SUFFIX "-PRODUCTION" + +#define CERT_BASE64_FRAGMENT_MAX_SIZE 32 + +static uint8_t cert_app_key[SID_ODC_ED25519_PUK_SIZE]; +static uint8_t cert_chain_buffer[SID_ODC_SCC_MAX_SIZE]; +static struct sid_on_dev_cert_chain_params cert_chain_params = { + .cert_chain = NULL, + .cert_chain_size = 0, +}; + +// Persistent state for receiving the encoded certificate chain in multiple fragments +static struct sid_base64_ctx cert_chain_base64_ctx = { + .next_out = NULL, + .avail_out = 0, + .total_out = 0, + .state = 0, +}; + +static struct sid_base64_ctx cert_app_key_base64_ctx = { + .next_out = NULL, + .avail_out = 0, + .total_out = 0, + .state = 0, +}; + +static sid_error_t sid_on_dev_cert_cli_print_base64(const struct shell *shell, const uint8_t *input, + size_t input_size) +{ + struct sid_base64_ctx ctx; + sid_base64_init(&ctx); + uint8_t out[CERT_BASE64_FRAGMENT_MAX_SIZE + 1]; // allow extra byte for null terminator + + ctx.next_in = input; + ctx.avail_in = input_size; + ctx.next_out = out; + ctx.avail_out = CERT_BASE64_FRAGMENT_MAX_SIZE; + + while (true) { + sid_error_t status = sid_base64_encode(&ctx, true); + if (status == SID_ERROR_NONE || status == SID_ERROR_BUFFER_OVERFLOW) { + // add a null terminator + *ctx.next_out = '\0'; + + shell_info(shell, CERT_MSG_BASE64, out); + + if (status == SID_ERROR_NONE) { + break; + } + // Reset the output buffer for the next line + ctx.next_out = out; + ctx.avail_out = CERT_BASE64_FRAGMENT_MAX_SIZE; + } else { + return SID_ERROR_GENERIC; + } + } + return SID_ERROR_NONE; +} + +static int sid_on_dev_cert_cli_init_cmd(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 1) { + sid_error_t ret = sid_on_dev_cert_init(); + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_BASE64_MTU, CERT_BASE64_FRAGMENT_MAX_SIZE); + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_INIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_deinit(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 1) { + sid_on_dev_cert_deinit(); + + shell_info(shell, CERT_MSG_OK); + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_DEINIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_smsn(const struct shell *shell, int32_t argc, const char **argv) +{ + // command format: cert smsn [] + if (argc >= 4 && argc <= 5) { + sid_error_t ret = SID_ERROR_NONE; + // Note on device-type. It gets transformed into "Amazon-id" which is -PRODUCTION + char *dev_type_production = + sid_hal_malloc(strlen(argv[1]) + strlen(CERT_DEV_TYPE_SUFFIX) + 1); + if (!dev_type_production) { + ret = SID_ERROR_OOM; + goto exit; + } + dev_type_production[0] = 0; + strcat(dev_type_production, argv[1]); + strcat(dev_type_production, CERT_DEV_TYPE_SUFFIX); + + const struct sid_on_dev_cert_info dev_info = { + .dev_type = dev_type_production, + .dsn = argv[2], + .apid = argv[3], + .board_id = argc == 5 ? argv[4] : NULL, + }; + + uint8_t smsn[SID_ODC_SMSN_SIZE]; + memset(smsn, 0xFF, SID_ODC_SMSN_SIZE); + if ((ret = sid_on_dev_cert_generate_smsn(&dev_info, smsn)) == SID_ERROR_NONE) { + ret = sid_on_dev_cert_cli_print_base64(shell, smsn, SID_ODC_SMSN_SIZE); + } + + sid_hal_free(dev_type_production); + exit: + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_SMSN_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_csr(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + uint8_t csr[SID_ODC_CSR_MAX_SIZE]; + size_t csr_size = SID_ODC_CSR_MAX_SIZE; + enum sid_on_dev_cert_algo_type algo; + + if (strcmp(argv[1], CERT_ED25519_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_ED25519; + } else if (strcmp(argv[1], CERT_P256R1_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_P256R1; + } else { + ret = SID_ERROR_INVALID_ARGS; + } + + if (ret == SID_ERROR_NONE && + (ret = sid_on_dev_cert_generate_csr(algo, csr, &csr_size)) == SID_ERROR_NONE && + (ret = sid_on_dev_cert_cli_print_base64(shell, csr, csr_size)) == + SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CSR_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_chain_start(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + enum sid_on_dev_cert_algo_type algo; + + if (strcmp(argv[1], CERT_ED25519_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_ED25519; + } else if (strcmp(argv[1], CERT_P256R1_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_P256R1; + } else { + ret = SID_ERROR_INVALID_ARGS; + } + if (ret == SID_ERROR_NONE) { + sid_base64_init(&cert_chain_base64_ctx); + cert_chain_base64_ctx.next_out = cert_chain_buffer; + cert_chain_base64_ctx.avail_out = (algo == SID_ODC_CRYPT_ALGO_ED25519) ? + SID_ODC_ED25519_SCC_MAX_SIZE : + SID_ODC_P256R1_SCC_MAX_SIZE; + cert_chain_params.algo = algo; + cert_chain_params.cert_chain = cert_chain_buffer; + cert_chain_params.cert_chain_size = 0; + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CHAIN_START_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_chain_write(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + size_t base64_fragment_len = strlen(argv[1]); + + if (base64_fragment_len >= 1 && + base64_fragment_len <= CERT_BASE64_FRAGMENT_MAX_SIZE) { + if (cert_chain_params.cert_chain) { + cert_chain_base64_ctx.next_in = (const uint8_t *)argv[1]; + cert_chain_base64_ctx.avail_in = base64_fragment_len; + ret = sid_base64_decode(&cert_chain_base64_ctx); + /* + * If we have a successful result, we should check: + * - the input buffer should be empty + * - if the output buffer is empty and there are 8 bits or more of temporary data, it is an error + */ + if (ret != SID_ERROR_NONE || cert_chain_base64_ctx.avail_in != 0 || + (cert_chain_base64_ctx.avail_out == 0 && + cert_chain_base64_ctx.temp_len >= 8)) { + // Clear chain pointer. This makes chain uninitialized. + cert_chain_params.cert_chain = NULL; + // Reset base64 context + sid_base64_init(&cert_chain_base64_ctx); + ret = SID_ERROR_INVALID_ARGS; + } + } else { + ret = SID_ERROR_UNINITIALIZED; + } + } else { + ret = SID_ERROR_PARAM_OUT_OF_RANGE; + } + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CHAIN_WRITE_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_chain_commit(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 1) { + sid_error_t ret = SID_ERROR_NONE; + if (cert_chain_params.cert_chain) { + cert_chain_params.cert_chain_size = cert_chain_base64_ctx.total_out; + ret = sid_on_dev_cert_write_cert_chain(&cert_chain_params); + // Clear chain pointer. This makes chain uninitialized. + cert_chain_params.cert_chain = NULL; + // Reset base64 context + sid_base64_init(&cert_chain_base64_ctx); + } else { + ret = SID_ERROR_UNINITIALIZED; + } + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CHAIN_COMMIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_app_key_start(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 1) { + sid_base64_init(&cert_app_key_base64_ctx); + cert_app_key_base64_ctx.next_out = cert_app_key; + cert_app_key_base64_ctx.avail_out = SID_ODC_ED25519_PUK_SIZE; + shell_info(shell, CERT_MSG_OK); + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_APPKEY_START_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_app_key_write(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + size_t base64_fragment_len = strlen(argv[1]); + + if (base64_fragment_len >= 1 && + base64_fragment_len <= CERT_BASE64_FRAGMENT_MAX_SIZE) { + if (cert_app_key_base64_ctx.next_out) { + cert_app_key_base64_ctx.next_in = (const uint8_t *)argv[1]; + cert_app_key_base64_ctx.avail_in = base64_fragment_len; + + ret = sid_base64_decode(&cert_app_key_base64_ctx); + /* + * If we have a successful result, we should check: + * - the input buffer should be empty + * - if the output buffer is empty and there are 8 bits or more of temporary data, it is an error + */ + if (ret != SID_ERROR_NONE || + cert_app_key_base64_ctx.avail_in != 0 || + (cert_app_key_base64_ctx.avail_out == 0 && + cert_app_key_base64_ctx.temp_len >= 8)) { + // Reset base64 context + sid_base64_init(&cert_app_key_base64_ctx); + ret = SID_ERROR_INVALID_ARGS; + } + } else { + ret = SID_ERROR_UNINITIALIZED; + } + } else { + ret = SID_ERROR_PARAM_OUT_OF_RANGE; + } + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_APPKEY_WRITE_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_app_key_commit(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 1) { + sid_error_t ret = SID_ERROR_NONE; + + if (cert_app_key_base64_ctx.total_out == SID_ODC_ED25519_PUK_SIZE && + cert_app_key_base64_ctx.avail_out == 0) { + ret = sid_on_dev_cert_write_app_server_key((uint8_t *)cert_app_key); + } else { + ret = SID_ERROR_UNINITIALIZED; + } + // Reset base64 context + sid_base64_init(&cert_app_key_base64_ctx); + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_APPKEY_COMMIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_store(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 1) { + sid_error_t ret = sid_on_dev_cert_verify_and_store(); + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_STORE_CMD); + } + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_chain, + SHELL_CMD_ARG(start, NULL, CERT_CHAIN_START_H, sid_on_dev_cert_cli_chain_start, 2, 0), + SHELL_CMD_ARG(write, NULL, CERT_CHAIN_WRITE_H, sid_on_dev_cert_cli_chain_write, 2, 0), + SHELL_CMD_ARG(commit, NULL, CERT_APPKEY_COMMIT_H, sid_on_dev_cert_cli_chain_commit, 1, 0), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_app_key, + SHELL_CMD_ARG(start, NULL, CERT_APPKEY_START_H, sid_on_dev_cert_cli_app_key_start, 1, 0), + SHELL_CMD_ARG(write, NULL, CERT_APPKEY_WRITE_H, sid_on_dev_cert_cli_app_key_write, 2, 0), + SHELL_CMD_ARG(commit, NULL, CERT_APPKEY_COMMIT_H, sid_on_dev_cert_cli_app_key_commit, 1, 0), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_services, SHELL_CMD_ARG(init, NULL, CERT_INIT_H, sid_on_dev_cert_cli_init_cmd, 1, 0), + SHELL_CMD_ARG(deinit, NULL, CERT_DEINIT_H, sid_on_dev_cert_cli_deinit, 1, 0), + SHELL_CMD_ARG(smsn, NULL, CERT_SMSN_H, sid_on_dev_cert_cli_smsn, 4, 1), + SHELL_CMD_ARG(csr, NULL, CERT_CSR_H, sid_on_dev_cert_cli_csr, 2, 0), + SHELL_CMD_ARG(chain, &sub_chain, CERT_CHAIN_H, NULL, 1, 0), + SHELL_CMD_ARG(app_key, &sub_app_key, CERT_APPKEY_H, NULL, 1, 0), + SHELL_CMD_ARG(store, NULL, CERT_STORE_H, sid_on_dev_cert_cli_store, 1, 0), + SHELL_SUBCMD_SET_END); + +// command, subcommands, help, handler +SHELL_CMD_REGISTER(cert, &sub_services, "sidewalk testing CLI", NULL); diff --git a/samples/lbm_sid_end_device/src/hello/app.c b/samples/lbm_sid_end_device/src/hello/app.c index 424a679..e5f4d0b 100644 --- a/samples/lbm_sid_end_device/src/hello/app.c +++ b/samples/lbm_sid_end_device/src/hello/app.c @@ -11,30 +11,32 @@ #include #include #if defined(CONFIG_GPIO) -#include +#include #endif #if defined(CONFIG_LOG) -#include +#include #endif #include #include #include #include -#include -#include +#include +#ifdef CONFIG_RADIO_LR11XX #include // radio_dbg pin +#endif /* CONFIG_RADIO_LR11XX */ LOG_MODULE_REGISTER(app, CONFIG_SIDEWALK_LOG_LEVEL); static uint32_t persistent_link_mask; +#ifdef CONFIG_RADIO_LR11XX halo_drv_semtech_ctx_t *radio_ctx; +#endif /* CONFIG_RADIO_LR11XX */ static void on_sidewalk_event(bool in_isr, void *context) { int err = sidewalk_event_send(SID_EVENT_SIDEWALK, NULL); - //sid_pal_gpio_toggle(radio_ctx->config->gpios.led_sniff); if (err) { LOG_ERR("Send event err %d", err); }; @@ -94,6 +96,13 @@ static void on_sidewalk_msg_sent(const struct sid_msg_desc *msg_desc, void *cont "on_msg_sent", JSON_OBJ(JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0)))))); application_state_sending(&global_state_notifier, false); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc *msg_desc, @@ -106,6 +115,14 @@ static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0))))))); application_state_sending(&global_state_notifier, false); + + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_factory_reset(void *context) @@ -131,7 +148,7 @@ static void on_sidewalk_status_changed(const struct sid_status *status, void *co } else { memcpy(new_status, status, sizeof(struct sid_status)); } - sidewalk_event_send(SID_EVENT_NEW_STATUS, new_status); + err = sidewalk_event_send(SID_EVENT_NEW_STATUS, new_status); switch (status->state) { case SID_STATE_READY: @@ -264,7 +281,9 @@ void app_start(void) .sub_ghz_link_config = app_get_sub_ghz_config(), }; +#ifdef CONFIG_RADIO_LR11XX radio_ctx = lr11xx_get_drv_ctx(); +#endif /* CONFIG_RADIO_LR11XX */ sidewalk_start(&sid_ctx); } diff --git a/samples/lbm_sid_end_device/src/lbm/app_lbm_sidewalk.c b/samples/lbm_sid_end_device/src/lbm/app_lbm_sidewalk.c index 4926b78..b816afd 100644 --- a/samples/lbm_sid_end_device/src/lbm/app_lbm_sidewalk.c +++ b/samples/lbm_sid_end_device/src/lbm/app_lbm_sidewalk.c @@ -9,12 +9,12 @@ //#include //#endif #if defined(CONFIG_GPIO) -#include +#include #endif #if defined(CONFIG_LOG) -#include +#include #endif -#include +#include #include LOG_MODULE_REGISTER(app_lbm_sid, CONFIG_SIDEWALK_LOG_LEVEL); diff --git a/samples/lbm_sid_end_device/src/lbm/app_nav3_lbm.c b/samples/lbm_sid_end_device/src/lbm/app_nav3_lbm.c index 294e48a..d180c37 100644 --- a/samples/lbm_sid_end_device/src/lbm/app_nav3_lbm.c +++ b/samples/lbm_sid_end_device/src/lbm/app_nav3_lbm.c @@ -4,10 +4,10 @@ #include #include #if defined(CONFIG_GPIO) -#include +#include #endif #if defined(CONFIG_LOG) -#include +#include #endif #include diff --git a/samples/lbm_sid_end_device/src/lbm/app_nav3_sidewalk.c b/samples/lbm_sid_end_device/src/lbm/app_nav3_sidewalk.c index 568b3d4..11cc95d 100644 --- a/samples/lbm_sid_end_device/src/lbm/app_nav3_sidewalk.c +++ b/samples/lbm_sid_end_device/src/lbm/app_nav3_sidewalk.c @@ -5,15 +5,14 @@ #include #include #if defined(CONFIG_GPIO) -#include +#include #endif #if defined(CONFIG_LOG) -#include +#include #endif #include -#include -#include +#include #include LOG_MODULE_REGISTER(app, CONFIG_SIDEWALK_LOG_LEVEL); diff --git a/samples/lbm_sid_end_device/src/lbm/main_geolocation_lbm.c b/samples/lbm_sid_end_device/src/lbm/main_geolocation_lbm.c index c71ec72..c2cff96 100644 --- a/samples/lbm_sid_end_device/src/lbm/main_geolocation_lbm.c +++ b/samples/lbm_sid_end_device/src/lbm/main_geolocation_lbm.c @@ -271,6 +271,8 @@ void state_nav3_run(void *o) break; case SID_EVENT_TOGGLE_LBM_SIDEWALK: break; + case SID_EVENT_REBOOT: + break; case SID_EVENT_LAST: break; } // ..switch (sm->event.id) diff --git a/samples/lbm_sid_end_device/src/lbm/main_geolocation_sidewalk.c b/samples/lbm_sid_end_device/src/lbm/main_geolocation_sidewalk.c index e432698..7bc63fd 100644 --- a/samples/lbm_sid_end_device/src/lbm/main_geolocation_sidewalk.c +++ b/samples/lbm_sid_end_device/src/lbm/main_geolocation_sidewalk.c @@ -575,6 +575,8 @@ void state_nav3_run(void *o) send_scan_result(sm->sid); break; #endif /* NAV3_SEND */ + case SID_EVENT_REBOOT: { + } break; case SID_EVENT_LAST: break; } // ..switch (sm->event.id) diff --git a/samples/lbm_sid_end_device/src/lbm/main_periodical_uplink.c b/samples/lbm_sid_end_device/src/lbm/main_periodical_uplink.c index cb695a0..7bb9e0f 100644 --- a/samples/lbm_sid_end_device/src/lbm/main_periodical_uplink.c +++ b/samples/lbm_sid_end_device/src/lbm/main_periodical_uplink.c @@ -343,6 +343,8 @@ void state_lbm_run(void *o) case SID_EVENT_TOGGLE_LBM_SIDEWALK: smf_set_state(SMF_CTX(sm), &sid_states[STATE_SIDEWALK]); break; + case SID_EVENT_REBOOT: + break; case SID_EVENT_LAST: break; } diff --git a/samples/lbm_sid_end_device/src/main.c b/samples/lbm_sid_end_device/src/main.c index e050000..e7a910a 100644 --- a/samples/lbm_sid_end_device/src/main.c +++ b/samples/lbm_sid_end_device/src/main.c @@ -8,9 +8,6 @@ #include #include -#include -LOG_MODULE_REGISTER(main, CONFIG_SIDEWALK_LOG_LEVEL); - int main(void) { PRINT_SIDEWALK_VERSION(); diff --git a/samples/lbm_sid_end_device/src/sensor_monitoring/app.c b/samples/lbm_sid_end_device/src/sensor_monitoring/app.c index 5817426..6a43a1d 100644 --- a/samples/lbm_sid_end_device/src/sensor_monitoring/app.c +++ b/samples/lbm_sid_end_device/src/sensor_monitoring/app.c @@ -71,6 +71,13 @@ static void on_sidewalk_msg_received(const struct sid_msg_desc *msg_desc, const static void on_sidewalk_msg_sent(const struct sid_msg_desc *msg_desc, void *context) { LOG_DBG("sent message(type: %d, id: %u)", (int)msg_desc->type, msg_desc->id); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc *msg_desc, @@ -78,6 +85,13 @@ static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc { LOG_ERR("Send message err %d", (int)error); LOG_DBG("Failed to send message(type: %d, id: %u)", (int)msg_desc->type, msg_desc->id); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_factory_reset(void *context) @@ -151,10 +165,23 @@ static void on_sidewalk_status_changed(const struct sid_status *status, void *co static void sidewalk_btn_handler(uint32_t event) { - int err = sidewalk_event_send((app_event_t)event, NULL); + int err = sidewalk_event_send((sidewalk_event_t)event, NULL); if (err) { LOG_ERR("Send event err %d", err); + return; }; + + if (SID_EVENT_NORDIC_DFU == event) { + static bool in_dfu; + if (in_dfu) { + in_dfu = false; + k_timer_start(¬ify_timer, K_MSEC(NOTIFY_TIMER_DURATION_MS), + K_MSEC(CONFIG_SID_END_DEVICE_NOTIFY_DATA_PERIOD_MS)); + } else { + in_dfu = true; + k_timer_stop(¬ify_timer); + } + } } static int app_buttons_init(void) diff --git a/samples/lbm_sid_end_device/src/sensor_monitoring/app_tx.c b/samples/lbm_sid_end_device/src/sensor_monitoring/app_tx.c index f1ee8f2..6773184 100644 --- a/samples/lbm_sid_end_device/src/sensor_monitoring/app_tx.c +++ b/samples/lbm_sid_end_device/src/sensor_monitoring/app_tx.c @@ -46,9 +46,10 @@ static void state_notify_data(void *o); static void button_timer_cb(struct k_timer *timer_id); static const struct smf_state app_states[] = { - [STATE_APP_INIT] = SMF_CREATE_STATE(NULL, state_init, NULL), - [STATE_APP_NOTIFY_CAPABILITY] = SMF_CREATE_STATE(NULL, state_notify_capability, NULL), - [STATE_APP_NOTIFY_DATA] = SMF_CREATE_STATE(NULL, state_notify_data, NULL), + [STATE_APP_INIT] = SMF_CREATE_STATE(NULL, state_init, NULL, NULL, NULL), + [STATE_APP_NOTIFY_CAPABILITY] = + SMF_CREATE_STATE(NULL, state_notify_capability, NULL, NULL, NULL), + [STATE_APP_NOTIFY_DATA] = SMF_CREATE_STATE(NULL, state_notify_data, NULL, NULL, NULL), }; static uint8_t __aligned(4) @@ -179,7 +180,7 @@ static void state_notify_capability(void *o) app_sm_t *sm = (app_sm_t *)o; switch (sm->event) { - case APP_EVENT_NOTIFY_SENSOR: + case APP_EVENT_NOTIFY_SENSOR: { // Prepare message struct sid_demo_capability_discovery cap = { .link_type = last_link_mask_get(), @@ -219,7 +220,7 @@ static void state_notify_capability(void *o) } LOG_INF("Capability send"); - break; + } break; case APP_EVENT_CAPABILITY_SUCCESS: smf_set_state(SMF_CTX(sm), &app_states[STATE_APP_NOTIFY_DATA]); break; @@ -243,7 +244,7 @@ static void state_notify_data(void *o) int err = 0; switch (sm->event) { - case APP_EVENT_NOTIFY_BUTTON: + case APP_EVENT_NOTIFY_BUTTON: { // Read button state uint8_t button_arr[APP_BUTTONS_MAX] = { 0 }; uint8_t num_buttons = 0; @@ -299,8 +300,8 @@ static void state_notify_data(void *o) app_btn_pending_flag_clear(); LOG_INF("Notify button send"); - break; - case APP_EVENT_NOTIFY_SENSOR: + } break; + case APP_EVENT_NOTIFY_SENSOR: { // Read sensor data int16_t temp = 0; err = app_sensor_temperature_get(&temp); @@ -346,8 +347,8 @@ static void state_notify_data(void *o) } LOG_INF("Notify sensor send"); - break; - case APP_EVENT_RESP_LED_ON: + } break; + case APP_EVENT_RESP_LED_ON: { // Read led status uint8_t led_on_arr[APP_BUTTONS_MAX] = { 0 }; uint8_t num_leds_on = 0; @@ -374,8 +375,8 @@ static void state_notify_data(void *o) } LOG_INF("Response LED ON send"); - break; - case APP_EVENT_RESP_LED_OFF: + } break; + case APP_EVENT_RESP_LED_OFF: { // Read led status uint8_t led_off_arr[APP_BUTTONS_MAX] = { 0 }; uint8_t num_leds_off = 0; @@ -401,7 +402,7 @@ static void state_notify_data(void *o) } LOG_INF("Response LED OFF send"); - break; + } break; case APP_EVENT_TIME_SYNC_FAIL: smf_set_state(SMF_CTX(sm), &app_states[STATE_APP_NOTIFY_CAPABILITY]); case APP_EVENT_TIME_SYNC_SUCCESS: @@ -431,7 +432,7 @@ void app_tx_task(void *dummy1, void *dummy2, void *dummy3) k_timer_start(&button_timer, K_MSEC(APP_NOTIFY_BUTTON_PERIOD_MS), K_MSEC(APP_NOTIFY_BUTTON_PERIOD_MS)); - k_msgq_init(&app_sm.msgq, app_msgq_buff, sizeof(app_event_t), + k_msgq_init(&app_sm.msgq, (char *)app_msgq_buff, sizeof(app_event_t), CONFIG_SID_END_DEVICE_TX_THREAD_QUEUE_SIZE); smf_set_initial(SMF_CTX(&app_sm), &app_states[STATE_APP_INIT]); diff --git a/samples/lbm_sid_end_device/src/sidewalk.c b/samples/lbm_sid_end_device/src/sidewalk.c index fcb7981..49bdb53 100644 --- a/samples/lbm_sid_end_device/src/sidewalk.c +++ b/samples/lbm_sid_end_device/src/sidewalk.c @@ -3,27 +3,38 @@ * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#include +#include +#include -#include -#include - +#include +#include #include -#include -#include -#include +#ifdef CONFIG_SIDEWALK_SUBGHZ_SUPPORT +#include +#endif /* CONFIG_SIDEWALK_SUBGHZ_SUPPORT */ +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER +#include +#include +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU +#include +#include +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ +#include // print hash only +#include // print hash only +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER */ #ifdef CONFIG_SID_END_DEVICE_CLI #include -#endif +#endif /* CONFIG_SID_END_DEVICE_CLI */ #ifdef CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK #include -#endif +#endif /* CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK */ + #include +#include +#include #include -#include -#ifdef CONFIG_SIDEWALK_SUBGHZ_SUPPORT -#include -#endif -#include +#include #include #ifdef CONFIG_SMTC_CLI @@ -46,10 +57,36 @@ LOG_MODULE_REGISTER(sidewalk_app, CONFIG_SIDEWALK_LOG_LEVEL); static struct k_thread sid_thread; K_THREAD_STACK_DEFINE(sid_thread_stack, CONFIG_SIDEWALK_THREAD_STACK_SIZE); -//extern uint32_t radio_dbg; +sys_slist_t pending_message_list = SYS_SLIST_STATIC_INIT(&pending_message_list); +K_MUTEX_DEFINE(pending_message_list_mutex); + +sidewalk_msg_t *get_message_buffer(uint16_t message_id) +{ + sidewalk_msg_t *pending_message; + sidewalk_msg_t *iterator; + int mutex_err = + k_mutex_lock(&pending_message_list_mutex, k_is_in_isr() ? K_NO_WAIT : K_FOREVER); + if (mutex_err != 0) { + LOG_ERR("Failed to lock mutex for message list"); + return NULL; + } + SYS_SLIST_FOR_EACH_CONTAINER_SAFE (&pending_message_list, pending_message, iterator, node) { + if (pending_message->desc.id == message_id) { + if (sys_slist_find_and_remove(&pending_message_list, + &pending_message->node) == false) { + LOG_ERR("Failed to remove pending message from list"); + }; + k_mutex_unlock(&pending_message_list_mutex); + return pending_message; + } + } + k_mutex_unlock(&pending_message_list_mutex); + return NULL; +} static void state_sidewalk_run(void *o); static void state_sidewalk_entry(void *o); +static void state_sidewalk_exit(void *o); static void state_dfu_entry(void *o); static void state_dfu_run(void *o); #ifdef CONFIG_SMTC_CLI @@ -58,15 +95,16 @@ static void state_smtc_run(void *o); #endif /* CONFIG_SMTC_CLI */ const struct smf_state sid_states[] = { - [STATE_SIDEWALK] = SMF_CREATE_STATE(state_sidewalk_entry, state_sidewalk_run, NULL), - [STATE_DFU] = SMF_CREATE_STATE(state_dfu_entry, state_dfu_run, NULL), + [STATE_SIDEWALK] = SMF_CREATE_STATE(state_sidewalk_entry, state_sidewalk_run, + state_sidewalk_exit, NULL, NULL), + [STATE_DFU] = SMF_CREATE_STATE(state_dfu_entry, state_dfu_run, NULL, NULL, NULL), #if defined(CONFIG_LBM_END_DEVICE) - [STATE_LBM] = SMF_CREATE_STATE(state_lbm_entry, state_lbm_run, NULL), + [STATE_LBM] = SMF_CREATE_STATE(state_lbm_entry, state_lbm_run, NULL, NULL, NULL), #elif defined(CONFIG_SID_END_DEVICE_NAV3) || defined(CONFIG_LBM_END_DEVICE_NAV3) - [STATE_NAV3] = SMF_CREATE_STATE(state_nav3_entry, state_nav3_run, NULL), + [STATE_NAV3] = SMF_CREATE_STATE(state_nav3_entry, state_nav3_run, NULL, NULL, NULL), #endif #ifdef CONFIG_SMTC_CLI - [STATE_SMTC] = SMF_CREATE_STATE(state_smtc_entry, state_smtc_run, NULL), + [STATE_SMTC] = SMF_CREATE_STATE(state_smtc_entry, state_smtc_run, NULL, NULL, NULL), #endif /* CONFIG_SMTC_CLI */ }; @@ -85,7 +123,7 @@ static void state_sidewalk_entry(void *o) (radio_lr11xx_device_config_t *)get_radio_cfg(), #else (radio_sx126x_device_config_t *)get_radio_cfg(), -#endif /* CONFIG_RADIO_LR11XX */ +#endif /* CONFIG_RADIO_LR11XX */ #endif /* CONFIG_SIDEWALK_SUBGHZ_SUPPORT */ }; @@ -123,7 +161,6 @@ static void state_sidewalk_entry(void *o) (SID_LINK_TYPE_3 & sm->sid->config.link_mask) ? "LoRa" : (SID_LINK_TYPE_2 & sm->sid->config.link_mask) ? "FSK" : "BLE"); - e = sid_init(&sm->sid->config, &sm->sid->handle); if (e) { LOG_ERR("sid init err %d", (int)e); @@ -137,10 +174,7 @@ static void state_sidewalk_entry(void *o) } #endif /* CONFIG_SMTC_CLI */ -#ifdef CONFIG_SIDEWALK_FILE_TRANSFER - app_file_transfer_demo_init(sm->sid->handle); -#endif - e = sid_start(sm->sid->handle, sm->sid->config.link_mask); // state_sidewalk_entry() + e = sid_start(sm->sid->handle, sm->sid->config.link_mask); if (e) { LOG_ERR("sid start err %d", (int)e); } @@ -172,6 +206,16 @@ static void state_sidewalk_entry(void *o) #endif /* CONFIG_SID_END_DEVICE_AUTO_CONN_REQ */ #endif /* CONFIG_SIDEWALK_AUTO_START */ + +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + int dfu_err = boot_write_img_confirmed(); + if (dfu_err) { + LOG_ERR("img confirm fail %d", dfu_err); + } +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER + app_file_transfer_demo_init(((sm_t *)o)->sid->handle); +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER */ } static void state_sidewalk_run(void *o) @@ -181,9 +225,7 @@ static void state_sidewalk_run(void *o) switch (sm->event.id) { case SID_EVENT_SIDEWALK: { - //sid_pal_gpio_write(radio_dbg, 0); e = sid_process(sm->sid->handle); - //sid_pal_gpio_write(radio_dbg, 1); if (e) { LOG_ERR("sid process err %d", (int)e); } @@ -240,7 +282,7 @@ static void state_sidewalk_run(void *o) #ifdef CONFIG_SIDEWALK_FILE_TRANSFER app_file_transfer_demo_init(sm->sid->handle); #endif - e = sid_start(sm->sid->handle, sm->sid->config.link_mask); // state_sidewalk_run() SID_EVENT_LINK_SWITCH + e = sid_start(sm->sid->handle, sm->sid->config.link_mask); if (e) { LOG_ERR("sid start err %d", (int)e); } @@ -303,8 +345,13 @@ static void state_sidewalk_run(void *o) LOG_ERR("sid send err %d", (int)e); } LOG_DBG("sid send (type: %d, id: %u)", (int)p_msg->desc.type, p_msg->desc.id); - sid_hal_free(p_msg->msg.data); - sid_hal_free(p_msg); + int mutex_err = k_mutex_lock(&pending_message_list_mutex, K_FOREVER); + if (mutex_err != 0) { + LOG_ERR("Failed to lock mutex for message list"); + break; + } + sys_slist_append(&pending_message_list, &p_msg->node); + k_mutex_unlock(&pending_message_list_mutex); } break; case SID_EVENT_CONNECT: { if (!(sm->sid->config.link_mask & SID_LINK_TYPE_1)) { @@ -318,17 +365,20 @@ static void state_sidewalk_run(void *o) } break; case SID_EVENT_FILE_TRANSFER: { #ifdef CONFIG_SIDEWALK_FILE_TRANSFER - struct data_received_args *args = (struct data_received_args *)sm->event.ctx; - if (!args) { + sidewalk_transfer_t *transfer = (sidewalk_transfer_t *)sm->event.ctx; + if (!transfer) { LOG_ERR("File transfer event data is NULL"); break; } - LOG_INF("Received file Id %d; buffer size %d; file offset %d", args->desc.file_id, - args->buffer->size, args->desc.file_offset); + + LOG_INF("Received file Id %d; buffer size %d; file offset %d", transfer->file_id, + transfer->data_size, transfer->file_offset); + + // print data hash uint8_t hash_out[32]; sid_pal_hash_params_t params = { .algo = SID_PAL_HASH_SHA256, - .data = args->buffer->data, - .data_size = args->buffer->size, + .data = transfer->data, + .data_size = transfer->data_size, .digest = hash_out, .digest_size = sizeof(hash_out) }; @@ -345,15 +395,46 @@ static void state_sidewalk_run(void *o) LOG_INF("SHA256: %s", hex_str); } - sid_error_t ret = sid_bulk_data_transfer_release_buffer( - sm->sid->handle, args->desc.file_id, args->buffer); - if (ret != SID_ERROR_NONE) { - LOG_ERR("sid_bulk_data_transfer_release_buffer returned %s", - SID_ERROR_T_STR(ret)); +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + int err = nordic_dfu_img_write(transfer->file_offset, transfer->data, + transfer->data_size); + + if (err) { + LOG_ERR("Fail to write img %d", err); + err = nordic_dfu_img_cancel(); + if (err) { + LOG_ERR("Fail to complete dfu %d", err); + } + e = sid_bulk_data_transfer_cancel( + sm->sid->handle, transfer->file_id, + SID_BULK_DATA_TRANSFER_REJECT_REASON_FILE_TOO_BIG); + if (e != SID_ERROR_NONE) { + LOG_ERR("sbdt cancel ret %s", SID_ERROR_T_STR(e)); + } + + sid_hal_free(transfer); + break; + } +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ + const struct sid_bulk_data_transfer_buffer sbdt_buffer = { + .data = transfer->data, + .size = transfer->data_size, + }; + e = sid_bulk_data_transfer_release_buffer(sm->sid->handle, transfer->file_id, + &sbdt_buffer); + if (e != SID_ERROR_NONE) { + LOG_ERR("sbdt release ret %s", SID_ERROR_T_STR(e)); } - sid_hal_free(args); + + // free event context + sid_hal_free(transfer); #endif /* CONFIG_SIDEWALK_FILE_TRANSFER */ } break; + case SID_EVENT_REBOOT: { + LOG_INF("Rebooting..."); + LOG_PANIC(); + sys_reboot(SYS_REBOOT_WARM); + } break; case SID_EVENT_TOGGLE_LBM_SIDEWALK: e = sid_stop(sm->sid->handle, sm->sid->config.link_mask); if (e) { @@ -412,6 +493,25 @@ static void state_smtc_run(void *o) } #endif /* CONFIG_SMTC_CLI */ +static void state_sidewalk_exit(void *o) +{ + int mutex_err = k_mutex_lock(&pending_message_list_mutex, K_FOREVER); + if (mutex_err != 0) { + LOG_ERR("Failed to lock mutex for message list"); + return; + } + sys_snode_t *list_element = sys_slist_get(&pending_message_list); + + while (list_element != NULL) { + sidewalk_msg_t *message = SYS_SLIST_CONTAINER(list_element, message, node); + sid_hal_free(message->msg.data); + sid_hal_free(message); + + list_element = sys_slist_get(&pending_message_list); + } + k_mutex_unlock(&pending_message_list_mutex); +} + static void state_dfu_entry(void *o) { sm_t *sm = (sm_t *)o; @@ -452,6 +552,9 @@ static void state_dfu_run(void *o) case SID_EVENT_LINK_SWITCH: case SID_EVENT_SIDEWALK: case SID_EVENT_FILE_TRANSFER: + case SID_EVENT_REBOOT: + LOG_INF("Operation not supported in DFU mode"); + break; case SID_EVENT_TOGGLE_LBM_SIDEWALK: LOG_INF("Operation not supported in DFU mode"); break; @@ -475,7 +578,7 @@ static void sid_thread_entry(void *context, void *unused, void *unused2) k_msgq_init(&sid_sm.msgq, sid_msgq_buff, sizeof(sidewalk_ctx_event_t), CONFIG_SIDEWALK_THREAD_QUEUE_SIZE); -#if defined(CONFIG_SMTC_CLI) +#ifdef CONFIG_SMTC_CLI smf_set_initial(SMF_CTX(&sid_sm), &sid_states[STATE_SMTC]); #elif defined(CONFIG_LBM_END_DEVICE) smf_set_initial(SMF_CTX(&sid_sm), &sid_states[STATE_LBM]); @@ -502,11 +605,7 @@ static void sid_thread_entry(void *context, void *unused, void *unused2) break; } } else { - if (err == -ENOMSG) - LOG_ERR("msgq ENOMSG"); // returned without waiting - else - LOG_ERR("Sidewalk msgq err %d", err); - k_sleep(K_MSEC(100)); + LOG_ERR("Sidewalk msgq err %d", err); } } @@ -528,8 +627,17 @@ int sidewalk_event_send(sidewalk_event_t event, void *ctx) .ctx = ctx, }; - const int result = k_msgq_put(&sid_sm.msgq, (void *)&ctx_event, K_NO_WAIT); + k_timeout_t timeout = K_NO_WAIT; + +#ifdef CONFIG_SIDEWALK_THREAD_QUEUE_TIMEOUT + if (!k_is_in_isr()) { + timeout = K_MSEC(CONFIG_SIDEWALK_THREAD_QUEUE_TIMEOUT_VALUE); + } +#endif /* CONFIG_SIDEWALK_THREAD_QUEUE_TIMEOUT */ + + const int result = k_msgq_put(&sid_sm.msgq, (void *)&ctx_event, timeout); LOG_DBG("sidewalk_event_send event = %d (%s), context = %p, k_msgq_put result %d", event, SIDEWALK_EVENT_T_STR(event), ctx, result); + return result; } diff --git a/samples/lbm_sid_end_device/sysbuild/ipc_radio/prj.conf b/samples/lbm_sid_end_device/sysbuild/ipc_radio/prj.conf new file mode 100644 index 0000000..654a36b --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/ipc_radio/prj.conf @@ -0,0 +1,35 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Bluetooth +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_CTLR_ASSERT_HANDLER=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=n +CONFIG_BT_MAX_CONN=1 +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 + +# IPC +CONFIG_IPC_SERVICE=y +CONFIG_MBOX=y + +# Debug +CONFIG_LOG=n +CONFIG_SERIAL=n +CONFIG_ASSERT=y +CONFIG_DEBUG_INFO=y +CONFIG_EXCEPTION_STACK_TRACE=y +CONFIG_RESET_ON_FATAL_ERROR=y + +# ipc_radio +CONFIG_IPC_RADIO_BT=y +CONFIG_IPC_RADIO_BT_HCI_IPC=y diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 0000000..c333f7b --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,10 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Configure QSPI for external flash +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000..6ea6421 --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + zephyr,code-partition = &boot_partition; + nordic,pm-ext-flash = &mx25r64; + }; +}; diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 0000000..96cb33e --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,28 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 + +CONFIG_BOOT_SWAP_USING_MOVE=n +# Multi-image updates do not support image swapping yet. +CONFIG_BOOT_UPGRADE_ONLY=y + +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +# Enable custom command to erase settings partition. +CONFIG_ENABLE_MGMT_PERUSER=y +CONFIG_ZCBOR=y +CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y diff --git a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay similarity index 77% rename from samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay rename to samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay index 055f044..6ea6421 100644 --- a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -4,8 +4,9 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ - / { +/ { chosen { + zephyr,code-partition = &boot_partition; nordic,pm-ext-flash = &mx25r64; }; }; diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf new file mode 100644 index 0000000..16bd5b0 --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n + +# Workaroud: fprotect and watchdog feed +# are not supported in NCS v2.6.0 +CONFIG_FPROTECT=n +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay new file mode 100644 index 0000000..6220cb2 --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* There is no aditional config needed for this version of PDK */ diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay new file mode 100644 index 0000000..8edfb64 --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; diff --git a/samples/sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf similarity index 93% rename from samples/sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf rename to samples/lbm_sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf index a0db3d5..561529f 100644 --- a/samples/sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf @@ -4,9 +4,6 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - # Configure MCUboot features CONFIG_NRF53_MULTI_IMAGE_UPDATE=y CONFIG_BOOT_UPGRADE_ONLY=y diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay new file mode 100644 index 0000000..6ea6421 --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + zephyr,code-partition = &boot_partition; + nordic,pm-ext-flash = &mx25r64; + }; +}; diff --git a/samples/lbm_sid_end_device/sysbuild/mcuboot/prj.conf b/samples/lbm_sid_end_device/sysbuild/mcuboot/prj.conf new file mode 100644 index 0000000..0a05853 --- /dev/null +++ b/samples/lbm_sid_end_device/sysbuild/mcuboot/prj.conf @@ -0,0 +1,39 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +CONFIG_MAIN_STACK_SIZE=10240 + +CONFIG_BOOT_SWAP_SAVE_ENCTLV=n +CONFIG_BOOT_BOOTSTRAP=n +CONFIG_PM=n + +CONFIG_FLASH=y +CONFIG_FPROTECT=y + +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Use minimal C library instead of the Picolib +CONFIG_MINIMAL_LIBC=y + +# Disable logs +CONFIG_NCS_BOOT_BANNER=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n +CONFIG_LOG=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_BOOT_BANNER=n + +# Bootloader size optimization +CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_GPIO=n +CONFIG_TIMESLICING=n +CONFIG_MULTITHREADING=n +CONFIG_TICKLESS_KERNEL=n +CONFIG_TIMEOUT_64BIT=n +CONFIG_NRF_ENABLE_ICACHE=n diff --git a/samples/sid_end_device/CMakeLists.txt b/samples/sid_end_device/CMakeLists.txt index c0a6550..2e2c967 100644 --- a/samples/sid_end_device/CMakeLists.txt +++ b/samples/sid_end_device/CMakeLists.txt @@ -6,24 +6,6 @@ cmake_minimum_required(VERSION 3.20.0) -# Sidewalk version -include(bootloader_version.cmake) - -# Child images -set(hci_ipc_KCONFIG_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/child_image/hci_ipc/Kconfig.root) -set(mcuboot_KCONFIG_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/child_image/mcuboot/Kconfig.root) - -# Configurations -if(CONF_FILE) - get_filename_component(CONFIG_FILE_NAME ${CONF_FILE} NAME) -endif() - -if("${CONFIG_FILE_NAME}" STREQUAL "prj_no_dfu.conf") - set(PM_STATIC_YML_FILE ${CMAKE_CURRENT_SOURCE_DIR}/configuration/pm_static_no_dfu.yml) -else() - set(PM_STATIC_YML_FILE ${CMAKE_CURRENT_SOURCE_DIR}/configuration/${BOARD}/pm_static_dfu.yml) -endif() - # Zephyr CMake project find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(sidewalk_sid_end_device) @@ -35,7 +17,10 @@ target_sources(app PRIVATE src/sidewalk.c ) -target_sources_ifdef(CONFIG_SIDEWALK_FILE_TRANSFER app PRIVATE src/file_transfer.c) +target_sources_ifdef(CONFIG_SIDEWALK_FILE_TRANSFER app PRIVATE + src/sbdt/file_transfer.c + src/sbdt/scratch_buffer.c +) if(CONFIG_SID_END_DEVICE_SENSOR_MONITORING) target_sources(app PRIVATE @@ -61,6 +46,7 @@ if(CONFIG_SID_END_DEVICE_CLI) src/cli/app_dut.c src/cli/app_shell.c ) + target_sources_ifdef(CONFIG_SIDEWALK_ON_DEV_CERT app PRIVATE src/cli/sid_on_dev_cert_cli.c) endif() if(CONFIG_SMTC_CLI) diff --git a/samples/sid_end_device/Kconfig b/samples/sid_end_device/Kconfig index 6bcff8e..9cdc196 100644 --- a/samples/sid_end_device/Kconfig +++ b/samples/sid_end_device/Kconfig @@ -45,10 +45,10 @@ config SID_END_DEVICE_CLI The interface commands are compatilbe with former dut sample. config SMTC_CLI - bool "enable semtech CLI" - imply SHELL - help - Enables semtech radio command line interface. + bool "enable semtech CLI" + imply SHELL + help + Enables semtech radio command line interface. config SID_END_DEVICE_AUTO_START default y @@ -71,9 +71,35 @@ config SID_END_DEVICE_EVENT_HEAP_SIZE config SIDEWALK_FILE_TRANSFER select EXPERIMENTAL - bool "Add File transfer capability to the application" + bool "Enable Sidewalk file transfer" help - Include the callbacks necesary to handle file transfer + Add support for Sidewalk Bulk Data Transfer (SBDT) + in application. + +if SIDEWALK_FILE_TRANSFER + +config SIDEWALK_FILE_TRANSFER_HEAP_SIZE + int "Sidewalk file transfer heap size" + default 10240 + help + Heap size in bytes to be allocated + for Sidewalk Bulk Data Transfer (SBDT). + +config SIDEWALK_FILE_TRANSFER_DFU + bool "Sildewak file transfer and dfu" + default SIDEWALK_FILE_TRANSFER + imply SIDEWALK_DFU_IMG_UTILS + imply DFU_MULTI_IMAGE + imply DFU_TARGET + imply DFU_TARGET_MCUBOOT + imply STREAM_FLASH + imply STREAM_FLASH_ERASE + imply SIDEWALK_THREAD_QUEUE_TIMEOUT + help + Save recived data to flash. Expect CBOR manifest. + Autoatically reset device after file transfer. + +endif rsource "Kconfig.defconfig" diff --git a/samples/sid_end_device/Kconfig.sysbuild b/samples/sid_end_device/Kconfig.sysbuild new file mode 100644 index 0000000..404f972 --- /dev/null +++ b/samples/sid_end_device/Kconfig.sysbuild @@ -0,0 +1,54 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config NRF_DEFAULT_IPC_RADIO + default y if BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP + +choice BOOTLOADER + default BOOTLOADER_MCUBOOT +endchoice + +if BOOTLOADER_MCUBOOT + +config DFU_MULTI_IMAGE_PACKAGE_BUILD + default y + +config DFU_MULTI_IMAGE_PACKAGE_APP + default y + +if (BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP) + +config MCUBOOT_UPDATEABLE_IMAGES + default 2 + +choice MCUBOOT_MODE + default MCUBOOT_MODE_OVERWRITE_ONLY +endchoice + +choice BOOT_SIGNATURE_TYPE + default BOOT_SIGNATURE_TYPE_RSA +endchoice + +config SECURE_BOOT + default y + +config SECURE_BOOT_NETCORE + default y + +config NETCORE_APP_UPDATE + default y + +config DFU_MULTI_IMAGE_PACKAGE_NET + default y + +endif # BOOTLOADER_MCUBOOT + +endif # (BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP) + +config PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY + default y if BOARD_NRF52840DK_NRF52840 || BOARD_NRF5340DK_NRF5340_CPUAPP || BOARD_THINGY53_NRF5340_CPUAPP + +source "${ZEPHYR_BASE}/share/sysbuild/Kconfig" diff --git a/samples/sid_end_device/VERSION b/samples/sid_end_device/VERSION new file mode 100644 index 0000000..808984a --- /dev/null +++ b/samples/sid_end_device/VERSION @@ -0,0 +1,5 @@ +VERSION_MAJOR = 2 +VERSION_MINOR = 6 +PATCHLEVEL = 99 +VERSION_TWEAK = 0 +EXTRAVERSION = diff --git a/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 1fd0180..0000000 --- a/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Configure QSPI for external flash -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 - -CONFIG_DFU_MULTI_IMAGE=y -CONFIG_NRF53_ENFORCE_IMAGE_VERSION_EQUALITY=y diff --git a/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay index 4395ed1..d0a7679 100644 --- a/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -20,6 +20,37 @@ }; /{ + state_notifier_gpios{ + compatible = "gpio-keys"; + state_notifier_error: error { + gpios = <&gpio1 0x8 0x0>; + label = "Application state error"; + }; + state_notifier_dfu: dfu { + gpios = <&gpio1 0x2 0x0>; + label = "Application state dfu"; + }; + state_notifier_sending: sending { + gpios = <&gpio1 0x4 0x0>; + label = "Application state sending"; + }; + state_notifier_receiving: receiving { + gpios = <&gpio1 0x3 0x0>; + label = "Application state receiving"; + }; + }; + + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + state-notifier-error = &state_notifier_error; + state-notifier-dfu = &state_notifier_dfu; + state-notifier-sending = &state_notifier_sending; + state-notifier-receiving = &state_notifier_receiving; + }; + semtech_sx1262_gpios{ compatible = "gpio-keys"; semtech_sx1262_cs: cs { diff --git a/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp_release.conf b/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp_release.conf deleted file mode 100644 index 1fd0180..0000000 --- a/samples/sid_end_device/boards/nrf5340dk_nrf5340_cpuapp_release.conf +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Configure QSPI for external flash -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 - -CONFIG_DFU_MULTI_IMAGE=y -CONFIG_NRF53_ENFORCE_IMAGE_VERSION_EQUALITY=y diff --git a/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf new file mode 100644 index 0000000..d6a0780 --- /dev/null +++ b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Workaround: +# The nRF54L15 PDK (PCA10156) revisions v0.2.0 AA0-ES2, v0.2.0 AA0-ES3, +# and v0.2.1 AB0-ES5 have Buttons 3 and 4 connected to the GPIO port +# which does not support interrupts. +CONFIG_DK_LIBRARY_BUTTON_NO_ISR=y + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay new file mode 100644 index 0000000..811a033 --- /dev/null +++ b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; +&pinctrl { + spi00_default: spi00_default { + group1 { + psels = , + , + ; + }; + }; + + spi00_sleep: spi00_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + +sid_semtech: &spi00 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi00_default>; + pinctrl-1 = <&spi00_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; +}; + +/{ + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + }; + + semtech_sx1262_gpios{ + compatible = "gpio-keys"; + semtech_sx1262_cs: cs { + gpios = <&gpio2 0xa GPIO_PULL_UP>; + label = "semtech_sx1262 CS"; + }; + semtech_sx1262_reset_gpios: reset { + gpios = <&gpio1 0xb (GPIO_ACTIVE_LOW|GPIO_PULL_UP)>; + label = "semtech_sx1262 Reset"; + }; + semtech_sx1262_busy_gpios: busy { + gpios = <&gpio1 0xc 0x0>; + label = "semtech_sx1262 Busy"; + }; + semtech_sx1262_antenna_enable_gpios: antena_enable { + gpios = <&gpio2 0x7 0x0>; + label = "semtech_sx1262 Antena Enable"; + }; + semtech_sx1262_dio1_gpios: dio1 { + gpios = <&gpio1 0xa 0x0>; + label = "semtech_sx1262 DIO1"; + }; + radio_gnss_lna: gnss_lna { + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + label = "gnss antenna"; + }; + radio_led_sniff: led_sniff { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "yellow LED"; + }; + radio_led_tx: led_tx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "red tx LED"; + }; + radio_led_rx: led_rx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "green rx LED"; + }; + }; + +}; + +&gpiote20 { + status = "okay"; +}; + +&gpiote30 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; diff --git a/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf new file mode 100644 index 0000000..e99da54 --- /dev/null +++ b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay new file mode 100644 index 0000000..5a08ece --- /dev/null +++ b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; + + &pinctrl { + spi21_default: spi21_default { + group1 { + psels = , + , + ; + }; + }; + + spi21_sleep: spi21_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + + sid_semtech: &spi21 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi21_default>; + pinctrl-1 = <&spi21_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; +}; + +/{ + aliases { + state-notifier-connected = &led0; + state-notifier-time-sync = &led1; + state-notifier-registered = &led2; + state-notifier-working = &led3; + }; + + semtech_sx1262_gpios{ + compatible = "gpio-keys"; + semtech_sx1262_cs: cs { + gpios = <&gpio2 0xa GPIO_PULL_UP>; + label = "semtech_sx1262 CS"; + }; + semtech_sx1262_reset_gpios: reset { + gpios = <&gpio0 0x2 (GPIO_ACTIVE_LOW|GPIO_PULL_UP)>; + label = "semtech_sx1262 Reset"; + }; + semtech_sx1262_busy_gpios: busy { + gpios = <&gpio0 0x0 0x0>; + label = "semtech_sx1262 Busy"; + }; + semtech_sx1262_antenna_enable_gpios: antena_enable { + gpios = <&gpio0 0x1 0x0>; + label = "semtech_sx1262 Antena Enable"; + }; + semtech_sx1262_dio1_gpios: dio1 { + gpios = <&gpio0 0x3 0x0>; + label = "semtech_sx1262 DIO1"; + }; + radio_gnss_lna: gnss_lna { + gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; + label = "gnss antenna"; + }; + radio_led_sniff: led_sniff { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "yellow LED"; + }; + radio_led_tx: led_tx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "red tx LED"; + }; + radio_led_rx: led_rx { + gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; + label = "green rx LED"; + }; + }; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; diff --git a/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf new file mode 100644 index 0000000..d6a0780 --- /dev/null +++ b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_2_1.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Workaround: +# The nRF54L15 PDK (PCA10156) revisions v0.2.0 AA0-ES2, v0.2.0 AA0-ES3, +# and v0.2.1 AB0-ES5 have Buttons 3 and 4 connected to the GPIO port +# which does not support interrupts. +CONFIG_DK_LIBRARY_BUTTON_NO_ISR=y + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf new file mode 100644 index 0000000..e99da54 --- /dev/null +++ b/samples/sid_end_device/boards/nrf54l15pdk_nrf54l15_cpuapp_release_0_3_0.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SOC_FLASH_NRF_TIMEOUT_MULTIPLIER=100 diff --git a/samples/sid_end_device/boards/thingy53_nrf5340_cpuapp_no_dfu.conf b/samples/sid_end_device/boards/thingy53_nrf5340_cpuapp_no_dfu.conf deleted file mode 100644 index e9fc49e..0000000 --- a/samples/sid_end_device/boards/thingy53_nrf5340_cpuapp_no_dfu.conf +++ /dev/null @@ -1,10 +0,0 @@ -# -# Copyright (c) 2023 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# -CONFIG_BOOTLOADER_MCUBOOT=n - -CONFIG_I2C=y -CONFIG_SENSOR=y -CONFIG_BME680=y diff --git a/samples/sid_end_device/child_image/hci_ipc/Kconfig.root b/samples/sid_end_device/child_image/hci_ipc/Kconfig.root deleted file mode 100644 index 60e8bd4..0000000 --- a/samples/sid_end_device/child_image/hci_ipc/Kconfig.root +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# The purpose of this file is to create a wrapper Kconfig file that will be set as -# hci_ipc_KCONFIG_ROOT and processed before any other Kconfig for hci_ipc child image. - -config HEAP_MEM_POOL_SIZE - default 8192 - -config MAIN_STACK_SIZE - default 2048 - -config SYSTEM_WORKQUEUE_STACK_SIZE - default 2048 - -config BT - default y - -config BT_HCI_RAW - default y - -config BT_MAX_CONN - default 1 - -config BT_PERIPHERAL - default y - -config BT_CENTRAL - default n - -config BT_BUF_ACL_RX_SIZE - default 502 - -config BT_BUF_ACL_TX_SIZE - default 251 - -config BT_CTLR_DATA_LENGTH_MAX - default 251 - -config BT_CTLR_ASSERT_HANDLER - default y - -config BT_HCI_RAW_RESERVE - default 1 - -# Workaround: Unable to allocate command buffer when using K_NO_WAIT since -# Host number of completed commands does not follow normal flow control. -config BT_BUF_CMD_TX_COUNT - default 10 - -config ASSERT - default y - -config DEBUG_INFO - default y - -config EXCEPTION_STACK_TRACE - default y - -config IPC_SERVICE - default y - -config MBOX - default y - -source "Kconfig.zephyr" diff --git a/samples/sid_end_device/child_image/hci_ipc/prj.conf b/samples/sid_end_device/child_image/hci_ipc/prj.conf deleted file mode 100644 index c89bda6..0000000 --- a/samples/sid_end_device/child_image/hci_ipc/prj.conf +++ /dev/null @@ -1,8 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/sid_end_device/child_image/hci_ipc/prj_no_dfu.conf b/samples/sid_end_device/child_image/hci_ipc/prj_no_dfu.conf deleted file mode 100644 index c89bda6..0000000 --- a/samples/sid_end_device/child_image/hci_ipc/prj_no_dfu.conf +++ /dev/null @@ -1,8 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/sid_end_device/child_image/hci_ipc/prj_release.conf b/samples/sid_end_device/child_image/hci_ipc/prj_release.conf deleted file mode 100644 index 86c0d92..0000000 --- a/samples/sid_end_device/child_image/hci_ipc/prj_release.conf +++ /dev/null @@ -1,9 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_LOG=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_RESET_ON_FATAL_ERROR=y diff --git a/samples/sid_end_device/child_image/mcuboot/Kconfig.root b/samples/sid_end_device/child_image/mcuboot/Kconfig.root deleted file mode 100644 index d6ecb28..0000000 --- a/samples/sid_end_device/child_image/mcuboot/Kconfig.root +++ /dev/null @@ -1,127 +0,0 @@ -# -# Copyright (c) 2022 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# The purpose of this file is to create a wrapper Kconfig file that will be set as -# mcuboot_KCONFIG_ROOT and processed before any other Kconfig for mcuboot child image. - - -config MAIN_STACK_SIZE - default 10240 - -config BOOT_SWAP_SAVE_ENCTLV - default n - -config BOOT_ENCRYPT_RSA - default n - -config BOOT_ENCRYPT_EC256 - default n - -config BOOT_ENCRYPT_X25519 - default n - -config BOOT_BOOTSTRAP - default n - -config PM - default n - -config FLASH - default y - -config FPROTECT - default y - -config NORDIC_QSPI_NOR - default y - -config NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE - default 4096 - -config NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE - default 16 - -config BOOT_MAX_IMG_SECTORS - default 256 - -config LOG - default n - -choice LIBC_IMPLEMENTATION - default MINIMAL_LIBC -endchoice - -config COMMON_LIBC_CALLOC - default y - -config COMMON_LIBC_MALLOC - default y - -config COMMON_LIBC_REALLOCARRAY - default y - -config NCS_SAMPLES_DEFAULTS - default n - -config PRINTK - default n - -config REBOOT - default n - -config NRF_RTC_TIMER - default y if SOC_SERIES_NRF53X - default n - -config CONSOLE - default n - -config CONSOLE_HANDLER - default n - -config GPIO - default n - -config KERNEL_MEM_POOL - default n - -config ASSERT - default n - -config BOOT_BANNER - default n - -config SERIAL - default n - -config UART_CONSOLE - default n - -config TIMESLICING - default n - -config USE_SEGGER_RTT - default n - -config RESET_ON_FATAL_ERROR - default n - -config SECURE_BOOT_DEBUG - default n - -config MULTITHREADING - default n - -config TICKLESS_KERNEL - default n - -config TIMEOUT_64BIT - default n - -config NRF_ENABLE_ICACHE - default n - -source "${ZEPHYR_BASE}/../bootloader/mcuboot/boot/zephyr/Kconfig" diff --git a/samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay b/samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay deleted file mode 100644 index 69bf975..0000000 --- a/samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay b/samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay deleted file mode 100644 index 69bf975..0000000 --- a/samples/sid_end_device/child_image/mcuboot/boards/nrf52840dk_nrf52840_release.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf deleted file mode 100644 index 3664dd3..0000000 --- a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - -# Configure MCUboot features -CONFIG_NRF53_MULTI_IMAGE_UPDATE=y -CONFIG_BOOT_UPGRADE_ONLY=y -CONFIG_BOOT_MAX_IMG_SECTORS=256 -CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Enable flash simulator -CONFIG_PCD_APP=y -CONFIG_FLASH_SIMULATOR=y -CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y -CONFIG_FLASH_SIMULATOR_STATS=n - -# Configure QSPI for external flash -CONFIG_FLASH=y -CONFIG_FPROTECT=y -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf b/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf deleted file mode 100644 index 3664dd3..0000000 --- a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.conf +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - -# Configure MCUboot features -CONFIG_NRF53_MULTI_IMAGE_UPDATE=y -CONFIG_BOOT_UPGRADE_ONLY=y -CONFIG_BOOT_MAX_IMG_SECTORS=256 -CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y - -# Allow for storing two images in MCUboot partitions -CONFIG_UPDATEABLE_IMAGE_NUMBER=2 - -# Store new images inside external flash -CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y - -# Enable flash simulator -CONFIG_PCD_APP=y -CONFIG_FLASH_SIMULATOR=y -CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y -CONFIG_FLASH_SIMULATOR_STATS=n - -# Configure QSPI for external flash -CONFIG_FLASH=y -CONFIG_FPROTECT=y -CONFIG_NORDIC_QSPI_NOR=y -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay b/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay deleted file mode 100644 index c670799..0000000 --- a/samples/sid_end_device/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp_release.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - - / { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay b/samples/sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay deleted file mode 100644 index 055f044..0000000 --- a/samples/sid_end_device/child_image/mcuboot/boards/thingy53_nrf5340dk_nrf5340_cpuapp.overlay +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2023 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - - / { - chosen { - nordic,pm-ext-flash = &mx25r64; - }; -}; diff --git a/samples/sid_end_device/child_image/mcuboot/prj.conf b/samples/sid_end_device/child_image/mcuboot/prj.conf deleted file mode 100644 index 78d7622..0000000 --- a/samples/sid_end_device/child_image/mcuboot/prj.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" - -CONFIG_BOOT_UPGRADE_ONLY=n -CONFIG_RESET_ON_FATAL_ERROR=y - -CONFIG_LOG=n -CONFIG_PRINTK=n -CONFIG_CONSOLE_HANDLER=n -CONFIG_ASSERT=n -CONFIG_BOOT_BANNER=n -CONFIG_CONSOLE=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_USE_SEGGER_RTT=n -CONFIG_GPIO=n -CONFIG_NO_RUNTIME_CHECKS=y -CONFIG_SIZE_OPTIMIZATIONS=y diff --git a/samples/sid_end_device/child_image/mcuboot/prj_release.conf b/samples/sid_end_device/child_image/mcuboot/prj_release.conf deleted file mode 100644 index 78d7622..0000000 --- a/samples/sid_end_device/child_image/mcuboot/prj_release.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2021 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" - -CONFIG_BOOT_UPGRADE_ONLY=n -CONFIG_RESET_ON_FATAL_ERROR=y - -CONFIG_LOG=n -CONFIG_PRINTK=n -CONFIG_CONSOLE_HANDLER=n -CONFIG_ASSERT=n -CONFIG_BOOT_BANNER=n -CONFIG_CONSOLE=n -CONFIG_SERIAL=n -CONFIG_UART_CONSOLE=n -CONFIG_USE_SEGGER_RTT=n -CONFIG_GPIO=n -CONFIG_NO_RUNTIME_CHECKS=y -CONFIG_SIZE_OPTIMIZATIONS=y diff --git a/samples/sid_end_device/configuration/pm_static_no_dfu.yml b/samples/sid_end_device/configuration/pm_static_no_dfu.yml deleted file mode 100644 index 658ce75..0000000 --- a/samples/sid_end_device/configuration/pm_static_no_dfu.yml +++ /dev/null @@ -1,5 +0,0 @@ -mfg_storage: - address: 0xff000 - end_address: 0x100000 - region: flash_primary - size: 0x1000 diff --git a/samples/sid_end_device/include/app.h b/samples/sid_end_device/include/app.h index 077f3ca..6eff418 100644 --- a/samples/sid_end_device/include/app.h +++ b/samples/sid_end_device/include/app.h @@ -4,9 +4,14 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#ifndef SAMPLE_APP_H +#define SAMPLE_APP_H + /** * @brief Start Sidewalk end device application. * * @note This function should never return. */ void app_start(void); + +#endif /* SAMPLE_APP_H */ diff --git a/samples/sid_end_device/include/file_transfer.h b/samples/sid_end_device/include/file_transfer.h deleted file mode 100644 index ad62fc4..0000000 --- a/samples/sid_end_device/include/file_transfer.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2024 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#include - -struct data_received_args { - struct sid_bulk_data_transfer_desc desc; - struct sid_bulk_data_transfer_buffer *buffer; - void *context; -}; - -void app_file_transfer_demo_init(struct sid_handle *handle); - -void app_file_transfer_demo_deinit(struct sid_handle *handle); diff --git a/samples/sid_end_device/include/sbdt/file_transfer.h b/samples/sid_end_device/include/sbdt/file_transfer.h new file mode 100644 index 0000000..c51eb1b --- /dev/null +++ b/samples/sid_end_device/include/sbdt/file_transfer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef FILE_TRANSFER_H +#define FILE_TRANSFER_H + +#include + +/** + * @brief Initilize Sidewalk Bulk Data Transfer module. + * + * @param handle Sidewalk handle given by sid_init. + */ +void app_file_transfer_demo_init(struct sid_handle *handle); + +/** + * @brief Deinitilize Sidewalk Bulk Data Transfer module. + * + * @param handle Sidewalk handle given by sid_init. + */ +void app_file_transfer_demo_deinit(struct sid_handle *handle); + +#endif /* FILE_TRANSFER_H */ diff --git a/samples/sid_end_device/include/sbdt/scratch_buffer.h b/samples/sid_end_device/include/sbdt/scratch_buffer.h new file mode 100644 index 0000000..7de934e --- /dev/null +++ b/samples/sid_end_device/include/sbdt/scratch_buffer.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef SCRATCH_BUFFER_H +#define SCRATCH_BUFFER_H + +#include +#include + +/** + * @brief Init scratch buffer + * + * Inilizing module is neccessary to find files by id. + * + */ +void scratch_buffer_init(void); + +/** + * @brief Create scratch buffor assigned for file id. + * + * @param id file id + * @param size size of file buffer + * @return void* pointer to allocated memory. NULL on error. + */ +void *scratch_buffer_create(uint32_t id, size_t size); + +/** + * @brief Remove scratch buffer for file id. + * + * @param id file id + */ +void scratch_buffer_remove(uint32_t id); + +#endif /* SCRATCH_BUFFER_H */ diff --git a/samples/sid_end_device/include/sidewalk.h b/samples/sid_end_device/include/sidewalk.h index 35e1307..e66e37e 100644 --- a/samples/sid_end_device/include/sidewalk.h +++ b/samples/sid_end_device/include/sidewalk.h @@ -8,6 +8,9 @@ #include +#include +#include + typedef enum { SID_EVENT_SIDEWALK, SID_EVENT_FACTORY_RESET, @@ -17,6 +20,7 @@ typedef enum { SID_EVENT_LINK_SWITCH, SID_EVENT_NORDIC_DFU, SID_EVENT_FILE_TRANSFER, + SID_EVENT_REBOOT, SID_EVENT_LAST, } sidewalk_event_t; @@ -32,6 +36,7 @@ typedef struct { } sidewalk_ctx_t; typedef struct { + sys_snode_t node; struct sid_msg msg; struct sid_msg_desc desc; } sidewalk_msg_t; @@ -42,8 +47,17 @@ typedef struct { size_t data_len; } sidewalk_option_t; +typedef struct { + uint32_t file_id; + size_t file_offset; + void *data; + size_t data_size; +} sidewalk_transfer_t; + void sidewalk_start(sidewalk_ctx_t *context); int sidewalk_event_send(sidewalk_event_t event, void *ctx); +sidewalk_msg_t *get_message_buffer(uint16_t message_id); + #endif /* SIDEWALK_APP_H */ diff --git a/samples/sid_end_device/overlay-dut.conf b/samples/sid_end_device/overlay-dut.conf index a499386..38ab735 100644 --- a/samples/sid_end_device/overlay-dut.conf +++ b/samples/sid_end_device/overlay-dut.conf @@ -14,6 +14,9 @@ CONFIG_SIDEWALK_BLE_ADAPTER_LOG_LEVEL_DBG=y CONFIG_SID_END_DEVICE_AUTO_START=n CONFIG_SID_END_DEVICE_ECHO_MSGS=n +CONFIG_SIDEWALK_FILE_TRANSFER=y +CONFIG_SIDEWALK_FILE_TRANSFER_DFU=n + # smtc: #CONFIG_SIDEWALK_LINK_MASK_FSK=y # causes DUAL_LINK_SUPPORT=0 then non-working "sid init 3" #CONFIG_SIDEWALK_LINK_MASK_LORA=y diff --git a/samples/sid_end_device/pm_static.yml b/samples/sid_end_device/pm_static.yml deleted file mode 100644 index 55dba59..0000000 --- a/samples/sid_end_device/pm_static.yml +++ /dev/null @@ -1,10 +0,0 @@ -mcuboot: - address: 0x0 - end_address: 0x8000 - region: flash_primary - size: 0x8000 -mfg_storage: - address: 0xff000 - end_address: 0x100000 - region: flash_primary - size: 0x1000 diff --git a/samples/sid_end_device/pm_static_nrf52840dk_nrf52840.yml b/samples/sid_end_device/pm_static_nrf52840dk_nrf52840.yml new file mode 100644 index 0000000..10bf45a --- /dev/null +++ b/samples/sid_end_device/pm_static_nrf52840dk_nrf52840.yml @@ -0,0 +1,73 @@ +app: + address: 0x7200 + end_address: 0xfd000 + region: flash_primary + size: 0xf5e00 +external_flash: + address: 0xf6000 + end_address: 0x800000 + region: external_flash + size: 0x70a000 +mcuboot: + address: 0x0 + end_address: 0x7000 + region: flash_primary + size: 0x7000 +mcuboot_pad: + address: 0x7000 + end_address: 0x7200 + placement: + align: + start: 0x1000 + before: + - mcuboot_primary_app + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x7000 + end_address: 0xfd000 + orig_span: &id001 + - app + - mcuboot_pad + region: flash_primary + size: 0xf6000 + span: *id001 +mcuboot_primary_app: + address: 0x7200 + end_address: 0xfd000 + orig_span: &id002 + - app + region: flash_primary + size: 0xf5e00 + span: *id002 +mcuboot_secondary: + address: 0x0 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0xf6000 + placement: + align: + start: 0x4 + region: external_flash + share_size: + - mcuboot_primary + size: 0xf6000 +mfg_storage: + address: 0xff000 + end_address: 0x100000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0xfd000 + end_address: 0xff000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml b/samples/sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml new file mode 100644 index 0000000..10bf45a --- /dev/null +++ b/samples/sid_end_device/pm_static_nrf52840dk_nrf52840_release.yml @@ -0,0 +1,73 @@ +app: + address: 0x7200 + end_address: 0xfd000 + region: flash_primary + size: 0xf5e00 +external_flash: + address: 0xf6000 + end_address: 0x800000 + region: external_flash + size: 0x70a000 +mcuboot: + address: 0x0 + end_address: 0x7000 + region: flash_primary + size: 0x7000 +mcuboot_pad: + address: 0x7000 + end_address: 0x7200 + placement: + align: + start: 0x1000 + before: + - mcuboot_primary_app + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x7000 + end_address: 0xfd000 + orig_span: &id001 + - app + - mcuboot_pad + region: flash_primary + size: 0xf6000 + span: *id001 +mcuboot_primary_app: + address: 0x7200 + end_address: 0xfd000 + orig_span: &id002 + - app + region: flash_primary + size: 0xf5e00 + span: *id002 +mcuboot_secondary: + address: 0x0 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0xf6000 + placement: + align: + start: 0x4 + region: external_flash + share_size: + - mcuboot_primary + size: 0xf6000 +mfg_storage: + address: 0xff000 + end_address: 0x100000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0xfd000 + end_address: 0xff000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp.yml b/samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp.yml new file mode 100644 index 0000000..5764ee5 --- /dev/null +++ b/samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp.yml @@ -0,0 +1,119 @@ +EMPTY_0: + address: 0xfe000 + end_address: 0xff000 + placement: + after: + - settings_storage + region: flash_primary + size: 0x1000 +app: + address: 0x8200 + end_address: 0xfc000 + region: flash_primary + size: 0xf3e00 +external_flash: + address: 0x134000 + end_address: 0x800000 + region: external_flash + size: 0x6cc000 +mcuboot: + address: 0x0 + end_address: 0x8000 + region: flash_primary + size: 0x8000 +mcuboot_pad: + address: 0x8000 + end_address: 0x8200 + placement: + align: + start: 0x4000 + before: + - mcuboot_primary_app + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x8000 + end_address: 0xfc000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + size: 0xf4000 + span: *id001 +mcuboot_primary_1: + address: 0x0 + device: nordic_ram_flash_controller + end_address: 0x40000 + region: ram_flash + size: 0x40000 +mcuboot_primary_app: + address: 0x8200 + end_address: 0xfc000 + orig_span: &id002 + - app + region: flash_primary + size: 0xf3e00 + span: *id002 +mcuboot_secondary: + address: 0x0 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0xf4000 + placement: + align: + start: 0x4 + region: external_flash + share_size: + - mcuboot_primary + size: 0xf4000 +mcuboot_secondary_1: + address: 0xf4000 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0x134000 + region: external_flash + size: 0x40000 +mfg_storage: + address: 0xff000 + end_address: 0x100000 + region: flash_primary + size: 0x1000 +otp: + address: 0xff8100 + end_address: 0xff83fc + region: otp + size: 0x2fc +pcd_sram: + address: 0x20000000 + end_address: 0x20002000 + placement: + after: + - start + region: sram_primary + size: 0x2000 +ram_flash: + address: 0x40000 + end_address: 0x40000 + region: ram_flash + size: 0x0 +rpmsg_nrf53_sram: + address: 0x20070000 + end_address: 0x20080000 + placement: + before: + - end + region: sram_primary + size: 0x10000 +settings_storage: + address: 0xfc000 + end_address: 0xfe000 + placement: + align: + start: 0x4000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20002000 + end_address: 0x20070000 + region: sram_primary + size: 0x6e000 diff --git a/samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml b/samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml new file mode 100644 index 0000000..5764ee5 --- /dev/null +++ b/samples/sid_end_device/pm_static_nrf5340dk_nrf5340_cpuapp_release.yml @@ -0,0 +1,119 @@ +EMPTY_0: + address: 0xfe000 + end_address: 0xff000 + placement: + after: + - settings_storage + region: flash_primary + size: 0x1000 +app: + address: 0x8200 + end_address: 0xfc000 + region: flash_primary + size: 0xf3e00 +external_flash: + address: 0x134000 + end_address: 0x800000 + region: external_flash + size: 0x6cc000 +mcuboot: + address: 0x0 + end_address: 0x8000 + region: flash_primary + size: 0x8000 +mcuboot_pad: + address: 0x8000 + end_address: 0x8200 + placement: + align: + start: 0x4000 + before: + - mcuboot_primary_app + region: flash_primary + size: 0x200 +mcuboot_primary: + address: 0x8000 + end_address: 0xfc000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + size: 0xf4000 + span: *id001 +mcuboot_primary_1: + address: 0x0 + device: nordic_ram_flash_controller + end_address: 0x40000 + region: ram_flash + size: 0x40000 +mcuboot_primary_app: + address: 0x8200 + end_address: 0xfc000 + orig_span: &id002 + - app + region: flash_primary + size: 0xf3e00 + span: *id002 +mcuboot_secondary: + address: 0x0 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0xf4000 + placement: + align: + start: 0x4 + region: external_flash + share_size: + - mcuboot_primary + size: 0xf4000 +mcuboot_secondary_1: + address: 0xf4000 + device: DT_CHOSEN(nordic_pm_ext_flash) + end_address: 0x134000 + region: external_flash + size: 0x40000 +mfg_storage: + address: 0xff000 + end_address: 0x100000 + region: flash_primary + size: 0x1000 +otp: + address: 0xff8100 + end_address: 0xff83fc + region: otp + size: 0x2fc +pcd_sram: + address: 0x20000000 + end_address: 0x20002000 + placement: + after: + - start + region: sram_primary + size: 0x2000 +ram_flash: + address: 0x40000 + end_address: 0x40000 + region: ram_flash + size: 0x0 +rpmsg_nrf53_sram: + address: 0x20070000 + end_address: 0x20080000 + placement: + before: + - end + region: sram_primary + size: 0x10000 +settings_storage: + address: 0xfc000 + end_address: 0xfe000 + placement: + align: + start: 0x4000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20002000 + end_address: 0x20070000 + region: sram_primary + size: 0x6e000 diff --git a/samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml b/samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml new file mode 100644 index 0000000..2aae80f --- /dev/null +++ b/samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp.yml @@ -0,0 +1,71 @@ +app: + address: 0xc800 + end_address: 0xc3000 + region: flash_primary + size: 0xb6800 +mcuboot: + address: 0x0 + end_address: 0xc000 + placement: + before: + - mcuboot_primary + region: flash_primary + size: 0xc000 +mcuboot_pad: + address: 0xc000 + end_address: 0xc800 + placement: + before: + - mcuboot_primary_app + region: flash_primary + size: 0x800 +mcuboot_primary: + address: 0xc000 + end_address: 0xc3000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + sharers: 0x1 + size: 0xb7000 + span: *id001 +mcuboot_primary_app: + address: 0xc800 + end_address: 0xc3000 + orig_span: &id002 + - app + region: flash_primary + size: 0xb6800 + span: *id002 +mcuboot_secondary: + address: 0xc3000 + end_address: 0x17a000 + placement: + after: + - mcuboot_primary + align: + start: 0x1000 + region: flash_primary + share_size: + - mcuboot_primary + size: 0xb7000 +mfg_storage: + address: 0x17c000 + end_address: 0x17d000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0x17a000 + end_address: 0x17c000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml b/samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml new file mode 100644 index 0000000..2aae80f --- /dev/null +++ b/samples/sid_end_device/pm_static_nrf54l15pdk_nrf54l15_cpuapp_release.yml @@ -0,0 +1,71 @@ +app: + address: 0xc800 + end_address: 0xc3000 + region: flash_primary + size: 0xb6800 +mcuboot: + address: 0x0 + end_address: 0xc000 + placement: + before: + - mcuboot_primary + region: flash_primary + size: 0xc000 +mcuboot_pad: + address: 0xc000 + end_address: 0xc800 + placement: + before: + - mcuboot_primary_app + region: flash_primary + size: 0x800 +mcuboot_primary: + address: 0xc000 + end_address: 0xc3000 + orig_span: &id001 + - mcuboot_pad + - app + region: flash_primary + sharers: 0x1 + size: 0xb7000 + span: *id001 +mcuboot_primary_app: + address: 0xc800 + end_address: 0xc3000 + orig_span: &id002 + - app + region: flash_primary + size: 0xb6800 + span: *id002 +mcuboot_secondary: + address: 0xc3000 + end_address: 0x17a000 + placement: + after: + - mcuboot_primary + align: + start: 0x1000 + region: flash_primary + share_size: + - mcuboot_primary + size: 0xb7000 +mfg_storage: + address: 0x17c000 + end_address: 0x17d000 + region: flash_primary + size: 0x1000 +settings_storage: + address: 0x17a000 + end_address: 0x17c000 + placement: + align: + start: 0x1000 + before: + - end + region: flash_primary + size: 0x2000 +sram_primary: + address: 0x20000000 + end_address: 0x20040000 + region: sram_primary + size: 0x40000 diff --git a/samples/sid_end_device/configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml b/samples/sid_end_device/pm_static_thingy53_nrf5340_cpuapp.yml similarity index 100% rename from samples/sid_end_device/configuration/thingy53_nrf5340_cpuapp/pm_static_dfu.yml rename to samples/sid_end_device/pm_static_thingy53_nrf5340_cpuapp.yml diff --git a/samples/sid_end_device/prj_no_dfu.conf b/samples/sid_end_device/prj_no_dfu.conf deleted file mode 100644 index 3f14d8c..0000000 --- a/samples/sid_end_device/prj_no_dfu.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (c) 2023 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# - -# Sidewalk -CONFIG_SIDEWALK=y -CONFIG_SIDEWALK_DFU=n -CONFIG_SMF=y - -# Log -CONFIG_LOG=y -CONFIG_LOG_PRINTK=y -CONFIG_LOG_BUFFER_SIZE=2048 -CONFIG_NVS_LOG_LEVEL_WRN=y - -# Bluetooth -CONFIG_BT_DEVICE_NAME="Nordic" - -# Debug -CONFIG_RESET_ON_FATAL_ERROR=n diff --git a/samples/sid_end_device/sample.yaml b/samples/sid_end_device/sample.yaml index a61c19d..e4635fc 100644 --- a/samples/sid_end_device/sample.yaml +++ b/samples/sid_end_device/sample.yaml @@ -1,96 +1,62 @@ sample: name: Sidewalk end device sample description: Sample implementing Amazon Sidewalk End Device +common: + sysbuild: true + build_only: true + platform_allow: + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54l15pdk/nrf54l15/cpuapp + integration_platforms: + - nrf52840dk/nrf52840 + - nrf5340dk/nrf5340/cpuapp + - nrf54l15pdk/nrf54l15/cpuapp tests: sample.sidewalk.hello: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp + - CONFIG_SIDEWALK_FILE_TRANSFER=y tags: Sidewalk hello sample.sidewalk.hello.release: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: - CONF_FILE=prj_release.conf + FILE_SUFFIX=release extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp - tags: Sidewalk hello - - sample.sidewalk.hello.no_dfu: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp - extra_args: - CONF_FILE=prj_no_dfu.conf - extra_configs: - - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello sample.sidewalk.hello.ble_only: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello BLE sample.sidewalk.hello.ble_only.release: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: - CONF_FILE=prj_release.conf + FILE_SUFFIX=release extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk hello BLE sample.sidewalk.demo: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: OVERLAY_CONFIG="overlay-demo.conf" extra_configs: - CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk demo sample.sidewalk.demo.ble_only: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp thingy53_nrf5340_cpuapp + platform_allow: + - thingy53/nrf5340/cpuapp extra_args: OVERLAY_CONFIG="overlay-demo.conf" extra_configs: - CONFIG_SIDEWALK_SUBGHZ_SUPPORT=n integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp - - thingy53_nrf5340_cpuapp + - thingy53/nrf5340/cpuapp tags: Sidewalk demo BLE sample.sidewalk.dut: - build_only: true - platform_allow: nrf52840dk_nrf52840 nrf5340dk_nrf5340_cpuapp extra_args: OVERLAY_CONFIG="overlay-dut.conf" - extra_configs: - - CONFIG_SIDEWALK_FILE_TRANSFER=y - integration_platforms: - - nrf52840dk_nrf52840 - - nrf5340dk_nrf5340_cpuapp tags: Sidewalk cli diff --git a/samples/sid_end_device/src/cli/app.c b/samples/sid_end_device/src/cli/app.c index 0dad5c5..97eef5e 100644 --- a/samples/sid_end_device/src/cli/app.c +++ b/samples/sid_end_device/src/cli/app.c @@ -11,8 +11,7 @@ #include #include #include -#include -#include +#include #include LOG_MODULE_REGISTER(app, CONFIG_SIDEWALK_LOG_LEVEL); @@ -40,6 +39,13 @@ static void on_sidewalk_msg_sent(const struct sid_msg_desc *msg_desc, void *cont LOG_INF("Message send success"); printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( "on_msg_sent", JSON_OBJ(JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0)))))); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc *msg_desc, @@ -50,6 +56,13 @@ static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc "on_send_error", JSON_OBJ(JSON_LIST_2(JSON_VAL_sid_error_t("error", error), JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0))))))); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_factory_reset(void *context) diff --git a/samples/sid_end_device/src/cli/app_dut.c b/samples/sid_end_device/src/cli/app_dut.c index fbbe28e..9788694 100644 --- a/samples/sid_end_device/src/cli/app_dut.c +++ b/samples/sid_end_device/src/cli/app_dut.c @@ -4,13 +4,14 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#include "sid_error.h" #include #include #include #include #include #include -#include +#include LOG_MODULE_REGISTER(sid_cli, CONFIG_SIDEWALK_LOG_LEVEL); @@ -76,7 +77,7 @@ static void dut_option_get(sidewalk_option_t *p_option, struct sid_handle *handl case SID_OPTION_GET_LINK_POLICY_AUTO_CONNECT_PARAMS: { struct sid_link_auto_connect_params params = { 0 }; memcpy(¶ms.link_type, p_option->data, sizeof(uint32_t)); - sid_error_t e = sid_option(handle, opt, ¶ms, sizeof(params)); + sid_error_t e = sid_option(handle, opt, (void *)¶ms, sizeof(params)); LOG_INF("sid_option returned %d; AC Policy, link %d, enable %d priority %d timeout %d", e, params.link_type, params.enable, params.priority, params.connection_attempt_timeout_seconds); @@ -123,6 +124,9 @@ void app_dut_event_process(sidewalk_ctx_event_t event, sidewalk_ctx_t *sid) sid->config.link_mask = dut_ctx_get_uint32(event.ctx); sid_error_t e = sid_init(&sid->config, &sid->handle); LOG_INF("sid_init returned %d", e); + if (e != SID_ERROR_NONE) { + return; + } #ifdef CONFIG_SIDEWALK_FILE_TRANSFER app_file_transfer_demo_init(sid->handle); #endif @@ -157,13 +161,13 @@ void app_dut_event_process(sidewalk_ctx_event_t event, sidewalk_ctx_t *sid) case DUT_EVENT_GET_MTU: { uint32_t link_mask = dut_ctx_get_uint32(event.ctx); size_t mtu = 0; - sid_error_t e = sid_get_mtu(sid->handle, link_mask, &mtu); + sid_error_t e = sid_get_mtu(sid->handle, (enum sid_link_type)link_mask, &mtu); LOG_INF("sid_get_mtu returned %d, MTU: %d", e, mtu); } break; case DUT_EVENT_GET_TIME: { uint32_t format = dut_ctx_get_uint32(event.ctx); struct sid_timespec curr_time = { 0 }; - sid_error_t e = sid_get_time(sid->handle, format, &curr_time); + sid_error_t e = sid_get_time(sid->handle, (enum sid_time_format)format, &curr_time); LOG_INF("sid_get_time returned %d, SEC: %d NSEC: %d", e, curr_time.tv_sec, curr_time.tv_nsec); } break; diff --git a/samples/sid_end_device/src/cli/app_shell.c b/samples/sid_end_device/src/cli/app_shell.c index 5cffbc1..728dd31 100644 --- a/samples/sid_end_device/src/cli/app_shell.c +++ b/samples/sid_end_device/src/cli/app_shell.c @@ -227,7 +227,7 @@ static int cmd_sid_option(cli_event_t event, enum sid_option option, void *data, p_opt->data = NULL; } - int err = sidewalk_event_send(event, p_opt); + int err = sidewalk_event_send((sidewalk_event_t)event, p_opt); if (err) { if (p_opt->data) { sid_hal_free(p_opt->data); @@ -262,7 +262,7 @@ static int cmd_sid_simple_param(cli_event_t event, uint32_t *data) } memcpy(event_ctx, data, sizeof(uint32_t)); - int err = sidewalk_event_send(event, event_ctx); + int err = sidewalk_event_send((sidewalk_event_t)event, event_ctx); if (err) { sid_hal_free(event_ctx); return -ENOMSG; @@ -282,10 +282,12 @@ int cmd_sid_init(const struct shell *shell, int32_t argc, const char **argv) uint32_t link_type = 0; if (!IN_RANGE(connection_type, CLI_CMD_OPT_LINK_BLE, CLI_CMD_OPT_LINK_ANY)) { + shell_error(shell, "invalid value"); return -EINVAL; } if (!cli_parse_link_mask_opt(connection_type, &link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } cli_cfg.send_link_type = link_type; @@ -297,7 +299,7 @@ int cmd_sid_deinit(const struct shell *shell, int32_t argc, const char **argv) { CHECK_ARGUMENT_COUNT(argc, CMD_SID_DEINIT_ARG_REQUIRED, CMD_SID_DEINIT_ARG_OPTIONAL); - return sidewalk_event_send(DUT_EVENT_DEINIT, NULL); + return sidewalk_event_send((sidewalk_event_t)DUT_EVENT_DEINIT, NULL); } int cmd_sid_start(const struct shell *shell, int32_t argc, const char **argv) @@ -309,6 +311,7 @@ int cmd_sid_start(const struct shell *shell, int32_t argc, const char **argv) link_type = cli_cfg.send_link_type; } else { if (!cli_parse_link_mask_opt(atoi(argv[1]), &link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } } @@ -325,6 +328,7 @@ int cmd_sid_stop(const struct shell *shell, int32_t argc, const char **argv) link_type = cli_cfg.send_link_type; } else { if (!cli_parse_link_mask_opt(atoi(argv[1]), &link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } } @@ -419,6 +423,7 @@ int cmd_sid_send(const struct shell *shell, int32_t argc, const char **argv) } if (!cli_parse_link_mask_opt(atoi(argv[opt]), &desc.link_type)) { + shell_error(shell, "invalid value"); return -EINVAL; } continue; @@ -497,7 +502,7 @@ int cmd_sid_send(const struct shell *shell, int32_t argc, const char **argv) memcpy(&send->msg, &msg, sizeof(struct sid_msg)); memcpy(&send->desc, &desc, sizeof(struct sid_msg_desc)); - int err = sidewalk_event_send(SID_EVENT_SEND_MSG, send); + int err = sidewalk_event_send((sidewalk_event_t)SID_EVENT_SEND_MSG, send); if (err) { sid_hal_free(send->msg.data); sid_hal_free(send); @@ -512,7 +517,7 @@ int cmd_sid_factory_reset(const struct shell *shell, int32_t argc, const char ** CHECK_ARGUMENT_COUNT(argc, CMD_SID_FACTORY_RESET_ARG_REQUIRED, CMD_SID_FACTORY_RESET_ARG_OPTIONAL); - int err = sidewalk_event_send(SID_EVENT_FACTORY_RESET, NULL); + int err = sidewalk_event_send((sidewalk_event_t)SID_EVENT_FACTORY_RESET, NULL); if (err) { shell_error(shell, "event err %d", err); } @@ -537,6 +542,7 @@ int cmd_sid_get_mtu(const struct shell *shell, int32_t argc, const char **argv) link_mask = SID_LINK_TYPE_3; break; default: + shell_error(shell, "invalid value"); return -EINVAL; } @@ -584,6 +590,7 @@ int cmd_sid_option_lp_set(const struct shell *shell, int32_t argc, const char ** data_raw = strtol(argv[1], &end, 0); if (end == argv[1]) { + shell_error(shell, "invalid value"); return -EINVAL; } if (!IN_RANGE(data_raw, 0, UINT8_MAX)) { @@ -885,7 +892,10 @@ int cmd_sid_option_gc(const struct shell *shell, int32_t argc, const char **argv } memset(p_link_mask, 0x0, sizeof(*p_link_mask)); - cli_parse_link_mask_opt((uint8_t)link_type, p_link_mask); + if (!cli_parse_link_mask_opt((uint8_t)link_type, p_link_mask)) { + shell_error(shell, "Can not parse link mask"); + return -EINVAL; + } int err = cmd_sid_option_get_input_data(SID_OPTION_GET_LINK_POLICY_AUTO_CONNECT_PARAMS, p_link_mask, sizeof(uint32_t)); @@ -901,7 +911,9 @@ int cmd_sid_last_status(const struct shell *shell, int32_t argc, const char **ar CHECK_ARGUMENT_COUNT(argc, CMD_SID_LAST_STATUS_ARG_REQUIRED, CMD_SID_LAST_STATUS_ARG_OPTIONAL); - sidewalk_event_send(DUT_EVENT_GET_STATUS, NULL); + if (0 != sidewalk_event_send((sidewalk_event_t)DUT_EVENT_GET_STATUS, NULL)) { + shell_error(shell, "Failed to send Event"); + } return 0; } @@ -919,6 +931,7 @@ int cmd_sid_conn_request(const struct shell *shell, int32_t argc, const char **a conn_req = 0U; break; default: + shell_error(shell, "invalid value"); return -EINVAL; } @@ -930,6 +943,7 @@ int cmd_sid_get_time(const struct shell *shell, int32_t argc, const char **argv) CHECK_ARGUMENT_COUNT(argc, CMD_SID_GET_TIME_ARG_REQUIRED, CMD_SID_GET_TIME_ARG_OPTIONAL); if (argv[1][0] != '0') { + shell_error(shell, "invalid value"); return -EINVAL; } uint32_t time_type = SID_GET_GPS_TIME; @@ -970,6 +984,7 @@ int cmd_sid_set_send_link(const struct shell *shell, int32_t argc, const char ** break; } default: { + shell_error(shell, "invalid value"); return -EINVAL; } } diff --git a/samples/sid_end_device/src/cli/sid_on_dev_cert_cli.c b/samples/sid_end_device/src/cli/sid_on_dev_cert_cli.c new file mode 100644 index 0000000..e6d1df5 --- /dev/null +++ b/samples/sid_end_device/src/cli/sid_on_dev_cert_cli.c @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include + +#include +#include + +#include + +LOG_MODULE_REGISTER(sid_dev_cert_shell, CONFIG_SIDEWALK_LOG_LEVEL); + +#ifdef SYNTAX_ERR +#undef SYNTAX_ERR +#endif +#define SYNTAX_ERR "Syntax err \r\n" + +#define CERT_INIT_H "Initialization of On-Device Certificate Generation library" +#define CERT_DEINIT_H "Deinitialization of On-Device Certificate Generation Library" +#define CERT_SMSN_H "Generate Sidewalk Manufacturing Serial Number (SMSN)" +#define CERT_CSR_H "Generate Certificate Signing Request (CSR)" +#define CERT_CHAIN_H "Write Sidewalk Certificate Chain" +#define CERT_CHAIN_START_H "Start writing of Sidewalk Certificate Chain" +#define CERT_CHAIN_WRITE_H "Write data fragment to Sidewalk Certificate Chain" +#define CERT_CHAIN_COMMIT_H "Commit previously writed Sidewalk Certificate Chain" +#define CERT_APPKEY_H "Write application server ED25519 public key" +#define CERT_APPKEY_START_H "Start writing of app server ED25519 public key" +#define CERT_APPKEY_WRITE_H "Write data fragment to app server ED25519 public key" +#define CERT_APPKEY_COMMIT_H "Commit previously writed app server ED25519 public key" +#define CERT_STORE_H "Verify and store Sidewalk certificates" + +#define CERT_INIT_CMD "cert init" +#define CERT_DEINIT_CMD "cert deinit" +#define CERT_SMSN_CMD "cert smsn []" +#define CERT_CSR_CMD "cert csr " +#define CERT_CHAIN_START_CMD "cert chain start " +#define CERT_CHAIN_WRITE_CMD "cert chain write " +#define CERT_CHAIN_COMMIT_CMD "cert chain commit" +#define CERT_APPKEY_START_CMD "cert app_key start" +#define CERT_APPKEY_WRITE_CMD "cert app_key write " +#define CERT_APPKEY_COMMIT_CMD "cert app_key commit" +#define CERT_STORE_CMD "cert store" + +#define CERT_MSG_OK "{CERT OK}" +#define CERT_MSG_ERROR "{CERT ERROR %d}" +#define CERT_MSG_BASE64 "{CERT <%s>}" +#define CERT_MSG_BASE64_MTU "{CERT BASE64_MTU=%d}" +#define CERT_ED25519_STR "ed25519" +#define CERT_P256R1_STR "p256r1" +#define CERT_DEV_TYPE_SUFFIX "-PRODUCTION" + +#define CERT_BASE64_FRAGMENT_MAX_SIZE 32 + +static uint8_t cert_app_key[SID_ODC_ED25519_PUK_SIZE]; +static uint8_t cert_chain_buffer[SID_ODC_SCC_MAX_SIZE]; +static struct sid_on_dev_cert_chain_params cert_chain_params = { + .cert_chain = NULL, + .cert_chain_size = 0, +}; + +// Persistent state for receiving the encoded certificate chain in multiple fragments +static struct sid_base64_ctx cert_chain_base64_ctx = { + .next_out = NULL, + .avail_out = 0, + .total_out = 0, + .state = 0, +}; + +static struct sid_base64_ctx cert_app_key_base64_ctx = { + .next_out = NULL, + .avail_out = 0, + .total_out = 0, + .state = 0, +}; + +static sid_error_t sid_on_dev_cert_cli_print_base64(const struct shell *shell, const uint8_t *input, + size_t input_size) +{ + struct sid_base64_ctx ctx; + sid_base64_init(&ctx); + uint8_t out[CERT_BASE64_FRAGMENT_MAX_SIZE + 1]; // allow extra byte for null terminator + + ctx.next_in = input; + ctx.avail_in = input_size; + ctx.next_out = out; + ctx.avail_out = CERT_BASE64_FRAGMENT_MAX_SIZE; + + while (true) { + sid_error_t status = sid_base64_encode(&ctx, true); + if (status == SID_ERROR_NONE || status == SID_ERROR_BUFFER_OVERFLOW) { + // add a null terminator + *ctx.next_out = '\0'; + + shell_info(shell, CERT_MSG_BASE64, out); + + if (status == SID_ERROR_NONE) { + break; + } + // Reset the output buffer for the next line + ctx.next_out = out; + ctx.avail_out = CERT_BASE64_FRAGMENT_MAX_SIZE; + } else { + return SID_ERROR_GENERIC; + } + } + return SID_ERROR_NONE; +} + +static int sid_on_dev_cert_cli_init_cmd(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 1) { + sid_error_t ret = sid_on_dev_cert_init(); + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_BASE64_MTU, CERT_BASE64_FRAGMENT_MAX_SIZE); + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_INIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_deinit(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 1) { + sid_on_dev_cert_deinit(); + + shell_info(shell, CERT_MSG_OK); + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_DEINIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_smsn(const struct shell *shell, int32_t argc, const char **argv) +{ + // command format: cert smsn [] + if (argc >= 4 && argc <= 5) { + sid_error_t ret = SID_ERROR_NONE; + // Note on device-type. It gets transformed into "Amazon-id" which is -PRODUCTION + char *dev_type_production = + sid_hal_malloc(strlen(argv[1]) + strlen(CERT_DEV_TYPE_SUFFIX) + 1); + if (!dev_type_production) { + ret = SID_ERROR_OOM; + goto exit; + } + dev_type_production[0] = 0; + strcat(dev_type_production, argv[1]); + strcat(dev_type_production, CERT_DEV_TYPE_SUFFIX); + + const struct sid_on_dev_cert_info dev_info = { + .dev_type = dev_type_production, + .dsn = argv[2], + .apid = argv[3], + .board_id = argc == 5 ? argv[4] : NULL, + }; + + uint8_t smsn[SID_ODC_SMSN_SIZE]; + memset(smsn, 0xFF, SID_ODC_SMSN_SIZE); + if ((ret = sid_on_dev_cert_generate_smsn(&dev_info, smsn)) == SID_ERROR_NONE) { + ret = sid_on_dev_cert_cli_print_base64(shell, smsn, SID_ODC_SMSN_SIZE); + } + + sid_hal_free(dev_type_production); + exit: + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_SMSN_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_csr(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + uint8_t csr[SID_ODC_CSR_MAX_SIZE]; + size_t csr_size = SID_ODC_CSR_MAX_SIZE; + enum sid_on_dev_cert_algo_type algo; + + if (strcmp(argv[1], CERT_ED25519_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_ED25519; + } else if (strcmp(argv[1], CERT_P256R1_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_P256R1; + } else { + ret = SID_ERROR_INVALID_ARGS; + } + + if (ret == SID_ERROR_NONE && + (ret = sid_on_dev_cert_generate_csr(algo, csr, &csr_size)) == SID_ERROR_NONE && + (ret = sid_on_dev_cert_cli_print_base64(shell, csr, csr_size)) == + SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CSR_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_chain_start(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + enum sid_on_dev_cert_algo_type algo; + + if (strcmp(argv[1], CERT_ED25519_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_ED25519; + } else if (strcmp(argv[1], CERT_P256R1_STR) == 0) { + algo = SID_ODC_CRYPT_ALGO_P256R1; + } else { + ret = SID_ERROR_INVALID_ARGS; + } + if (ret == SID_ERROR_NONE) { + sid_base64_init(&cert_chain_base64_ctx); + cert_chain_base64_ctx.next_out = cert_chain_buffer; + cert_chain_base64_ctx.avail_out = (algo == SID_ODC_CRYPT_ALGO_ED25519) ? + SID_ODC_ED25519_SCC_MAX_SIZE : + SID_ODC_P256R1_SCC_MAX_SIZE; + cert_chain_params.algo = algo; + cert_chain_params.cert_chain = cert_chain_buffer; + cert_chain_params.cert_chain_size = 0; + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CHAIN_START_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_chain_write(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + size_t base64_fragment_len = strlen(argv[1]); + + if (base64_fragment_len >= 1 && + base64_fragment_len <= CERT_BASE64_FRAGMENT_MAX_SIZE) { + if (cert_chain_params.cert_chain) { + cert_chain_base64_ctx.next_in = (const uint8_t *)argv[1]; + cert_chain_base64_ctx.avail_in = base64_fragment_len; + ret = sid_base64_decode(&cert_chain_base64_ctx); + /* + * If we have a successful result, we should check: + * - the input buffer should be empty + * - if the output buffer is empty and there are 8 bits or more of temporary data, it is an error + */ + if (ret != SID_ERROR_NONE || cert_chain_base64_ctx.avail_in != 0 || + (cert_chain_base64_ctx.avail_out == 0 && + cert_chain_base64_ctx.temp_len >= 8)) { + // Clear chain pointer. This makes chain uninitialized. + cert_chain_params.cert_chain = NULL; + // Reset base64 context + sid_base64_init(&cert_chain_base64_ctx); + ret = SID_ERROR_INVALID_ARGS; + } + } else { + ret = SID_ERROR_UNINITIALIZED; + } + } else { + ret = SID_ERROR_PARAM_OUT_OF_RANGE; + } + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CHAIN_WRITE_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_chain_commit(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 1) { + sid_error_t ret = SID_ERROR_NONE; + if (cert_chain_params.cert_chain) { + cert_chain_params.cert_chain_size = cert_chain_base64_ctx.total_out; + ret = sid_on_dev_cert_write_cert_chain(&cert_chain_params); + // Clear chain pointer. This makes chain uninitialized. + cert_chain_params.cert_chain = NULL; + // Reset base64 context + sid_base64_init(&cert_chain_base64_ctx); + } else { + ret = SID_ERROR_UNINITIALIZED; + } + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_CHAIN_COMMIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_app_key_start(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 1) { + sid_base64_init(&cert_app_key_base64_ctx); + cert_app_key_base64_ctx.next_out = cert_app_key; + cert_app_key_base64_ctx.avail_out = SID_ODC_ED25519_PUK_SIZE; + shell_info(shell, CERT_MSG_OK); + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_APPKEY_START_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_app_key_write(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 2) { + sid_error_t ret = SID_ERROR_NONE; + size_t base64_fragment_len = strlen(argv[1]); + + if (base64_fragment_len >= 1 && + base64_fragment_len <= CERT_BASE64_FRAGMENT_MAX_SIZE) { + if (cert_app_key_base64_ctx.next_out) { + cert_app_key_base64_ctx.next_in = (const uint8_t *)argv[1]; + cert_app_key_base64_ctx.avail_in = base64_fragment_len; + + ret = sid_base64_decode(&cert_app_key_base64_ctx); + /* + * If we have a successful result, we should check: + * - the input buffer should be empty + * - if the output buffer is empty and there are 8 bits or more of temporary data, it is an error + */ + if (ret != SID_ERROR_NONE || + cert_app_key_base64_ctx.avail_in != 0 || + (cert_app_key_base64_ctx.avail_out == 0 && + cert_app_key_base64_ctx.temp_len >= 8)) { + // Reset base64 context + sid_base64_init(&cert_app_key_base64_ctx); + ret = SID_ERROR_INVALID_ARGS; + } + } else { + ret = SID_ERROR_UNINITIALIZED; + } + } else { + ret = SID_ERROR_PARAM_OUT_OF_RANGE; + } + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_APPKEY_WRITE_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_app_key_commit(const struct shell *shell, int32_t argc, + const char **argv) +{ + if (argc == 1) { + sid_error_t ret = SID_ERROR_NONE; + + if (cert_app_key_base64_ctx.total_out == SID_ODC_ED25519_PUK_SIZE && + cert_app_key_base64_ctx.avail_out == 0) { + ret = sid_on_dev_cert_write_app_server_key((uint8_t *)cert_app_key); + } else { + ret = SID_ERROR_UNINITIALIZED; + } + // Reset base64 context + sid_base64_init(&cert_app_key_base64_ctx); + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_APPKEY_COMMIT_CMD); + } + return 0; +} + +static int sid_on_dev_cert_cli_store(const struct shell *shell, int32_t argc, const char **argv) +{ + if (argc == 1) { + sid_error_t ret = sid_on_dev_cert_verify_and_store(); + + if (ret == SID_ERROR_NONE) { + shell_info(shell, CERT_MSG_OK); + } else { + shell_info(shell, CERT_MSG_ERROR, ret); + } + + } else { + shell_error(shell, SYNTAX_ERR); + shell_warn(shell, CERT_STORE_CMD); + } + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_chain, + SHELL_CMD_ARG(start, NULL, CERT_CHAIN_START_H, sid_on_dev_cert_cli_chain_start, 2, 0), + SHELL_CMD_ARG(write, NULL, CERT_CHAIN_WRITE_H, sid_on_dev_cert_cli_chain_write, 2, 0), + SHELL_CMD_ARG(commit, NULL, CERT_APPKEY_COMMIT_H, sid_on_dev_cert_cli_chain_commit, 1, 0), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_app_key, + SHELL_CMD_ARG(start, NULL, CERT_APPKEY_START_H, sid_on_dev_cert_cli_app_key_start, 1, 0), + SHELL_CMD_ARG(write, NULL, CERT_APPKEY_WRITE_H, sid_on_dev_cert_cli_app_key_write, 2, 0), + SHELL_CMD_ARG(commit, NULL, CERT_APPKEY_COMMIT_H, sid_on_dev_cert_cli_app_key_commit, 1, 0), + SHELL_SUBCMD_SET_END); + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_services, SHELL_CMD_ARG(init, NULL, CERT_INIT_H, sid_on_dev_cert_cli_init_cmd, 1, 0), + SHELL_CMD_ARG(deinit, NULL, CERT_DEINIT_H, sid_on_dev_cert_cli_deinit, 1, 0), + SHELL_CMD_ARG(smsn, NULL, CERT_SMSN_H, sid_on_dev_cert_cli_smsn, 4, 1), + SHELL_CMD_ARG(csr, NULL, CERT_CSR_H, sid_on_dev_cert_cli_csr, 2, 0), + SHELL_CMD_ARG(chain, &sub_chain, CERT_CHAIN_H, NULL, 1, 0), + SHELL_CMD_ARG(app_key, &sub_app_key, CERT_APPKEY_H, NULL, 1, 0), + SHELL_CMD_ARG(store, NULL, CERT_STORE_H, sid_on_dev_cert_cli_store, 1, 0), + SHELL_SUBCMD_SET_END); + +// command, subcommands, help, handler +SHELL_CMD_REGISTER(cert, &sub_services, "sidewalk testing CLI", NULL); diff --git a/samples/sid_end_device/src/file_transfer.c b/samples/sid_end_device/src/file_transfer.c deleted file mode 100644 index c4b2178..0000000 --- a/samples/sid_end_device/src/file_transfer.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2023 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -LOG_MODULE_REGISTER(file_transfer, CONFIG_SIDEWALK_LOG_LEVEL); - -#define PARALEL_TRANSFER_MAX 3 - -struct buffer_repo_element { - uint32_t file_id; - void *memory_slab_for_transfer; -}; - -static struct buffer_repo_element buffer_repo[PARALEL_TRANSFER_MAX]; - -static void on_transfer_request(const struct sid_bulk_data_transfer_request *const transfer_request, - struct sid_bulk_data_transfer_response *const transfer_response, - void *context) -{ - printk(JSON_NEW_LINE(JSON_OBJ( - JSON_NAME("on_transfer_request", JSON_OBJ(JSON_VAL_sid_bulk_data_transfer_request( - "transfer_request", transfer_request)))))); - LOG_HEXDUMP_INF(transfer_request->file_descriptor, transfer_request->file_descriptor_size, - "file_descriptor"); - size_t repo_index = UINT_MAX; - for (size_t i = 0; i < PARALEL_TRANSFER_MAX; i++) { - if (buffer_repo[i].memory_slab_for_transfer == NULL) { - repo_index = i; - break; - } - } - if (repo_index > PARALEL_TRANSFER_MAX) { - LOG_ERR("Failed to find slot for transfer"); - transfer_response->status = SID_BULK_DATA_TRANSFER_ACTION_REJECT; - transfer_response->reject_reason = SID_BULK_DATA_TRANSFER_REJECT_REASON_GENERIC; - return; - } - void *ptr = sid_hal_malloc(transfer_request->minimum_scratch_buffer_size); - if (ptr == NULL) { - LOG_ERR("Failed to alloc memory"); - transfer_response->status = SID_BULK_DATA_TRANSFER_ACTION_REJECT; - transfer_response->reject_reason = SID_BULK_DATA_TRANSFER_REJECT_REASON_NO_SPACE; - return; - } - memset(ptr, 0x0, sizeof(transfer_request->minimum_scratch_buffer_size)); - - // accept all requests if only we have avaliable memory for scratch buffer - buffer_repo[repo_index].memory_slab_for_transfer = ptr; - buffer_repo[repo_index].file_id = transfer_request->file_id; - transfer_response->status = SID_BULK_DATA_TRANSFER_ACTION_ACCEPT; - transfer_response->reject_reason = SID_BULK_DATA_TRANSFER_REJECT_REASON_NONE; - transfer_response->scratch_buffer = ptr; - transfer_response->scratch_buffer_size = transfer_request->minimum_scratch_buffer_size; -} - -static void on_data_received(const struct sid_bulk_data_transfer_desc *const desc, - const struct sid_bulk_data_transfer_buffer *const buffer, - void *context) -{ - printk(JSON_NEW_LINE(JSON_OBJ( - JSON_LIST_2(JSON_NAME("on_data_received", - JSON_OBJ(JSON_VAL_sid_bulk_data_transfer_desc("desc", desc))), - JSON_NAME("data_size", JSON_INT(buffer->size)))))); - - struct data_received_args *args = - (struct data_received_args *)sid_hal_malloc(sizeof(struct data_received_args)); - if (!args) { - LOG_ERR("Failed to allocate memory for received data descriptor"); - return; - } - memset(args, 0x0, sizeof(*args)); - args->desc = *desc; - args->buffer = (struct sid_bulk_data_transfer_buffer *)buffer; - args->context = context; - int err = sidewalk_event_send(SID_EVENT_FILE_TRANSFER, args); - if (err) { - sid_hal_free(args); - } -} - -static void on_finalize_request(uint32_t file_id, void *context) -{ - printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( - "on_finalize_request", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); - - // Illustrative API indicating verification of file - // validate received file - - // always report success - sid_error_t ret = sid_bulk_data_transfer_finalize( - (struct sid_handle *)context, file_id, SID_BULK_DATA_TRANSFER_FINAL_STATUS_SUCCESS); - if (ret != SID_ERROR_NONE) { - LOG_ERR("sid_bulk_data_transfer_finalize returned %s", SID_ERROR_T_STR(ret)); - } -} - -static void on_cancel_request(uint32_t file_id, void *context) -{ - printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( - "on_cancel_request", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); -} - -static void on_error(uint32_t file_id, void *context) -{ - printk(JSON_NEW_LINE(JSON_OBJ( - JSON_NAME("on_error", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); -} - -static void on_release_scratch_buffer(uint32_t file_id, void *context) -{ - printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( - "on_release_scratch_buffer", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); - - for (size_t i = 0; i < PARALEL_TRANSFER_MAX; i++) { - if (buffer_repo[i].file_id == file_id) { - sid_hal_free(buffer_repo[i].memory_slab_for_transfer); - - buffer_repo[i].memory_slab_for_transfer = NULL; - buffer_repo[i].file_id = UINT32_MAX; - return; - } - } - LOG_ERR("failed to find file_id to be freed"); -} - -static struct sid_bulk_data_transfer_event_callbacks ft_callbacks = { - .context = NULL, - .on_transfer_request = on_transfer_request, - .on_data_received = on_data_received, - .on_finalize_request = on_finalize_request, - .on_cancel_request = on_cancel_request, - .on_error = on_error, - .on_release_scratch_buffer = on_release_scratch_buffer -}; - -void app_file_transfer_demo_init(struct sid_handle *handle) -{ - ft_callbacks.context = (void *)handle; - - sid_error_t err = sid_bulk_data_transfer_init( - &(struct sid_bulk_data_transfer_config){ .callbacks = &ft_callbacks }, handle); - if (err != SID_ERROR_NONE) { - LOG_ERR("sid_bulk_data_transfer_init returned %s", SID_ERROR_T_STR(err)); - } -} - -void app_file_transfer_demo_deinit(struct sid_handle *handle) -{ - sid_error_t err = sid_bulk_data_transfer_deinit(handle); - if (err != SID_ERROR_NONE) { - LOG_ERR("sid_bulk_data_transfer_deinit returned %s", SID_ERROR_T_STR(err)); - } -} diff --git a/samples/sid_end_device/src/hello/app.c b/samples/sid_end_device/src/hello/app.c index 6cac668..e5f4d0b 100644 --- a/samples/sid_end_device/src/hello/app.c +++ b/samples/sid_end_device/src/hello/app.c @@ -11,18 +11,17 @@ #include #include #if defined(CONFIG_GPIO) -#include +#include #endif #if defined(CONFIG_LOG) -#include +#include #endif #include #include #include #include -#include -#include +#include #ifdef CONFIG_RADIO_LR11XX #include // radio_dbg pin #endif /* CONFIG_RADIO_LR11XX */ @@ -38,7 +37,6 @@ halo_drv_semtech_ctx_t *radio_ctx; static void on_sidewalk_event(bool in_isr, void *context) { int err = sidewalk_event_send(SID_EVENT_SIDEWALK, NULL); - //sid_pal_gpio_toggle(radio_ctx->config->gpios.led_sniff); if (err) { LOG_ERR("Send event err %d", err); }; @@ -98,6 +96,13 @@ static void on_sidewalk_msg_sent(const struct sid_msg_desc *msg_desc, void *cont "on_msg_sent", JSON_OBJ(JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0)))))); application_state_sending(&global_state_notifier, false); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc *msg_desc, @@ -110,6 +115,14 @@ static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc JSON_VAL_sid_msg_desc("sid_msg_desc", msg_desc, 0))))))); application_state_sending(&global_state_notifier, false); + + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_factory_reset(void *context) @@ -135,7 +148,7 @@ static void on_sidewalk_status_changed(const struct sid_status *status, void *co } else { memcpy(new_status, status, sizeof(struct sid_status)); } - sidewalk_event_send(SID_EVENT_NEW_STATUS, new_status); + err = sidewalk_event_send(SID_EVENT_NEW_STATUS, new_status); switch (status->state) { case SID_STATE_READY: diff --git a/samples/sid_end_device/src/main.c b/samples/sid_end_device/src/main.c index e050000..e7a910a 100644 --- a/samples/sid_end_device/src/main.c +++ b/samples/sid_end_device/src/main.c @@ -8,9 +8,6 @@ #include #include -#include -LOG_MODULE_REGISTER(main, CONFIG_SIDEWALK_LOG_LEVEL); - int main(void) { PRINT_SIDEWALK_VERSION(); diff --git a/samples/lbm_sid_end_device/src/file_transfer.c b/samples/sid_end_device/src/sbdt/file_transfer.c similarity index 59% rename from samples/lbm_sid_end_device/src/file_transfer.c rename to samples/sid_end_device/src/sbdt/file_transfer.c index c4b2178..db3d42e 100644 --- a/samples/lbm_sid_end_device/src/file_transfer.c +++ b/samples/sid_end_device/src/sbdt/file_transfer.c @@ -4,30 +4,22 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ -#include -#include -#include +#include +#include +#include #include -#include +#include +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU +#include +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ +#include #include +#include #include -#include -#include - -#include LOG_MODULE_REGISTER(file_transfer, CONFIG_SIDEWALK_LOG_LEVEL); -#define PARALEL_TRANSFER_MAX 3 - -struct buffer_repo_element { - uint32_t file_id; - void *memory_slab_for_transfer; -}; - -static struct buffer_repo_element buffer_repo[PARALEL_TRANSFER_MAX]; - static void on_transfer_request(const struct sid_bulk_data_transfer_request *const transfer_request, struct sid_bulk_data_transfer_response *const transfer_response, void *context) @@ -37,34 +29,30 @@ static void on_transfer_request(const struct sid_bulk_data_transfer_request *con "transfer_request", transfer_request)))))); LOG_HEXDUMP_INF(transfer_request->file_descriptor, transfer_request->file_descriptor_size, "file_descriptor"); - size_t repo_index = UINT_MAX; - for (size_t i = 0; i < PARALEL_TRANSFER_MAX; i++) { - if (buffer_repo[i].memory_slab_for_transfer == NULL) { - repo_index = i; - break; - } - } - if (repo_index > PARALEL_TRANSFER_MAX) { - LOG_ERR("Failed to find slot for transfer"); + +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + int dfu_err = nordic_dfu_img_init(); + if (dfu_err) { + LOG_ERR("dfu img init fail %d", dfu_err); transfer_response->status = SID_BULK_DATA_TRANSFER_ACTION_REJECT; transfer_response->reject_reason = SID_BULK_DATA_TRANSFER_REJECT_REASON_GENERIC; + transfer_response->scratch_buffer_size = 0; return; } - void *ptr = sid_hal_malloc(transfer_request->minimum_scratch_buffer_size); - if (ptr == NULL) { - LOG_ERR("Failed to alloc memory"); +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ + + transfer_response->scratch_buffer = scratch_buffer_create( + transfer_request->file_id, transfer_request->minimum_scratch_buffer_size); + + if (!transfer_response->scratch_buffer) { transfer_response->status = SID_BULK_DATA_TRANSFER_ACTION_REJECT; transfer_response->reject_reason = SID_BULK_DATA_TRANSFER_REJECT_REASON_NO_SPACE; + transfer_response->scratch_buffer_size = 0; return; } - memset(ptr, 0x0, sizeof(transfer_request->minimum_scratch_buffer_size)); - // accept all requests if only we have avaliable memory for scratch buffer - buffer_repo[repo_index].memory_slab_for_transfer = ptr; - buffer_repo[repo_index].file_id = transfer_request->file_id; transfer_response->status = SID_BULK_DATA_TRANSFER_ACTION_ACCEPT; transfer_response->reject_reason = SID_BULK_DATA_TRANSFER_REJECT_REASON_NONE; - transfer_response->scratch_buffer = ptr; transfer_response->scratch_buffer_size = transfer_request->minimum_scratch_buffer_size; } @@ -77,19 +65,35 @@ static void on_data_received(const struct sid_bulk_data_transfer_desc *const des JSON_OBJ(JSON_VAL_sid_bulk_data_transfer_desc("desc", desc))), JSON_NAME("data_size", JSON_INT(buffer->size)))))); - struct data_received_args *args = - (struct data_received_args *)sid_hal_malloc(sizeof(struct data_received_args)); - if (!args) { - LOG_ERR("Failed to allocate memory for received data descriptor"); + sidewalk_transfer_t *transfer = + (sidewalk_transfer_t *)sid_hal_malloc(sizeof(sidewalk_transfer_t)); + if (!transfer) { + LOG_ERR("Fail transfer alloc"); return; } - memset(args, 0x0, sizeof(*args)); - args->desc = *desc; - args->buffer = (struct sid_bulk_data_transfer_buffer *)buffer; - args->context = context; - int err = sidewalk_event_send(SID_EVENT_FILE_TRANSFER, args); + transfer->file_id = desc->file_id; + transfer->file_offset = desc->file_offset; + transfer->data = buffer->data; + transfer->data_size = buffer->size; + + int err = sidewalk_event_send(SID_EVENT_FILE_TRANSFER, transfer); if (err) { - sid_hal_free(args); + LOG_ERR("Event transfer err %d", err); + LOG_INF("Cancelig file transfer"); +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + err = nordic_dfu_img_cancel(); + if (err) { + LOG_ERR("Fail to complete dfu %d", err); + } +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ + sid_error_t ret = + sid_bulk_data_transfer_cancel((struct sid_handle *)context, + transfer->file_id, + SID_BULK_DATA_TRANSFER_REJECT_REASON_GENERIC); + if (ret != SID_ERROR_NONE) { + LOG_ERR("Fail to cancel sbdt %d", ret); + } + sid_hal_free(transfer); } } @@ -98,27 +102,52 @@ static void on_finalize_request(uint32_t file_id, void *context) printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( "on_finalize_request", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); - // Illustrative API indicating verification of file - // validate received file - - // always report success + // report transfer success sid_error_t ret = sid_bulk_data_transfer_finalize( (struct sid_handle *)context, file_id, SID_BULK_DATA_TRANSFER_FINAL_STATUS_SUCCESS); if (ret != SID_ERROR_NONE) { LOG_ERR("sid_bulk_data_transfer_finalize returned %s", SID_ERROR_T_STR(ret)); } + +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + // request upgrade and reboot + int err = 0; + err = nordic_dfu_img_finalize(); + if (err) { + LOG_ERR("dfu image finalize fail %d", err); + } + + err = sidewalk_event_send(SID_EVENT_REBOOT, NULL); + if (err) { + LOG_ERR("reboot event send ret %d", err); + } +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ } static void on_cancel_request(uint32_t file_id, void *context) { printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( "on_cancel_request", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); + +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + int err = nordic_dfu_img_cancel(); + if (err) { + LOG_ERR("Fail to complete dfu %d", err); + } +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ } static void on_error(uint32_t file_id, void *context) { printk(JSON_NEW_LINE(JSON_OBJ( JSON_NAME("on_error", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); + +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + int err = nordic_dfu_img_cancel(); + if (err) { + LOG_ERR("Fail to complete dfu %d", err); + } +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ } static void on_release_scratch_buffer(uint32_t file_id, void *context) @@ -126,16 +155,7 @@ static void on_release_scratch_buffer(uint32_t file_id, void *context) printk(JSON_NEW_LINE(JSON_OBJ(JSON_NAME( "on_release_scratch_buffer", JSON_OBJ(JSON_NAME("file_id", JSON_INT(file_id))))))); - for (size_t i = 0; i < PARALEL_TRANSFER_MAX; i++) { - if (buffer_repo[i].file_id == file_id) { - sid_hal_free(buffer_repo[i].memory_slab_for_transfer); - - buffer_repo[i].memory_slab_for_transfer = NULL; - buffer_repo[i].file_id = UINT32_MAX; - return; - } - } - LOG_ERR("failed to find file_id to be freed"); + scratch_buffer_remove(file_id); } static struct sid_bulk_data_transfer_event_callbacks ft_callbacks = { @@ -150,12 +170,13 @@ static struct sid_bulk_data_transfer_event_callbacks ft_callbacks = { void app_file_transfer_demo_init(struct sid_handle *handle) { - ft_callbacks.context = (void *)handle; + scratch_buffer_init(); - sid_error_t err = sid_bulk_data_transfer_init( + ft_callbacks.context = (void *)handle; + sid_error_t ret = sid_bulk_data_transfer_init( &(struct sid_bulk_data_transfer_config){ .callbacks = &ft_callbacks }, handle); - if (err != SID_ERROR_NONE) { - LOG_ERR("sid_bulk_data_transfer_init returned %s", SID_ERROR_T_STR(err)); + if (ret != SID_ERROR_NONE) { + LOG_ERR("sid_bulk_data_transfer_init returned %s", SID_ERROR_T_STR(ret)); } } diff --git a/samples/sid_end_device/src/sbdt/scratch_buffer.c b/samples/sid_end_device/src/sbdt/scratch_buffer.c new file mode 100644 index 0000000..4a61ce8 --- /dev/null +++ b/samples/sid_end_device/src/sbdt/scratch_buffer.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(scratch_buffer); + +#define SCRATCH_FILE_ID_UNUSED UINT32_MAX +#define SCRATCH_BUFFERS_MAX (3) + +typedef struct { + uint32_t id; + void *buffer; +} file_ctx_t; + +static file_ctx_t files[SCRATCH_BUFFERS_MAX]; + +void scratch_buffer_init(void) +{ + for (uint8_t i = 0; i < SCRATCH_BUFFERS_MAX; i++) { + files[i].id = SCRATCH_FILE_ID_UNUSED; + } +} + +void *scratch_buffer_create(uint32_t file_id, size_t size) +{ + file_ctx_t *p_file = NULL; + for (uint8_t i = 0; i < SCRATCH_BUFFERS_MAX; i++) { + if (!p_file && files[i].id == SCRATCH_FILE_ID_UNUSED) { + p_file = &files[i]; + } + if(files[i].id == file_id){ + LOG_ERR("buffer already assigned to file (id %d)", file_id); + return NULL; + } + } + + if (!p_file) { + LOG_ERR("too many buffers (max %d)", SCRATCH_BUFFERS_MAX); + return NULL; + } + + p_file->id = file_id; + + p_file->buffer = sid_hal_malloc(size); + if (!p_file->buffer) { + LOG_ERR("buffor alloc fail (size %d)", size); + return NULL; + } + + memset(p_file->buffer, 0x0, size); + + return p_file->buffer; +} + +void scratch_buffer_remove(uint32_t file_id) +{ + file_ctx_t *p_file = NULL; + for (uint8_t i = 0; i < SCRATCH_BUFFERS_MAX; i++) { + if (files[i].id == file_id) { + p_file = &files[i]; + break; + } + } + + if (!p_file) { + LOG_ERR("buffer not found for file (id %d)", file_id); + return; + } + + p_file->id = SCRATCH_FILE_ID_UNUSED; + if (p_file->buffer) { + sid_hal_free(p_file->buffer); + p_file->buffer = NULL; + } +} diff --git a/samples/sid_end_device/src/sensor_monitoring/app.c b/samples/sid_end_device/src/sensor_monitoring/app.c index 5817426..6a43a1d 100644 --- a/samples/sid_end_device/src/sensor_monitoring/app.c +++ b/samples/sid_end_device/src/sensor_monitoring/app.c @@ -71,6 +71,13 @@ static void on_sidewalk_msg_received(const struct sid_msg_desc *msg_desc, const static void on_sidewalk_msg_sent(const struct sid_msg_desc *msg_desc, void *context) { LOG_DBG("sent message(type: %d, id: %u)", (int)msg_desc->type, msg_desc->id); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc *msg_desc, @@ -78,6 +85,13 @@ static void on_sidewalk_send_error(sid_error_t error, const struct sid_msg_desc { LOG_ERR("Send message err %d", (int)error); LOG_DBG("Failed to send message(type: %d, id: %u)", (int)msg_desc->type, msg_desc->id); + sidewalk_msg_t *message = get_message_buffer(msg_desc->id); + if (message == NULL) { + LOG_ERR("failed to find message buffer to clean"); + return; + } + sid_hal_free(message->msg.data); + sid_hal_free(message); } static void on_sidewalk_factory_reset(void *context) @@ -151,10 +165,23 @@ static void on_sidewalk_status_changed(const struct sid_status *status, void *co static void sidewalk_btn_handler(uint32_t event) { - int err = sidewalk_event_send((app_event_t)event, NULL); + int err = sidewalk_event_send((sidewalk_event_t)event, NULL); if (err) { LOG_ERR("Send event err %d", err); + return; }; + + if (SID_EVENT_NORDIC_DFU == event) { + static bool in_dfu; + if (in_dfu) { + in_dfu = false; + k_timer_start(¬ify_timer, K_MSEC(NOTIFY_TIMER_DURATION_MS), + K_MSEC(CONFIG_SID_END_DEVICE_NOTIFY_DATA_PERIOD_MS)); + } else { + in_dfu = true; + k_timer_stop(¬ify_timer); + } + } } static int app_buttons_init(void) diff --git a/samples/sid_end_device/src/sensor_monitoring/app_tx.c b/samples/sid_end_device/src/sensor_monitoring/app_tx.c index f1ee8f2..6773184 100644 --- a/samples/sid_end_device/src/sensor_monitoring/app_tx.c +++ b/samples/sid_end_device/src/sensor_monitoring/app_tx.c @@ -46,9 +46,10 @@ static void state_notify_data(void *o); static void button_timer_cb(struct k_timer *timer_id); static const struct smf_state app_states[] = { - [STATE_APP_INIT] = SMF_CREATE_STATE(NULL, state_init, NULL), - [STATE_APP_NOTIFY_CAPABILITY] = SMF_CREATE_STATE(NULL, state_notify_capability, NULL), - [STATE_APP_NOTIFY_DATA] = SMF_CREATE_STATE(NULL, state_notify_data, NULL), + [STATE_APP_INIT] = SMF_CREATE_STATE(NULL, state_init, NULL, NULL, NULL), + [STATE_APP_NOTIFY_CAPABILITY] = + SMF_CREATE_STATE(NULL, state_notify_capability, NULL, NULL, NULL), + [STATE_APP_NOTIFY_DATA] = SMF_CREATE_STATE(NULL, state_notify_data, NULL, NULL, NULL), }; static uint8_t __aligned(4) @@ -179,7 +180,7 @@ static void state_notify_capability(void *o) app_sm_t *sm = (app_sm_t *)o; switch (sm->event) { - case APP_EVENT_NOTIFY_SENSOR: + case APP_EVENT_NOTIFY_SENSOR: { // Prepare message struct sid_demo_capability_discovery cap = { .link_type = last_link_mask_get(), @@ -219,7 +220,7 @@ static void state_notify_capability(void *o) } LOG_INF("Capability send"); - break; + } break; case APP_EVENT_CAPABILITY_SUCCESS: smf_set_state(SMF_CTX(sm), &app_states[STATE_APP_NOTIFY_DATA]); break; @@ -243,7 +244,7 @@ static void state_notify_data(void *o) int err = 0; switch (sm->event) { - case APP_EVENT_NOTIFY_BUTTON: + case APP_EVENT_NOTIFY_BUTTON: { // Read button state uint8_t button_arr[APP_BUTTONS_MAX] = { 0 }; uint8_t num_buttons = 0; @@ -299,8 +300,8 @@ static void state_notify_data(void *o) app_btn_pending_flag_clear(); LOG_INF("Notify button send"); - break; - case APP_EVENT_NOTIFY_SENSOR: + } break; + case APP_EVENT_NOTIFY_SENSOR: { // Read sensor data int16_t temp = 0; err = app_sensor_temperature_get(&temp); @@ -346,8 +347,8 @@ static void state_notify_data(void *o) } LOG_INF("Notify sensor send"); - break; - case APP_EVENT_RESP_LED_ON: + } break; + case APP_EVENT_RESP_LED_ON: { // Read led status uint8_t led_on_arr[APP_BUTTONS_MAX] = { 0 }; uint8_t num_leds_on = 0; @@ -374,8 +375,8 @@ static void state_notify_data(void *o) } LOG_INF("Response LED ON send"); - break; - case APP_EVENT_RESP_LED_OFF: + } break; + case APP_EVENT_RESP_LED_OFF: { // Read led status uint8_t led_off_arr[APP_BUTTONS_MAX] = { 0 }; uint8_t num_leds_off = 0; @@ -401,7 +402,7 @@ static void state_notify_data(void *o) } LOG_INF("Response LED OFF send"); - break; + } break; case APP_EVENT_TIME_SYNC_FAIL: smf_set_state(SMF_CTX(sm), &app_states[STATE_APP_NOTIFY_CAPABILITY]); case APP_EVENT_TIME_SYNC_SUCCESS: @@ -431,7 +432,7 @@ void app_tx_task(void *dummy1, void *dummy2, void *dummy3) k_timer_start(&button_timer, K_MSEC(APP_NOTIFY_BUTTON_PERIOD_MS), K_MSEC(APP_NOTIFY_BUTTON_PERIOD_MS)); - k_msgq_init(&app_sm.msgq, app_msgq_buff, sizeof(app_event_t), + k_msgq_init(&app_sm.msgq, (char *)app_msgq_buff, sizeof(app_event_t), CONFIG_SID_END_DEVICE_TX_THREAD_QUEUE_SIZE); smf_set_initial(SMF_CTX(&app_sm), &app_states[STATE_APP_INIT]); diff --git a/samples/sid_end_device/src/sidewalk.c b/samples/sid_end_device/src/sidewalk.c index bf9fde0..2dc8c79 100644 --- a/samples/sid_end_device/src/sidewalk.c +++ b/samples/sid_end_device/src/sidewalk.c @@ -3,34 +3,43 @@ * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#include +#include +#include -#include -#include +#ifdef CONFIG_SMTC_CLI +#include +extern volatile int csuso; +#endif /* CONFIG_SMTC_CLI */ +#include +#include #include -#include -#include -#include +#ifdef CONFIG_SIDEWALK_SUBGHZ_SUPPORT +#include +#endif /* CONFIG_SIDEWALK_SUBGHZ_SUPPORT */ +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER +#include +#include +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU +#include +#include +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ +#include // print hash only +#include // print hash only +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER */ #ifdef CONFIG_SID_END_DEVICE_CLI #include -#endif +#endif /* CONFIG_SID_END_DEVICE_CLI */ #ifdef CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK #include -#endif +#endif /* CONFIG_SID_END_DEVICE_PERSISTENT_LINK_MASK */ + #include #include +#include #include -#include -#ifdef CONFIG_SIDEWALK_SUBGHZ_SUPPORT -#include -#endif -#include - -#include -#ifdef CONFIG_SMTC_CLI -#include -extern volatile int csuso; -#endif /* CONFIG_SMTC_CLI */ +#include #ifdef CONFIG_SIDEWALK_LINK_MASK_BLE #define DEFAULT_LM (uint32_t)(SID_LINK_TYPE_1) @@ -47,7 +56,32 @@ LOG_MODULE_REGISTER(sidewalk_app, CONFIG_SIDEWALK_LOG_LEVEL); static struct k_thread sid_thread; K_THREAD_STACK_DEFINE(sid_thread_stack, CONFIG_SIDEWALK_THREAD_STACK_SIZE); -//extern uint32_t radio_dbg; +sys_slist_t pending_message_list = SYS_SLIST_STATIC_INIT(&pending_message_list); +K_MUTEX_DEFINE(pending_message_list_mutex); + +sidewalk_msg_t *get_message_buffer(uint16_t message_id) +{ + sidewalk_msg_t *pending_message; + sidewalk_msg_t *iterator; + int mutex_err = + k_mutex_lock(&pending_message_list_mutex, k_is_in_isr() ? K_NO_WAIT : K_FOREVER); + if (mutex_err != 0) { + LOG_ERR("Failed to lock mutex for message list"); + return NULL; + } + SYS_SLIST_FOR_EACH_CONTAINER_SAFE (&pending_message_list, pending_message, iterator, node) { + if (pending_message->desc.id == message_id) { + if (sys_slist_find_and_remove(&pending_message_list, + &pending_message->node) == false) { + LOG_ERR("Failed to remove pending message from list"); + }; + k_mutex_unlock(&pending_message_list_mutex); + return pending_message; + } + } + k_mutex_unlock(&pending_message_list_mutex); + return NULL; +} typedef struct sm_s { struct smf_ctx ctx; @@ -64,6 +98,7 @@ enum state { static void state_sidewalk_run(void *o); static void state_sidewalk_entry(void *o); +static void state_sidewalk_exit(void *o); static void state_dfu_entry(void *o); static void state_dfu_run(void *o); #ifdef CONFIG_SMTC_CLI @@ -72,10 +107,11 @@ static void state_smtc_run(void *o); #endif /* CONFIG_SMTC_CLI */ static const struct smf_state sid_states[] = { - [STATE_SIDEWALK] = SMF_CREATE_STATE(state_sidewalk_entry, state_sidewalk_run, NULL), - [STATE_DFU] = SMF_CREATE_STATE(state_dfu_entry, state_dfu_run, NULL), + [STATE_SIDEWALK] = SMF_CREATE_STATE(state_sidewalk_entry, state_sidewalk_run, + state_sidewalk_exit, NULL, NULL), + [STATE_DFU] = SMF_CREATE_STATE(state_dfu_entry, state_dfu_run, NULL, NULL, NULL), #ifdef CONFIG_SMTC_CLI - [STATE_SMTC] = SMF_CREATE_STATE(state_smtc_entry, state_smtc_run, NULL), + [STATE_SMTC] = SMF_CREATE_STATE(state_smtc_entry, state_smtc_run, NULL, NULL, NULL), #endif /* CONFIG_SMTC_CLI */ }; @@ -94,7 +130,7 @@ static void state_sidewalk_entry(void *o) (radio_lr11xx_device_config_t *)get_radio_cfg(), #else (radio_sx126x_device_config_t *)get_radio_cfg(), -#endif /* CONFIG_RADIO_LR11XX */ +#endif /* CONFIG_RADIO_LR11XX */ #endif /* CONFIG_SIDEWALK_SUBGHZ_SUPPORT */ }; @@ -132,7 +168,6 @@ static void state_sidewalk_entry(void *o) (SID_LINK_TYPE_3 & sm->sid->config.link_mask) ? "LoRa" : (SID_LINK_TYPE_2 & sm->sid->config.link_mask) ? "FSK" : "BLE"); - e = sid_init(&sm->sid->config, &sm->sid->handle); if (e) { LOG_ERR("sid init err %d", (int)e); @@ -146,10 +181,7 @@ static void state_sidewalk_entry(void *o) } #endif /* CONFIG_SMTC_CLI */ -#ifdef CONFIG_SIDEWALK_FILE_TRANSFER - app_file_transfer_demo_init(sm->sid->handle); -#endif - e = sid_start(sm->sid->handle, sm->sid->config.link_mask); // state_sidewalk_entry() + e = sid_start(sm->sid->handle, sm->sid->config.link_mask); if (e) { LOG_ERR("sid start err %d", (int)e); } @@ -181,6 +213,16 @@ static void state_sidewalk_entry(void *o) #endif /* CONFIG_SID_END_DEVICE_AUTO_CONN_REQ */ #endif /* CONFIG_SIDEWALK_AUTO_START */ + +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + int dfu_err = boot_write_img_confirmed(); + if (dfu_err) { + LOG_ERR("img confirm fail %d", dfu_err); + } +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER + app_file_transfer_demo_init(((sm_t *)o)->sid->handle); +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER */ } static void state_sidewalk_run(void *o) @@ -190,9 +232,7 @@ static void state_sidewalk_run(void *o) switch (sm->event.id) { case SID_EVENT_SIDEWALK: { - //sid_pal_gpio_write(radio_dbg, 0); e = sid_process(sm->sid->handle); - //sid_pal_gpio_write(radio_dbg, 1); if (e) { LOG_ERR("sid process err %d", (int)e); } @@ -249,7 +289,7 @@ static void state_sidewalk_run(void *o) #ifdef CONFIG_SIDEWALK_FILE_TRANSFER app_file_transfer_demo_init(sm->sid->handle); #endif - e = sid_start(sm->sid->handle, sm->sid->config.link_mask); // state_sidewalk_run() SID_EVENT_LINK_SWITCH + e = sid_start(sm->sid->handle, sm->sid->config.link_mask); if (e) { LOG_ERR("sid start err %d", (int)e); } @@ -312,8 +352,13 @@ static void state_sidewalk_run(void *o) LOG_ERR("sid send err %d", (int)e); } LOG_DBG("sid send (type: %d, id: %u)", (int)p_msg->desc.type, p_msg->desc.id); - sid_hal_free(p_msg->msg.data); - sid_hal_free(p_msg); + int mutex_err = k_mutex_lock(&pending_message_list_mutex, K_FOREVER); + if (mutex_err != 0) { + LOG_ERR("Failed to lock mutex for message list"); + break; + } + sys_slist_append(&pending_message_list, &p_msg->node); + k_mutex_unlock(&pending_message_list_mutex); } break; case SID_EVENT_CONNECT: { if (!(sm->sid->config.link_mask & SID_LINK_TYPE_1)) { @@ -327,17 +372,20 @@ static void state_sidewalk_run(void *o) } break; case SID_EVENT_FILE_TRANSFER: { #ifdef CONFIG_SIDEWALK_FILE_TRANSFER - struct data_received_args *args = (struct data_received_args *)sm->event.ctx; - if (!args) { + sidewalk_transfer_t *transfer = (sidewalk_transfer_t *)sm->event.ctx; + if (!transfer) { LOG_ERR("File transfer event data is NULL"); break; } - LOG_INF("Received file Id %d; buffer size %d; file offset %d", args->desc.file_id, - args->buffer->size, args->desc.file_offset); + + LOG_INF("Received file Id %d; buffer size %d; file offset %d", transfer->file_id, + transfer->data_size, transfer->file_offset); + + // print data hash uint8_t hash_out[32]; sid_pal_hash_params_t params = { .algo = SID_PAL_HASH_SHA256, - .data = args->buffer->data, - .data_size = args->buffer->size, + .data = transfer->data, + .data_size = transfer->data_size, .digest = hash_out, .digest_size = sizeof(hash_out) }; @@ -354,15 +402,46 @@ static void state_sidewalk_run(void *o) LOG_INF("SHA256: %s", hex_str); } - sid_error_t ret = sid_bulk_data_transfer_release_buffer( - sm->sid->handle, args->desc.file_id, args->buffer); - if (ret != SID_ERROR_NONE) { - LOG_ERR("sid_bulk_data_transfer_release_buffer returned %s", - SID_ERROR_T_STR(ret)); +#ifdef CONFIG_SIDEWALK_FILE_TRANSFER_DFU + int err = nordic_dfu_img_write(transfer->file_offset, transfer->data, + transfer->data_size); + + if (err) { + LOG_ERR("Fail to write img %d", err); + err = nordic_dfu_img_cancel(); + if (err) { + LOG_ERR("Fail to complete dfu %d", err); + } + e = sid_bulk_data_transfer_cancel( + sm->sid->handle, transfer->file_id, + SID_BULK_DATA_TRANSFER_REJECT_REASON_FILE_TOO_BIG); + if (e != SID_ERROR_NONE) { + LOG_ERR("sbdt cancel ret %s", SID_ERROR_T_STR(e)); + } + + sid_hal_free(transfer); + break; } - sid_hal_free(args); +#endif /* CONFIG_SIDEWALK_FILE_TRANSFER_DFU */ + const struct sid_bulk_data_transfer_buffer sbdt_buffer = { + .data = transfer->data, + .size = transfer->data_size, + }; + e = sid_bulk_data_transfer_release_buffer(sm->sid->handle, transfer->file_id, + &sbdt_buffer); + if (e != SID_ERROR_NONE) { + LOG_ERR("sbdt release ret %s", SID_ERROR_T_STR(e)); + } + + // free event context + sid_hal_free(transfer); #endif /* CONFIG_SIDEWALK_FILE_TRANSFER */ } break; + case SID_EVENT_REBOOT: { + LOG_INF("Rebooting..."); + LOG_PANIC(); + sys_reboot(SYS_REBOOT_WARM); + } break; case SID_EVENT_LAST: break; } @@ -399,6 +478,25 @@ static void state_smtc_run(void *o) } #endif /* CONFIG_SMTC_CLI */ +static void state_sidewalk_exit(void *o) +{ + int mutex_err = k_mutex_lock(&pending_message_list_mutex, K_FOREVER); + if (mutex_err != 0) { + LOG_ERR("Failed to lock mutex for message list"); + return; + } + sys_snode_t *list_element = sys_slist_get(&pending_message_list); + + while (list_element != NULL) { + sidewalk_msg_t *message = SYS_SLIST_CONTAINER(list_element, message, node); + sid_hal_free(message->msg.data); + sid_hal_free(message); + + list_element = sys_slist_get(&pending_message_list); + } + k_mutex_unlock(&pending_message_list_mutex); +} + static void state_dfu_entry(void *o) { sm_t *sm = (sm_t *)o; @@ -437,6 +535,7 @@ static void state_dfu_run(void *o) case SID_EVENT_LINK_SWITCH: case SID_EVENT_SIDEWALK: case SID_EVENT_FILE_TRANSFER: + case SID_EVENT_REBOOT: LOG_INF("Operation not supported in DFU mode"); break; case SID_EVENT_LAST: @@ -489,8 +588,17 @@ int sidewalk_event_send(sidewalk_event_t event, void *ctx) .ctx = ctx, }; - const int result = k_msgq_put(&sid_sm.msgq, (void *)&ctx_event, K_NO_WAIT); + k_timeout_t timeout = K_NO_WAIT; + +#ifdef CONFIG_SIDEWALK_THREAD_QUEUE_TIMEOUT + if (!k_is_in_isr()) { + timeout = K_MSEC(CONFIG_SIDEWALK_THREAD_QUEUE_TIMEOUT_VALUE); + } +#endif /* CONFIG_SIDEWALK_THREAD_QUEUE_TIMEOUT */ + + const int result = k_msgq_put(&sid_sm.msgq, (void *)&ctx_event, timeout); LOG_DBG("sidewalk_event_send event = %d (%s), context = %p, k_msgq_put result %d", event, SIDEWALK_EVENT_T_STR(event), ctx, result); + return result; } diff --git a/samples/sid_end_device/sysbuild/ipc_radio/prj.conf b/samples/sid_end_device/sysbuild/ipc_radio/prj.conf new file mode 100644 index 0000000..654a36b --- /dev/null +++ b/samples/sid_end_device/sysbuild/ipc_radio/prj.conf @@ -0,0 +1,35 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +# Bluetooth +CONFIG_BT=y +CONFIG_BT_HCI_RAW=y +CONFIG_BT_CTLR_ASSERT_HANDLER=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=n +CONFIG_BT_MAX_CONN=1 +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 + +# IPC +CONFIG_IPC_SERVICE=y +CONFIG_MBOX=y + +# Debug +CONFIG_LOG=n +CONFIG_SERIAL=n +CONFIG_ASSERT=y +CONFIG_DEBUG_INFO=y +CONFIG_EXCEPTION_STACK_TRACE=y +CONFIG_RESET_ON_FATAL_ERROR=y + +# ipc_radio +CONFIG_IPC_RADIO_BT=y +CONFIG_IPC_RADIO_BT_HCI_IPC=y diff --git a/samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf new file mode 100644 index 0000000..c333f7b --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.conf @@ -0,0 +1,10 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Configure QSPI for external flash +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 diff --git a/samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000..6ea6421 --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + zephyr,code-partition = &boot_partition; + nordic,pm-ext-flash = &mx25r64; + }; +}; diff --git a/samples/sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 0000000..96cb33e --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,28 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 + +CONFIG_BOOT_SWAP_USING_MOVE=n +# Multi-image updates do not support image swapping yet. +CONFIG_BOOT_UPGRADE_ONLY=y + +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +# Enable custom command to erase settings partition. +CONFIG_ENABLE_MGMT_PERUSER=y +CONFIG_ZCBOR=y +CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y diff --git a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay similarity index 77% rename from samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay rename to samples/sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay index 055f044..6ea6421 100644 --- a/samples/SWTL001/child_image/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -4,8 +4,9 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ - / { +/ { chosen { + zephyr,code-partition = &boot_partition; nordic,pm-ext-flash = &mx25r64; }; }; diff --git a/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf new file mode 100644 index 0000000..16bd5b0 --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n + +# Workaroud: fprotect and watchdog feed +# are not supported in NCS v2.6.0 +CONFIG_FPROTECT=n +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay new file mode 100644 index 0000000..6220cb2 --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_2_1.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* There is no aditional config needed for this version of PDK */ diff --git a/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay new file mode 100644 index 0000000..8edfb64 --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/nrf54l15pdk_nrf54l15_cpuapp_0_3_0.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + + +/* Application does not use cpuflpr core. Assign whole RRAM to cpuapp. */ +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; +}; diff --git a/samples/SWTL001/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf b/samples/sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf similarity index 93% rename from samples/SWTL001/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf rename to samples/sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf index a0db3d5..561529f 100644 --- a/samples/SWTL001/child_image/mcuboot/boards/thingy53_nrf5340_cpuapp.conf +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.conf @@ -4,9 +4,6 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # -# Increase main stack size -CONFIG_MAIN_STACK_SIZE=10240 - # Configure MCUboot features CONFIG_NRF53_MULTI_IMAGE_UPDATE=y CONFIG_BOOT_UPGRADE_ONLY=y diff --git a/samples/sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay b/samples/sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay new file mode 100644 index 0000000..6ea6421 --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/boards/thingy53_nrf5340_cpuapp.overlay @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + zephyr,code-partition = &boot_partition; + nordic,pm-ext-flash = &mx25r64; + }; +}; diff --git a/samples/sid_end_device/sysbuild/mcuboot/prj.conf b/samples/sid_end_device/sysbuild/mcuboot/prj.conf new file mode 100644 index 0000000..0a05853 --- /dev/null +++ b/samples/sid_end_device/sysbuild/mcuboot/prj.conf @@ -0,0 +1,39 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +CONFIG_MAIN_STACK_SIZE=10240 + +CONFIG_BOOT_SWAP_SAVE_ENCTLV=n +CONFIG_BOOT_BOOTSTRAP=n +CONFIG_PM=n + +CONFIG_FLASH=y +CONFIG_FPROTECT=y + +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Use minimal C library instead of the Picolib +CONFIG_MINIMAL_LIBC=y + +# Disable logs +CONFIG_NCS_BOOT_BANNER=n +CONFIG_CONSOLE=n +CONFIG_SERIAL=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n +CONFIG_LOG=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_BOOT_BANNER=n + +# Bootloader size optimization +CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_GPIO=n +CONFIG_TIMESLICING=n +CONFIG_MULTITHREADING=n +CONFIG_TICKLESS_KERNEL=n +CONFIG_TIMEOUT_64BIT=n +CONFIG_NRF_ENABLE_ICACHE=n diff --git a/v261_nRF52840_sidewalk_lr11xx.diff b/v261_nRF52840_sidewalk_lr11xx.diff deleted file mode 100644 index 972f19d..0000000 --- a/v261_nRF52840_sidewalk_lr11xx.diff +++ /dev/null @@ -1,106 +0,0 @@ -diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' '--exclude=version.conf' sidewalk/Kconfig.dependencies sidewalk_with_lr/Kconfig.dependencies ---- sidewalk/Kconfig.dependencies 2024-05-23 13:53:14.017676539 -0700 -+++ sidewalk_with_lr/Kconfig.dependencies 2024-05-23 13:52:39.704938865 -0700 -@@ -172,7 +172,7 @@ - - config SIDEWALK_GPIO_MAX - int -- default 6 if SIDEWALK_SUBGHZ_SUPPORT -+ default 12 if SIDEWALK_SUBGHZ_SUPPORT - default 0 - help - Maximum number of GPIO assigned for use with Sidewalk GPIO API -diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' '--exclude=version.conf' sidewalk/subsys/config/common/CMakeLists.txt sidewalk_with_lr/subsys/config/common/CMakeLists.txt ---- sidewalk/subsys/config/common/CMakeLists.txt 2024-05-23 13:53:14.045677140 -0700 -+++ sidewalk_with_lr/subsys/config/common/CMakeLists.txt 2024-05-23 10:14:18.792084848 -0700 -@@ -8,6 +8,8 @@ - - zephyr_library_sources_ifdef(CONFIG_SIDEWALK src/app_ble_config.c) - --zephyr_library_sources_ifdef(CONFIG_SIDEWALK_SUBGHZ_SUPPORT src/app_subGHz_config.c) -+if(NOT CONFIG_RADIO_LR11XX) -+ zephyr_library_sources_ifdef(CONFIG_SIDEWALK_SUBGHZ_SUPPORT src/app_subGHz_config.c) -+endif() - zephyr_library_sources_ifndef(CONFIG_SIDEWALK_SUBGHZ_SUPPORT src/app_subGHz_config_empty.c) - -diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' '--exclude=version.conf' sidewalk/subsys/config/common/include/app_subGHz_config.h sidewalk_with_lr/subsys/config/common/include/app_subGHz_config.h ---- sidewalk/subsys/config/common/include/app_subGHz_config.h 2024-05-23 13:53:14.045677140 -0700 -+++ sidewalk_with_lr/subsys/config/common/include/app_subGHz_config.h 2024-05-23 09:39:15.946953949 -0700 -@@ -16,11 +16,19 @@ - #ifndef APP_900_CONFIG_H - #define APP_900_CONFIG_H - -+#if defined(CONFIG_RADIO_LR11XX) -+#include -+#else - #include -+#endif - #include - #include - --const radio_sx126x_device_config_t* get_radio_cfg(void); -+#if defined(CONFIG_RADIO_LR11XX) -+const radio_lr11xx_device_config_t *get_radio_cfg(void); -+#else -+const radio_sx126x_device_config_t *get_radio_cfg(void); -+#endif - const sid_pal_mfg_store_region_t* get_mfg_cfg(void); - struct sid_sub_ghz_links_config* app_get_sub_ghz_config(void); - #endif -diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' '--exclude=version.conf' sidewalk/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h sidewalk_with_lr/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h ---- sidewalk/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h 2024-05-23 13:53:14.045677140 -0700 -+++ sidewalk_with_lr/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h 2024-05-23 10:23:45.460148547 -0700 -@@ -17,14 +17,22 @@ - #define SID_PAL_PLATFORM_INIT_TYPES_H - - #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) -+#if defined(CONFIG_RADIO_LR11XX) -+#include -+#else - #include --#endif -+#endif /* CONFIG_RADIO_LR11XX */ -+#endif /* CONFIG_SIDEWALK_SUBGHZ_SUPPORT */ - - typedef struct { - //place holder for platform specific init parameters - #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) -+#if defined(CONFIG_RADIO_LR11XX) -+ radio_lr11xx_device_config_t * radio_cfg; -+#else - radio_sx126x_device_config_t * radio_cfg; --#endif -+#endif /* CONFIG_RADIO_LR11XX */ -+#endif /* CONFIG_SIDEWALK_SUBGHZ_SUPPORT */ - } platform_specific_init_parameters_t; - - #endif -diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' '--exclude=version.conf' sidewalk/subsys/sal/sid_pal/src/sid_common.c sidewalk_with_lr/subsys/sal/sid_pal/src/sid_common.c ---- sidewalk/subsys/sal/sid_pal/src/sid_common.c 2024-05-23 13:53:14.049677227 -0700 -+++ sidewalk_with_lr/subsys/sal/sid_pal/src/sid_common.c 2024-05-23 10:20:59.916632544 -0700 -@@ -12,8 +12,12 @@ - #endif /* defined(CONFIG_SOC_SERIES_NRF53X) && defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) */ - - #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) -+#if defined(CONFIG_RADIO_LR11XX) -+#include -+#else - #include --#endif -+#endif /* CONFIG_RADIO_LR11XX */ -+#endif /* CONFIG_SIDEWALK_SUBGHZ_SUPPORT */ - - #include - #include -@@ -31,7 +35,11 @@ - return SID_ERROR_INCOMPATIBLE_PARAMS; - } - #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) -+#if defined(CONFIG_RADIO_LR11XX) -+ set_radio_lr11xx_device_config(platform_init_parameters->radio_cfg); -+#else - set_radio_sx126x_device_config(platform_init_parameters->radio_cfg); -+#endif - #if defined(CONFIG_SOC_SERIES_NRF53X) - (void)bt_enable(NULL); - (void)bt_disable(); diff --git a/v270_sidewalk_lr11xx.diff b/v270_sidewalk_lr11xx.diff new file mode 100644 index 0000000..b791137 --- /dev/null +++ b/v270_sidewalk_lr11xx.diff @@ -0,0 +1,89 @@ +diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' sidewalk/subsys/config/common/CMakeLists.txt lr_sidewalk/subsys/config/common/CMakeLists.txt +--- sidewalk/subsys/config/common/CMakeLists.txt 2024-07-09 14:02:55.256569753 -0700 ++++ lr_sidewalk/subsys/config/common/CMakeLists.txt 2024-07-08 14:18:06.339408639 -0700 +@@ -8,6 +8,8 @@ + + zephyr_library_sources_ifdef(CONFIG_SIDEWALK src/app_ble_config.c) + +-zephyr_library_sources_ifdef(CONFIG_SIDEWALK_SUBGHZ_SUPPORT src/app_subGHz_config.c) ++if(NOT DEFINED CONFIG_RADIO_LR11XX) ++ zephyr_library_sources_ifdef(CONFIG_SIDEWALK_SUBGHZ_SUPPORT src/app_subGHz_config.c) ++endif() + zephyr_library_sources_ifndef(CONFIG_SIDEWALK_SUBGHZ_SUPPORT src/app_subGHz_config_empty.c) + +diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' sidewalk/subsys/config/common/include/app_subGHz_config.h lr_sidewalk/subsys/config/common/include/app_subGHz_config.h +--- sidewalk/subsys/config/common/include/app_subGHz_config.h 2024-07-09 14:02:55.256569753 -0700 ++++ lr_sidewalk/subsys/config/common/include/app_subGHz_config.h 2024-07-08 14:16:00.309593858 -0700 +@@ -16,11 +16,16 @@ + #ifndef APP_900_CONFIG_H + #define APP_900_CONFIG_H + +-#include + #include + #include +- ++#ifdef CONFIG_RADIO_LR11XX ++#include ++const radio_lr11xx_device_config_t* get_radio_cfg(void); ++#else ++#include + const radio_sx126x_device_config_t* get_radio_cfg(void); ++#endif ++ + const sid_pal_mfg_store_region_t* get_mfg_cfg(void); + struct sid_sub_ghz_links_config* app_get_sub_ghz_config(void); + #endif +diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' sidewalk/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h lr_sidewalk/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h +--- sidewalk/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h 2024-07-09 14:02:55.256569753 -0700 ++++ lr_sidewalk/subsys/sal/common/sid_pal_ifc/sid_pal_platform_init_types.h 2024-07-08 14:24:11.326089757 -0700 +@@ -17,13 +17,21 @@ + #define SID_PAL_PLATFORM_INIT_TYPES_H + + #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) +-#include ++ #if defined(CONFIG_RADIO_LR11XX) ++ #include ++ #else ++ #include ++ #endif + #endif + + typedef struct { + //place holder for platform specific init parameters + #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) ++ #if defined(CONFIG_RADIO_LR11XX) ++ radio_lr11xx_device_config_t * radio_cfg; ++ #else + radio_sx126x_device_config_t * radio_cfg; ++ #endif + #endif + } platform_specific_init_parameters_t; + +diff -urN '--exclude=.git' '--exclude=build' '--exclude=.github' sidewalk/subsys/sal/sid_pal/src/sid_common.c lr_sidewalk/subsys/sal/sid_pal/src/sid_common.c +--- sidewalk/subsys/sal/sid_pal/src/sid_common.c 2024-07-09 14:02:55.256569753 -0700 ++++ lr_sidewalk/subsys/sal/sid_pal/src/sid_common.c 2024-07-08 14:20:32.697876640 -0700 +@@ -12,7 +12,11 @@ + #endif /* defined(CONFIG_SOC_SERIES_NRF53X) && defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) */ + + #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) +-#include ++ #if defined(CONFIG_RADIO_LR11XX) ++ #include ++ #else ++ #include ++ #endif + #endif + + #include +@@ -31,7 +35,11 @@ + return SID_ERROR_INCOMPATIBLE_PARAMS; + } + #if defined(CONFIG_SIDEWALK_SUBGHZ_SUPPORT) ++ #if defined(CONFIG_RADIO_LR11XX) ++ set_radio_lr11xx_device_config(platform_init_parameters->radio_cfg); ++ #else + set_radio_sx126x_device_config(platform_init_parameters->radio_cfg); ++ #endif + #if defined(CONFIG_SOC_SERIES_NRF53X) + (void)bt_enable(NULL); + (void)bt_disable(); diff --git a/west.yml b/west.yml index d78e68d..e7f0f37 100644 --- a/west.yml +++ b/west.yml @@ -15,12 +15,12 @@ manifest: - name: nrf remote: ncs repo-path: sdk-nrf - revision: v2.6.1 + revision: v2.7.0 import: true - name: sidewalk remote: ncs repo-path: sdk-sidewalk - revision: v2.6.1 + revision: v2.7.0 - name: lbm431 remote: lora-net repo-path: SWSD006